简体   繁体   中英

Java - convert timestamp to Date and back to timestamp changes the date

I'm creating a string out from current time and I wanted to convert it to timestamp again, but the thing is, that it's subtracts 2 hours while converting.

This is the steps I'm doing -

    DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
                .append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
    long ts = Clock.systemUTC().millis();
    System.out.println(ts);
    Instant instant = Instant.ofEpochMilli(ts);
    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
    String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());


    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    try {
        long timestamp = simpleDateFormat.parse(str).getTime();
        System.out.println(timestamp);
    } catch (ParseException e) {
        e.printStackTrace();
    }

output - 1639065502667 1639058302000

(2021-12-09 15:58:22 2021-12-09 13:58:22)

why is the diff of the 2 hours? how can I parse it so that the outputs will be equal?

The dateTimeFomatter builder uses format without milliseconds and without timezone.
That's why the str value contain no information about them.

Then simpleDateFormat.parse(str) uses timezone of JVM which is UTC+02:00 in this case.

Trace what is going on:

Instant instant = Instant.now(); 
    // => 2021-12-09 15:58:22.798 +00:00

String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
    // => "2021-12-09 15:58:22"

simpleDateFormat.parse(str);
    // => 2021-12-09 15:58:22.000 +02:00

You just need to fix the pattern (add millis .SSS and timezone XXX parts) to make the results consistent as expected:

DateTimeFormatter.ofPattern("HH:mm:ss.SSSXXX")

// and something similar for SimpleDateFormat if needed

Parsing Instant from a custom formatted string.

This example shows how to parse Instant from serialized time assuming that there is a fixed timezone for all cases.

var serializedDateTime = "2020-01-01 10:20:30";
var zoneId = ZoneOffset.UTC; // may be any other zone
    
var format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
var instant = LocalDateTime
                 .parse(serializedDateTime, format)
                    // LocalDateTime is unzoned, just representation of (date + time) numbers in a single object 
                 .atZone(zoneId)  
                    // apply a zone to get ZonedDateTime (point in space-time on Earth)
                 .toInstant(); 
                    // convert to Instant (the same point adjusted to UTC+00:00)

The answer was only setting the timezone to UTC -

DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
            .append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());


SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
    *******
    simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    *******
    long timestamp = simpleDateFormat.parse(str).getTime();
    System.out.println(timestamp);
} catch (ParseException e) {
    e.printStackTrace();
}

Understand that a date with time-of-day is inherently ambiguous, is not a moment.

If you are tracking 4 PM on the 9th, we do not know if that means 4 PM in Tokyo Japan, 4 PM in Toulouse France, or 4 PM in Toledo Ohio US — three very different moments that happen several hours apart.

LocalDateTime ldt = LocalDateTime.parse( "2021-12-09 16:00:00" ) ;

To track a moment, a point on the timeline, you must place ne date-with-time in the context of an offset from UTC or of a time zone.

An offset is merely a number of hours-minutes-seconds ahead or behind the baseline of modern timekeeping, the prime meridian at Royal Observatory, Greenwich.

A time zone is much more. A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region. Each zone has a name in format of Continent/Region such as Europe/Berlin or Asia/Tokyo .

To track moments as seen in UTC, with an offset of zero, use Instant .

Instant instant = Instant.now() ;

To see that same moment through the wall-clock time used by people in a region, apply a ZoneId to get a ZonedDateTime .

ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

As for your use of SimpleDateFormat , Date , and Calendar , don't. Avoid these legacy date-time classes. Hey were designed by people who did not understand date-time handling. They were supplanted years ago by the modern java.time classes defined in JSR 310. Sun, Oracle, and the JCP community all gave up on those classes. I suggest you do the same.

In your code:

long ts = Clock.systemUTC().millis();
Instant instant = Instant.ofEpochMilli(ts);

That is the same as doing this:

Instant.now().truncatedTo( ChronoUnit.MILLISECONDS )

In your code:

ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);

(A) When working with mere offsets rather than time zones, use OffsetDateTime class. The ZonedDateTime class is for working with time zones.

(B) A briefer way to adjust from Instant to a zoned moment was shown above:

myInstant.atZone( z )

Let me guess, your timezone is UTC+2 ?

simpleDateFormat.parse(str) assume that your date in current system timezone, but it is in UTC.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM