Informação de horário de corte de data Java

121

Eu tenho um objeto Java Date contendo informações de data e hora. Quero escrever um método que corte as informações de tempo, trunca as horas-minutos-segundos, de modo que só me resta a data.

Exemplo de entrada:

2008-01-01 13:15:00

Saída esperada:

2008-01-01 00:00:00

Você tem uma dica? Tentei fazer algo assim:

(timestamp / (24 * 60 * 60 * 1000)) * (24 * 60 * 60 * 1000)

mas tive problemas com o fuso horário.

Marco
fonte

Respostas:

178

A maneira recomendada de fazer a manipulação de data / hora é usar um Calendarobjeto:

Calendar cal = Calendar.getInstance(); // locale-specific
cal.setTime(dateObject);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long time = cal.getTimeInMillis();
cletus
fonte
2
@cletus, é possível remover a hora do objeto Date. Eu quero uma data pura como 01/01/2008 sem parte do tempo.
Prem
2
Eu não diria que java.util.Calendar é recomendado. A Sun / Oracle suplantou essa classe no Java 8 com o novo pacote java.time. Use isso ou Joda-Time.
Basil Bourque de
3
O calendário não é mais recomendado. Consulte stackoverflow.com/a/28868089/40064 se estiver usando Java 8.
Wim Deblauwe
2
Para sua informação, as velhas classes problemáticas de data e hora, como java.util.Calendaragora são legadas , suplantadas pelas classes java.time . Veja o Tutorial da Oracle .
Basil Bourque,
148

Você já olhou para o método de truncamento DateUtils no Apache Commons Lang?

Date truncatedDate = DateUtils.truncate(new Date(), Calendar.DATE);

irá remover o elemento de tempo.

SOU
fonte
9
Link atualizado novamente (apache! Pare de movimentar seus documentos!)
Ogre Salmo33
6
Menos dessa abordagem, o fuso horário em que a operação de truncamento é realizada é sempre o fuso horário local do PC. Portanto, se você estiver trabalhando com calendário e datas no fuso horário UTC ou em qualquer outro fuso horário que não seja o local, isso pode truncar a data não conforme o pretendido.
Nuvem
como @Cloud apontou, o fato de eu não ver a instância do Calendar aqui (não tenho controle sobre ela) me deixa um pouco nervoso :)
Jaroslav Záruba
Esse método é surpreendentemente longo e teve pelo menos duas correções de bugs.
Pino
42

Você olhou para Joda ? É uma maneira muito mais fácil e intuitiva de trabalhar com datas e horas. Por exemplo, você pode converter trivialmente entre (digamos) LocalDateTime e LOCALDATE objetos.

por exemplo (para ilustrar a API)

LocalDate date = new LocalDateTime(milliseconds).toLocalDate()

Além disso, ele resolve alguns problemas de segurança de thread com formatadores de data / hora e é altamente recomendado para trabalhar com quaisquer problemas de data / hora em Java.

Brian Agnew
fonte
12
Eu acredito que sim. É uma recomendação usar uma biblioteca melhor em vez de lutar com as classes java.util danificadas (conforme evidenciado pela pergunta)
Brian Agnew
10
Claro que se refere, Joda é A biblioteca para manipulação de data, hora e calendário em Java. Seria um abandono do dever não recomendá-lo.
Joel
1
O problema de segurança de thread é muito interessante, eu achei este post pesquisando um erro Oracle / Hibernate "IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)". Fala sobre outros problemas do Oracle que só aparecem com carga pesada forums.oracle.com/forums/thread.jspa?threadID=325576 É realmente difícil até mesmo criar um objeto com milissegundos inválidos em código normal. Para mim, acho que vou reduzir minha discussão usando Joda, em vez de deixar a Oracle usar as coisas possivelmente suspeitas não-Joda.
Mark Bennett
4
Eu gostaria de poder votar contra todos que votaram a favor. Quem são todas essas pessoas que marcam uma resposta com +1, que nem mesmo tenta responder à pergunta? Uma resposta usando Joda seria mais do que bem-vinda, mas não é isso ...
Ryan
1
@Ryan Como esta resposta não é relevante? A questão perguntava sobre a data sem hora do dia. Essa resposta apresenta uma classe cujo único objetivo é representar uma data sem hora do dia.
Basil Bourque de
35

