Как я могу вернуть LocalDate.now() в миллисекундах?

Я создаю дату сейчас:

ZoneId gmt = ZoneId.of("GMT");
LocalDateTime localDateTime = LocalDateTime.now();
LocalDate localDateNow = localDateTime.toLocalDate();

Затем я хочу вернуть эту дату в миллисекундах:

localDateNow.atStartOfDay(gmt) - 22.08.2017
localDateNow.atStartOfDay(gmt).toEpochSecond(); - 1503360000 (18.01.70)

Как я могу вернуть LocalDate.now() в миллисекундах?


person user5620472    schedule 22.08.2017    source источник
comment
Как дата может иметь миллисекунды? Миллисекунды являются частью метки времени, а не даты   -  person Jens    schedule 22.08.2017
comment
@Jens localDateTime.atZone(gmt).toEpochSecond() возвращает 1503403331 (18.01.70) в   -  person user5620472    schedule 22.08.2017
comment
О чем вы говорите LocalDate или LocalDateTime? Вы спрашиваете LocalDate и приводите пример для LocalDateTime.   -  person Jens    schedule 22.08.2017
comment
Вместо вызова toEpochSecond() вызовите toInstant().toEpochMilli()   -  person JB Nizet    schedule 22.08.2017
comment
@JB Низе Спасибо! то что нужно!   -  person user5620472    schedule 22.08.2017


Ответы (1)


Вызов toInstant().toEpochMilli(), как предложено в комментарии @JB Nizet , является правильным ответом, но есть небольшая и хитрая деталь об использовании местных дат, о которой вы должны знать.

Но перед этим еще несколько мелких деталей:

  • Вместо ZoneId.of("GMT") можно использовать встроенную константу ZoneOffset.UTC. Они эквивалентны, но нет необходимости создавать дополнительные избыточные объекты, если API уже предоставляет объект, который делает то же самое.
  • Вместо того, чтобы вызывать LocalDateTime.now(), а затем .toLocalDate(), вы можете напрямую вызвать LocalDate.now() — они эквивалентны.

Теперь сложные детали: когда вы вызываете метод now() (либо для LocalDateTime, либо для LocalDate), он использует часовой пояс JVM по умолчанию для получения значений текущей даты, и это значение может отличаться в зависимости от часового пояса, настроенного в JVM.

В JVM, которую я использую, часовой пояс по умолчанию — America/Sao_Paulo, а местное время — 09:37. Таким образом, LocalDate.now() возвращает 2017-08-22 (22th августа 2017 г.).

Но если я изменю часовой пояс по умолчанию на Pacific/Kiritimati, он вернет 2017-08-23. Это потому, что в Киритимати сейчас уже 23го августа 2017 года (и местное время там, на момент, когда я это пишу, 02:37< /сильный>).

Итак, если я запускаю этот код, когда часовой пояс по умолчанию равен Pacific/Kiritimati:

LocalDate dtNow = LocalDate.now(); // 2017-08-23
System.out.println(dtNow.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli());

Результат:

1503446400000

Что эквивалентно полуночи 23th августа 2017 года по всемирному координированному времени.

Если я запущу тот же код, когда часовой пояс по умолчанию America/Sao_Paulo, результат будет таким:

1503360000000

Что эквивалентно полуночи 22th августа 2017 года по всемирному координированному времени.

Использование now() делает ваш код зависимым от часового пояса JVM по умолчанию. И эта конфигурация может быть изменена без предварительного уведомления, даже во время выполнения, что приведет к тому, что ваш код будет возвращать разные результаты при таких изменениях.

И вам не нужен такой крайний случай (например, кто-то неправильно настроил JVM на «очень дальний» часовой пояс). В моем случае, например, в часовом поясе America/Sao_Paulo, если я запущу код в 23:00, LocalDate вернет 22августа, но текущая дата в формате UTC уже будет 23августа. Это связано с тем, что 23:00 в Сан-Паулу совпадают с 2:00 следующего дня по UTC:

// August 22th 2017, at 11 PM in Sao Paulo
ZonedDateTime z = ZonedDateTime.of(2017, 8, 22, 23, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2017-08-22T23:00-03:00[America/Sao_Paulo]
System.out.println(z.toInstant()); // 2017-08-23T02:00:00Z (in UTC is already August 23th)

Таким образом, использование LocalDate.now() не является гарантией того, что у меня всегда будет текущая дата в формате UTC.


Если вам нужна текущая дата в формате UTC (независимо от часового пояса JVM по умолчанию) и установите время на полночь, лучше использовать ZonedDateTime:

// current date in UTC, no matter what the JVM default timezone is 
ZonedDateTime zdtNow = ZonedDateTime.now(ZoneOffset.UTC);
// set time to midnight and get the epochMilli
System.out.println(zdtNow.with(LocalTime.MIDNIGHT).toInstant().toEpochMilli());

Результат:

1503360000000

Что эквивалентно полуночи 22th августа 2017 года по всемирному координированному времени.

Другой альтернативой является передача часового пояса в LocalDate.now, чтобы он мог получить правильные значения для текущей даты в указанной зоне:

// current date in UTC, no matter what the JVM default timezone is 
LocalDate dtNowUtc = LocalDate.now(ZoneOffset.UTC);
// set time to midnight and get the epochMilli
System.out.println(dtNow.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli());
person Community    schedule 22.08.2017