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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2473034/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 21:21:47  来源:igfitidea点击:

See if the current time falls within a specific range of time in the current day in Java

javadatetimedatetemporal

提问by ring bearer

I am sure this was done 1000 times in 1000 different places. The question is I want to know if there is a better/standard/faster way to check if current "time" is between two time values given in hh:mm:ssformat. For example, my big business logic should not run between 18:00:00 and 18:30:00. So here is what I had in mind:

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

Example test case:

示例测试用例:

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

What I am looking for is code that is better

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

  • in performance
  • in looks
  • in correctness
  • 在表现
  • 在看起来
  • 正确

What I am not looking for

我不是在寻找什么

  • third-party libraries
  • Exception handling debate
  • variable naming conventions
  • method modifier issues
  • 第三方库
  • 异常处理辩论
  • 变量命名约定
  • 方法修饰符问题

回答by

this is all you should need to do, this method is loosely coupled from the input and highly coherent.

这就是您需要做的全部,此方法与输入松散耦合且高度连贯。

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

how you get the Date objects for start and end is irrelevant to comparing them. You are making things way more complicated than you need to with passing Stringrepresentations around.

如何获取开始和结束的日期对象与比较它们无关。通过传递String表示,您使事情变得比您需要的更复杂。

Here is a better way to get the start and end dates, again loosely coupled and highly coherent.

这是获取开始日期和结束日期的更好方法,同样是松散耦合且高度一致。

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

Now you can make two well named method calls that will be pretty self documenting.

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

回答by Pineechio

As pointed out by Kevin, Fuzzy Lollipop's Regex won't pick up times between 14:00 and 19:00.

正如 Kevin 所指出的,Fuzzy Lollipop 的 Regex 不会在 14:00 到 19:00 之间接听时间。

To get match a full 24 hour clock, you can use this:

要匹配完整的 24 小时时钟,您可以使用以下命令:

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

回答by Adam Yocum

The following Class is something I just created out of some of the code from other answers. It encapsulates the behavior of a 'time period' without relating to specific days. Our system is using this Class to check if the current time is within one of our designated maintenance windows. i.e. 05:00:00 - 07:00:00

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

    }
}

回答by Basil Bourque

tl;dr

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

Using java.time

使用 java.time

You are using old date-time classes that have proven to be poorly designed, confusing, and troublesome. They are now legacy, supplanted by the java.time classes.

您正在使用旧的日期时间类,这些类已被证明设计不佳、令人困惑且麻烦。它们现在是legacy,被 java.time 类取代。

LocalTime

LocalTime

Do not pass mere strings representing time-of-day values. We now have a type for that, the LocalTimeclass.

不要只传递表示时间值的字符串。我们现在有一个类型,LocalTime类。

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

Pass those instances to your utility method. That method should not have to do any parsing, so no need to throw the parsing exception.

将这些实例传递给您的实用程序方法。该方法不应该做任何解析,所以不需要抛出解析异常。

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

ZonedDateTime

ZonedDateTime

A time zone is crucial in determining the current date and time-of-day. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris Franceis a new day while still “yesterday” in Montréal Québec.

时区对于确定当前日期和时间至关重要。对于任何给定时刻,日期因地区而异。例如,在法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天” 。

Specify a proper time zone namein the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as ESTor ISTas they are nottrue time zones, not standardized, and not even unique(!).

以、、 或等格式指定正确的时区名称。永远不要使用 3-4 个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。continent/regionAmerica/MontrealAfrica/CasablancaPacific/AucklandESTIST

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

To compare the time-of-day of now, we could simply extract a LocalTimefrom that ZonedDateTime. But we have the problem of anomalies, such as Daylight Saving Time (DST) and politicians redefining time zones. There may not be any 6 PM hour on a particular date. The solution to this conundrum depends on your business context and your business rules. You could either ignore the conundrum and stick with literally asking if the current time is between your target start-stop time. Or you could apply the time zone to your start-stop times-of-day of day and let ZonedDateTimeclass make adjustments as it sees fit. Let's look at both approaches.

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

