Como extrair epoch de LocalDate e LocalDateTime?

102

Como extraio o valor de época Longde instâncias de LocalDateTimeou LocalDate? Tentei o seguinte, mas obtive outros resultados:

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

O que eu quero é simplesmente o valor 1391539861da data e hora local "04.02.2014 19:51:01". Meu fuso horário é Europe/OsloUTC + 1 com horário de verão.

Vadim Kotov
fonte
Explique o seu número esperado 1396468261. Recebo sem correção de fuso horário: 1391543461 (consulte a edição em minha resposta). 57 dias de diferença!
Meno Hochschild de
@MenoHochschild Eu atualizei minha pergunta com informações de fuso horário e corrigi o valor real de GTM para hora local. Existe uma maneira mais fácil de obter a época de alguma LocalDateTimeoutra coisa do que calculá-la manualmente?
Voltando da minha pausa, veja minha atualização.
Meno Hochschild de

Respostas:

147

As classes LocalDatee LocalDateTimenão contêm informações sobre o fuso horário ou deslocamento de tempo , e os segundos desde a época seriam ambíguos sem essas informações. No entanto, os objetos têm vários métodos para convertê-los em objetos de data / hora com fusos horários, passando uma ZoneIdinstância.

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();
nosid
fonte
2
É possível evitar o uso de ZoneId ou usar com uma instância personalizada de ZoneId constante (de +0, que significa GMT)? Eu pergunto isso porque quero que todos os cálculos sejam normalizados para isso. Além disso, como faço o oposto: converter de epoch time em LocalDate / LocalDateTime (também sem ZoneId ou com GMT)?
desenvolvedor Android
2
Deixa pra lá. Encontrei:ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))
desenvolvedor Android de
7
Uma abordagem mais simples é apenas long epoch = time.toEpochSecond(ZoneOffset.UTC)para casos UTC, ou onde você já sabe o fuso horário, ou long epoch = time.toEpochSecond(ZoneId.systemDefault());se deseja seguir esse caminho.
Marcus
Isso pode parecer ridículo, mas que tal converter de volta de época para LocalDate, LocalTime & LocalDateTime
samuel owino
Não importa, eu acho que encontrei; Instant.ofEpochMilli(responseTime).atZone(ZoneId.systemDefault()).toLocalTime()
samuel owino
27

'Millis since unix epoch' representa um instante, então você deve usar a classe Instant:

private long toEpochMilli(LocalDateTime localDateTime)
{
  return localDateTime.atZone(ZoneId.systemDefault())
    .toInstant().toEpochMilli();
}
Werner Harnisch
fonte
é incorreto para usar ZoneId.systemDefault()porque unix epoch refere-se a UTC
ACV
10

A conversão de que você precisa requer o deslocamento de UTC / Greewich ou um fuso horário.

Se você tiver um deslocamento, existe um método dedicado on LocalDateTimepara esta tarefa:

long epochSec = localDateTime.toEpochSecond(zoneOffset);

Se você tiver apenas um ZoneId, poderá obter o ZoneOffsetde ZoneId:

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

Mas você pode encontrar conversão por meio de uma forma ZonedDateTimemais simples:

long epochSec = ldt.atZone(zoneId).toEpochSecond();
JodaStephen
fonte
4
Se você só se preocupa com UTC, existelong epochSec = localDateTime.toEpochSecond(ZoneOffset.UTC);
ruhong
2

Observe este método para ver quais campos são suportados. Você encontrará por 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 

O campo INSTANT_SECONDS - é claro - não é suportado porque um LocalDateTimenão pode se referir a nenhum carimbo de data / hora absoluto (global). Mas o que é útil é o campo EPOCH_DAY que conta os dias decorridos desde 01-01-1970. Pensamentos semelhantes são válidos para o tipo LocalDate(com campos ainda menos suportados).

Se você pretende obter o campo millis-since-unix-epoch não existente, você também precisa do fuso horário para converter de um tipo local em global. Essa conversão pode ser feita de maneira muito mais simples, consulte outros posts do SO .

Voltando à sua pergunta e aos números em seu código:

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 segundos desde 1970-01-01T00: 00: 00 (atenção, sem fuso horário) Então, você pode subtrair o deslocamento do fuso horário (cuidado com a possível multiplicação por 1000 se em milissegundos).

ATUALIZAR após determinadas informações de fuso horário:

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

Como código JSR-310 com duas abordagens equivalentes:

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();
Meno Hochschild
fonte
1
Obrigada pelo esclarecimento. Obteve o resultado certo usando LocalDateTime time = LocalDateTime.parse("04.02.2014 19:51:01", DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"));e então Long epoch = time.atZone(ZoneId.of("Europe/Oslo")).toEpochSecond();.
1

Converter de data legível por humanos em época :

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

Converter de época em data legível por humanos :

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

Para outro conversor de idioma: https://www.epochconverter.com

Michmich
fonte
0

Esta é uma maneira sem usar fuso horário:

LocalDateTime now = LocalDateTime.now();
long epoch = (now.getLong(ChronoField.EPOCH_DAY) * 86400000) + now.getLong(ChronoField.MILLI_OF_DAY);
aled evans
fonte