在我的应用程序中,用户可以创建重复的事件,例如“每个星期六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时间提取该信息,则显示夏令时/冬令时。
答案 0 :(得分:3)
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/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的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 ) ;
}
关于在数据库中存储“每周六中午的午餐”:
TIME WITHOUT TIME ZONE
类型中。myPreparedStatement.setObject( … , localTime ) ;
LocalTime localTime = myResultSet.getObject( … , LocalTime.class ) ;
DayOfWeek
枚举值名称(例如MONDAY
)将星期几作为文本存储在VARCHAR中。或者您可以将整周的数字存储为星期几。对于一个数字,我强烈建议在周一至周日使用ISO 8601标准1-7。请务必清楚地记录您的号码的后代含义。Continent/Region
format中的VARCHAR中。 ZonedDateTime
个对象应该只是瞬态的,根据需要动态创建。所以你不会存储它们。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和&amp; SimpleDateFormat
现在位于Joda-Time的maintenance 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的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
我会使用java8 LocalDateTime类服务器端并忽略时区,如果这是你想要的。
LocalDateTime完全用于处理您想要在没有时区信息的情况下说明日期(和时间)的情况。
如果您的数据库不支持存储无时区的日期时间,您可以随时使用相同的时区(例如,UTC)将其转换为服务器上的时间戳。只需确保将其发送到客户端,然后从客户端接收,作为LocalDateTime。
编辑:在回复下面的评论时,如果您想根据当地时区向用户显示日期,最好将ZonedDateTime存储在标准时区(例如UTC)中并存储{{ 3}}用户。然后,您可以使用区域设置将日期转换为用户特定的日期格式,这将记住DST和其他时区更改。
EDIT2:要转换日期时间,您可以使用区域Locale。 Locale对格式化仍然有用。