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

时间:2017-10-27 10:53:35

标签: java jodatime

在我的应用程序中,用户可以创建重复的事件,例如“每个星期六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时间提取该信息,则显示夏令时/冬令时。

2 个答案:

答案 0 :(得分:3)

java.time

Answer by Buurman几乎是正确的,但并不完全正确。

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

如果您尝试代表“每周六中午午餐”,那么无法可靠地存储将来的确切时刻。世界各地的政治家们都倾向于重新定义他们的时区。他们经常出人意料地这样做,而且经常这么做很少发出警告。因此,就时钟的时间而言,你无法知道“中午”的时间。时钟的变化定义意味着你正在追逐一个移动的目标。

因此,“每周六中午午餐”需要跟踪一周中的某一天并跟踪一天中的时间。 Java有两个类。

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

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

当您说“中午”时,您还需要存储预期的时区。每天,中午在印度的发生时间比在法国发生的要早得多,甚至在魁北克也会发生。因此,在特定时区的背景下,时间只有意义。

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

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

您可以预测一系列这些午餐,确定每个午餐的确切时刻(时间轴上的点)。但你只能临时这样做。鉴于政府已经证明他们愿意重新定义其时区的偏移量,只需预警几个月,几周,days甚至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添加一周来继续此预测,并重复上述步骤以在下一个日期请求中午。如果该时间段中该时间恰好在该区域中的该日期无效(例如夏令时“提前弹出”切换),则ZonedDateTime类会相应地进行调整。请务必阅读文档以了解其调整算法。

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

数据库

关于在数据库中存储“每周六中午的午餐”:

  • 时间可以存储在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框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

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

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

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

从哪里获取java.time类?

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

答案 1 :(得分:2)

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

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

如果您的数据库不支持存储无时区的日期时间,您可以随时使用相同的时区(例如,UTC)将其转换为服务器上的时间戳。只需确保将其发送到客户端,然后从客户端接收,作为LocalDateTime。

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

EDIT2:要转换日期时间,您可以使用区域Locale。 Locale对格式化仍然有用。