How can I achieve the following in Java 8 java.time
:
DateTime dt = new DateTime(year, month, date, hours, min, sec, milli);
But I have found this option:
OffsetDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond, offset)
But it has only method without milliseconds.
How can I achieve the same as Joda-Time?
Best Answer
To adjust the milliseconds to nanoseconds, just multiply the milliseconds value by 1000000 (1 million).
To get the correct offset, though, it's a little bit tricky.
Joda's
DateTime
uses the system's default timezone when you don't specify one. So, injava.time
, it's natural to think that it's just a matter of usingZoneId.systemDefault()
(the system's default timezone) to get the valid offset for that date - and this will work for most times, but there are some corner cases that you must be aware of.Timezones may have different offsets during history - nowadays, most of these changes occur due to Daylight Saving Time (DST), but it can also happen because some government/administration decided to change it.
Example: in 1985, the brazilian state of Acre had standard offset
UTC-05:00
(andUTC-04:00
during DST), then in 1988 it was onUTC-05:00
without DST, then in 2008 the standard changed toUTC-04:00
(and no DST), and since 2013 it's back toUTC-05:00
and no DST. This may sound like an extreme case, but changes like that can occur at anytime/anywhere.All of this just to make it clear that an offset depends on both the timezone and the date. And you'll have to make some decisions when facing corner cases.
For this example, I'll use my default timezone, which is
America/Sao_Paulo
(here we have DST every year). Currently, our offset is-03:00
, but in October 15th 2017, at midnight, the clocks will shift 1 hour forward (from midnight to 1 AM) and the offset will be changed to-02:00
.In this case, the period between midnight and 1 AM is "skipped" and any local time between them is invalid (this is called a gap). Actually, if you try to create such date and time in Joda-Time, it'll throw an exception:
This throws:
But in
java.time
, theZonedDateTime
class adjusts it to the next valid offset:The value of
z
is:Note that it adjusted 00:10 to the next valid offset (
-02:00
) and the time was also adjusted accordingly to 01:10. You can then do:to get your
OffsetDateTime
(in this case, the value will be2017-10-15T01:10-02:00
).When DST ends it's also complicated. In São Paulo, DST will end in February 18th, 2018. At midnight, the clocks will shift back 1 hour (to February 17th at 11 PM) and the offset will be back to
-03:00
.So, the period between February 17th at 23 PM and February 18th at midnight will exist twice (first in
-02:00
offset, then in-03:00
offset - that's called an overlap). For the local times between 11 PM and midnight, there will be 2 valid offsets, so you'll have to decide which one to use.Fortunately,
ZonedDateTime
provides the 2 options: you can usewithEarlierOffsetAtOverlap()
to get the offset before the DST change (in my case, it'll be-02:00
) andwithLaterOffsetAtOverlap()
to get the offset after the DST change (in my case, it'll be-03:00
):The output will be:
Note how the methods
with...OffsetAtOverlap
get the offsets before and after the DST change - also note that the local time 23:30 exists twice, in 2 different offsets (-02:00
and-03:00
). Once you choose which one to use, you can calltoOffsetDateTime()
to get yourOffsetDateTime
instance:beforeDSTChange
will be2018-02-17T23:30-02:00
andafterDSTChange
will be2018-02-17T23:30-03:00
.By default,
ZonedDateTime
creates the date/time with the earlier offset, but I believe it's better to make it explicit which one you're using, by calling the corresponding method. And in cases where only one offset is valid, both methods return the same thing.By the way, I've tested this same date in Joda-Time 2.9.9 and it gets the earlier offset
-02:00
:And the
DateTime
class also has the methodswithEarlierOffsetAtOverlap()
andwithLaterOffsetAtOverlap()
that work in similar way toZonedDateTime
.Of course for most times you can just use the
ZonedDateTime
without worries - DST and any other offset changes don't occur all the time. But it's important to be aware of these corner cases and adjust it accordingly.One additional warning: instead of using
ZoneId.systemDefault()
, you can make it explicit which timezone you want, by usingZoneId.of("zonename")
. The API uses IANA timezones names (always in the formatRegion/City
, likeAmerica/Sao_Paulo
orEurope/Berlin
). Avoid using the 3-letter abbreviations (likeCST
orPST
) because they are ambiguous and not standard.You can get a list of available timezones (and choose the one that fits best your system) by calling
ZoneId.getAvailableZoneIds()
. It's better to make it explicit what timezone you're using, because the system's default can be changed without notice, even at runtime.If you have both Joda's
DateTime
andjava.time
available, you can also convert from one to another.You can get the epoch millis value from the Joda's
DateTime
to create ajava.time.Instant
. Then you create aZoneId
with the same ID of theDateTime
's zone, and get the respective offset for theInstant
and join the pieces to create theOffsetDateTime
:The result will be
2018-02-17T23:30-02:00
. Note that theDateTime
object uses the earlier offset (as explained above), but you can optionally used.withLaterOffsetAtOverlap().getMillis()
if you want to use the later offset.Just reminding that in cases of a DST gap, Joda-Time throws an exception, as previously explained.