Apenas uma atualização rápida à luz das classes java.time agora integradas ao Java 8 e posterior.

LocalDateTime tem um truncatedTo método que aborda efetivamente o que você está falando aqui:

LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)

Isso expressará o tempo atual em minutos apenas:

2015-03-05T11:47

Você pode usar um ChronoUnit(ou a TemporalUnit) menor que DAYS para executar o truncamento (já que o truncamento é aplicado apenas à parte da hora de LocalDateTime, não à parte da data).

Adam Berry
fonte
3
Você só pode usar ChronoUnits até DAYS - unidades maiores irão gerar uma exceção.
assylias
26
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
date = cal.getTime();
tremor
fonte
22

Com Joda você pode facilmente obter a data esperada.

A partir da versão 2.7 (talvez desde alguma versão anterior maior do que 2.2), como um comentador observa, toDateMidnightfoi preterido em favor ou nomeado apropriadamente withTimeAtStartOfDay(), tornando o conveniente

DateTime.now().withTimeAtStartOfDay()

possível.

Benefício adicionado de uma API muito mais agradável.

Com versões mais antigas, você pode fazer

new DateTime(new Date()).toDateMidnight().toDate()
h7r
fonte
6
Os métodos e classes relacionados à meia-noite no Joda-Time agora estão obsoletos. Os desenvolvedores não recomendam usá-los. Em vez disso, use o método withTimeAtStartOfDay.
Basil Bourque de
Obrigado por apontar. Atualizada a resposta para considerar isso.
h7r
Você também pode usar toLocalDatepara obter um objeto que nem mesmo tem um componente de tempo.
Charles Wood
Para sua informação, o projeto Joda-Time está agora em modo de manutenção , com a equipe aconselhando a migração para as classes java.time . Veja o Tutorial da Oracle .
Basil Bourque
8

Fiz o truncamento com a nova API java8. Eu me deparei com uma coisa estranha, mas em geral está truncada ...

Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.DAYS);
date = Date.from(instant);
Sergio Kosik
fonte
palavras-chave: java.time, Java 8, LocalDateTime.truncatedTo ()
Alexander Taylor
1
Essa resposta produz apenas datas para o contexto do fuso horário UTC.
Basil Bourque
6

Use DateUtils do Apache, com truncar, assim:

DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DATE);
Byron Marambio Troncoso
fonte
Isso irá truncar no fuso horário local, não no UTC que Date (getTime) representa.
epóxi
6

Para carimbos de data / hora:

timestamp -= timestamp % (24 * 60 * 60 * 1000)
Gan Quan
fonte
3
Há um pequeno erro neste código - o colchete esquerdo não está colocado corretamente, deve ser timestamp - = timestamp% (24 * 60 * 60 * 1000). Isso cortará a parte do tempo. E tenho certeza de que essa maneira é mais suficiente do que criar um objeto Caledar e brincar com seus métodos ou mesmo usar qualquer outra biblioteca de terceiros
Stan
2
Não, esta NÃO é a solução correta, CUIDADO. Isso é o mesmo que arredondar o valor da data para um piso, ou seja, você pode obter 28 de março para o carimbo de data / hora de 29 de março (em sua localidade). Portanto, soluções baseadas em calendário (o DateUtils do Apache também usa o calendário) é a única maneira
Drew
Isso também não funcionará quando a hora mudar devido ao horário de verão.
Alexandru Severin
5

De java.util.Date JavaDocs:

A classe Date representa um instante específico no tempo, com precisão de milissegundos

e de java.sql.Date JavaDocs:

Para estar em conformidade com a definição de SQL DATE, os valores de milissegundos envolvidos por uma instância java.sql.Date devem ser 'normalizados' configurando as horas, minutos, segundos e milissegundos para zero no fuso horário específico ao qual a instância está associada .

