Jboss Java 日期夏令时
Jboss Java Date daylight saving time
有一个问题,当时钟由于夏令时(每年两次)而移动时,Java 中的日期不正确(我在中欧:GMT+2
夏天,GMT+1
冬天)
如果时间提前 1 小时,new Date()
仍然是 returns 旧时间(比当前时间晚 1 小时)。
在Java 7中,是否可以在不重新启动Jboss应用程序服务器的情况下解决这个问题?
如果我在 Windows 中手动更改时间,重现问题:Date
不会更新为系统日期,除非 jboss 重新启动。
Calendar c = Calendar.getInstance();
c.setTime(new Date());
使用 ZonedDateTime
class 来自 JDK 8 java.time
。它适应夏令时的变化。
详情请参阅:https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html
在 Java <= 7 中,您可以使用 ThreeTen Backport,这是 Java 8 的新 date/time 的一个很好的向后移植] classes.
有了这个,您可以轻松处理 DST 更改。
首先,您可以使用 org.threeten.bp.DateTimeUtils
与 Calendar
相互转换。
下面的代码将 Calendar
转换为 org.threeten.bp.Instant
,这是一个 class,代表一个 "UTC instant"(独立于时区的时间戳:现在,此时这一刻,世界上的每个人都在同一时刻,尽管他们的本地日期和时间可能会有所不同,具体取决于他们所在的位置)。
然后,Instant
被转换为 org.threeten.bp.ZonedDateTime
(这意味着:此刻,这个时区的日期和时间是多少?)。我还使用 org.threeten.bp.ZoneId
获取时区:
Calendar c = Calendar.getInstance();
c.setTime(new Date());
// get the current instant in UTC timestamp
Instant now = DateTimeUtils.toInstant(c);
// convert to some timezone
ZonedDateTime z = now.atZone(ZoneId.of("Europe/Berlin"));
// today is 08/06/2017, so Berlin is in DST (GMT+2)
System.out.println(z); // 2017-06-08T14:11:58.608+02:00[Europe/Berlin]
// testing with a date in January (not in DST, GMT+1)
System.out.println(z.withMonth(1)); // 2017-01-08T14:11:58.608+01:00[Europe/Berlin]
我刚刚选择了一些使用中欧时区 (Europe/Berlin
) 的时区:您不能使用那些 3 个字母的缩写,因为它们是 ambiguous and not standard。您可以将代码更改为最适合您系统的时区(您可以使用 ZoneId.getAvailableZoneIds()
获得所有可用时区的列表)。
我更喜欢这个解决方案,因为它明确了我们使用哪个时区向用户显示(Date
和 Calendar
的 toString()
方法在幕后使用默认时区而且你永远不知道他们在做什么)。
并且在内部,我们可以继续使用 UTC 格式的 Instant
,因此它不受时区的影响(并且您可以随时在需要时转换时区)- 如果您想转换ZonedDateTime
返回 Instant
,只需使用 toInstant()
方法。
实际上,如果您想获得当前的 date/time,只需忘记旧的 classes(Date
和 Calendar
)并仅使用 Instant
:
// get the current instant in UTC timestamp
Instant now = Instant.now();
但如果您仍然需要使用旧的 classes,只需使用 DateTimeUtils
进行转换。
上面例子的输出是ZonedDateTime.toString()
方法的结果。如果要更改格式,请使用 org.threeten.bp.format.DateTimeFormatter
class(查看 javadoc 以获取有关所有可能格式的更多详细信息):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss z X");
// DST (GMT+02)
System.out.println(formatter.format(z)); // 08/06/2017 14:11:58 CEST +02
// not DST (GMT+01)
System.out.println(formatter.format(z.withMonth(1))); // 08/01/2017 14:11:58 CET +01
有一个问题,当时钟由于夏令时(每年两次)而移动时,Java 中的日期不正确(我在中欧:GMT+2
夏天,GMT+1
冬天)
如果时间提前 1 小时,new Date()
仍然是 returns 旧时间(比当前时间晚 1 小时)。
在Java 7中,是否可以在不重新启动Jboss应用程序服务器的情况下解决这个问题?
如果我在 Windows 中手动更改时间,重现问题:Date
不会更新为系统日期,除非 jboss 重新启动。
Calendar c = Calendar.getInstance();
c.setTime(new Date());
使用 ZonedDateTime
class 来自 JDK 8 java.time
。它适应夏令时的变化。
详情请参阅:https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html
在 Java <= 7 中,您可以使用 ThreeTen Backport,这是 Java 8 的新 date/time 的一个很好的向后移植] classes.
有了这个,您可以轻松处理 DST 更改。
首先,您可以使用 org.threeten.bp.DateTimeUtils
与 Calendar
相互转换。
下面的代码将 Calendar
转换为 org.threeten.bp.Instant
,这是一个 class,代表一个 "UTC instant"(独立于时区的时间戳:现在,此时这一刻,世界上的每个人都在同一时刻,尽管他们的本地日期和时间可能会有所不同,具体取决于他们所在的位置)。
然后,Instant
被转换为 org.threeten.bp.ZonedDateTime
(这意味着:此刻,这个时区的日期和时间是多少?)。我还使用 org.threeten.bp.ZoneId
获取时区:
Calendar c = Calendar.getInstance();
c.setTime(new Date());
// get the current instant in UTC timestamp
Instant now = DateTimeUtils.toInstant(c);
// convert to some timezone
ZonedDateTime z = now.atZone(ZoneId.of("Europe/Berlin"));
// today is 08/06/2017, so Berlin is in DST (GMT+2)
System.out.println(z); // 2017-06-08T14:11:58.608+02:00[Europe/Berlin]
// testing with a date in January (not in DST, GMT+1)
System.out.println(z.withMonth(1)); // 2017-01-08T14:11:58.608+01:00[Europe/Berlin]
我刚刚选择了一些使用中欧时区 (Europe/Berlin
) 的时区:您不能使用那些 3 个字母的缩写,因为它们是 ambiguous and not standard。您可以将代码更改为最适合您系统的时区(您可以使用 ZoneId.getAvailableZoneIds()
获得所有可用时区的列表)。
我更喜欢这个解决方案,因为它明确了我们使用哪个时区向用户显示(Date
和 Calendar
的 toString()
方法在幕后使用默认时区而且你永远不知道他们在做什么)。
并且在内部,我们可以继续使用 UTC 格式的 Instant
,因此它不受时区的影响(并且您可以随时在需要时转换时区)- 如果您想转换ZonedDateTime
返回 Instant
,只需使用 toInstant()
方法。
实际上,如果您想获得当前的 date/time,只需忘记旧的 classes(Date
和 Calendar
)并仅使用 Instant
:
// get the current instant in UTC timestamp
Instant now = Instant.now();
但如果您仍然需要使用旧的 classes,只需使用 DateTimeUtils
进行转换。
上面例子的输出是ZonedDateTime.toString()
方法的结果。如果要更改格式,请使用 org.threeten.bp.format.DateTimeFormatter
class(查看 javadoc 以获取有关所有可能格式的更多详细信息):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss z X");
// DST (GMT+02)
System.out.println(formatter.format(z)); // 08/06/2017 14:11:58 CEST +02
// not DST (GMT+01)
System.out.println(formatter.format(z.withMonth(1))); // 08/01/2017 14:11:58 CET +01