-1

I have timestamp as 2020-12-03T05:35:59.398+0000 in String format being recieved in a streaming batch.

I want only 2020-12-03 05:35:59 as java.sql.Timestamp instance in order to be able to compare it with other Timestamp instance.

Getting following error with Timestamp.valueOf() function:

Exception in thread "main" java.time.format.DateTimeParseException : Text '2020-12-03T05:35:59.398+0000' could not be parsed at index 23

I tried the answer given here , and conversion did happen but the time was changed to 2020-12-03 11:05:59

I have tried changing between the formats given here but still no solution.

Is there even a format for timestamp with wierd + in between 398+0000?

Harshvardhan Solanki
  • 647
  • 2
  • 11
  • 32
  • 1
    comments are welcome for negative vote on the question – Harshvardhan Solanki Dec 03 '20 at 16:58
  • Please check now after edit. – Harshvardhan Solanki Dec 04 '20 at 05:09
  • 1
    Can you avoid using `Timestamp`? That class is poorly and confusingly designed and long outdated. If you tell us where you got the other `Timestamp` from, we may be able to offer better alternatives substituting with (or converting to) classes from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) like for example `OffsetDateTIme`. – Ole V.V. Dec 04 '20 at 05:39
  • Other Timestamp to which I am comparing to is also String type. Basically I am using Timestamp.valueOf only to convert String to TImestamp. – Harshvardhan Solanki Dec 04 '20 at 05:47
  • 2
    It sounds like you have 2 dates of type `String` and you want to convert them to a Date/Time type in order to invoke comparison methods on them. Is that correct? If so I'd recommend using the `java.time` library and stay away from `java.sql.Timestamp`, which is a thin wrapper around the old and outdated `java.util.Date`. – jwvh Dec 04 '20 at 06:19
  • 1
    *but the time was changed to `2020-12-03 11:05:59`*. No, it was not changed. It was just printed in your time zone (probably Asia/Kolkata). – Ole V.V. Dec 04 '20 at 06:27
  • @OleV.V. If it so then how can I check the Time Zone from Timestap and update it accordingly? – Harshvardhan Solanki Dec 04 '20 at 06:38
  • @jwvh Yeah. I am comparing timestamps – Harshvardhan Solanki Dec 04 '20 at 06:39
  • Cant do much about problem X as it is an input from raw unstructured data. So problem Y only needs to be enhanced. – Harshvardhan Solanki Dec 04 '20 at 08:21

3 Answers3

3

java.time

I recommend that you use java.time, the modern Java date and time API, for your date and time work.

    DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
    DateTimeFormatter sqlTimestampFormatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(' ')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .toFormatter();
    
    String aTimestampString = "2020-12-03T05:35:59.398+0000";
    String anotherTimestampString = "2020-12-04 06:43:58.556385";
    
    Instant anInstant = isoFormatter.parse(aTimestampString, Instant::from);
    Instant anotherInstant = LocalDateTime.parse(anotherTimestampString, sqlTimestampFormatter)
            .atOffset(ZoneOffset.UTC)
            .toInstant();
    
    if (anInstant.isBefore(anotherInstant)) {
        System.out.println(aTimestampString + " is earlier");
    } else {
        System.out.println(anotherTimestampString + " is earlier");
    }

Output from this example is:

2020-12-03T05:35:59.398+0000 is earlier

The +0000 in the former string above is an offset from UTC — an offset of 00 hours 00 minutes. Since it is zero, we know that the time is in UTC. I don’t know the time zone or UTC offset of the other string. You need to know, or you will get incorrect results. In the code above I have assumed that the other string is in UTC too.

Don’t use Timestamp

I tried the answer given here, and conversion did happen but the time was changed to 2020-12-03 11:05:59

This is how confusing the Timestamp class is. You got the correct timestamp value. What happens when you print the Timestamp object, is that you are (implicitly or explicitly) calling its toString method. Timestamp.toString() confusingly uses the default time zone of your JVM for rendering the string. So if your timestamp is equal to 2020-12-03T05:35:59.398 UTC and your time zone is, say, Asia/Kolkata, then time is converted to Asia/Kolkata time zone and the string 2020-12-03 11:05:59 is returned and printed.

You have nothing good to use the old-fashioned java.sql.Timestamp class for. It was originally meant for transferring timestamp values with and without time zone to and from SQL databases. Since JDBC 4.2 we prefer OffsetDateTime, Instant and LocalDateTime for that purpose. So just forget about the Timestamp class.

Link

Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
2

Is there even a format for timestamp with wierd + in between 398+0000?

The 398 part is fraction-of-second (millisecond) while the +0000 part is the zone offset part.

You can parse 2020-12-03T05:35:59.398+0000 into an OffsetDateTime using the format pattern, uuuu-MM-dd'T'HH:mm:ss.SSSX.

Demo:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        System.out.println(odt);
    }
}

Output:

2020-12-03T05:35:59.398Z

Check the DateTimeFormatter documentation page to learn more about the letters used for formatting.

You can use isBefore and isAfter functions of OffsetDateTime to compare its two instances.

Demo:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        OffsetDateTime odtNow = OffsetDateTime.now();
        System.out.println(odtNow.isBefore(odt));
        System.out.println(odtNow.isAfter(odt));
    }
}

Output:

false
true

Learn more about the modern date-time API at Trail: Date Time. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API. Since java.sql.Timestamp extends java.util.Date, it is recommended to stop using that as well. However, for any reason, if you still want to use conversion between the modern and the legacy date-time API, use Instant as the bridge.

import java.sql.Timestamp;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String text = "2020-12-03T05:35:59.398+0000";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
        Instant instant = odt.toInstant();
        Timestamp timestamp = new Timestamp(instant.toEpochMilli());
        System.out.println(timestamp);
    }
}

Output:

2020-12-03 05:35:59.398
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

You can use a custom DateFormatter for non-standard formats. Here is a working example for your use case.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

import static java.time.temporal.ChronoField.*;

public class Main {

    private static final DateTimeFormatter INPUT_NON_STANDARD_FORMAT;

    static {
        INPUT_NON_STANDARD_FORMAT =
                new DateTimeFormatterBuilder()
                        .parseCaseInsensitive()
                        .append(DateTimeFormatter.ISO_LOCAL_DATE)
                        .appendLiteral('T')
                        .appendValue(HOUR_OF_DAY, 2)
                        .appendLiteral(':')
                        .appendValue(MINUTE_OF_HOUR, 2)
                        .optionalStart()
                        .appendLiteral(':')
                        .appendValue(SECOND_OF_MINUTE, 2)
                        .optionalStart()
                        .appendLiteral('.')
                        .appendValue(MILLI_OF_SECOND, 3)
                        .appendLiteral("+0000")
                        .toFormatter();
    }

    public static void main(String[] args) {
        String timestamp = "2020-12-03T05:35:59.398+0000";
        final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
        LocalDateTime localDateTime = LocalDateTime.parse(timestamp, INPUT_NON_STANDARD_FORMAT);
        System.out.println(localDateTime.format(dateTimeFormatter));
    }
}

Output

2020-12-03 05:35:59
Tushar
  • 670
  • 3
  • 14
  • 1
    Beware of hte case of format pattern letters. Uppercase `YYYY` will give you surprises around New Year. – Ole V.V. Dec 04 '20 at 08:34