How to tackle daylight savings using TimeZone in Java
我必须在我的Java应用程序中打印EST时间。 我使用以下方法将时区设置为EST:
但是当在这个时区中遵循夏令时时,我的代码不能打印正确的时间(它打印的时间减少1小时)。
无论是否观察到夏令时,如何使代码始终能够正确读取正确的时间?
PS:我尝试将时区设置为EDT,但它没有解决问题。
-
你能扩展你的示例代码,准确显示你想要做什么吗?
-
除了打印日期外,没有什么可以做的。但是,当我看到输出中的小时时,它显示错误(减少1小时)在美国东部时间遵循夏令时。如何照顾夏令时是我的问题
-
请使用该信息编辑您的问题。它可能会在评论中被遗漏,但可能不在您的问题中。另外,请编辑并澄清此声明PS: I tried setting the timezone to EDT, but it solve the problem
-
夏令时在夏令时开始时将时钟提前一小时,并在时间结束时将时钟延迟相同的数量。在我看来,你看到的行为完全正常。请浏览en.wikipedia.org/wiki/Daylight_saving_time
-
"当美国东部时间采用夏令时" - 你的意思是"东部时间正在遵循夏令时"。 EST是东部标准时间 - 标准与日光相反。
-
@JonSkeet:我来自地球的东边。所以我不太了解夏令时。谢谢(你的)信息。我现在有了这个概念。
-
类似:stackoverflow.com/questions/9863625/…
这是问题的开始:
应全心全意地避免使用3个字母的缩写,以支持TZDB区域ID。 EST是东部标准时间 - 标准时间从未观察到DST;它不是真正的全时区名称。它是用于部分时区的名称。 (不幸的是,我没有遇到这个"半时区"概念的好词。)
您需要一个完整的时区名称。例如,America/New_York位于东部时区:
-
谢谢Jon Skeet。这很完美。
-
谢谢@Jon,是否有可能用DateFormat format = new SimpleDateFormat("HH:mm:ss:ms MM/dd/yyyy").getDateTimeInstance()之类的东西改变格式?我的示例生成警告"应该以静态方式访问DateFormat类型的静态方法getDateTimeInstance()"。
-
@IanCampbell:你完全不清楚你期望做什么 - 它是一种静态方法,所以你通常只使用DateFormat format = DateFormat.getDateTimeInstance()。它是一个静态方法,因此它与现有实例无关。目前还不清楚你要做什么,但你应该问一个新问题。
-
好的,谢谢@Jon,我只是想知道如何将输出格式更改为"HH:mm:ss:ms MM/dd/yyyy"。
-
@IanCampbell:改变输出格式是什么意思?为什么不用这种格式创建一个新的SimpleDateFormat?或者您在谈论更改默认日期/时间格式?你不是很清楚。
-
很抱歉@Jon混淆了,我刚刚做了你的建议:SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss:ms MM/dd/yyyy"); System.out.println(format.format(new Date())); ...从答案中替换最后一行代码以获得所需的输出。感谢您的帮助。
-
@JonSkeet如果我依赖系统时区并将日期用作:new Date();并参考它。当夏令时发生时会导致一小时的差异吗?什么是避免它的方法?
-
@maximus:我不确定你的意思。 new Date()的结果不依赖于系统时区。如果你的意思是你将SimpleDateFormat与系统时区一起使用,那么是的,调用sdf.format(new Date());然后在几毫秒后立即sdf.format(new Date());会导致字符串表示,它们似乎相隔一个小时。
-
@JonSkeet得到了它,我的错误。谢谢!
-
@JonSkeet如何通过服务器端从此数据(UTC-08:00) Pacific Time (US & Canada)获取区域ID。结果就像America/Dawson。
-
@SalmanS我建议作为一个新问题,更多关于产生价值的背景。
其他答案是正确的,尤其是Jon Skeet的答案,但过时了。
java.time
这些旧的日期时间类已被Java 8及更高版本中内置的java.time框架所取代。
如果您只想要UTC中的当前时间,请使用Instant类。
1
| Instant now = Instant.now(); |
EST不是时区,正如Jon Skeet的正确答案中所述。这样的3-4个字母代码既不是标准化也不是唯一的,并且进一步混淆了夏令时(DST)。使用"continent / region"格式的正确时区名称。
也许你的意思是北美东海岸的东部标准时间?还是埃及标准时间?还是欧洲标准时间?
1 2 3
| ZoneId zoneId = ZoneId.of("America/New_York" );
ZoneId zoneId = ZoneId.of("Africa/Cairo" );
ZoneId zoneId = ZoneId.of("Europe/Lisbon" ); |
使用任何此类ZoneId对象将当前时刻调整到特定时区以生成ZonedDateTime对象。
1
| ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ; |
通过从第一个生成另一个ZonedDateTime对象,将ZonedDateTime调整到不同的时区。 java.time框架使用不可变对象而不是更改(变异)现有对象。
1
| ZonedDateTime zdtGuam = zdt.withZoneSameInstant( ZoneId.of("Pacific/Guam" ) ) ; |
-
Instant与UTC中的时间不同。例如,后者可以有闰秒。
-
@OrangeDog当Instant被视为日期和时间时,闰秒变得无关紧要。这就是闰秒的全部要点,让我们的时钟停下来让我们的日历与地球通常放慢的天文位置同步。虽然确实Instant的内部表示由于到目前为止已宣布的闰秒数(每两年左右一次)而缩短,但在讨论日期和时间时这种区别是没有意义的。课堂文档中讨论了那些关心的细节。 Upshot:实际上,Instant是UTC。
-
呃,不。 UTC闰秒不会"停止时钟",它又增加了一秒(2016-12-24T23:23:60Z是最新的)。由Instant表示的Java时间也不会停止时钟 - 它具有与当天相同的毫秒数,但它们都稍长。即使你不想这样想,它仍然是如何实现的,这就是为什么,例如你不能使用ISO_DATE_TIME格式化Instant。
-
幸运的是,转换很容易 - Instant.now().atZone(ZoneOffset.UTC)
-
添加秒确实会在日期和时间方面停止计时。我们需要额外的时间才能进入日历中的下一个日期。所以我们的日历吸收了闰秒。正如我所说,这是闰秒的全部要点:减慢我们的时钟以减缓我们的日历跟踪。虽然你忽略闰秒的观点在技术上在java.time类的内部表示方面是正确的,但实际上你的观点是无关紧要的,迂腐的,并且对于努力学习如何表示日期和时间的许多程序员来说是不必要的混淆。
-
这些误解和不精确正是人们在应用程序中正确处理时间问题的原因,也正是为什么Java试图让它很容易做到正确。如果你想在连续时间内使用Instant。如果您想要UTC时区中的日期和时间,请使用ZonedDateTime。
-
@OrangeDog不正确,ZonedDateTime正是您不需要的UTC值。这是'zoned'这个词的标题,以及它的类doc和OffsetDateTime和Instant的存在都指向了这个事实。 ZonedDateTime用于为UTC值分配时区。虽然您确实可以将UTC指定为该时区,但这并不是该类的目的。
-
好的,然后使用OffsetDateTime。但是如果你使用Instant,会有大量的情况会抛出,而少数情况下它会做错误的事情。
-
我刚刚发现Java仍然没有处理闰秒,因为它们已被定义。 OffsetDateTime.parse("2016-12-31T23:59:59Z").plusSeconds(1).toString()给出2017-01-01T00:00Z。因此,如果您想避免偶尔出现的错误,您需要找到另一个库。
-
@OrangeDog你不理解闰秒的目的:将我们的时间(地球在其轴上的旋转)与我们的日历(围绕太阳的地球轨道)同步。对于几乎任何实际目的,我们实际上不需要计算闰秒的额外第61秒。忽略跳跃让我们将它吸收到我们的日历中。例如,谷歌选择使用Leap Smear将他们的许多计算机上的时钟移动几小时,以便在不知不觉中停止延迟时钟。
-
@OrangeDog如果你真的需要跟踪Leap Second,我有个好消息:在java.time的ThreeTen-Extra项目中找到的UtcInstant和UtcRules类。第一个是在UTC时间尺度上以闰秒测量的时间线上的瞬时点。第二个提供了定义UTC时标的规则,特别是在发生闰秒时。
-
这正是Java Instant所做的,这正是它与UTC不同的原因。
您可以输入"EST5EDT",而不是输入"EST"作为时区。如你所知,只是"EDT"不起作用。这将解决夏令时问题。代码行如下所示:
1 2 3 4 5 6 7 8
| private static Long DateTimeNowTicks (){
long TICKS_AT_EPOCH = 621355968000000000L ;
TimeZone timeZone = Calendar. getInstance(). getTimeZone();
int offs = timeZone. getRawOffset();
if (timeZone. inDaylightTime(new Date()))
offs += 60 * 60 * 1000;
return (System. currentTimeMillis() + offs ) * 10000 + TICKS_AT_EPOCH ;
} |
-
这些可怕的旧日期时间类在几年前由java.time类取代,采用了JSR 310.这些遗留类应该不再使用了。
-
有时开发人员被迫陷入可怕的旧遗留情况,但很高兴知道,谢谢。
-
对于Java 6& Java 7,大多数java.time功能都在ThreeTen-Backport项目中使用几乎相同的API进行反向移植。所以真的没有必要使用糟糕的遗留日期时间类,如Date,Calendar,SimpleDateFormat。
-
有时开发人员被迫进入一个没有backport的可怕的旧遗留情况,因此别无选择,只能使用可怕的遗留日期时间类,但很高兴知道,谢谢!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public static float calculateTimeZone (String deviceTimeZone ) {
float ONE_HOUR_MILLIS = 60 * 60 * 1000;
// Current timezone and date
TimeZone timeZone = TimeZone. getTimeZone(deviceTimeZone );
Date nowDate = new Date();
float offsetFromUtc = timeZone. getOffset(nowDate. getTime()) / ONE_HOUR_MILLIS ;
// Daylight Saving time
if (timeZone. useDaylightTime()) {
// DST is used
// I'm saving this is preferences for later use
// save the offset value to use it later
float dstOffset = timeZone. getDSTSavings() / ONE_HOUR_MILLIS ;
// DstOffsetValue = dstOffset
// I'm saving this is preferences for later use
// save that now we are in DST mode
if (timeZone. inDaylightTime(nowDate )) {
Log. e(Utility. class. getName(), "in Daylight Time");
return -(ONE_HOUR_MILLIS * dstOffset );
} else {
Log. e(Utility. class. getName(), "not in Daylight Time");
return 0;
}
} else
return 0;
} |
-
仅供参考,几年前现代java.time课程取代了这些非常麻烦的旧课程。
根据这个答案:
在java中,默认情况下DateFormatter使用DST,为避免日光节约(DST),您需要手动执行操作,
首先你必须得到DST偏移,即应用了多少毫秒DST,对于某些地方,DST也是45分钟,有些地方是30分钟左右
但在大多数情况下,DST为1小时
您必须使用Timezone对象并检查日期是否属于DST,然后您必须手动将DST的偏移量添加到其中。例如:
1 2 3 4 5 6 7 8 9
| TimeZone tz = TimeZone. getTimeZone("EST");
boolean isDST = tz. inDaylightTime(yourDateObj );
if(isDST ){
int sec = tz. getDSTSavings()/1000;// for no. of seconds
Calendar cal = Calendar. getInstance();
cal. setTime(yourDateObj );
cal. add(Calendar. Seconds,sec );
System. out. println(cal. getTime());// your Date with DST neglected
} |
实现TimeZone类以将时区设置为日历可以节省夏令时。
java.util.TimeZone表示时区偏移量,并且还计算出夏令时。
示例代码:
1 2 3
| TimeZone est_timeZone = TimeZoneIDProvider. getTimeZoneID(TimeZoneID. US_EASTERN). getTimeZone();
Calendar enteredCalendar = Calendar. getInstance();
enteredCalendar. setTimeZone(est_timeZone ); |
-
是TimeZoneIDProvider自定义类?在这种情况下,您需要提供其实现。