Como obter milissegundos de LocalDateTime em Java 8

285

Eu estou querendo saber se existe uma maneira de obter milissegundos atuais desde 1970/01/01 (época), utilizando o novo LocalDate, LocalTimeou LocalDateTimeclasses de Java 8.

A maneira conhecida está abaixo:

long currentMilliseconds = new Date().getTime();

ou

long currentMilliseconds = System.currentTimeMillis();
George Siggouroglou
fonte
6
O que há de errado System.currentTimeMillis()?
Dawood ibn Kareem
16
@DavidWallace Ele está tentando obter a hora de uma data, não a hora atual?
Anubian Noob
2
"... uma maneira de obter milissegundos atuais ..."
Dawood ibn Kareem
milissegundos contando de 1-1-1970. Eu estava vagando se existe um método para obtê-los usando as novas classes do Java 8 LocalDate, LocalTime e LocalDateTime, porque eu não encontrei um.
George Siggouroglou
1
Minha compreensão do propósito dessas classes é que é uma compreensão do tempo "focada no homem" e currentTimeMillisseria irrelevante nesse contexto. Pense no Calendário + Relógio de parede com uma precisão muito boa e sem preocupações com fusos horários e localidade. Portanto, não há como voltar ao "UTC Time" de um LocalTime
Gus

Respostas:

325

Não sei bem o que você quer dizer com "milissegundos atuais", mas presumo que seja o número de milissegundos desde a "época", ou seja, meia-noite de 1º de janeiro de 1970, UTC.

Se você deseja encontrar o número de milissegundos desde a época no momento, use System.currentTimeMillis()como o Anubian Noob indicou . Nesse caso, não há razão para usar qualquer uma das novas APIs java.time para fazer isso.

No entanto, talvez você já tenha um LocalDateTimeobjeto ou similar de algum lugar e deseje convertê-lo em milissegundos desde a época. Não é possível fazer isso diretamente, pois a LocalDateTimefamília de objetos não tem noção de em que fuso horário eles estão. Portanto, as informações de fuso horário precisam ser fornecidas para encontrar o horário relativo à época, que está no UTC.

Suponha que você tenha um LocalDateTimeseguinte:

LocalDateTime ldt = LocalDateTime.of(2014, 5, 29, 18, 41, 16);

Você precisa aplicar as informações de fuso horário, fornecendo a ZonedDateTime. Estou no mesmo fuso horário de Los Angeles, então eu faria algo assim:

ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/Los_Angeles"));

Obviamente, isso faz suposições sobre o fuso horário. E existem casos extremos que podem ocorrer, por exemplo, se a hora local indicar um horário próximo à transição do horário de verão. Vamos deixar isso de lado, mas você deve estar ciente de que esses casos existem.

De qualquer forma, se você puder obter um válido ZonedDateTime, poderá convertê-lo no número de milissegundos desde a época, da seguinte forma:

long millis = zdt.toInstant().toEpochMilli();
Stuart Marks
fonte
5
Observe que o ZonedDateTimepossui o getEpochSecondmétodo (via padrão ChronoZonedDateTime ). Não há necessidade de Instant.
Mabi
13
Claro, se tudo que você precisa é de segundos, não de milissegundos.
Stuart Marks
18
Evite a matemática dos nanos usando zdt.get (ChronoField.MILLI_OF_SECOND). Evite todas as contas usando zdt.toInstant () toEpochMilli ().
JodaStephen
2
Observe que zdt.toInstant().toEpochMilli();você fornecerá os milissegundos para o valor UTC correspondente , não para a data e hora da zona. Achei isso da maneira mais difícil. toInstant()fornece um instante UTC. Se você quiser a hora local em milis, isso não fornecerá o que você espera . Portanto, se você estiver, por exemplo, usando um tempo de zoneamento +02: 00, o tempo retornado será 3600 * 2 * 1000 milissegundos menor do que o esperado.
Por Lundberg
2
@PerLundberg Eu só comparação ZonedDateTime.now().toInstant().toEpochMilli(), LocalDateTime.now().toInstant(OffsetDateTime.now().getOffset()).toEpochMilli(), LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli(), Calendar.getInstance().getTime().getTime(), new Date().getTime()e System.currentTimeMillis(). O único método que parece "desativado" é o que você propôs: LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli()fornece os milis para sua data local como se fosse uma data UTC , ou seja, se a data local for UTC + 2, seu método fornece milis por 2 horas no futuro .
walen 6/05/19
81

O que faço para não especificar um fuso horário é,

System.out.println("ldt " + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
System.out.println("ctm " + System.currentTimeMillis());

ldt 1424812121078 
ctm 1424812121281

Como você pode ver, os números são os mesmos, exceto por um pequeno tempo de execução.

Caso você não goste de System.currentTimeMillis, use Instant.now().toEpochMilli()

Brian
fonte
3
Não, a Epoch é relativa ao UTC, mas você obtém o horário atual no seu fuso horário.
Rafael Membrives
2
@ ChristofferHammarström a quantidade de milissegundos que passaram desde a época é um fato independente do fuso horário. É a mesma quantidade de milissegundos, independentemente da hora do dia em que você a chama. Os dois resultados são diferentes porque a máquina de brian levou 203 milissegundos para executar o segundo comando.
Fletch
Posso usar o UTC quando o usuário talvez em um fuso horário diferente
GilbertS
2
@ GilbertS, você provavelmente nunca deve usar esse código exato, use a API conforme planejado. A data e a hora sempre devem ser UTC quando armazenadas em um computador ou transmitidas para outro usuário. Geralmente, ele deve ser apresentado ao usuário como um horário local usando suas próprias configurações de fuso horário.
brian
20

Para evitar o ZoneId, você pode:

LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0);

