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?
CodePudding user response:
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.