Como obter a hora de início e a hora de término de um dia?

121

Como obter a hora de início e a hora de término de um dia?

código como este não é preciso:

 private Date getStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.set(year, month, day, 0, 0, 0);
    return calendar.getTime();
}

private Date getEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.set(year, month, day, 23, 59, 59);
    return calendar.getTime();
}

Não é preciso até o milissegundo.

kalman03
fonte
8
Como assim, "não é preciso"?
Kirk Woll
O que há de errado com o código? O que você está recebendo e não espera? Em que fuso horário você deseja que seus dias iniciem e terminem de acordo?
Mark Reed
4
O código acima nem usa a data passada no método. Deveria estar executando: calendar.setTime (date) após a criação do calendário.
Jerry Destremps 10/10
2
Esta pergunta supõe que os valores de data e hora tenham uma resolução de milissegundos. Verdadeiro para java.util.Date e Joda-Time. Mas o pacote False for java.time no Java 8 (nanossegundos), alguns bancos de dados como Postgres (microssegundos), bibliotecas unix (segundos inteiros) e talvez outros. Veja minha resposta para uma abordagem mais segura usando o Half-Open.
Basil Bourque
2
Na verdade, você deve obter o início do dia seguinte e, ao iterar os itens naquele dia (supondo que você queira fazer isso), especificará o limite superior usando <em vez de <=. Isso excluiria o dia seguinte, mas incluiria todo o dia anterior até o nanossegundo.
Daniel F

Respostas:

115

tl; dr

LocalDate                       // Represents an entire day, without time-of-day and without time zone.
.now(                           // Capture the current date.
    ZoneId.of( "Asia/Tokyo" )   // Returns a `ZoneId` object.
)                               // Returns a `LocalDate` object.
.atStartOfDay(                  // Determines the first moment of the day as seen on that date in that time zone. Not all days start at 00:00!
    ZoneId.of( "Asia/Tokyo" ) 
)                               // Returns a `ZonedDateTime` object.

Início do dia

Obtenha o comprimento total do hoje como visto em um fuso horário.

Utilizando a abordagem semi-aberta, onde o início é inclusivo e o final é exclusivo . Essa abordagem resolve a falha no seu código que falha em explicar o último segundo do dia.

ZoneId zoneId = ZoneId.of( "Africa/Tunis" ) ;
LocalDate today = LocalDate.now( zoneId  ) ;

ZonedDateTime zdtStart = today.atStartOfDay( zoneId ) ;
ZonedDateTime zdtStop = today.plusDays( 1 ).atStartOfDay( zoneId ) ;

zdtStart.toString () = 2020-01-30T00: 00 + 01: 00 [África / Tunis]

zdtStop.toString () = 2020-01-31T00: 00 + 01: 00 [África / Tunis]

Veja os mesmos momentos no UTC.

Instant start = zdtStart.toInstant() ;
Instant stop = zdtStop.toInstant() ;

start.toString () = 2020-01-29T23: 00: 00Z

stop.toString () = 2020-01-30T23: 00: 00Z

Se você deseja o dia inteiro de uma data como visto no UTC, e não no fuso horário, use OffsetDateTime.

LocalDate today = LocalDate.now( ZoneOffset.UTC  ) ;

OffsetDateTime odtStart = today.atTime( OffsetTime.MIN ) ;
OffsetDateTime odtStop = today.plusDays( 1 ).atTime( OffsetTime.MIN ) ;

odtStart.toString () = 2020-01-30T00: 00 + 18: 00

odtStop.toString () = 2020-01-31T00: 00 + 18: 00

Esses OffsetDateTime objetos já estarão no UTC, mas você pode chamar toInstantse precisar desses objetos que estão sempre no UTC, por definição.

Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;

start.toString () = 2020-01-29T06: 00: 00Z

stop.toString () = 2020-01-30T06: 00: 00Z

Dica: você pode estar interessado em adicionar a biblioteca ThreeTen-Extra ao seu projeto para usar sua Intervalclasse para representar esse par de Instantobjetos. Isto oferece classe métodos úteis para comparação, como abuts, overlaps, containse muito mais.

