Issue
I have a function which returns the current UTC time in Date
object, simple. What we do is find current Instant
of UTC, put it in Date
object and return. Note: Local Host time here is New York/America.
The problem we are now facing is that Date refuses to store March 13 2:02 AM, since the time doesn't exist (clocks skip an hour from 2 AM to 3 AM on second Sunday of March in NY), but the same exists in UTC, and we want UTC time.
Is there any way to represent "20220313 02:02:00.000"
in java Date object.
This is when New York time(local) is "20220312 21:02.00.000"
public static Date getCurrUtc() throws ParseException {
Instant instant = Instant.now();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
Date date = new Date();
int year = ldt.getYear(),
month = ldt.getMonthValue() - 1,
day = ldt.getDayOfMonth(),
hour = ldt.getHour(),
minute = ldt.getMinute(),
second = ldt.getSecond(),
milliseconds = (int) date.getTime() % 1000;
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
isoFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
date = isoFormat.parse(String.format("%d-%d-%dT%d:%d:%d.%d", year, month, day, hour, minute, second, milliseconds));
return date;
}
Solution
You are currently mixing calls to two very different APIs (old and outdated java.util.Date
and classes from java.time
, like LocalDateTime
). I would stick to the newer API because it makes life a lot easier if you want to express the same instant in two different time zones.
You can still use the LocalDateTime
in order to parse the value from the String
and then add a ZoneId
to make it represent a real moment in time.
Here's an example:
public static void main(String[] args) {
// your example datetime String
String datetime = "20220313 02:02:00.000";
// a pattern representing the format of your datetime String
String formatPattern = "uuuuMMdd HH:mm:ss.SSS";
// a formatter using that pattern (can parse and format)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(formatPattern);
// the two time zones involved
ZoneId americaNewYork = ZoneId.of("America/New_York");
ZoneId utc = ZoneId.of("UTC");
// the date and time of day without any zone or offset
LocalDateTime zonelessDateTime = LocalDateTime.parse(datetime, dtf);
// that date and time of day in New York
ZonedDateTime newYorkTime = zonelessDateTime.atZone(americaNewYork);
System.out.println(newYorkTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// the same instant in UTC (same date, different time of day, no offset
ZonedDateTime utcTime = zonelessDateTime.atZone(utc);
System.out.println(utcTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
As you can see in the output, the hours of day are different and so is the zone:
2022-03-13T03:02:00-04:00[America/New_York]
2022-03-13T02:02:00Z[UTC]
If you have to produce/consume java.util.Date
s, you can make use of compatibility methods that have been implemented for reasons like yours: Dealing with a considerable large amount of legacy code.
Short: A java.time.ZonedDateTime
and a java.time.OffsetDateTime
are representations of moments in time, instants. Fortunately, theres a
java.time.Instant, too, and you can convert a
java.util.Datefrom/to an
Instant. There's
Date.from(Instant)and
Date.toInstant(). Here's how you would use the results of the code example above in order to have the values as
Date`:
// convert the Instants represented by the ZonedDateTimes to Dates
Date newYorkDate = Date.from(newYorkTime.toInstant());
Date utcDate = Date.from(utcTime.toInstant());
// print the Date values
System.out.println(newYorkDate);
System.out.println(utcDate);
These lines would produce the following output:
Sun Mar 13 08:02:00 CET 2022
Sun Mar 13 03:02:00 CET 2022
Please have a closer look at the values of the java.util.Date
s.
Zones implicitly changed and values adjusted (even though a Date
does not really have a zone). You have basically no real control over zone shifts and time conversions.
There are several reasons for a totally new and different datetime API introduced in Java 8… The mentioned is just one of them.
Is there any way to represent "20220313 02:02:00.000" in java Date object?
Yes, there is… You could create the Date
and return it. How this Date
instance will be represented as String
depends on the TimeZone
used. See this:
// create the date
Date date = Date.from(
LocalDateTime.parse("20220313 02:02:00.000", dtf)
.atZone(utc)
.toInstant()
);
// get the milliseconds since 1970-01-01, the only numerical value stored by a Date
long epochMillis = date.getTime();
// create a format for visualization
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
// add a time zone to the format
isoFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
// and use it to print the date in that zone
System.out.println(epochMillis + " in New York: " + isoFormat.format(date));
// set the time zone of the format to UTC
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
// and use it to print the date in a different zone
System.out.println(epochMillis + " in UTC: " + isoFormat.format(date));
The output is the following, mind that the same Date
is used:
1647136920000 in New York: 2022-03-12T21:02:00.000
1647136920000 in UTC: 2022-03-13T02:02:00.000
OK, the format is not stored in the Date
variable, but the underlying value is, at least, which makes it representable in different formats that have different time zones.
Thing to think about:
Do you think the time zone should be part of the format rather than part of a datetime object itself? The answer to this question could be mapped to the question Do you want to use java.util.Date
or java.time
? ;-)
Answered By - deHaar
Answer Checked By - Timothy Miller (JavaFixing Admin)