简体   繁体   中英

java.util.Calendar not updating the timezone properly

The goal of this method is to take a utc date, convert it to the timezone specified and return the DateTimeValue object. However we recently found a bug with this method when using it with certain timezones.

private static DateTimeValue toDateTimeValue(Date endDate, TimeZone timeZone) {     
    Calendar cal = Calendar.getInstance();
    cal.setTime(endDate);
    cal.setTimeZone(timeZone);
    cal.set(Calendar.HOUR_OF_DAY, 23); // move to the end of the day
    cal.set(Calendar.MINUTE, 59);
    cal.set(Calendar.SECOND, 59);
    int year = cal.get(Calendar.YEAR);
    int month = cal.get(Calendar.MONTH) + 1;
    int day = cal.get(Calendar.DAY_OF_MONTH);  //ends up being 3 but should be 4
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    int minute = cal.get(Calendar.MINUTE);
    int second = cal.get(Calendar.SECOND);

    return new DateTimeValueImpl(year, month, day, hour, minute, second);
}

Main case illustrating bug:

  • endDate value: Mon Oct 03 21:00:00 UTC 2022
  • timezone value: Europe/Helsinki +3

In the method above the Day value ends up being the 3rd, but it should be the 4th since Oct 03 21:00:00 in UTC is actually Oct 4th in the Helsinki timezone

I did some further testing with this code in place of that method.

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    String utcDate = sdf.format(endDate); 
    System.out.println(utcDate);   //2022-10-03 09:00:00

    sdf.setTimeZone(timeZone);
    String timeZoneDate = sdf.format(endDate);
    System.out.println(timeZoneDate);   //2022-10-04 12:00:00
    
        

So this shows the correct/expected results however this is a string, and I need it as a DateTimeValue .

Why does java.util.calendar not update the date (the day) when we set the timezone to Helsinki?

The java.util date-time API is outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API .

The following demo shows how easily and cleanly you could do/test it by using the modern date-time API.

Demo :

public class Main {
    public static void main(String[] args) {
        ZonedDateTime endDate = ZonedDateTime.of(LocalDate.of(2022, 10, 3), LocalTime.of(21, 0), ZoneOffset.UTC);
        ZonedDateTime zdtDesired = endDate.withZoneSameInstant(ZoneId.of("Europe/Helsinki"));
        System.out.println(zdtDesired);
        System.out.println(zdtDesired.getDayOfMonth());
    }
}

Output :

2022-10-04T00:00+03:00[Europe/Helsinki]
4

How to convert java.util.Date into ZonedDateTime ?

You can convert java.util.Date into Instant which can be converted into ZonedDateTime . It means you do not even need to use ZonedDateTime#withZoneSameInstant as shown in the above demo.

public class Main {
    public static void main(String[] args) {
        // In your case, it will be endDate.toInstant()
        Instant instant = new Date().toInstant();
        ZonedDateTime zdtDesired = instant.atZone(ZoneId.of("Europe/Helsinki"));
        System.out.println(zdtDesired);
    }
}

Output :

2022-09-30T21:57:50.487+03:00[Europe/Helsinki]

Learn more about the the modern date-time API from Trail: Date Time .

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