Interval interval = Interval.of( start , stop ) ;

interval.toString () = 2020-01-29T06: 00: 00Z / 2020-01-30T06: 00: 00Z

Meio aberto

A resposta do mprivat está correta. Seu argumento é não tentar obter o final de um dia, mas comparar com "antes do início do dia seguinte". Sua idéia é conhecida como a abordagem "semi-aberta", em que um período de tempo tem um começo inclusivo, enquanto o final é exclusivo .

  • As estruturas atuais de data e hora do Java (java.util.Date/Calendar e Joda-Time ) usam milissegundos da época . Mas no Java 8, as novas classes JSR 310 java.time. * Usam resolução de nanossegundos. Qualquer código que você escreveu com base na imposição da contagem de milissegundos do último momento do dia estaria incorreto se alternado para as novas classes.
  • A comparação de dados de outras fontes se torna defeituosa se empregar outras resoluções. Por exemplo, as bibliotecas Unix normalmente empregam segundos inteiros, e bancos de dados como o Postgres resolvem a data e hora em microssegundos.
  • Algumas alterações no horário de verão acontecem à meia-noite, o que pode confundir ainda mais as coisas.

insira a descrição da imagem aqui

Joda-Time 2.3 oferece um método para este mesmo fim, para obter primeiro momento do dia: withTimeAtStartOfDay(). Da mesma forma em java.time LocalDate::atStartOfDay,.

Pesquise StackOverflow por "joda half-open" para ver mais discussões e exemplos.

Veja este post, Intervalos de tempo e outros intervalos devem estar semi-abertos , por Bill Schneider.

Evite classes de data e hora herdadas

As classes java.util.Date e .Calendar são notoriamente problemáticas. Evite-os.

Use as classes java.time . A estrutura java.time é o sucessor oficial da bem - sucedida biblioteca Joda-Time .

java.time

A estrutura java.time é integrada ao Java 8 e posterior. Portado para Java 6 e 7 no projeto ThreeTen-Backport , adaptado ao Android no projeto ThreeTenABP .

An Instanté um momento na linha do tempo no UTC com uma resolução de nanossegundos .

Instant instant = Instant.now();

Aplique um fuso horário para obter a hora do relógio de parede em alguma localidade.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Para obter o primeiro momento do dia, leia a LocalDateaula e seu atStartOfDaymétodo.

ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );

Usando a abordagem semi-aberta, obtenha o primeiro momento do dia seguinte.

ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );

Tabela de todos os tipos de data e hora em Java, modernos e herdados

Atualmente, a estrutura java.time não possui uma Intervalclasse, conforme descrito abaixo para o Joda-Time. No entanto, o projeto ThreeTen-Extra estende o java.time com classes adicionais. Este projeto é o campo de prova para possíveis adições futuras ao java.time. Entre suas classes é Interval. Construa um Intervalpassando um par de Instantobjetos. Podemos extrair um Instantde nossos ZonedDateTimeobjetos.

Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );

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.

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

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

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 strings, não há necessidade de java.sql.*classes. O Hibernate 5 e o JPA 2.2 suportam java.time .

Onde obter as classes java.time?


Joda-Time

ATUALIZAÇÃO: O projeto Joda-Time agora está no modo de manutenção e aconselha a migração para as classes java.time . Estou deixando esta seção intacta para a história.

Joda-Time tem três classes para representar um espaço de tempo de várias maneiras: Interval, Period, e Duration. Um Intervaltem um começo e um fim específicos na linha do tempo do Universo. Isso se encaixa em nossa necessidade de representar "um dia".

Chamamos o método em withTimeAtStartOfDayvez de definir a hora do dia como zeros. Por causa do horário de verão e de outras anomalias, o primeiro momento do dia pode não ser 00:00:00.

Exemplo de código usando o Joda-Time 2.3.

DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
DateTime now = DateTime.now( timeZone );
DateTime todayStart = now.withTimeAtStartOfDay();
DateTime tomorrowStart = now.plusDays( 1 ).withTimeAtStartOfDay();
Interval today = new Interval( todayStart, tomorrowStart );