Portanto, a melhor abordagem é usar java.sql.Date se você não precisar da parte do tempo

java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());

e a saída é:

java.util.Date : Thu Apr 26 16:22:53 PST 2012
java.sql.Date  : 2012-04-26
Sunil Manheri
fonte
4

tl; dr

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.toString()                                     // Generate a String in standard ISO 8601 format, wisely extended to append the name of the time zone in square brackets.

01-01-2008T00: 00 + 01: 00 [Europa / Paris]

Para gerar uma String no formato desejado, passe a DateTimeFormatter.

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.format(                                        // Generate a String representing the object’s value.
    DateTimeFormatter.ISO_LOCAL_DATE_TIME       // Built-in predefined formatter close to what you want. 
)
.replace( "T" , " " )                           // Replace the standard’s use of a 'T' in the middle with your desired SPACE character.

01-01-2008 00:00:00

Detalhes

Outras respostas estão corretas, mas use classes de data e hora antigas, agora ultrapassadas pela estrutura java.time.

java.time

A estrutura java.time é integrada ao Java 8 e posterior. Grande parte da funcionalidade java.time é portada para Java 6 e 7 ( ThreeTen-Backport ) e posteriormente adaptada para Android ( ThreeTenABP ).

Primeiro altere a string de entrada para ficar em conformidade com a versão canônica do formato ISO 8601. Os formatos padrão ISO 8601 são usados ​​por padrão nas classes java.time para analisar / gerar strings que representam valores de data e hora. Precisamos substituir esse ESPAÇO no meio por um T.

String input = "2008-01-01 13:15:00".replace( " " , "T" );  // → 2008-01-01T13:15:00

Agora podemos analisá-lo como um LocalDateTime, onde “Local” significa nenhuma localidade específica. A entrada não possui informações de deslocamento do UTC ou fuso horário.

LocalDateTime ldt = LocalDateTime.parse( input );

ldt.toString ()… 2008-01-01T13: 15: 00

Se você não se importa com a hora do dia nem com o fuso horário, converta para a LocalDate.

LocalDate ld = ldt.toLocalDate();

ld.toString ()… 01-01-2008

Primeiro momento do dia

Se, em vez disso, você quiser que a hora do dia seja definida como o primeiro momento do dia, use uma ZonedDateTimeclasse e, a seguir, converta em um LocalDateobjeto para chamar seu atStartOfDaymétodo. Esteja ciente de que o primeiro momento pode não ser o momento00:00:00 devido ao horário de verão ou talvez outras anomalias.

O fuso horário é crucial porque, para qualquer momento, a data varia em todo o mundo por zona. Por exemplo, alguns momentos depois da meia-noite em Paris é um novo dia para os parisienses, mas ainda é “ontem” em Montreal para os canadenses.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
LocalDate ldFromZdt = zdt.toLocalDate();
ZonedDateTime zdtStartOfDay = ldFromZdt.atStartOfDay( zoneId );

zdtStartOfDay.toString ()… 2008-01-01T00: 00: 00-05: 00 [América / Montreal]

UTC

Para ver aquele momento pelas lentes do fuso horário UTC , extraia um Instantobjeto. Ambos, ZonedDateTimee Instantrepresentarão o mesmo momento na linha do tempo, mas aparecerão como dois tempos diferentes .

An Instanté a classe de bloco de construção básica em java.time, sempre em UTC por definição. Use essa classe com frequência, como você geralmente deve fazer sua lógica de negócios, armazenamento de dados e troca de dados em UTC.

Instant instant = zdtStartOfDay.toInstant();

instant.toString ()… 2008-01-01T05: 00: 00Z

Vemos 5h da manhã em vez de meia-noite. No formato padrão, Zno final é a abreviação de Zulue significa “UTC”.


Sobre java.time

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para o java.time classes .

