Veja se o horário atual se enquadra em um intervalo específico de tempo no dia atual em Java

StackOverflow https://stackoverflow.com/questions/2473034

  •  20-09-2019
  •  | 
  •  

Pergunta

Tenho certeza de que isso foi feito 1000 vezes em 1000 lugares diferentes. A questão é: quero saber se existe uma maneira melhor/padrão/mais rápida de verificar se o "tempo" atual está entre dois valores de tempo dados em hh:mm:ss formato. Por exemplo, minha grande lógica de negócios não deve ser executada entre 18:00:00 and 18:30:00. Então aqui está o que eu tinha em mente:

 public static  boolean isCurrentTimeBetween(String starthhmmss, String endhhmmss) throws ParseException{
  DateFormat hhmmssFormat = new SimpleDateFormat("yyyyMMddhh:mm:ss");
  Date now = new Date();
  String yyyMMdd = hhmmssFormat.format(now).substring(0, 8);

  return(hhmmssFormat.parse(yyyMMdd+starthhmmss).before(now) &&
    hhmmssFormat.parse(yyyMMdd+endhhmmss).after(now));
 }

Exemplo de caso de teste:

  String doNotRunBetween="18:00:00,18:30:00";//read from props file
  String[] hhmmss = downTime.split(",");
  if(isCurrentTimeBetween(hhmmss[0], hhmmss[1])){
   System.out.println("NOT OK TO RUN");
  }else{
   System.out.println("OK TO RUN");
  }

O que estou procurando é o código que é melhor

  • no desempenho
  • na aparência
  • em correção

O que não estou procurando

  • Bibliotecas de terceiros
  • Debate de manuseio de exceção
  • Convenções de nomeação variável
  • Questões do modificador de método
Foi útil?

Solução

É tudo o que você precisa fazer, esse método é vagamente acoplado a partir da entrada e altamente coerente.

boolean isNowBetweenDateTime(final Date s, final Date e)
{
    final Date now = new Date();
    return now.after(s) && now.before(e);
}

Como você obtém os objetos da data para início e fim é irrelevante para compará -los. Você está tornando as coisas muito mais complicadas do que você precisa com a passagem String representações ao redor.

Aqui está uma maneira melhor de obter as datas de início e fim, novamente pouco acoplado e altamente coerente.

private Date dateFromHourMinSec(final String hhmmss)
{
    if (hhmmss.matches("^[0-2][0-9]:[0-5][0-9]:[0-5][0-9]$"))
    {
        final String[] hms = hhmmss.split(":");
        final GregorianCalendar gc = new GregorianCalendar();
        gc.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hms[0]));
        gc.set(Calendar.MINUTE, Integer.parseInt(hms[1]));
        gc.set(Calendar.SECOND, Integer.parseInt(hms[2]));
        gc.set(Calendar.MILLISECOND, 0);
        return gc.getTime();
    }
    else
    {
        throw new IllegalArgumentException(hhmmss + " is not a valid time, expecting HH:MM:SS format");
    }
}

Agora você pode fazer duas chamadas de método bem nomeadas que serão bastante autoconvoma.

Outras dicas

Conforme apontado por Kevin, o Regex do Fuzzy Lollipop não atende horários entre as 14:00 e as 19:00.

Para corresponder a um relógio completo de 24 horas, você pode usar isso:

if (hhmmss.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
{
    // Do stuff here
}

A aula a seguir é algo que acabei de criar a partir de algumas das outras respostas. Ele encapsula o comportamento de um 'período de tempo' sem se relacionar com dias específicos. Nosso sistema está usando esta classe para verificar se o horário atual está dentro de uma das nossas janelas de manutenção designadas. ou seja, 05:00:00 - 07:00:00

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
*
* @author Adam Yocum
*/
public class ExclusionTimePeriod {
    private String timeStart;
    private String timeEnd;

    /**
    * @return the timeStart
    */
    public String getTimeStart() {
        return timeStart;
    }

    /**
    * @param timeStart the timeStart to set
    */
    public void setTimeStart(String timeStart) {
        if (timeStart.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
        {
            this.timeStart = timeStart;
        }
        else
        {
            throw new IllegalArgumentException(timeStart + " is not a valid time, expecting HH:MM:SS format");
        }

    }

    /**
    * @return the timeEnd
    */
    public String getTimeEnd() {
        return timeEnd;
    }

    /**
    * @param timeEnd the timeEnd to set
    */
    public void setTimeEnd(String timeEnd) {
        if (timeEnd.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
        {
            this.timeEnd = timeEnd;
        }
        else
        {
            throw new IllegalArgumentException(timeEnd + " is not a valid time, expecting HH:MM:SS format");
        }
    }

    private Date toDate(String hhmmss){
        final String[] hms = hhmmss.split(":");
        final GregorianCalendar gc = new GregorianCalendar();
        gc.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hms[0]));
        gc.set(Calendar.MINUTE, Integer.parseInt(hms[1]));
        gc.set(Calendar.SECOND, Integer.parseInt(hms[2]));
        gc.set(Calendar.MILLISECOND, 0);
        Date date = gc.getTime();
        return date;
    }

    public boolean isNowInPeriod()
    {
        final Date now = new Date();
        return now.after(toDate(getTimeStart())) && now.before(toDate(getTimeEnd()));
    }

    public static void main(String[] args){

        //Test All possible hours
        for(int hour=0;hour<=23;hour++){

            String hourStr = "";
            if(hour<=9){
                hourStr = "0"+hour;
            }else{
                hourStr = ""+hour;
            }

            for(int min=0;min<60;min++){
                String minStr = "";
                if(min<=9){
                    minStr = "0"+min;
                }else{
                    minStr = ""+min;
                }

                for(int sec=0;sec<60;sec++){
                    String secStr = "";
                    if(sec<=9){
                        secStr = "0"+sec;
                    }else{
                        secStr = ""+sec;
                    }

                    String hhmmss = hourStr+":"+minStr+":"+secStr;

                    ExclusionTimePeriod period = new ExclusionTimePeriod();
                    period.setTimeStart(hhmmss);
                    period.setTimeEnd(hhmmss);

                    System.out.println(hhmmss+" Ok");
                }
            }
        }


        //Test isInPeriod functionality
        ExclusionTimePeriod isInTest = new ExclusionTimePeriod();
        isInTest.setTimeStart("10:00:00");
        isInTest.setTimeEnd("10:43:00");

        System.out.println((new Date())+" is between "+isInTest.getTimeStart()+" and "+isInTest.getTimeEnd()+" = "+isInTest.isNowInPeriod());

    }
}

A data de método de turma é falha como escrito. Não permitirá horas em que o dígito do segundo é maior que 3, por exemplo, 18:00:00. Se você alterar para permitir [0-2] [0-9], ele permitirá horários como 29:00:00. Tem uma correção para isso?

tl; dr

LocalTime now = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )
                             .toLocalTime() ;