Se necessário, você pode converter para um java.util.Date.

java.util.Date date = todayStart.toDate();
Basil Bourque
fonte
Preciso usarLocalDateTime
user924 25/02
170

Java 8


public static Date atStartOfDay(Date date) {
    LocalDateTime localDateTime = dateToLocalDateTime(date);
    LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
    return localDateTimeToDate(startOfDay);
}

public static Date atEndOfDay(Date date) {
    LocalDateTime localDateTime = dateToLocalDateTime(date);
    LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
    return localDateTimeToDate(endOfDay);
}

private static LocalDateTime dateToLocalDateTime(Date date) {
    return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}

private static Date localDateTimeToDate(LocalDateTime localDateTime) {
    return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

Atualização : Adicionei estes 2 métodos às minhas Java Utility Classes aqui

Está no Repositório Central do Maven em:

<dependency>
  <groupId>com.github.rkumsher</groupId>
  <artifactId>utils</artifactId>
  <version>1.3</version>
</dependency>

Java 7 e anteriores


Com o Apache Commons

public static Date atEndOfDay(Date date) {
    return DateUtils.addMilliseconds(DateUtils.ceiling(date, Calendar.DATE), -1);
}

public static Date atStartOfDay(Date date) {
    return DateUtils.truncate(date, Calendar.DATE);
}

Sem Apache Commons

public Date atEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);
    calendar.set(Calendar.MILLISECOND, 999);
    return calendar.getTime();
}

public Date atStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
}
RKumsher
fonte
5
Eu recomendo essa abordagem, devido aos problemas de borda que você encontrará nos fusos horários, horário de verão etc. que uma biblioteca de horários como Joda cuidará de você.
Edward Anderson
7
Eu acho que getStartOfDaydeveria ser: LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);Ou seja, LocalTime, não LocalDateTime.
Konstantin Kulagin
Não pense nisso getEndOfDay(Date date). getStartOfDay(Date date)Os métodos na seção Java 8 estão corretos, ou seja, LocalDateTime.MAX e .MIN, colocam você nas primeiras datas possíveis no passado e no futuro, não no início e no final de um dia. Em vez disso, sugiro .atStartOfDay () para start e .plusDays (1) .atStartOfDay (). MinusNanos (1) para end.
Glen Mazza
Método localDateTimeToDate(LocalDateTime startOfDay)throws Exception - java.lang.IllegalArgumentException: java.lang.ArithmeticException: long estouro
sanluck
1
@GlenMazza, a solução Java 8 usa LocalTime .MAX , não LocalDateTime.MAX . A solução está correta.
Frederico Pantuzza 31/10
28

em getEndOfDay, você pode adicionar:

calendar.set(Calendar.MILLISECOND, 999);

Embora matematicamente falando, você não pode especificar o final de um dia a não ser dizendo que é "antes do início do dia seguinte".

Então, em vez de dizer, if(date >= getStartOfDay(today) && date <= getEndOfDay(today))você deve dizer: if(date >= getStartOfDay(today) && date < getStartOfDay(tomorrow)). Essa é uma definição muito mais sólida (e você não precisa se preocupar com precisão de milissegundos).

mprivat
fonte
1
A segunda metade desta resposta é a melhor maneira. Essa abordagem é conhecida como "semi-aberta". Eu explico mais na minha resposta .
Basil Bourque
14

java.time

Usando a java.timeestrutura incorporada no Java 8.

import java.time.LocalTime;
import java.time.LocalDateTime;

LocalDateTime now = LocalDateTime.now(); // 2015-11-19T19:42:19.224
// start of a day
now.with(LocalTime.MIN); // 2015-11-19T00:00
now.with(LocalTime.MIDNIGHT); // 2015-11-19T00:00
// end of a day
now.with(LocalTime.MAX); // 2015-11-19T23:59:59.999999999
Przemek
fonte
3
Esta resposta ignora anomalias como horário de verão (DST). As Local…classes não têm conceito de fuso horário e ajuste para anomalias. Por exemplo, alguns fusos horários têm uma alteração no horário de verão à meia-noite, portanto, o primeiro momento do dia é no horário 01:00:00.0(01:00) e não no 00:00:00.0valor produzido por esse código. Use em ZonedDateTimevez disso.
Basil Bourque 16/05
4

