De acordo com a documentação da class SimpleDateFormat , o Java
não suporta granularidade de tempo acima de milissegundos em seus padrões de data.
Então, uma string de data como
quando analisado através do padrão
Na verdade, interpreta o número inteiro após o .
símbolo como (quase 1 bilhão!) milissegundos e não como nanossegundos, resultando na data
ou seja, mais de 11 dias à frente. Surpreendentemente, usar um número menor de símbolos S
ainda resulta em todos os 9 dígitos sendo analisados (em vez de, digamos, o mais à esquerda, 3 para .SSS
).
Existem duas maneiras de lidar com esse problema corretamente:
Haveria alguma outra maneira de obter uma solução correta simplesmente fornecendo um padrão para a implementação SimpleDateFormat
padrão, sem outras modificações de código ou manipulação de string?
LocalDateTime.parse( // With resolution of nanoseconds, represent the idea of a date and time somewhere, unspecified. Does *not* represent a moment, is *not* a point on the timeline. To determine an actual moment, place this date+time into context of a time zone (apply a `ZoneId` to get a `ZonedDateTime`). "2015-05-09 00:10:23.999750900" // A `String` nearly in standard ISO 8601 format. .replace( " " , "T" ) // Replace SPACE in middle with `T` to comply with ISO 8601 standard format. ) // Returns a `LocalDateTime` object.
Não, você não pode usar SimpleDateFormat para manipular nanossegundos .
Mas sua premissa que …
Java não suporta granularidade de tempo acima de milissegundos em seus padrões de data
… Não é mais verdade a partir do Java 8 , 9, 10 e posterior com as classs java.time internas . E também não é verdade no Java 6 e no Java 7, já que a maior parte da funcionalidade java.time é portada de volta .
SimpleDateFormat
e as classs java.util.Date
/ .Calendar
relacionadas agora são .Calendar
pelo novo pacote java.time encontrado no Java 8 ( Tutorial ).
As novas classs java.time suportam resolução de nanossegundos . Esse suporte inclui a análise e geração de nove dígitos de segundo fracionário. Por exemplo, quando você usa a API java.time.format
DateTimeFormatter
, a letra de padrão S
indica uma “fração do segundo” em vez de “milissegundos” e pode lidar com valores de nanossegundos.
Instant
Por exemplo, a class Instant
representa um momento no UTC . Seu método toString
gera um object String
usando o formato padrão ISO 8601 . O Z
no final significa UTC, pronunciado “Zulu”.
instant.toString() // Generate a `String` representing this moment, using standard ISO 8601 format.
2013-08-20T12: 34: 56.123456789Z
Observe que a captura do momento atual no Java 8 é limitada a uma resolução de milissegundos. As classs java.time podem conter um valor em nanossegundos, mas só podem determinar a hora atual com milissegundos. Essa limitação é devido à implementação do Clock
. No Java 9 e posteriores, uma nova implementação do Clock
pode capturar o momento atual em resolução mais fina, dependendo dos limites do hardware e do sistema operacional do seu host, geralmente microssegundos na minha experiência.
Instant instant = Instant.now() ; // Capture the current moment. May be in milliseconds or microseconds rather than the maximum resolution of nanoseconds.
LocalDateTime
Sua string de input de exemplo de 2015-05-09 00:10:23.999750900
não possui um indicador de fuso horário ou deslocamento de UTC. Isso significa que não representa um momento, não é um ponto na linha do tempo. Em vez disso, representa momentos potenciais ao longo de um intervalo de cerca de 26-27 horas, o intervalo de fusos horários em todo o mundo.
Pares uma input como um object LocalDateTime
. Primeiro, substitua o SPACE no meio por um T
para atender ao formato ISO 8601, usado por padrão ao analisar / gerar strings. Portanto, não há necessidade de especificar um padrão de formatação.
LocalDateTime ldt = LocalDateTime.parse( "2015-05-09 00:10:23.999750900".replace( " " , "T" ) // Replace SPACE in middle with `T` to comply with ISO 8601 standard format. ) ;
java.sql.Timestamp
A class java.sql.Timestamp
também lida com resolução de nanossegundos, mas de uma maneira estranha. Geralmente é melhor fazer o seu trabalho dentro das classs java.time. Não há necessidade de usar o Timestamp
novamente a partir do JDBC 4.2 e posterior.
myPreparedStatement.setObject( … , instant ) ;
E recuperação.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Se estiver usando um driver JDBC pré-4.2 mais antigo, você poderá usar toInstant
e, a from
methods, alternar entre java.sql.Timestamp e java.time.
O framework java.time está embutido no Java 8 e posterior. Essas classs substituem as antigas classs herdadas de data e hora legadas , como java.util.Date
, Calendar
e SimpleDateFormat
.
O projeto Joda-Time , agora em modo de manutenção , aconselha a migration para as classs java.time .
Para saber mais, veja o Oracle Tutorial . E pesquise o Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .
Você pode trocar objects java.time diretamente com seu database. Use um driver JDBC compatível com o JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de classs java.sql.*
.
Onde obter as classs java.time?
O projeto ThreeTen-Extra estende java.time com classs adicionais. Este projeto é um campo de testes para possíveis futuras adições ao java.time. Você pode encontrar algumas classs úteis aqui como Interval
, YearWeek
, YearQuarter
e muito mais .