Boolean isBetween = ( ! now.isBefore( LocalTime.of( 18 , 0 ) )  // "not before" means "is equal to OR after".
                    && 
                    now.isBefore( LocalTime.of( 18 , 30 ) ) ;  // Half-Open, beginning is *inclusive* while ending is *exclusive*.

Usando java.time

Você está usando aulas antigas de data de data que provaram ser mal projetadas, confusas e problemáticas. Eles são agora legado, suplantado pelas classes Java.Time.

LocalTime

Não passem meras strings representando valores da época do dia. Agora temos um tipo para isso, o LocalTime classe.

LocalTime start = LocalTime.of( 18 , 0 );
LocalTime stop = LocalTime.of( 18 , 30 );

Passe essas instâncias para o seu método de utilidade. Esse método não deve ter que fazer a análise, portanto, não há necessidade de jogar a exceção de análise.

public static  boolean isCurrentTimeBetween( LocalTime start , LocalTime stop ) {
…

ZonedDateTime

Um fuso horário é crucial para determinar a data e a hora do dia atuais. Por qualquer momento, a data varia em todo o mundo por zona. Por exemplo, alguns minutos depois da meia -noite em Paris, França é um novo dia enquanto ainda "ontem" em Montréal Quebec.

Especifique a Nome do fuso horário adequado no formato de continent/region, como America/Montreal, Africa/Casablanca, ou Pacific/Auckland. Nunca use a abreviação de 3-4 cartas, como EST ou IST como eles são não Fusos horários verdadeiros, não padronizados, e nem mesmo exclusivos (!).

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.now( z );

Para comparar a época do dia de agora, poderíamos simplesmente extrair um LocalTime a partir desse ZonedDateTime. Mas temos o problema das anomalias, como o horário de verão (DST) e os políticos, redefinindo os fusos horários. Pode não haver nenhuma hora das 6:00 em uma data específica. A solução para esse enigma depende do seu contexto de negócios e de suas regras de negócios. Você pode ignorar o enigma e manter-se literalmente perguntando se o horário atual é entre o tempo de partida do alvo. Ou você pode aplicar o fuso horário ao seu horário de início do dia e deixar ZonedDateTime A classe faz ajustes como achar adequado. Vejamos as duas abordagens.

Ignore anomalias

Primeiro, ignore quaisquer anomalias. Pergunte de maneira simples e literalmente se a hora atual do dia estiver entre o início da meta e a parada do dia.

Podemos extrair um objeto na hora do dia do objeto de data-hora zonado.

LocalTime localTimeNow = zdt.toLocalTime(); // Extract a time-of-day from the zoned date-time object.

Compare isso com o nosso horário de parada. Observe que usamos aqui a abordagem semi-aberta para definir um período de tempo. Nesta abordagem, o começo é inclusive Enquanto o final é exclusivo. Essa abordagem é comum no trabalho de data de data e geralmente é o caminho sábio a percorrer.

Boolean isNowOnOrAfterStart = ( ! localTimeNow.isBefore( start ) ) ;  // A briefer way of asking "is equal to OR is after" is "is not before". 
Boolean isNowBeforeStop = localTimeNow.isBefore( stop );
Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.

Considere anomalias

Em seguida, consideramos anomalias. Aplicamos o horário de início e parada da data atual na mesma fuso horário. Extraímos a data-somente do objeto de data de data zoneado.

LocalDate localDateToday = zdt.toLocalDate();
ZonedDateTime zdtStart = ZonedDateTime.of( localDateToday , start , z );
ZonedDateTime zdtStop = ZonedDateTime.of( localDateToday , stop , z );

Estude a documentação da classe para entender o comportamento de ZonedDateTime.of na resolução de valores de hora do dia inválidos. Não existe uma maneira perfeita de resolver valores não existentes na hora do dia; portanto, você deve decidir se a maneira dessa classe atende às suas regras de negócios.

ZonedDateTime.of

public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)

Obtém uma instância do ZonedDateTime a partir de uma data e hora local. Isso cria uma data de data para a data que corresponde à entrada e hora de entrada o mais próximo possível. As regras do fuso do tempo, como a economia do dia, significam que nem toda data local é válida para a zona especificada, portanto o encontro local pode ser ajustado.

A data local e a primeira combinação para formar uma data de data local. A data de data local é resolvida para um único instante na linha de tempo. Isso é conseguido encontrando um deslocamento válido da UTC/Greenwich para o encontro local, conforme definido pelas regras do ID da zona.

Na maioria dos casos, existe apenas um deslocamento válido para uma data local. No caso de uma sobreposição, quando os relógios são atrasados, há dois compensações válidas. Este método usa o deslocamento anterior normalmente correspondente a "verão".

No caso de uma lacuna, quando os relógios saltam para a frente, não há deslocamento válido. Em vez disso, a data local é ajustada para ser posterior pelo comprimento da lacuna. Para uma mudança típica de poupança de uma hora, o encontro local será movido uma hora depois para o deslocamento normalmente correspondente a "verão".

Aplique a mesma lógica de comparação que vimos acima.

Boolean isNowOnOrAfterStart = ( ! zdt.isBefore( zdtStart ) ) ;  // A briefer way of asking "is equal to OR is after" is "is not before". 
Boolean isNowBeforeStop = zdt.isBefore( zdtStop );
Boolean isNowInTargetZone = ( isNowOnOrAfterStart && isNowBeforeStop ); // Half-Open: beginning is inclusive while ending is exclusive.

Maneira alternativa de fazer a comparação é usar o prático Interval classe do projeto Threeten-Extra. Essa aula sofre uma dor de Instant objetos, que você pode extrair do seu ZonedDateTime objetos. o Instant classe representa um momento na linha do tempo em UTC com uma resolução de nanossegundos (até nove (9) dígitos de uma fração decimal).

Interval interval = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
Boolean isNowInTargetZone = interval.contains( zdt.toInstant() );

Sobre Java.Time

o Java.Time A estrutura é incorporada ao Java 8 e mais tarde. Essas aulas suplantam o velho problemático legado classes de data de data, como java.util.Date, Calendar, & SimpleDateFormat.

o Joda-Time Projeto, agora em Modo de manutenção, aconselha a migração para o Java.Time Aulas.

Para saber mais, consulte o Oracle Tutorial. E Pesquise o excesso de pilha para muitos exemplos e explicações. Especificação é JSR 310.

Onde obter as aulas de Java.Time?

o Threeten-Extra O projeto estende Java.Time com classes adicionais. Este projeto é um terreno de prova para possíveis adições futuras ao Java.Time. Você pode encontrar algumas aulas úteis aqui, como Interval, YearWeek, YearQuarter, e mais.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top