Reputation: 6456
I am confused with time handling in java time. I so long worked under the assumption that if a timestamp is specified as a zulu time, java would take care of the offset with regards to local time.
To illustrate. I am currently in BST which has an offset of UTC +1. With that in mind, I would expect this zulu time:
2016-09-12T13:15:17.309Z
to be
2016-09-12T14:15:17.309
LocalDateTime after parsing it. This is because my default systemtime is set to BST and the above timestamp (zulu time) specifies that it is a UTC time.
Instead however consider this sample:
String ts = "2016-09-12T13:15:17.309Z";
LocalDateTime parse = LocalDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
System.out.println(parse);
This will print:
2016-09-12T13:15:17.309
So the timestamp, parsed as a LocalDateTime, is not recognised as UTC time and instead treated as localtime directly. So I thought, maybe I need to parse it as a ZonedDateTime and convert it to LocalDateTime specifically in order to get the correct local time. With this test:
String ts = "2016-09-12T13:15:17.309Z";
ZonedDateTime parse = ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
System.out.println(parse);
System.out.println(parse.toLocalDateTime());
I get the outputs:
2016-09-12T13:15:17.309Z
2016-09-12T13:15:17.309
Same output for both dates.
The only way to correctly parse this that I could find, is:
String ts = "2016-09-12T13:15:17.309Z";
Instant instant = Instant.parse(ts); // parses UTC
LocalDateTime ofInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
System.out.println(instant);
System.out.println(ofInstant);
This prints:
2016-09-12T13:15:17.309Z
2016-09-12T14:15:17.309
Which is correct.
So the question(s) are:
LocalDateTime#parse
approach to get the correct result? Instant
for everything now and discard the parsing? The issue is that jersey/jackson
's java time modules parse the timestamps using the ISO format and the regular LocalDateTime#parse
methods. I realised that my times are no off since they are being treated as LocalTime
while in fact they are in Zulu time.
Upvotes: 13
Views: 18158
Reputation: 338564
LocalDateTime
. Instant
& ZonedDateTime
.Example:
Instant.parse( "2016-09-12T13:15:17.309Z" )
.atZone( ZoneId.of( "Europe/London" ) )
.toString();
2016-09-12T14:15:17.309+01:00[Europe/London]
The Answer by Krüske is correct. You misunderstand the meaning of LocalDateTime
class. It does not represent the date-time of a particular locality. Just the opposite, it does not represent an actual moment at all.
I suggest thinking of Instant
as your basic building-block class in java.time. The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Your input string complies with the ISO 8601 format used by default in the Instant
class for both parsing and generating string representations. The Z
on the end is short for Zulu
and means UTC. No need to specify a formatting pattern.
Instant instant = Instant.parse( "2016-09-12T13:15:17.309Z" );
As a programmer you should learn to think and work in UTC primarily. Forget about your own time zone. Think of UTC as the One True Time. Apply a time zone as a variation, and only as needed.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as BST
or EST
or IST
as they are not true time zones, not standardized, and not even unique(!). If by BST
you meant British Summer Time, then the actual time zone name would be Europe/London
. The java.time classes will determine how to adjust for any anomalies including Daylight Saving Time (DST).
ZoneId z = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = instant.atZone( z );
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Upvotes: 11
Reputation: 1288
You are misunderstanding the purpose of LocalDateTime
.
To quote the class documentation:
A date-time without a time-zone in the ISO-8601 calendar system, such as {@code 2007-12-03T10:15:30}.
…
This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.
So it's explicit purpose is just to represent a date and time without a time-zone. It's porpose is not to represent a date and time in the local time zone.
Therefore each conversion just strips the time zone.
So for your purposes you need a ZonedDateTime
with ZoneId.systemDefault()
as you already used in your third example.
For your second example this could be:
String ts = "2016-09-12T13:15:17.309Z";
ZonedDateTime parse =
ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME)
.withZoneSameInstant(ZoneId.systemDefault());
System.out.println(parse);
System.out.println(parse.toLocalDateTime());
Upvotes: 19