Как извлечь эпоху из LocalDate и LocalDateTime?

103

Как мне извлечь значение эпохи Longиз экземпляров LocalDateTimeили LocalDate? Я пробовал следующее, но это дает другие результаты:

LocalDateTime time = LocalDateTime.parse("04.02.2014  19:51:01", DateTimeFormatter.ofPattern("dd.MM.yyyy  HH:mm:ss"));
System.out.println(time.getLong(ChronoField.SECOND_OF_DAY)); // gives 71461
System.out.println(time.getLong(ChronoField.EPOCH_DAY)); // gives 16105

Я хочу просто значение 1391539861для локального datetime "04.02.2014 19:51:01". Мой часовой пояс - Europe/OsloUTC + 1 с переходом на летнее время.

Вадим Котов
источник
Пожалуйста, объясните свой ожидаемый номер 1396468261. Я получаю без коррекции часового пояса: 1391543461 (см. "Правка" в моем ответе). Разница в 57 дней!
Meno Hochschild
@MenoHochschild Я обновил свой вопрос информацией о часовом поясе и исправил фактическое значение с GTM на местное время. Есть ли более простой способ получить эпоху, LocalDateTimeчем вычислить ее вручную?
Вернувшись из паузы, смотри мое обновление.
Meno Hochschild

Ответы:

149

Классы LocalDateи LocalDateTimeне содержат информации о часовом поясе или смещении времени и секундах с начала эпохи без этой информации. Однако у объектов есть несколько методов для преобразования их в объекты даты / времени с часовыми поясами путем передачиZoneId экземпляра.

LocalDate

LocalDate date = ...;
ZoneId zoneId = ZoneId.systemDefault(); // or: ZoneId.of("Europe/Oslo");
long epoch = date.atStartOfDay(zoneId).toEpochSecond();

LocalDateTime

LocalDateTime time = ...;
ZoneId zoneId = ZoneId.systemDefault(); // or: ZoneId.of("Europe/Oslo");
long epoch = time.atZone(zoneId).toEpochSecond();
носид
источник
2
Можно ли избежать использования ZoneId или использовать с настраиваемым постоянным экземпляром ZoneId (равным +0, что означает GMT)? Я спрашиваю об этом, потому что хочу, чтобы к нему были нормализованы все расчеты. Кроме того, как мне сделать обратное: преобразовать время эпохи в LocalDate / LocalDateTime (также без ZoneId или с GMT)?
разработчик Android
2
Неважно. Нашел:ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))
разработчик android
7
Более простой подход подходит только long epoch = time.toEpochSecond(ZoneOffset.UTC)для случаев UTC, или когда вы уже знаете часовой пояс, или long epoch = time.toEpochSecond(ZoneId.systemDefault());если вы хотите пойти по этому маршруту.
Маркус
Это может показаться смешным, но как насчет обратного преобразования эпохи в LocalDate, LocalTime и LocalDateTime
Сэмюэл Оуино
Неважно, я думаю, что нашел его; Instant.ofEpochMilli(responseTime).atZone(ZoneId.systemDefault()).toLocalTime()
Сэмюэл Оуино,
27

«Миллис с эпохи unix» представляет собой мгновение, поэтому вам следует использовать класс Instant:

private long toEpochMilli(LocalDateTime localDateTime)
{
  return localDateTime.atZone(ZoneId.systemDefault())
    .toInstant().toEpochMilli();
}
Вернер Харниш
источник
его неправильно использовать, ZoneId.systemDefault()потому что эпоха unix относится к UTC
ACV
10

Требуемое преобразование требует смещения от UTC / Greewich или часового пояса.

Если у вас есть смещение, есть специальный метод на LocalDateTimeдля решения этой задачи:

long epochSec = localDateTime.toEpochSecond(zoneOffset);

Если у вас есть только ZoneId то вы можете получить его ZoneOffsetиз ZoneId:

ZoneOffset zoneOffset = ZoneId.of("Europe/Oslo").getRules().getOffset(ldt);

Но вы можете найти преобразование ZonedDateTimeпроще:

long epochSec = ldt.atZone(zoneId).toEpochSecond();
ДжодаСтивен
источник
4
Если вас интересует только UTC, то оно естьlong epochSec = localDateTime.toEpochSecond(ZoneOffset.UTC);
ruhong
2

