在Java中的夏令时

5
请看能否告诉我如何处理我的DST问题。
首先,我的应用是一个全球用户的物流系统,因此涉及时区问题。当设置用户本地预订日期时,我将按照以下方式处理:
1. 当用户登录应用程序时,我们可以根据登录IP获取用户的时区,但它只是一个偏移量(我记不住这个东西的术语),例如“GMT + 08”(北京)或“GMT-06”(芝加哥)。
2. 在用户保存预订之前,我们需要设置预订本地日期,因为我无法直接获取用户本地日期。所以我会先获取服务器日期(在我的情况下是北京时间),然后根据服务器日期和用户时区计算本地日期。例如,如果用户时区为“GMT-08”,服务器日期为2013年8月29日17:45:00,服务器时区为“GMT+08”,那么我将使用服务器日期-8-8,结果将为2013年8月29日01: 45:00。但是,由于我没有考虑DST,计算出的本地日期将与实际日期有所差异。例如,在旧金山现在,实际本地日期将比我使用这种方式计算时提前一个小时。
我发现Java TimeZone已经考虑了DST问题,但我需要在构造TimeZone时提供“位置”名称(例如US / Alaska,Pacific / Apia)。而在我的情况下,我只能获得偏移量。所以你能告诉我如何解决我的DST问题吗?
3个回答

6

是的,您应该在Java 8中使用Joda-Time或新的java.time包(受Joda-Time启发)。

偏移量是表示某个日期时间值的距离UTC(GMT)的小时-分钟-秒数。 西海岸为-08:00(忽略 夏令时的荒谬性),意味着比UTC早8个小时。

请注意,java.time在其初始版本中存在一个小错误,无法处理仅具有小时的偏移(例如+08)而没有分钟(例如+08:00)。

时区是特定地区人民使用的偏移量过去、现在和未来变化的历史。

使用适当的时区名称(大多数为洲/城市)。避免使用三个或四个字母的代码,例如EST,这些代码既不标准化也不唯一。

java.util.Date没有时区,而Joda-Time DateTime有时区。

要获取Web浏览器的时区,请参见此问题。但通常,这并不起作用。正如您可能已经看到的那样,许多网站要求用户选择时区。

您确切的用例很令人困惑。一般来说,最好的方法是将日期时间值用于UTC,然后根据需要调整为用户的本地时间。通常最好将软件的日期时间作为UTC工作和存储。然后呈现本地日期时间以适应用户。换句话说,全球思考(UTC),本地呈现(本地时区调整)。

通常系统管理员将服务器计算机设置为UTC(没有时区偏移)。如果你的操作系统(如Mac OS X)没有提供UTC,则使用Reykjavik,因为冰岛全年都使用UTC,没有任何夏令时。同样,数据库引擎几乎总是将日期时间值转换为UTC进行存储。

Joda-Time确实提供了LocalDate类,用于当您真正不关心时区或时间时。但通常最好使用日期时间(DateTime实例),并根据需要格式化为仅日期字符串。

Joda-Time 2.3中的示例代码。

DateTimeZone timeZoneChina = DateTimeZone.forID( "Asia/Shanghai" );
DateTime dateTimeChina = new DateTime( 2013, 8, 29, 17, 45, 00, timeZoneChina );
DateTime dateTimeUtc = dateTimeChina.withZone( DateTimeZone.UTC );
DateTime dateTimeParis = dateTimeChina.withZone( DateTimeZone.forID( "Europe/Paris" ) );

DateTimeZone timeZoneUsWestCoast = DateTimeZone.forID( "America/Los_Angeles" );
DateTime dateTimeUnitedStatesWestCoast = dateTimeChina.withZone( timeZoneUsWestCoast );

DateTimeFormatter formatter = ISODateTimeFormat.date();
String outputDateOnlyForUnitedStatesWestCoast = formatter.withZone( timeZoneUsWestCoast ).print( dateTimeUtc );

输出到控制台...

System.out.println( "dateTimeChina: " + dateTimeChina );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeParis: " + dateTimeParis );
System.out.println( "dateTimeUnitedStatesWestCoast: " + dateTimeUnitedStatesWestCoast );
System.out.println( "outputDateOnlyForUnitedStatesWestCoast: " + outputDateOnlyForUnitedStatesWestCoast );

当运行时...

dateTimeChina: 2013-08-29T17:45:00.000+08:00
dateTimeUtc: 2013-08-29T09:45:00.000Z
dateTimeParis: 2013-08-29T11:45:00.000+02:00
dateTimeUnitedStatesWestCoast: 2013-08-29T02:45:00.000-07:00
outputDateOnlyForUnitedStatesWestCoast: 2013-08-29

java.time

java.time中类似的代码。

当前的UTC时间。

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

将时间调整到芝加哥地区的时区,生成一个ZonedDateTime对象。

ZoneId zChicago = ZoneId.of( "America/Chicago" ) ;
ZonedDateTime zdtChicago = instant.atZone( z ) ;

调整为中国时区。

ZoneId zShanghai = ZoneId.of( "Asia/Shanghai" ) ;
ZonedDateTime zdtShanghai = zdtChicago.withZoneSameInstant( zChicago ) ;

所有三个对象(instantzdtChicagozdtShanghai)代表同一时刻,时间线上的同一点。同时发生,但通过不同的挂钟时间看到。
显然,您想要仅日期部分,没有每天的时间和时区,为此提取一个LocalDate对象。
LocalDate ld = zdtChicago.toLocalDate() ; 

当然,这可能与 zdtShanghai.toLocalDate() 返回的日期不同。

您可能需要构建一个特定的时刻。

ZonedDateTime zdtShanghai = ZonedDateTime.of( 2013, 8, 29, 17, 45, 0, 0 , zShanghai ) ;

位置和时区

你说:

我需要提供“位置”名称。

位置与时区无关。来自魁北克的商人可能想在东京旅行期间查看特定的 America/Montreal 时区的时间表。

您可以询问用户的Web浏览器或本地JVM其当前默认时区。但如果很重要,您必须与用户确认所需/预期的时区。


Table of date-time types in Java, both modern and legacy


关于java.time

java.time框架内置于Java 8及其后续版本中。这些类取代了过时的传统日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310Joda-Time项目现在处于维护模式,建议迁移到java.time类。

您可以直接使用与JDBC 4.2或更新版本兼容的JDBC驱动程序,直接与您的数据库交换java.time对象。无需字符串,无需java.sql.*类。Hibernate 5和JPA 2.2支持java.time

如何获取java.time类?


3

这是一个常见的头疼问题

  1. 就我的经验而言,通过IP地址确定位置并不总是可靠的,例如当人们使用公司VPN时。
  2. 你是正确的,“基于地区的时区”(“Europe/Paris”,“CET”)更适合处理夏令时。

我用以下方法解决了类似的问题: 在服务器端数据库中为每个用户关联一个精确的时区。当用户填写预订表单时,您会显示一个时区选择器,其中包含他的默认时区。这样他可以双重检查它(在我看来比通过IP地址猜测要安全得多),在服务器端,日期可以安全地从本地转换为服务器时间并返回。


1

是的,我听说Joda是一个非常强大的工具,但你能告诉我更具体的东西吗?例如如何使用Joda处理我的案例? - Chailie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接