Skip to content

Handle daylight savings time and representing UTC in Date object

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;
}

Answer

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.Dates, 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 anInstant. There's Date.from(Instant)andDate.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.Dates.
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? 😉