Para saber mais, consulte o Tutorial Oracle . E pesquise no Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de cordas, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

  • Java SE 8 , Java SE 9 e posterior
    • Construídas em.
    • Parte da API Java padrão com uma implementação agrupada.
    • Java 9 adiciona alguns recursos e correções menores.
  • Java SE 6 e Java SE 7
    • Grande parte da funcionalidade java.time é portada para Java 6 e 7 no ThreeTen-Backport .
  • Android
    • Versões posteriores de implementações de pacotes Android das classes java.time.
    • Para anteriormente Android (<26), o ThreeTenABP projeto adapta ThreeTen-Backport (mencionados acima). Consulte Como usar o ThreeTenABP… .

O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de provas para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
2

Use o método da classe Calendarset() para definir os campos HOUR_OF_DAY, MINUTE, SECONDe MILLISECONDcomo zero.

Michael Borgwardt
fonte
2

A pergunta é contraditória. Ele pede uma data sem hora do dia, mas exibe um exemplo com a hora de 00:00:00.

Joda-Time

ATUALIZAÇÃO: O projeto Joda-Time agora está em modo de manutenção , com a equipe aconselhando a migração para as classes java.time . Veja minha outra resposta para solução java.time.

Se, em vez disso, você quiser que a hora do dia seja definida como o primeiro momento do dia, use um DateTimeobjeto da biblioteca Joda-Time e chame seu withTimeAtStartOfDaymétodo. Esteja ciente de que o primeiro momento pode não ser 00:00:00devido ao horário de verão ou talvez outras anomalias.

Basil Bourque
fonte
Esta é a resposta definitiva (assumindo que alguém pode e deseja usar o Horário de Joda)
Clint Eastwood
2

Apenas clear()campos redundantes.

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Date truncatedDate = calendar.getTime();

No Java 8, a melhor opção é usar o truncatedTométodo de LocalDateTime, por exemplo:

LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)
m190
fonte
Isso não funciona, porque calendar.clear(Calendar.HOUR_OF_DAY);não limpa o horário. A solução da @cletus funciona bem.
Arcao
Desculpe, você está certo, eu corrigi, por horas você deve definir o valor. Em geral, é quase da mesma forma como truncar data ...
m190
1

Realmente me aborreceu que o novo construtor de calendário "aprimorado" não demorasse milissegundos como o "horrível" antigo Construtor de data. Eu então fiquei muito irritado e escrevi isto:

long stuffYou = startTime.getTimeInMillis() % 1000;
startTime.setTimeInMillis(startTime.getTimeInMillis() - stuffYou);

Não usei a palavra "coisas" na época, mas aí descobri a felicidade disso:

startTime.set(Calendar.MILLISECOND, 0);

Mas ainda estou muito irritado com isso.

Dave
fonte
1

Corrigi o problema assim (na zona oito oriental (horário de Pequim)):

private Date getTruncatedDate(Date d) {
    if (d == null) {
        return null;
    }
    long h = 60 * 60 * 1000L;
    long dateStamp = d.getTime() - (d.getTime() + 8 * h) % (24 * h);
    return new Date(dateStamp);
}

Em primeiro lugar, você deve ter claro o que é carimbo de data / hora. O registro de data e hora corresponde ao total de milissegundos de 01 de janeiro às 00:00:00 1970 do GMT (igual ao UTC) ou Qui, 01 de janeiro às 08:00:00 CST 1970 até agora.

Lembre-se: o registro de data e hora independe do fuso horário.

Portanto, você obtém o mesmo resultado com a seguinte instrução em fusos horários diferentes:

System.out.println(new Date().getTime());

E

System.out.println(new Date(0));

imprime informações de horário diferentes em fusos horários diferentes: Se você definir o fuso horário do seu PC como UTC, você obterá

Thu Jan 01 00:00:00 UTC 1970

Mas se você definir o fuso horário como (UTC +8: 00) Pequim, Chongqing, HongKong, Urumq, obterá:

Thu Jan 01 08:00:00 CST 1970

Java obtém o carimbo de data / hora e exibe informações de data e hora de acordo com o fuso horário.

