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

whhtz7ly  于 2022-12-28  发布在  Java
关注(0)|答案(6)|浏览(144)

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

我所寻找的是更好的代码

  • 演出中
  • 外貌
  • 不正确

我不想看到的是

  • 第三方库
  • 异常处理争论
  • 变量命名约定
  • 方法修饰符问题
iqih9akk

iqih9akk1#

dateFromHourMinSec方法在编写时存在缺陷。它不允许第二位数字大于3的任何小时数,例如18:00:00。如果您将其更改为允许[0-2][0-9],它将允许29:00:00这样的时间。是否有修复程序?

jv4diomz

jv4diomz2#

这就是你所需要做的,这个方法与输入是松散耦合的,并且是高度一致的。

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

如何获取Start和End的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");
    }
}

现在,您可以进行两个命名良好的方法调用,它们将是非常自文档化的。

6yt4nkrj

6yt4nkrj3#

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 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。切勿使用3 - 4个字母的缩写,例如ESTIST,因为它们 * 不是 * 真正的时区,没有标准化,甚至不唯一(!)。

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

为了比较现在的时间,我们可以简单地从ZonedDateTime中提取一个LocalTime。但是我们有异常的问题,如夏令时(夏令时)和政治家重新定义时区。在特定日期可能没有任何下午6点。这个难题的解决方案取决于您的业务环境和业务规则。您可以忽略这个难题,直接询问当前时间是否在目标起止时间之间,或者将时区应用于起止时间,让ZonedDateTime类根据需要进行调整,让我们来看看这两种方法。

忽略异常

首先,忽略任何异常情况,简单而直接地询问当前时间是否在目标开始时间和目标停止时间之间。
我们可以从zoned date-time对象中提取一个time-of-day对象。

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.

考虑异常

接下来,我们考虑任何异常情况。我们将一天中的开始时间和结束时间应用于同一时区内的当前日期。我们从zoned date-time对象中只提取日期。

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

研究类文档以了解ZonedDateTime.of在解析无效时间值时的行为。没有完美的方法来解析不存在的时间值,因此您必须确定此类的方法是否符合您的业务规则。

    • 分区日期时间/**

public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
从本地日期和时间获取ZonedDateTime的示例。这将创建与输入的本地日期和时间尽可能匹配的分区日期-时间。时区规则(如夏令时)意味着并非每个本地日期-时间都对指定的区域有效,因此可以调整本地日期-时间。
本地日期时间和第一个组合以形成本地日期时间。然后,本地日期时间被解析为时间线上的单个时刻。这是通过查找区域ID规则定义的本地日期时间相对于UTC/Greenwich的有效偏移量来实现的。
在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当时钟向后设置时,有两个有效偏移量。此方法使用通常对应于"summer"的较早偏移量。
在间隔的情况下,当时钟向前跳转时,没有有效的偏移量。相反,本地日期-时间被调整为晚间隔的长度。对于典型的一小时夏令时更改,本地日期-时间将被移动到通常对应于"summer"的偏移量的一小时后。
应用与我们上面看到的相同的比较逻辑。

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.时间

java.time框架内置于Java 8及更高版本中,这些类取代了麻烦的旧legacy日期-时间类,如java.util.DateCalendarSimpleDateFormat
Joda-Time项目(现在在maintenance mode中)建议迁移到java.time类。
要了解更多信息,请参阅Oracle Tutorial。并搜索堆栈溢出以获取许多示例和解释。规范是JSR 310
从哪里获得java. time类?

      • Java SE 8SE 9**及更高版本
  • 内置。
  • 标准Java API的一部分,捆绑了实现。
  • Java 9添加了一些次要特性和修复。
      • Java SE 6SE 7**
  • 大多数java.time功能都是在ThreeTen-Backport中向后移植到Java 6和7的。
  • Android
  • ThreeTenABP项目专门针对Android调整了 * ThreeTen-Backport *(如上所述)。
  • 参见How to use ThreeTenABP…

ThreeTen-Extra项目用额外的类扩展了java.time。这个项目是将来可能添加到java.time的一个试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuartermore

sg2wtvxw

sg2wtvxw4#

正如Kevin所指出的,Fuzzy Lollipop的Regex不会在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
}
nsc4cvqm

nsc4cvqm5#

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

    }
}
pjngdqdw

pjngdqdw6#

午夜的问题
其他答案没有提到它-OP也没有问-但你真的应该考虑间隔跨越午夜的时间
时间很紧,我特意留下了代码的“长”版本,没有缩写逻辑条件,以使它尽可能清楚地说明是什么和为什么。

/**
 * 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
}

冗长并不总是错的。这个方法将被隐藏在一个实用类中,两年后你会感谢自己理解了它的作用!

相关问题