Ignore anomalies

忽略异常

First, ignore any anomalies. Ask simply and literally if the current time-of-day is between the target start and stop times-of-day.

首先,忽略任何异常。从字面上简单地询问当前时间是否在目标开始时间和停止时间之间。

We can extract a time-of-day object from the zoned date-time object.

我们可以从分区的日期时间对象中提取时间对象。

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

Compare that to our stop-start times-of-day. Note that we use here the Half-Open approach to defining a span of time. In this approach the beginning is inclusivewhile the ending is exclusive. This approach is common in date-time work and generally is the wise way to go.

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

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.

Consider anomalies

考虑异常

Next we consider any anomalies. We apply the start and stop times-of-day to the current date within the same time zone. We extract the date-only from the zoned date-time object.

接下来我们考虑任何异常。我们将开始和结束时间应用于同一时区内的当前日期。我们从分区的日期时间对象中提取仅日期。

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

Study the class documentation to understand the behavior of ZonedDateTime.ofin resolving invalid time-of-day values. There is no perfect way to resolve nonexistent time-of-day values, so you must decide if this class' way meets your business rules.

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

ZonedDateTime.of

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

Obtains an instance of ZonedDateTime from a local date and time. This creates a zoned date-time matching the input local date and time as closely as possible. Time-zone rules, such as daylight savings, mean that not every local date-time is valid for the specified zone, thus the local date-time may be adjusted.

The local date time and first combined to form a local date-time. The local date-time is then resolved to a single instant on the time-line. This is achieved by finding a valid offset from UTC/Greenwich for the local date-time as defined by the rules of the zone ID.

In most cases, there is only one valid offset for a local date-time. In the case of an overlap, when clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to "summer".

In the case of a gap, when clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to "summer".

ZonedDateTime.of

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

从本地日期和时间获取 ZonedDateTime 的实例。这将创建一个尽可能与输入的本地日期和时间匹配的分区日期时间。时区规则,例如夏令时,意味着并非每个本地日期时间都对指定区域有效,因此本地日期时间可能会被调整。

本地日期时间和第一次合并形成本地日期时间。然后将本地日期时间解析为时间线上的单个时刻。这是通过为区域 ID 的规则定义的本地日期时间查找与 UTC/格林威治的有效偏移量来实现的。

在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当时钟回退时,有两个有效的偏移量。此方法使用通常对应于“夏天”的较早偏移量。

在有间隙的情况下,当时钟向前跳跃时,没有有效的偏移量。取而代之的是,本地日期时间被调整为延迟间隔的长度。对于典型的一小时夏令时更改,本地日期时间将在一小时后移动到通常对应于“夏天”的偏移量中。

Apply the same comparison logic as we saw above.

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

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.

Alternative way to make the comparison is to use the handy Intervalclass from the ThreeTen-Extra project. That class takes a pain of Instantobjects, which you can extract from your ZonedDateTimeobjects. The Instantclass represents a moment on the timeline in UTCwith a resolution of nanoseconds(up to nine (9) digits of a decimal fraction).

进行比较的另一种方法是使用IntervalThreeTen-Extra 项目中的方便类。那个类需要Instant对象的痛苦,你可以从你的ZonedDateTime对象中提取。该Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多小数的9个位数)。

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


About java.time

关于 java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310

Where to obtain the java.time classes?

从哪里获得 java.time 类?

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by Kevin

The dateFromHourMinSec method is flawed as written. It won't allow any hours where the seconde digit is greater than 3, e.g. 18:00:00. If you change it to allow [0-2][0-9] it will allow times such as 29:00:00. Have a fix for that?

dateFromHourMinSec 方法有缺陷。它不允许第二位大于 3 的任何小时,例如 18:00:00。如果您将其更改为允许 [0-2][0-9],它将允许诸如 29:00:00 之类的时间。有解决办法吗?