Uma maneira adicional de encontrar o início do dia com java8 java.time.ZonedDateTime em vez de passar LocalDateTimeé simplesmente truncar a entrada ZonedDateTime para DAYS:

zonedDateTimeInstance.truncatedTo( ChronoUnit.DAYS );
laur
fonte
2

Para java 8, as seguintes instruções de linha única estão funcionando. Neste exemplo, eu uso o fuso horário UTC. Por favor, considere alterar o TimeZone usado atualmente.

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

final LocalDateTime endOfDay       = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
final Date          endOfDayAsDate = Date.from(endOfDay.toInstant(ZoneOffset.UTC));

System.out.println(endOfDayAsDate);

final LocalDateTime startOfDay       = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
final Date          startOfDayAsDate = Date.from(startOfDay.toInstant(ZoneOffset.UTC));

System.out.println(startOfDayAsDate);

Se não houver diferença horária com a saída. Experimentar:ZoneOffset.ofHours(0)

Fırat KÜÇÜK
fonte
1
Como isso agrega valor além do das respostas existentes? Além disso, você ignora a questão crucial do fuso horário. E passar a constante UTC para toInstanté sintaxe ilegal e redundante conceitualmente.
Basil Bourque
@BasilBourque "Como isso agrega valor além do das respostas existentes?" É assim que o stackoverflow funciona. BTW, este é apenas um modelo. As pessoas podem mudar da maneira que querem. Passar o UTC para toInstant é totalmente legal. Você pode examinar as saídas.
23917
2

Java 8 ou ThreeTenABP

ZonedDateTime

ZonedDateTime curDate = ZonedDateTime.now();

public ZonedDateTime startOfDay() {
    return curDate
    .toLocalDate()
    .atStartOfDay()
    .atZone(curDate.getZone())
    .withEarlierOffsetAtOverlap();
}

public ZonedDateTime endOfDay() {

    ZonedDateTime startOfTomorrow =
        curDate
        .toLocalDate()
        .plusDays(1)
        .atStartOfDay()
        .atZone(curDate.getZone())
        .withEarlierOffsetAtOverlap();

    return startOfTomorrow.minusSeconds(1);
}

// based on https://stackoverflow.com/a/29145886/1658268

LocalDateTime

LocalDateTime curDate = LocalDateTime.now();

public LocalDateTime startOfDay() {
    return curDate.atStartOfDay();
}

public LocalDateTime endOfDay() {
    return startOfTomorrow.atTime(LocalTime.MAX);  //23:59:59.999999999;
}

// based on https://stackoverflow.com/a/36408726/1658268

Espero que ajude alguém.

SudoPlz
fonte
O método "atTime" é uma abordagem muito boa junto com a constante "LocalTime.MAX" para o final do dia. Agradável!
Mr. Anderson
2

Eu tentei esse código e funciona bem!

final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final ZonedDateTime startofDay =
    now.toLocalDate().atStartOfDay(ZoneOffset.UTC);
final ZonedDateTime endOfDay =
    now.toLocalDate().atTime(LocalTime.MAX).atZone(ZoneOffset.UTC);
Siddhey Sankhe
fonte
Se você tem ZonedDateTimee está Instantà sua disposição, não há necessidade de usá-lo java.sql.Timestamp. Essa classe é uma das terríveis e antigas classes herdadas agora substituídas pelas classes java.time . A partir do JDBC 4.2, podemos trocar diretamente objetos java.time com o banco de dados. Sua mistura do legado e das classes modernas não faz sentido para mim.
Basil Bourque
1

Outra solução que não depende de nenhuma estrutura é:

static public Date getStartOfADay(Date day) {
    final long oneDayInMillis = 24 * 60 * 60 * 1000;
    return new Date(day.getTime() / oneDayInMillis * oneDayInMillis);
}

static public Date getEndOfADay(Date day) {
    final long oneDayInMillis = 24 * 60 * 60 * 1000;
    return new Date((day.getTime() / oneDayInMillis + 1) * oneDayInMillis - 1);
}

