在Java中查看当前时间是否在当天的特定时间范围内

时间:2010-03-18 20:09:10

标签: java datetime date temporal

我确信这在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");
  }

我正在寻找的是更好的代码

  • in performance
  • in look
  • 正确无误

我不想要的东西

  • 第三方图书馆
  • 异常处理辩论
  • 变量命名约定
  • 方法修饰符问题

6 个答案:

答案 0 :(得分:20)

这就是你应该做的所有事情,这个方法与输入松散耦合并且高度连贯。

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

现在你可以做两个名字很好的方法调用,这些方法调用非常自我记录。

答案 1 :(得分:4)

正如凯文所指出的,模糊棒棒糖的正则表达式不会在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
}

答案 2 :(得分:1)

以下类是我刚从其他答案的一些代码中创建的。它封装了“时间段”的行为,与特定日期无关。我们的系统使用此类来检查当前时间是否在我们指定的维护窗口之内。即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());

    }
}

答案 3 :(得分:1)

午夜问题

其他答案没有提及-操作人员也没有询问-但是您应该真正考虑间隔何时跨越午夜

时间很难。我特意留下了代码的“长篇”版本,并且没有缩写逻辑条件,以便尽可能清楚地说明原因和原因。

/**
 * Takes into consideration that the interval may span accross midnight
 *
 * @param clock to make unit testing easier, just replace for Clock.systemUTC() in your code 
 * @param start the interval start
 * @param end the interval end
 * @return true if "now" is inside the specified interval
 */
static boolean isNowBetweenLocalTime(Clock clock, final LocalTime start, final LocalTime end) {
    LocalTime now = LocalTime.now(clock);

    // if interval crosses midnight
    if (end.isBefore(start)) {
        if (now.isAfter(start) && now.isAfter(end)) {
            return true;
        }
        if (now.isBefore(start) && now.isBefore(end)) {
            return true;
        }
        return false;
    }

    // if interval does not cross midnight
    if (end.isAfter(start)) {
        if (now.isAfter(start) && now.isBefore(end)) {
            return true;
        }
        return false;
    }

    return false; // interval is 0 so start and end always outside interval
}

细节并不总是错误的。此方法将被埋在实用程序类中,两年后,您将感谢您对它的理解!

答案 4 :(得分:0)

dateFromHourMinSec方法存在缺陷。它不允许任何小时数大于3的小时,例如18:00:00。如果将其更改为允许[0-2] [0-9],则允许​​时间为29:00:00。 有解决方法吗?

答案 5 :(得分:0)

TL;博士

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

时区对于确定当前日期和时间至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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.

将其与我们的停止开始时间进行比较。请注意,我们在这里使用半开放方法来定义时间跨度。在这种方法中,开头是包含,而结尾是独占。这种方法在日期工作中很常见,通常是明智的选择。

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的实例。   这将创建一个与输入本地日期和时间尽可能匹配的分区日期时间。时区规则(例如夏令时)意味着并非每个本地日期时间对指定区域都有效,因此可以调整本地日期时间。

     

本地日期时间和第一个组合形成本地日期时间。然后将本地日期时间解析为时间线上的单个瞬间。这是通过从区域ID规则定义的本地日期时间中找到UTC / Greenwich的有效偏移量来实现的。

     

在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当设置回时,有两个有效的偏移。此方法使用通常对应于&#34; summer&#34;。

的早期偏移量      

在间隙的情况下,当时钟向前跳跃时,没有有效的偏移。而是将本地日期时间调整为稍后的间隙长度。对于典型的一小时夏令时更改,本地日期时间将在一小时后移动到通常对应于&#34;夏天&#34;的偏移量。

应用我们在上面看到的相同的比较逻辑。

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.

进行比较的另一种方法是使用ThreeTen-Extra项目中的方便Interval类。该类会占用Instant个对象,您可以从ZonedDateTime个对象中提取这些对象。 Instant类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。

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

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore