Посмотрите, попадает ли текущее время в определенный диапазон времени в текущий день на Java - PullRequest
10 голосов
/ 18 марта 2010

Я уверен, что это было сделано 1000 раз в 1000 разных местах. Вопрос в том, что я хочу знать, есть ли лучший / стандартный / более быстрый способ проверить, находится ли текущее «время» между двумя значениями времени, заданными в формате hh:mm:ss. Например, моя логика большого бизнеса не должна работать между 18:00:00 and 18:30:00. Итак, вот что я имел в виду:

 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));
 }

Пример теста:

  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");
  }

Мне нужен код, который лучше

  • в исполнении
  • во взглядах
  • в правильности

Что я не ищу

  • сторонние библиотеки
  • Дебаты по обработке исключений
  • соглашения об именах переменных
  • Проблемы с модификатором метода

Ответы [ 5 ]

20 голосов
/ 18 марта 2010

это все, что вам нужно сделать, этот метод слабо связан с входом и очень когерентен.

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

то, как вы получаете объекты Date для начала и конца, не имеет отношения к их сравнению. Вы делаете вещи намного сложнее, чем нужно, передавая String представления вокруг.

Вот лучший способ получить даты начала и окончания, опять же слабо связанные и очень связные.

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");
    }
}

Теперь вы можете сделать два хорошо названных вызова метода, которые будут довольно самодокументированы.

4 голосов
/ 04 ноября 2010

Как указал Кевин, регулярное выражение Пушистого леденца не будет работать с 14:00 до 19:00.

Чтобы получить совпадение с полными 24-часовыми часами, вы можете использовать это:

if (hhmmss.matches("^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"))
{
    // Do stuff here
}
1 голос
/ 10 апреля 2012

Следующий класс - это то, что я только что создал из некоторого кода из других ответов. Он инкапсулирует поведение «периода времени» без привязки к конкретным дням. Наша система использует этот класс, чтобы проверить, находится ли текущее время в одном из наших назначенных интервалов технического обслуживания. т.е. с 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());

    }
}
0 голосов
/ 13 февраля 2017

ТЛ; др

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*.

Использование java.time

Вы используете старые классы даты и времени, которые оказались плохо продуманными, запутанными и хлопотными. Теперь они legacy , заменены классами java.time.

LocalTime

Не передавайте простые строки, представляющие значения времени дня. Теперь у нас есть тип для этого, LocalTime класс.

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

Передайте эти экземпляры вашему служебному методу. Этот метод не должен выполнять какой-либо синтаксический анализ, поэтому нет необходимости генерировать исключение синтаксического анализа.

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

ZonedDateTime

Часовой пояс имеет решающее значение при определении текущей даты и времени суток. В любой момент времени дата меняется по всему земному шару в зависимости от зоны. Например, через несколько минут после полуночи в Париж Франция - это новый день, но все еще "вчера" в Монреаль Квебек .

Укажите собственное имя часового пояса в формате continent/region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 3-4-буквенное сокращение, например EST или IST, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).

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

Чтобы сравнить текущее время, мы могли бы просто извлечь LocalTime из этого ZonedDateTime. Но у нас есть проблема аномалий, таких как переход на летнее время (DST) и политики, переопределяющие часовые пояса. Не может быть 6 часов вечера на определенную дату. Решение этой головоломки зависит от вашего бизнес-контекста и ваших бизнес-правил. Вы можете либо игнорировать головоломку, но и буквально спрашивать, находится ли текущее время между целевым временем начала и окончания. Или вы можете применить часовой пояс к времени начала и окончания дня и позволить классу ZonedDateTime вносить коррективы по своему усмотрению. Давайте посмотрим на оба подхода.

Игнорировать аномалии

Во-первых, игнорируйте любые аномалии. Спросите просто и буквально, находится ли текущее время суток между целевым временем начала и окончания.

Мы можем извлечь объект времени дня из зонированного объекта даты и времени.

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

Сравните это с нашим временем остановки. Обратите внимание, что мы используем здесь подход Half-Open для определения промежутка времени. В этом подходе начало включительно , а окончание исключительно . Этот подход распространен в работе с датой и временем и, как правило, является мудрым решением.

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.

Рассмотрим аномалии

Далее мы рассмотрим любые аномалии. Мы применяем время начала и окончания дня к текущей дате в том же часовом поясе. Мы извлекаем только дату из зонированного объекта даты и времени.

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

Изучите документацию класса, чтобы понять поведение ZonedDateTime.of при разрешении неверных значений времени дня. Не существует идеального способа разрешения несуществующих значений времени дня, поэтому вы должны решить, соответствует ли этот класс вашим бизнес-правилам.

ZonedDateTime.of

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

Получает экземпляр ZonedDateTime из локальной даты и времени. Это создает зонированную дату и время, максимально соответствующие входной локальной дате и времени. Правила часового пояса, такие как переход на летнее время, означают, что не каждая местная дата и время действительны для указанной зоны, поэтому можно настроить местное время и дату.

Местное время даты и первое объединенное, чтобы сформировать местное время даты. Затем локальная дата-время разрешается в один момент времени на временной шкале. Это достигается путем нахождения действительного смещения от UTC / Гринвич для локальной даты и времени, как определено правилами идентификатора зоны.

В большинстве случаев существует только одно допустимое смещение для локальной даты и времени. В случае перекрытия, когда часы сбрасываются, есть два допустимых смещения. Этот метод использует более раннее смещение, обычно соответствующее «лету».

В случае разрыва, когда часы прыгают вперед, действительного смещения нет. Вместо этого местная дата-время корректируется, чтобы быть позже на длину промежутка. Для типичного перехода на летнее время в течение одного часа местная дата и время будут перенесены на один час позже в смещение, обычно соответствующее значению «лето».

Примените ту же логику сравнения, как мы видели выше.

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.

Альтернативный способ сравнения - использовать удобный класс Interval из проекта ThreeTen-Extra. Этот класс переносит Instant объектов, которые вы можете извлечь из ваших ZonedDateTime объектов. Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).

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

О java.time

Фреймворк java.time встроен в Java 8 и более поздние версии. Эти классы заменяют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , в настоящее время находящийся в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Где получить классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 04 ноября 2010

Метод dateFromHourMinSec ошибочен, как написано. Это не позволит использовать часы, если вторая цифра больше 3, например 18:00:00. Если вы измените его, чтобы разрешить [0-2] [0-9], это позволит время, например, 29:00:00. Есть решение для этого?

...