Observe que ele retorna o horário com base no UTC

Alexander Chingarev
fonte
1
private Date getStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.setTimeInMillis(0);
    calendar.set(year, month, day, 0, 0, 0);
    return calendar.getTime();
    }

private Date getEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.setTimeInMillis(0);
    calendar.set(year, month, day, 23, 59, 59);
    return calendar.getTime();
    }

calendar.setTimeInMillis (0); fornece precisão de até milissegundos

manik venkat
fonte
1
Essas classes terríveis de data e hora foram substituídas há anos pelas modernas classes java.time definidas no JSR 310.
Basil Bourque
@BasilBourque o pacote java.time apareceu apenas no Java 8, disponível apenas desde o Android N (API 26)
ivan8m8
@ ivan8m8 A maior parte da funcionalidade java.time é portada para Java 6 e 7 na biblioteca ThreeTen-Backport . Mais adaptado para Android inicial (<26) na biblioteca ThreeTenABP .
Basil Bourque
@BasilBourque, mas o ThreeTen usa um mecanismo extremamente ineficiente no Android .
ivan8m8 27/02
@ ivan8m8 Você pode estar pensando no antecessor do java.time , o Joda-Time . Mais uma vez, consulte o projeto ThreeTenABP . Eu não ouvi falar de tal ineficiência no Android usando essa biblioteca.
Basil Bourque
0

Sei que é um pouco tarde, mas no caso do Java 8, se você estiver usando o OffsetDateTime (que oferece muitas vantagens, como TimeZone, Nanossegundos etc.), você pode usar o seguinte código:

OffsetDateTime reallyEndOfDay = someDay.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
// output: 2019-01-10T23:59:59.999999999Z
Florim
fonte
0

Tive vários inconvenientes com todas as soluções, porque precisava do tipo de variável Instant e o fuso horário sempre interferia na mudança de tudo. Depois, combinando soluções, vi que essa é uma boa opção.

        LocalDate today = LocalDate.now();
        Instant startDate = Instant.parse(today.toString()+"T00:00:00Z");
        Instant endDate = Instant.parse(today.toString()+"T23:59:59Z");

e nós temos como resultado

        startDate = 2020-01-30T00:00:00Z
        endDate = 2020-01-30T23:59:59Z

Espero que ajude você

yOshi
fonte
O dia termina na chegada do primeiro momento do dia seguinte . Seu código pula no último segundo inteiro do dia, um bilhão de nanossegundos que nunca são relatados. Veja minha resposta para aprender sobre a abordagem semi-aberta para definir um período de tempo.
Basil Bourque
Manipular seqüências de caracteres como uma maneira de lidar com valores de data e hora geralmente é uma abordagem ruim. Use objetos inteligentes em vez de cadeias estúpidas para este trabalho. As classes java.time fornecem toda a funcionalidade necessária para esse problema sem recorrer à manipulação de strings.
Basil Bourque
Acabei de adicionar uma seção tl; dr à minha resposta, mostrando como obter o intervalo de um dia como um par de Instantobjetos. Dica: você pode estar interessado em adicionar a biblioteca ThreeTen-Extra ao seu projeto para usar sua Intervalclasse.
Basil Bourque
0

Eu acho que o mais fácil seria algo como:

// Joda Time

DateTime dateTime=new DateTime(); 

StartOfDayMillis = dateTime.withMillis(System.currentTimeMillis()).withTimeAtStartOfDay().getMillis();
EndOfDayMillis = dateTime.withMillis(StartOfDayMillis).plusDays(1).minusSeconds(1).getMillis();

Esses milis podem ser convertidos em Calendário, Instant ou LocalDate conforme sua necessidade com o Joda Time.

happyvirus
fonte
-2
public static Date beginOfDay(Date 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);

    return cal.getTime();
}

public static Date endOfDay(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 23);
    cal.set(Calendar.MINUTE, 59);
    cal.set(Calendar.SECOND, 59);
    cal.set(Calendar.MILLISECOND, 999);

    return cal.getTime();
}
jack jin
fonte