System.out.println("Initial Epoch (TimeInMillis): " + date.toInstant(ZoneOffset.ofTotalSeconds(0)).toEpochMilli());

Obtendo 0 como valor, isso mesmo!

Dani
fonte
8
ZoneOffset.ofTotalSeconds(0)é o mesmo queZoneOffset.UTC
Anton Novopashin 9/11
1
Sim, fui eu que votei no comentário de AntonNovopashin. Como você precisava do UTC, está bem (você pode mencionar isso na resposta). BTW ZoneOffseté uma subclasse de ZoneId, então eu não iria dizer exatamente você ter evitado isso, mas enquanto você está feliz ...
Ole VV
Meu primeiro comentário foi um pouco duro, mas não foi tão ofensivo. Você se ofendeu. Sinto muito. Eu o apaguei. Permita-me perguntar o contrário: qual pode ser a nossa razão de querer evitar ZoneId?
Ole VV
@ OleV.V Posso usar o UTC? Não é apenas para pessoas que vivem no fuso horário UTC.
GilbertS 14/06
1
@ GilbertS Acho que ninguém mora no fuso horário UTC. É recomendável usar o UTC para horários usados ​​em fusos horários. Consulte, por exemplo, as práticas recomendadas de Java para manipulação / armazenamento de datas para usuários geograficamente diversos . Ou talvez eu não tenha entendido sua pergunta?
Ole VV
15

Desde o Java 8 você pode ligar java.time.Instant.toEpochMilli().

Por exemplo, a chamada

final long currentTimeJava8 = Instant.now().toEpochMilli();

fornece os mesmos resultados que

final long currentTimeJava1 = System.currentTimeMillis();
Marteng
fonte
1
"fornece os mesmos resultados que" ... mas consome mais energia da CPU e enfatiza muito mais o coletor de lixo.
VasiliNovikov
Eu gostaria de ver a acima quantificados (especialmente para o uso normal - por exemplo, não para alguma aplicação crítica em tempo real o desempenho ...)
Brian Agnew
11

Você java.sql.Timestamptambém pode usar para obter milissegundos.

LocalDateTime now = LocalDateTime.now();
long milliSeconds = Timestamp.valueOf(now).getTime();
System.out.println("MilliSeconds: "+milliSeconds);
Nalam
fonte
Você está ignorando a necessidade crucial de um fuso horário. Eu não recomendo fazê-lo desta maneira. Além disso, a Timestampclasse está desatualizada e cheia de problemas de design, então prefiro evitá-la, especialmente quando podemos usar as classes de java.timelike LocalDateTime.
Ole VV
@ OleV.V. Apenas curioso sobre os problemas de design Timestamp, você pode compartilhar algum artigo sobre isso? Isso me ajuda a evitar o uso Timestampdo meu código também. Obrigado!
Nalam 02/07
1
Em resumo, é implementado como uma subclasse de, Datemas geralmente não pode ser tratado como um Date. A maioria dos métodos herdados de Datee um construtor está obsoleta. Seu toStringmétodo usa o fuso horário da JVM, que confunde muitos, pois o mesmo Timestampé impresso de maneira diferente em computadores diferentes ( exemplo ).
VV Ole VV
Isso é perfeito para testes de unidade. Basta substituir o agora porLocalDate.of(year, month, day)
G_V 17/07/19
Essa deve ser a resposta aceita para solucionar os valores "sempre UTC".
LeYAUable
9

Para obter o tempo atual em milissegundos (desde a época), use System.currentTimeMillis().

Anubian Noob
fonte
3

Se você tiver um Java 8 Clock, poderá usá- clock.millis()lo (embora seja recomendável clock.instant()obter o Java 8 Instant, pois é mais preciso).

Por que você usaria um relógio Java 8? Portanto, em sua estrutura DI, você pode criar um bean Clock:

@Bean
public Clock getClock() {
    return Clock.systemUTC();
}

e, em seus testes, você pode zombar facilmente:

@MockBean private Clock clock;

ou você pode ter um bean diferente:

@Bean
public Clock getClock() {
    return Clock.fixed(instant, zone);
}

o que ajuda com testes que afirmam datas e horários imensamente.

JeeBee
fonte
1
  default LocalDateTime getDateFromLong(long timestamp) {
    try {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC);
    } catch (DateTimeException tdException) {
      //  throw new 
    }
}

default Long getLongFromDateTime(LocalDateTime dateTime) {
    return dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli();
}
Mohamed.Abdo
fonte
0

Por que ninguém mencionou o método LocalDateTime.toEpochSecond():

LocalDateTime localDateTime = ... // whatever e.g. LocalDateTime.now()
long time2epoch = localDateTime.toEpochSecond(ZoneOffset.UTC);

Isso parece muito mais curto do que muitas respostas sugeridas acima ...

maxxyme
fonte
era isso que eu estava procurando. Essa resposta aceita foi me dar vontade.
Brill Pappin 15/04
Aceite seu único disponível na API 26: {
Brill Pappin
1
Porque dá apenas alguns segundos. É estranho que não exista toEpochMillimétodo, levando em consideração o fato de que você pode especificar até nanossegundos em LocalDateTime: existe um método LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond).
izogfif