Статьи

Проклятие чешуекрылых: игра с java.time

Поп-викторина: Каким будет результат этой маленькой программы?

01
02
03
04
05
06
07
08
09
10
11
12
13
public class DateFun {
  
    public static void main(String[] args) {
        long hours = getHoursOfDay(LocalDate.now(), ZoneId.systemDefault());
        System.out.println(hours);
    }
  
    private static long getHoursOfDay(LocalDate date, ZoneId zoneId) {
        ZonedDateTime startOfDay = date.atStartOfDay(zoneId);
        Duration duration = Duration.between(startOfDay, startOfDay.plusDays(1));
        return duration.toHours();
    }
}

Ответ, как и в большинстве интересных вопросов, «это зависит». Как это может зависеть? Что ж, давайте попробуем несколько примеров:

  • getHoursOfDay(LocalDate.of(2014, 7, 15), ZoneId.of("Asia/Colombo")) возвращает 24 . Как и ожидалось
  • getHoursOfDay(LocalDate.of(2014, 7, 15), ZoneId.of("Europe/Oslo")) также возвращает 24 .
  • Но вот забавная версия: getHoursOfDay(LocalDate.of(2014, 3, 30), ZoneId.of("Europe/Oslo")) возвращает 23 ! Это летнее время.
  • Аналогично: getHoursOfDay(LocalDate.of(2014, 10, 26), ZoneId.of("Europe/Oslo")) также возвращает 25
  • И, конечно, внизу все перевернуто: getHoursOfDay(LocalDate.of(2014, 10, 5), ZoneId.of("Australia/Melbourne")) дает 23.
  • За исключением, конечно, в Квинсленде: getHoursOfDay(LocalDate.of(2014, 10, 5), ZoneId.of("Australia/Queensland")) => 24.

Летнее время: проклятие программистов!

Установлено летнее время с заявленной целью повышения производительности труда путем предоставления большего количества рабочих часов со светом. Многочисленные исследования не смогли доказать, что он работает как задумано.

Вместо этого, когда я изучил историю летнего времени в Норвегии, оказалось, что его лоббировал игрок в гольф и сборщик бабочек («чешуекрылые»), чтобы они могли лучше заниматься своими увлечениями после рабочего дня. Таким образом, название этого блога.

Большую часть времени вы можете игнорировать летнее время. Но когда ты не можешь, это может действительно кусать тебя сзади. Например: как выглядит почасовая выработка плана электропитания в день, который меняется с летнего времени на стандартное время? Еще один пример, приведенный мне коллегой: телепрограммы. Оказывается, некоторые телеканалы просто не могут показывать программы в течение дополнительного часа осенью. Или они покажут один и тот же час программирования дважды.

Может помочь Joda-Time API, а теперь и Java 8 time API java.time. Если вы используете это правильно. Вот код для отображения таблицы значений в час:

1
2
3
4
5
6
7
8
void displayHourlyTable(LocalDate date, ZoneId zoneId) {
    ZonedDateTime startOfDay = date.atStartOfDay(zoneId);
    ZonedDateTime end = startOfDay.plusDays(1);
  
    for (ZonedDateTime current = startOfDay; current.isBefore(end); current = current.plusHours(1)) {
        System.out.println(current.toLocalTime() + ": " + current.toInstant());
    }
}

Учитывая 2014/10/26 и Осло, это печатает:

1
2
3
4
5
6
00:00: 2014-10-25T22:00:00Z
01:00: 2014-10-25T23:00:00Z
02:00: 2014-10-26T00:00:00Z
02:00: 2014-10-26T01:00:00Z
03:00: 2014-10-26T02:00:00Z
....

И на 2014/3/30, это печатает:

1
2
3
4
5
00:00: 2014-03-29T23:00:00Z
01:00: 2014-03-30T00:00:00Z
03:00: 2014-03-30T01:00:00Z
04:00: 2014-03-30T02:00:00Z
....

Итак, если вы когда-нибудь будете писать код, подобный следующему: for (int hour=0; hour<24; hour++) doSomething(midnight.plusHours(hour)); Вы можете пересмотреть! Этот код будет (вероятно) ломаться два раза в год.

На первый взгляд, время — легкая концепция. Когда вы начинаете изучать детали, есть причина, по которой библиотека java.time содержит 20 классов (если вы не учитываете подпакеты). При правильном использовании вычисления времени просты. При неправильном использовании вычисления времени выглядят простыми, но содержат незначительные ошибки.

В следующий раз, возможно, мне стоит поразмышлять над тонкостями номеров недель.