Я пытаюсь найти разницу между двумя датами и временем, которые находятся в часовом поясе Аделаиды. Разница в часах возвращается правильно в случае начала летнего времени, но неверно для окончания летнего времени.
Согласно приведенному ниже выводу, в случае перехода на летнее время на 1 октября 2023 года разница в часах между 01:30 и 03:30 составляет 1, что, как и ожидалось, с учетом перехода на летнее время (время 02:30 не существует).
Но то же самое не работает в случае окончания летнего времени. На 2 апреля 2023 года разница в часах между 02:30 и 03:30 также должна быть равна 1, но результат равен 2 (120 минут).
Ниже тот же код и соответствующий вывод. Не могли бы вы сообщить мне, почему ZonedDateTime может обрабатывать только одну сторону изменения летнего времени, а не другую. Примечание. Использование Java 8.
public class DateHourDifference {
public static void main(String[] args) {
dstStart();
dstEnd();
}
private static void dstEnd() {
// When local daylight time is about to reach
// Sunday, 2 April 2023, 3:00:00 am clocks are turned backward 1 hour to
// Sunday, 2 April 2023, 2:00:00 am local standard time instead.
System.out.println("-------------DST End---------------------------------");
LocalDateTime schedulerRunTime = LocalDateTime.of(2023, 04, 02, 3, 30);
LocalDateTime lastStatusLogRecord = LocalDateTime.of(2023, 04, 02, 2, 30);
ZonedDateTime currentSchedulerRun = schedulerRunTime.atZone(ZoneId.of("Australia/Adelaide"));
ZonedDateTime lastSchedulerRun = lastStatusLogRecord.atZone(ZoneId.of("Australia/Adelaide"));
System.out.println("currentSchedulerRun " + currentSchedulerRun.toString());
System.out.println("lastSchedulerRun " + lastSchedulerRun.toString());
long deltaBetweenDatesInMinutes = ChronoUnit.MINUTES.between(lastSchedulerRun, currentSchedulerRun);
System.out.println("deltaBetweenDatesInMinutes " + deltaBetweenDatesInMinutes);
long deltaBetweenDatesInHours = ChronoUnit.HOURS.between(lastSchedulerRun, currentSchedulerRun);
System.out.println("deltaBetweenDatesInHours " + deltaBetweenDatesInHours);
}
private static void dstStart() {
// When local standard time is about to reach
// Sunday, 1 October 2023, 2:00:00 am clocks are turned forward 1 hour to
// Sunday, 1 October 2023, 3:00:00 am local daylight time instead.
System.out.println("---------------DST Start-------------------------------");
LocalDateTime schedulerRunTime = LocalDateTime.of(2023, 10, 01, 3, 30);
LocalDateTime lastStatusLogRecord = LocalDateTime.of(2023, 10, 01, 1, 30);
ZonedDateTime currentSchedulerRun = schedulerRunTime.atZone(ZoneId.of("Australia/Adelaide"));
ZonedDateTime lastSchedulerRun = lastStatusLogRecord.atZone(ZoneId.of("Australia/Adelaide"));
System.out.println("currentSchedulerRun " + currentSchedulerRun.toString());
System.out.println("lastSchedulerRun " + lastSchedulerRun.toString());
long deltaBetweenDatesInMinutes = ChronoUnit.MINUTES.between(lastSchedulerRun, currentSchedulerRun);
System.out.println("deltaBetweenDatesInMinutes " + deltaBetweenDatesInMinutes);
long deltaBetweenDatesInHours = ChronoUnit.HOURS.between(lastSchedulerRun, currentSchedulerRun);
System.out.println("deltaBetweenDatesInHours " + deltaBetweenDatesInHours);
}
}
---------------DST Start-------------------------------
currentSchedulerRun 2023-10-01T03:30+10:30[Australia/Adelaide]
lastSchedulerRun 2023-10-01T01:30+09:30[Australia/Adelaide]
deltaBetweenDatesInMinutes 60
deltaBetweenDatesInHours 1
-------------DST End---------------------------------
currentSchedulerRun 2023-04-02T03:30+09:30[Australia/Adelaide]
lastSchedulerRun 2023-04-02T02:30+10:30[Australia/Adelaide]
deltaBetweenDatesInMinutes 120
deltaBetweenDatesInHours 2
Ваша проблема в том, что 2:30 2 апреля неоднозначно. Через час после 2:30 по летнему времени снова 2:30, на этот раз стандартное время. Используйте lastSchedulerRun.withEarlierOffsetAtOverlap()
и lastSchedulerRun.withLaterOffsetAtOverlap()
, чтобы выбрать, какой из них вы получите (2023-04-02T02:30+10:30[Australia/Adelaide]
или 2023-04-02T02:30+09:30[Australia/Adelaide]
).
Когда часовой пояс установлен на java LocalDateTime, значение даты считается уже введенным/преобразованным в указанный TZ. Если в ТЗ есть настройки перехода на летнее время, он также считается уже добавленным/удаленным.
Для вашего случая: Летнее время в Австралии/Аделаиде начинается 1 октября в 02:00 и заканчивается 2 апреля в то же время.
Теперь я собираюсь объяснить, почему вывод правильный:
--------------- Начало летнего времени--------------------------------
currentSchedulerRun 2023-10-01T03:30+10:30[Австралия/Аделаида]
lastSchedulerRun 2023-10-01T01:30+09:30[Австралия/Аделаида]
deltaBetweenDatesInHours 1
Считается, что значение даты уже находится в TZ Аделаиды и к нему добавлено летнее время, поэтому исходное время без летнего времени составляет 02:30, что составляет ровно один час от 01:30.
----------------------------Конец летнего времени---------------------------------
currentSchedulerRun 2023-04-02T03:30+09:30[Австралия/Аделаида]
lastSchedulerRun 2023-04-02T02:30+10:30[Австралия/Аделаида]
deltaBetweenDatesInHours 2
Как и в приведенном выше случае, считается, что значение даты уже находится в TZ Аделаиды и летнее время удалено, поэтому исходное время с летним временем составляет 04:30, что составляет ровно два часа от 02:30.
Надеюсь, это поможет понять больше информации о часовых поясах Java.
Посмотрите, как часы будут переводиться вперед и назад в указанные даты:
Источник: timeanddate.com
02 апреля 2023 года: часы будут переведены на 02:00. В тот момент, когда время впервые достигнет 2:59:59, часы будут переведены обратно на 2:00:00 по стандартному времени, и они начнут отсчитывать 3 часа ночи во второй раз. Когда повторный час закончится, местное время изменится с 2:59:59 до 3:00:00, как и в любой другой день. Этот день длится 25 часов.
01 октября 2023 г.: час с 2:00:00 до 2:59:59 не будет существовать в ночь переключения. Часы будут переведены вперед с 1:59:59 по стандартному времени до 3:00:00 по летнему времени. Этот день длится 23 часа.
Учитывая это объяснение, следующий код ведет себя так, как ожидалось:
public class Main {
public static void main(String args[]) {
ZoneId zoneId = ZoneId.of("Australia/Adelaide");
ZonedDateTime schedulerRunTime = ZonedDateTime.of(LocalDateTime.of(2023, 4, 2, 3, 30), zoneId);
ZonedDateTime lastStatusLogRecord = ZonedDateTime.of(LocalDateTime.of(2023, 4, 2, 2, 30), zoneId);
System.out.println(ChronoUnit.HOURS.between(lastStatusLogRecord, schedulerRunTime));
schedulerRunTime = ZonedDateTime.of(LocalDateTime.of(2023, 10, 1, 3, 30), zoneId);
lastStatusLogRecord = ZonedDateTime.of(LocalDateTime.of(2023, 10, 1, 1, 30), zoneId);
System.out.println(ChronoUnit.HOURS.between(lastStatusLogRecord, schedulerRunTime));
}
}
Вывод:
2
1
0
перед десятичным целым числомВы использовали 01
, 02
, 03
и т. д., которые не вызвали проблем, просто случайно, потому что они меньше 8. Java рассматривает целое число, начинающееся с 0
, как целое число с основанием 8
, т. е. 08
и 09
не существуют ( диапазон восьмеричных цифр от 0 до 7). Если бы вы использовали что-то вроде LocalDate.of(2022, 08, 09)
, компилятор бы пожаловался.
Спасибо Арвинд за ответ. Таким образом, вы хотите сказать, что код возвращает 2, поскольку вывод в случае сценария окончания летнего времени является подтверждением/пониманием того, что время с 2:00 до 2:59 повторялось, чтобы указать повторяющийся час.
Для сценария окончания летнего времени, поскольку он возвращается как 2 часа, в моем списке временных меток есть 2 записи со значениями 02:30 и 02:30, и в качестве обходного пути я использую Set здесь, чтобы удалить дубликаты, но в идеале это не должно иметь было дело.