Посмотрите на этот метод, чтобы узнать, какие поля поддерживаются. Вы найдете для LocalDateTime:

NANO_OF_SECOND 
NANO_OF_DAY 
MICRO_OF_SECOND 
MICRO_OF_DAY 
MILLI_OF_SECOND 
MILLI_OF_DAY 
SECOND_OF_MINUTE 
SECOND_OF_DAY 
MINUTE_OF_HOUR 
MINUTE_OF_DAY 
HOUR_OF_AMPM 
CLOCK_HOUR_OF_AMPM 
HOUR_OF_DAY 
CLOCK_HOUR_OF_DAY 
AMPM_OF_DAY 
DAY_OF_WEEK 
ALIGNED_DAY_OF_WEEK_IN_MONTH 
ALIGNED_DAY_OF_WEEK_IN_YEAR 
DAY_OF_MONTH 
DAY_OF_YEAR 
EPOCH_DAY 
ALIGNED_WEEK_OF_MONTH 
ALIGNED_WEEK_OF_YEAR 
MONTH_OF_YEAR 
PROLEPTIC_MONTH 
YEAR_OF_ERA 
YEAR 
ERA 

Поле INSTANT_SECONDS, конечно, не поддерживается, потому что a LocalDateTimeне может ссылаться ни на какую абсолютную (глобальную) метку времени. Но что полезно, так это поле EPOCH_DAY, которое подсчитывает количество дней, прошедших с 01.01.1970. Подобные мысли справедливы и для типа LocalDate(с еще менее поддерживаемыми полями).

Если вы намереваетесь получить несуществующее поле millis-Since-unix-epoch, вам также понадобится часовой пояс для преобразования из локального в глобальный тип. Это преобразование можно сделать намного проще, см. Другие сообщения SO .

Возвращаясь к вашему вопросу и цифрам в вашем коде:

The result 1605 is correct
  => (2014 - 1970) * 365 + 11 (leap days) + 31 (in january 2014) + 3 (in february 2014)
The result 71461 is also correct => 19 * 3600 + 51 * 60 + 1

16105L * 86400 + 71461 = 1391543461 секунд с 1970-01-01T00: 00: 00 (внимание, без часового пояса) Затем вы можете вычесть смещение часового пояса (обратите внимание на возможное умножение на 1000, если оно в миллисекундах).

ОБНОВЛЕНИЕ после указанной информации о часовом поясе:

local time = 1391543461 secs
offset = 3600 secs (Europe/Oslo, winter time in february)
utc = 1391543461 - 3600 = 1391539861

Как JSR-310-код с двумя эквивалентными подходами:

long secondsSinceUnixEpoch1 =
  LocalDateTime.of(2014, 2, 4, 19, 51, 1).atZone(ZoneId.of("Europe/Oslo")).toEpochSecond();

long secondsSinceUnixEpoch2 =
  LocalDate
    .of(2014, 2, 4)
    .atTime(19, 51, 1)
    .atZone(ZoneId.of("Europe/Oslo"))
    .toEpochSecond();
Мено Хохшильд
источник
1
Спасибо за объяснение. Получил правильный результат, используя LocalDateTime time = LocalDateTime.parse("04.02.2014 19:51:01", DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"));а затем Long epoch = time.atZone(ZoneId.of("Europe/Oslo")).toEpochSecond();.
1

Преобразование из удобочитаемой даты в эпоху :

long epoch = new java.text.SimpleDateFormat("MM/dd/yyyyHH:mm:ss").parse("01/01/1970 01:00:00").getTime() / 1000;

Преобразование эпохи в удобочитаемую дату :

String date = new java.text.SimpleDateFormat("MM/dd/yyyyHH:mm:ss").format(new java.util.Date (epoch*1000));

Для конвертера других языков: https://www.epochconverter.com

Михмик
источник
0

Это один из способов без использования часового пояса:

LocalDateTime now = LocalDateTime.now();
long epoch = (now.getLong(ChronoField.EPOCH_DAY) * 86400000) + now.getLong(ChronoField.MILLI_OF_DAY);
Элед Эванс
источник