Para a introdução de como o Java exibe informações de data e hora em diferentes fusos horários, é fácil trancar as informações de tempo. Você deve obter o carimbo de hora e levar em consideração o fuso horário ao cortar as informações de hora. Em seguida, você pode criar um novo objeto Date com o carimbo de data / hora de corte (podemos chamá-lo de carimbo de data), o java compensará o fuso horário quando exibir informações de data.

Como na zona oito oriental (horário de Pequim), o horário de Pequim é 8 horas anterior ao GMT, então você deve subtrair mais 8 horas ao fazer a operação do módulo. Ou seja, você deve obter o horário GMT primeiro, então o Java adicionará 8 horas ao exibir o tempo com base na configuração de fuso horário do seu PC.


A questão do fuso horário é obscura e também me intriga por muito tempo. Agora eu deixo isso claro. A esperança ajuda.


04/01/2018 O método abaixo também funciona.

private Date getTruncatedDate2(Date d) {
    Calendar cal = Calendar.getInstance(); // locale-specific
    cal.setTime(d);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);


return cal.getTime();

}

Gelbert
fonte
Para sua informação, esta resposta usa velhas classes problemáticas de data e hora suplantadas anos atrás pelas classes java.time .
Basil Bourque
1

Você pode fazer isso para evitar problemas de fuso horário:

public static Date truncDate(Date date) {
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    return cal.getTime();
}

Embora o Dateobjeto Java seja o valor do carimbo de data / hora, mas durante o truncamento, ele será convertido para o fuso horário local, portanto, você obterá um valor surpreendente se esperar um valor do fuso horário UTC.

GMsoF
fonte
O problemático Calendare as Dateclasses tornaram-se legados anos atrás com as classes java.time implementando JSR 310 no Java 8 e posterior. Sugerir seu uso em 2018 não é um bom conselho.
Basil Bourque
0

Pode ser uma resposta tardia, mas aqui está uma maneira de fazer isso em uma linha sem usar nenhuma biblioteca:

new SimpleDateFormat("yyyy-MM-dd").parse(new SimpleDateFormat("yyyy-MM-dd").format(YOUR_TIMESTAMP))
Alex
fonte
0

Com Joda-Time desde a versão 2.0 você pode usar LocalDate.toDate().

Simplesmente

// someDatetime is whatever java.util.Date you have.
Date someDay = new LocalDate(someDatetime).toDate();
lamusique
fonte
[A] Sua resposta é redundante. Já postado por Brian Agnew . [B] Seu código está incorreto, pois ignora o fuso horário, o próprio problema levantado na pergunta.
Basil Bourque
Pode ser redundante, mas acabei de colocar um exemplo mais simples aqui porque reconheci que uma resposta não deveria estar nos comentários. Sim, java.util.Date ignora TimeZone mas a pergunta original era com a Java Date objectassim que usei java.util.Date. Além disso, não explica como seu fuso horário é problemático com java.util.Date (quando? Onde?), Embora ele não possa evitar outro que não seja Date.
lamusique
0

Para todas as respostas usando o Calendário, você deve usá-lo assim

public static Date truncateDate(Date date) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
    return c.getTime();
}

Mas eu prefiro isso:

public static Date truncateDate(Date date) {
    return new java.sql.Date(date.getTime());
}
Convidado
fonte
A java.sql.Dateclasse finge representar um valor apenas de data, mas na verdade tem um horário ajustado para o fuso horário UTC. Portanto, não é realmente uma solução adequada para a questão.
Basil Bourque de
0

Esta é uma maneira simples de obter a data sem hora se você estiver usando o Java 8+: Use o tipo java.time.LocalDate em vez de Date.

LocalDate now = LocalDate.now();
 System.out.println(now.toString());

A saída:

2019-05-30

https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html

Amy Doxy
fonte
1
Usar LocalDateé uma boa ideia para a maioria dos propósitos, mas não está claro como isso funcionará com a entrada da pergunta 2008-01-01 13:15:00,.
Ole VV