重复事件日期处理冬季 time/summer 时间

Repeating events date handling winter time/summer time

在我的应用程序中,用户可以创建重复事件,例如 "Lunch every saturday 12.00"。

现在我遇到了一个问题,我不确定如何正确处理。如果事件链很大并且具有不同的时区(冬季和夏季时间),列出所有这些事件会显示不同的时间。

例如,日期在服务器端存储为:

2017-10-28T12:00:00.000+02:00

因此,在客户端列出事件时,它可能如下所示:

2017-10-21 12:00:00.000 (parsed from: 2017-10-21T12:00:00.000+02:00)
2017-10-28 12:00:00.000 (parsed from: 2017-10-28T12:00:00.000+02:00)
2017-11-04 11:00:00.000 (parsed from: 2017-11-04T12:00:00.000+02:00)
2017-11-11 11:00:00.000 (parsed from: 2017-11-11T12:00:00.000+02:00)

在第二次和第三次之间,客户端更改为冬令时和+01:00。时间会相应调整,用户可能会认为事件突然开始并提前一小时,即使它是在同一时间开始的。

我希望它在客户端解析时始终显示事件时间 (12:00),而不管时区如何。如果可以用 Joda 时间提取信息,另一种解决方案是声明以夏令时/冬令时显示。

我会使用 java8 LocalDateTime class 服务器端并忽略时区,如果那是你想要的。

LocalDateTime 专门用于处理您想要在没有时区信息的情况下声明日期(和时间)的情况。

如果您的数据库不支持存储无时区日期时间,您始终可以每次使用相同的时区(例如,UTC)将其与服务器上的时间戳相互转换。只要确保将它发送给客户端,并从客户端接收它,作为 LocalDateTime。

编辑:针对以下评论,如果您想根据用户当地时区向用户显示日期,最好将 ZonedDateTime 存储在标准时区(例如,UTC)中并存储Locale 个用户。然后,您可以使用 Locale 将日期转换为用户特定的日期格式,这将记住 DST 和其他时区更改。

EDIT2:要转换日期时间,您可以使用区域 ZoneId。区域设置仍可用于格式化。

java.time

几乎正确,但不完全正确。

Joda-Time 项目确实处于维护模式,其创建者建议迁移到 java.time classes 内置于 Java 8 及更高版本中。 Joda-Time 和 java.time JSR 310 都由同一个人 Stephen Colebourne 领导。所以迁移相当容易,因为许多核心思想都是相同的。

如果您试图表示 "lunch every Saturday at noon",您 不能 可靠地存储未来的确切时刻。世界各地的政客都表现出重新定义时区的偏好。他们这样做的频率令人惊讶,而且常常在没有任何警告的情况下这样做。因此,根据时钟上的时间,您无法知道 "noon" 将在何时。时钟定义的变化意味着您正在追逐一个移动的目标。

因此,"lunch every Saturday at noon" 需要跟踪星期几和一天中的时间。 Java 两者都有 class。

DayOfWeek dayOfWeek = DayOfWeek.SATURDAY ;  // Enum with seven predefined objects, Monday-Saturday.

LocalTime localTime = LocalTime.of( 12 , 0 ) ;  // Noon.

您还需要存储您说 "noon" 时预期的时区。每天中午在印度比在法国早得多,在魁北克甚至更晚。因此,一天中的时间仅在特定时区的上下文中才有意义。

指定 proper time zone name in the format of continent/region, such as America/Montreal, Africa/CasablancaPacific/Auckland。切勿使用 ESTIST 等 3-4 字母缩写,因为它们 不是 真正的时区,未标准化,甚至不是唯一的(!)。

ZoneId zoneId = ZoneId.of( "America/Montreal" ) ;

您可以设计出一系列这样的午餐,确定每个午餐的确切时间(时间轴上的点)。但你只能暂时这样做。鉴于政府已证明他们愿意重新定义其时区与 UTC 的偏移量,并且仅预警数月、数周、days, or even hours,您无法存储这些预测。在运行时动态计算它们。

今天决定。这当然需要一个时区。对于任何给定时刻,日期因地区而异。

LocalDate today = LocalDate.now( zoneId ) ;

获取下一个星期六,如果已经是星期六,则留在今天。

LocalDate firstSaturday = today.with( TemporalAdjusters.nextOrSame( dayOfWeek ) ) ;

确定该日期该区域中午时间轴上的点。

ZonedDateTime firstLunch = ZonedDateTime.of( firstSaturday , localTime , zoneId ) ;

您可以通过提取 Instant 查看与 UTC 相同的时刻。

Instant firstLunchInstant = firstLunch.toInstant() ;

您可以通过在 LocalDate 上增加一周来继续此预测,并重复上述步骤以请求下一个日期的中午。如果该时间恰好在该区域的该日期无效(例如夏令时 "Spring-ahead" 转换),ZonedDateTime class 会相应调整。请务必阅读文档以了解其调整算法。

int weeksToProject = 10 ;  // Run out 10 weeks.
List< ZonedDateTime > lunches = new ArrayList<>( weeksToProject ) ;
LocalDate localDate = firstSaturday ;
for( int i = 0 ; i < 10 ; i ++ ) {
    localDate = localDate.plusWeeks( i ) ;
    ZonedDateTime zdt = ZonedDateTime.of( localDate , localTime , zoneId ) ;  
    lunches.add( zdt ) ;
}

数据库

至于在数据库中存储"lunch every Saturday at noon":

  • 时间可以存储在SQL-标准类型TIME WITHOUT TIME ZONE类型中。
    myPreparedStatement.setObject( … , localTime ) ;
    LocalTime localTime = myResultSet.getObject( … , LocalTime.class ) ;
  • 可以使用英语硬编码 DayOfWeek 枚举值名称(例如 MONDAY)将星期几作为文本存储在 VARCHAR 中。或者您可以将星期几存储为整数。对于数字,我强烈建议周一至周日使用 ISO 8601 标准 1-7。请务必清楚地记录您的号码的含义,以供后代使用。
  • 时区可以作为文本存储在 standard IANA Continent/Region format 中的 VARCHAR 中。

ZonedDateTime 对象应该只是暂时的,根据需要动态创建。所以你不会存储它们。


关于java.time

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

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

ThreeTen-Extra project 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.