【问题标题】:Proper validation of time intervals within the same day正确验证同一天内的时间间隔
【发布时间】:2017-04-18 06:40:42
【问题描述】:

我有一个方法如下:

public void storeAppointment(int year,
    int monthOfYear,
    int dayOfMonth,
    int hourOfDayFrom,
    int minuteFrom,
    int hourOfDayUntil, int minuteUntil) {

    Calendar appointmentCalendar = Calendar.getInstance();
    appointmentCalendar.set(year, monthOfYear, dayOfMonth);
    TimeZone tz = appointmentCalendar.getTimeZone();
    DateTimeZone jodaTz = DateTimeZone.forID(tz.getID());
    DateTime appointmentDateTime = new DateTime(appointmentCalendar.getTimeInMillis(), jodaTz);
    LocalDate localDate = appointmentDateTime.toLocalDate();

    // At this point I have the appointment date.  

    // Should e.g. throw an exception for invalid time interval
    validate(hourOfDayFrom, minuteFrom, hourOfDayUntil, minuteUntil);

    // set proper times for calendar
    appointmentCalendar.set(Calendar.HOUR, hourOfDay);
    appointmentCalendar.set(Calendar.MINUTE, minute);
    // store date and times  
    // Should I update the localDate instead of the appointmentCalendar?
}    

问题:

  1. 我应该如何验证小时/分钟?应该包括实际日期还是不相关?

  2. 我应该更新localDate而不是appointmentCalendar吗?

【问题讨论】:

    标签: java date jodatime localdate


    【解决方案1】:

    你在这里工作太辛苦了。

    避免使用旧的日期时间类

    避免使用麻烦的旧日期时间类,例如DateCalendar。现在是遗留的,被 java.time 类所取代。

    不要混合日期时间库

    不要混合使用不同的日期时间库。如果使用 Joda-Time,则不需要java.util.Date,也不需要java.util.Calendar。如果使用 java.time 类,则不需要 Joda-Time 也不需要 java.util.Date/.Calendar

    Joda-Time 项目现在位于 maintenance mode,建议迁移到 java.time。

    业务规则

    应该包括实际日期还是不相关?

    我们无法告诉您是否考虑日期。这取决于您的业务规则。

    例如,如果您的企业总是从中午到 13:00 午休,那么任何在该时间标有时间的企业记录都必须是无效的。如果您每天都采用相同的午休时间,那么这里的日期就无关紧要了。

    但是,如果您的场景类似于记录工人的工作时间,那么任何两个时间都不应在同一天重叠。在这种情况下,您必须考虑日期。

    ZonedDateTime

    我应该更新 localDate 而不是约会日历吗?

    a) 如上所述,您不应混合使用这些类。

    b) 在 Joda-Time 和 java.time 中,LocalDate 类表示没有时间的仅日期值。和它的兄弟Local… 类一样,它故意没有时区的概念。所以根本不适合你的目的。

    您需要使用ZonedDateTime 来表示在您的预期时区内有意义的日期和时间。

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

    ZoneId z = ZoneId.of( "America/Montreal" );
    LocalDate ld = LocalDate.of( 2016 , 1 , 23 );
    LocalTime lt = LocalTime.of( 12 , 30 );
    ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z );
    

    获取当前时刻:

    Instant instant = Instant.now();  // UTC.
    ZonedDateTime zdt = instant.atZone( z );
    

    …或者,作为捷径…

    ZonedDateTime zdt = ZonedDateTime.now( z );
    

    此外,java.time 类是immutable objects。因此,您不会更改(“变异”)它们的值。相反,您会根据原始值实例化一个新对象。

    间隔

    您可能会发现 ThreeTen-Extras 项目中的 Interval 类在这里会有所帮助。

    Interval a = Interval.of( zdtStart.toInstant() , zdtStop.toInstant() );
    

    您可以使用containsoverlapsenclosesisBeforeisAfter 等方法比较区间。

    Boolean overlaps = a.overlaps( b );
    

    传递对象

    与其传递零散数据的简单基元,不如传递对象。

    因此,不要传递诸如月份、日期和小时的整数之类的原语,而是传递诸如InstantOffsetDateTimeZonedDateTime 之类的 java.time 对象。如果您只有日期或只有时间,请传递 LocalDateLocalTime

    默认时区

    要获取 JVM 当前的默认时区,请调用 ZoneId.systemDefault

    但如果重要的话,您应该询问用户他们想要/预期的时区。该默认值可以随时由在该 JVM 中运行的任何应用程序的任何线程中的任何代码更改。

    【讨论】:

    • 确实很有帮助!当我试图通过Calendar.getInstance().getTimeZone() 进行通用时,您正在“硬编码”特定时区。如何替换它以便完全删除日历?
    • @Jim 请参阅最后添加的关于默认时区的部分。
    猜你喜欢
    • 2015-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    相关资源
    最近更新 更多