如果我运行以下程序,它解析两个相隔1秒引用的日期字符串,并对它们进行比较:
输出是:
353
为什么ld4-ld3不是1(正如我从时代的一秒钟差异中所预期的那样),而是353?
如果我在1秒后将日期更改为次:
1 2
| String str3 ="1927-12-31 23:54:08";
String str4 ="1927-12-31 23:54:09"; |
那么,ld4-ld3就是1。
Java版本:
1 2 3 4 5 6 7 8 9 10 11 12 13
| java version "1.6.0_22"
Java (TM ) SE Runtime Environment (build 1.6.0_22 -b04 )
Dynamic Code Evolution Client VM (build 0.2-b02 -internal, 19.0-b04 -internal, mixed mode )
Timezone (` TimeZone. getDefault()` ):
sun. util. calendar. ZoneInfo[id ="Asia/Shanghai",
offset =28800000,dstSavings =0,
useDaylight =false,
transitions =19,
lastRule =null]
Locale(Locale. getDefault()): zh_CN |
- 这可能是区域设置问题。
- 你是真的在现实生活中偶然发现了那个确切的情况,还是这个问题仅仅是为了好玩?
- @科斯蒂·丘达图:FWIW,我可以很容易地想象,这是由于减少了一个更大的错误——也就是说,"为什么这两个日期相隔一年,而不是相隔一年?"
- 真正的答案是,总是使用自一个epoch以来的秒数进行日志记录,就像unix epoch一样,使用64位整数表示(如果希望在epoch之前允许标记,请使用signed)。任何现实世界中的时间系统都有一些非线性的、非单调的行为,比如闰小时或夏令时。
- 最初于2011年7月23日发布为Oracle Bug ID 7070044
- 作为@costi ciudatu,我真的很想知道是哪个bug报告把这一个挖出来的。我也很确定,除了作为一个拼图,这对99.9%的用户(加上很多9位数字)是没用的。他确实因"比赛"而获得奖牌。
- 哈哈。他们在2011年为JDK6修的。然后,两年后,他们发现JDK7也应该修复它……当然,从7u25开始,我没有在发行说明中找到任何提示。有时我想知道Oracle修复了多少bug,但由于公共关系的原因,没有人告诉我。
- 一个关于这些事情的伟大视频:youtube.com/watch?V=-5WPM
- 另一个来自同一个人,@thorbj&248;rnravandersen:youtube.com/watch?v=uqjg8k1hxo(闰秒)。(这一个来自汤姆·斯科特自己的YouTube频道,不是来自ComputerPhile。)
- @菲利普,好的是,还有闰秒。所以即使这样也不行。
上海12月31日是时区变更。
上海1927年的详细情况见本页。基本上在1927年底的午夜,时钟倒转了5分52秒。因此,"1927年-12月31日23日54分08秒"实际上发生了两次,并且看起来Java是作为本地日期/时间的稍后可能的瞬间解析的,因此差异。
在这个经常怪异而美妙的时区世界里,这只是又一个插曲。
编辑:停止按!历史记录更改…
如果用TZDB的2013a版本重新构建,最初的问题将不再表现出完全相同的行为。在2013a,结果是358秒,过渡时间是23:54:03而不是23:54:08。
我只注意到这一点,因为我在noda时间以单元测试的形式收集这样的问题…测试现在已经被更改了,但它只是用来表明——即使是历史数据也不安全。
编辑:历史记录再次更改…
在TZDB 2014F中,更改的时间已经移动到1900-12-31,现在只剩下343秒的更改(所以,如果你明白我的意思,从t到t+1)之间的时间是344秒)。
编辑:回答1900年过渡期的问题…看起来,Java时区实现在1900 UTC开始之前的任何时刻都将所有时区简单地置于其标准时间内:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.util.TimeZone;
public class Test {
public static void main (String[] args ) throws Exception {
long startOf1900Utc = -2208988800000L ;
for (String id : TimeZone. getAvailableIDs()) {
TimeZone zone = TimeZone. getTimeZone(id );
if (zone. getRawOffset() != zone. getOffset(startOf1900Utc - 1)) {
System. out. println(id );
}
}
}
} |
上面的代码在我的Windows计算机上不产生任何输出。所以,任何一个时区在1900年开始时除了标准时区之外还有其他偏移量,它都将被视为一个过渡。TZDB本身就有一些比这更早的数据,并且不依赖于"固定"标准时间的任何概念(这是getRawOffset假设的有效概念),因此其他库不需要引入这种人工转换。
- 加雷斯:不检查"的细节,但上海时区转换周期是我在那的第一端口的呼叫。我一直在工作时间区的过渡时间为野田最近,包括相当多的可能性是在Forefront的思想无论如何…
- 乔恩:"好奇心",为什么他们的时钟回一集搜索"Weird"对吗?我会什么样"的逻辑,但它是52mins 5:如何来?
- 约翰:"让IT更全局正常时区,我相信这是造成偏移UTC + 8。巴黎是同一种东西:例如在1911年timeanddate.com / worldclock / clockchange.html?n = 195 &;1911年=
- "你知道,如果你到乔恩中处理与Java。1752年9月?我总是用爱抱怨,在Unix系统9 1752人。
- 先生驼鹿@:我相信时间想做不同的事情Joda取决于你的日历和时间拾取,实现cutover野田要当我们的日历。我不知道"本土"的Java和.NET类。
- 这也意味着,在某个地方约会的Java库,它是逻辑处理本案例(另一个奇异的边缘和边缘的情况下)?
- "yahelc:它不只是一般的时间区的代码,它处理的各种历史时间的过渡区。
- 名:"yahelc时区的变化,基本上都是不可能的parameterized小集的事件。这套工具的代码的事件时区数据库,和一个列表的事件参数和用品的每个时区。顺便说一下,这个时区数据库可以独立的更新:Java本身oracle.com Java / / / / technetwork JavaSE tzupdater - zwnj自述136440.h &;& # 8203;所
- 那么为什么你在5分钟后离开上海的怪人在第一位?
- 查尔斯:"很多地方都已经不那么传统的补偿反馈。在一些国家,不同的城市都有自己的一个偏移量为靠近并正确的作为可能的。
- 查尔斯:@然后旅行者知道后,认为是本地时间是不同的(因为它是)。此外,手表是机械和drifted快,这样的人我们用来调整他们根据当地的clocktower每一天无论如何,即使他们没有旅行。这样的知识是"塔集(这是时钟drifted)?最容易通过设置他们12点时达到其峰值太阳日报……这是不同的,在每一个地方不在同一经度。这是相当多的标准是,直到所需的一些铁路timetables sort of标准化。
- 然后安切洛蒂:孩子的知识在地球生存的这个知识的时代,所以近一世纪前,它是实现软件?在2011年,谁是一个时区oddities非软件工程师是我在像一个书呆子。(真的,因为它所有的人民和他们的软件抽象,不要在乎它疑,当他们说‘正午’,我们的软件工程师应该对付它)。但一人在上海的想象,在1927年12月,思维会注上搜索有关的事,那从来都不知何故此信息丢失,删除,等……心智的成熟。
- "把他带EDS。它实际上是只有15分钟,原因是它的显示16分钟时区差分定位偏差,由于A 2011年7月二十七由乔恩飞碟引起知识是可怕的。
- 我知道在它的案例,和为什么它是这样实现的。我个人认为应该是"在"undetermined效果最佳。现在的方式,它是实现,如果第一occurrences二十三:54:08 23:59:59永远不会发生。这是一个1和353正确答案。技术上的2个不同的时区,这是subtracted不觉得正确的行为。
- "它的中小企业。亚历克斯:没有,只有一个时区的任何两种不同的UTC偏移。一个API允许你确定这是你想在瞬间当时间解析模棱两可的地方,当然)
- "乔恩飞碟:好的,两种不同的UTC偏移:-)……但你明白我的意思。在Java中的实现和最有可能的其他库的精髓)是一位穷
- @It's SME…Alex:well EDOCX1&0&EDOCX1&1&1;.are certainly poor in a number of ways.现在,当你转换一个LocalDateTime到一个ZonedDateTime时,你必须给出一个指示,说明你想要如何处理模糊或滑稽的瞬间。
- 是否有一个完整的列表列出了所有这些历来都曾作出的修正?
- @kevinh.lin:Well there's the time zone data at Iana:iana.org/time zone s
- @Jonskeet if this answer is right,then it might be wish to add it to your answer too,that way it's super up to date
- @Dantemple:that answer isn't really correct-the time zone database changed the transition time slightly,but if you edited the original program appropriately,you'll still see a discontinuity.
- 我现在明白汤姆?斯科特为什么说不这样做了。YouTube.com/watch?五维宝石
- Tom Scott also has a video on leap [email protected]:youtube.com/watch?五=UQJG8K1HXO。This one is from Tom Scott's own Youtube channel,not from computerphile.)
- @Jon Skeet,我无法理解差异。你从同一时间段都有时间,如果你添加或突然添加时间到同一时间段,两次都必须受到影响。那就只有第二个区别了Then difference should be only one second.请用数字解释一下。
- @Gopsab:What do you mean"both times from the same time zone"?暂时忽视上海—你住在一个时间区里观察白昼节省时间吗?如果是这样,时间会改变什么?这是一个很好的方式,通过不同的UTC改变地方时间。。。]
- @Jonskeet yes,I meant time1 and time2 in the program are from the same time zone.是吗?
- @Gopsab:是的,但我不认为那是你想的意思。"同一时间段"Doesn't mean"same UTC ofset.比如说,我在时间区被认定为"欧洲/伦敦"。一年前,我们的本地时间在一小时内跳跃。一年前,我们的本地时间又一小时倒下了。在两种情况下,两种通用时刻是第二次分离结束的地方时刻,比第二次分离更多。
- @Gopsab:这对本地时间来说不是真的,因为本地时间往往向前或后退,通常是由于DST。复制这个网站码到您的网站上以设置一个投票箱在您的网站上。你说"两次都会改变"是什么意思?真的不知道你在想什么
- 我来自印度,没有在印度使用过。我对实现的理解是像一个阵列结构这样的东西,在那里,通过一段时间来减少所有元素不会改变算术操作。更加清晰,更加清晰,更加清晰减少一半,将是[0,1,2,3,4],再一次给同样的答案。我希望这能让我明白。
- @Gopsab:forget implementations and arrays-it sounds like you need to understand the difference between UTC and"time in a particular time zone"first.即使印度没有注意到你,你知道它是如何在真实的世界上工作吗?For example,in the UK,on the day the local time goes forward by an hour,the local time will go 00:59:58,00:59:59:02:00:00,02:00:00:00:01.
- 所以,时钟5:52在上海。So 11:54:08 is actually 00:00:00.So differentiating 11:54:07 from 11:54:08 actually works like 00:00:00:00-11:54:07.对吗?白天的光明拯救了时钟,使时钟变小,增加了白天,减少了黑夜的延续。
- @Gopsab:I wouldn't say"11:54:08 is actually 00:00:00",but the second occurrence of 11:54:08 is when 00:00:00:00 would have been,if the UTC offset hadn't changed.This isn't actually an example of DST,BTW.DST isn't about changing the duration of night time/day-it's just an attempt to make"typical working hours"occcur in more of the daylight hours.无论如何,我认为这是一个非常充分的讨论。
- 我对2011年7月27日8点15分(问问题的日期时间)之间的时间更感兴趣。2011年7月27日8点31分(回答问题的日期时间)。这真的是一个16分钟的差异,在多长时间内,乔恩斯基特知道这个答案并解释它?这不应该更长吗?或者我问这个问题真的低估了乔恩·斯基特?
- @斯科萨拉吉:如果你看一下第一个答案,那就很短了。如果你认为你可能知道去哪里找数据,找数据不会花很长时间…
您遇到了本地时间中断:
When local standard time was about to reach Sunday, 1. January 1928,
00:00:00 clocks were turned backward 0:05:52 hours to Saturday, 31.
December 1927, 23:54:08 local standard time instead
这并不特别奇怪,而且随着时区因政治或行政行为而被切换或更改,几乎到处都发生过这种情况。
- @杰森:对于睡前阅读,我建议(现在)IANA时区数据库(以前由一个可爱的叫奥尔森的人管理)是一个很好的资源:iana.org/time-zones。据我所知,大多数开源世界(如此提到的库)都将其作为时区数据的主要来源。
这种陌生感的寓意是:
- 尽可能使用UTC格式的日期和时间。
- 如果不能以UTC格式显示日期或时间,请始终指示时区。
- 如果您不能要求输入日期/时间(以UTC为单位),则需要一个明确指示的时区。
- 转换/存储到UTC对于上述问题真的没有帮助,因为您在转换到UTC时会遇到中断。
- @Mark Mann:如果您的程序在任何地方都在内部使用UTC,只在用户界面中转换到/从本地时区转换过来,那么您将不关心这种不连续性。
- @拉爱德华德:当然可以——1927-12-31 23:54:08的UTC时间是什么时候?(暂时忽略了UTC在1927年根本不存在)。在某个时刻,这个时间和日期将进入您的系统,您必须决定如何处理它。告诉用户他们必须以UTC格式输入时间,但这并不能消除问题。
- 对于这个线程上的活动量,我觉得是有道理的,因为我已经为一个大型应用程序的日期/时间重构工作了将近一年了。如果您正在进行类似日历的操作,则不能"简单"存储UTC,因为可能呈现它的时区定义将随着时间的推移而更改。我们存储"用户意图时间"(用户的本地时间及其时区)和用于搜索和排序的UTC,并且每当IANA数据库更新时,我们都会重新计算所有UTC时间。
递增时间时,应将其转换回UTC,然后加或减。仅将本地时间用于显示。
这样,您就可以在任何时间段内行走,时间或分钟发生两次。
如果转换为UTC,请每秒添加一次,然后转换为本地时间进行显示。你将经历11:54:08下午lmt-11:59:59下午lmt,然后11:54:08下午cst-11:59:59下午cst。
不用转换每个日期,而是使用以下代码
1 2
| long difference = (sDt4. getTime() - sDt3. getTime()) / 1000;
System. out. println(difference ); |
结果是:
- 恐怕情况并非如此。您可以在您的系统中尝试我的代码,它将输出1,因为我们有不同的区域设置。
- 这只是因为您没有在解析器输入中指定区域设置。这是糟糕的编码风格和Java中一个巨大的设计缺陷——它固有的本地化。个人而言,我把"TZ= UTC LCYALALL=C"放在我使用Java的地方,以避免这种情况。此外,您应该避免实现的每个本地化版本,除非您直接与用户交互并明确需要它。不要进行任何计算,包括本地化,除非绝对必要,否则始终使用locale.root和UTC时区。
很抱歉,时间上的不连续已经有了一点变化
两年前的JDK 6,最近的JDK 7更新了25。
要吸取的教训:不惜一切代价避免非UTC时间,除了可能的显示时间。
- 这是不正确的。不连续不是一个bug——只是TZDB的最新版本的数据略有不同。例如,在我用Java 8的机器上,如果你稍微改变代码,使用"1927—12 31 31 23 54"02和"1927—12 31 31 23 54"03,你仍然会看到不连续性,但是现在是358秒,而不是353秒。TZDB的最新版本还有另一个不同之处——请参阅我的答案了解详细信息。这里没有真正的bug,只是一个关于如何解析不明确的日期/时间文本值的设计决策。
- 真正的问题是,程序员不理解本地时间和通用时间之间的转换(在任何方向上)不是也不能100%可靠。对于旧的时间戳,我们掌握的关于当地时间的数据充其量是不稳定的。对于未来的时间戳,政治行动可以改变给定的当地时间所对应的世界时间。对于当前和最近的过去时间戳,您可能会遇到这样一个问题:更新TZ数据库和推出更改的过程可能比法律的实施计划慢。
正如其他人所解释的,那里有一个时间间断。在Asia/Shanghai处,1927-12-31 23:54:08有两个可能的时区偏移,但在1927-12-31 23:54:07处只有一个偏移。所以,根据使用的偏移量,要么有一秒的差异,要么有5分53秒的差异。
这种偏移量的轻微变化,而不是我们习惯的通常的一小时日光节约(夏季时间),稍微掩盖了这个问题。
请注意,2013年时区数据库的更新在几秒钟前就移动了这一不连续点,但效果仍然可以观察到。
Java 8上的新EDCOX1 3个包让我们更清楚地看到这一点,并提供工具来处理它。鉴于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder ();
dtfb. append(DateTimeFormatter. ISO_LOCAL_DATE);
dtfb. appendLiteral(' ');
dtfb. append(DateTimeFormatter. ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb. toFormatter();
ZoneId shanghai = ZoneId. of("Asia/Shanghai");
String str3 ="1927-12-31 23:54:07";
String str4 ="1927-12-31 23:54:08";
ZonedDateTime zdt3 = LocalDateTime. parse(str3, dtf ). atZone(shanghai );
ZonedDateTime zdt4 = LocalDateTime. parse(str4, dtf ). atZone(shanghai );
Duration durationAtEarlierOffset = Duration. between(zdt3. withEarlierOffsetAtOverlap(), zdt4. withEarlierOffsetAtOverlap());
Duration durationAtLaterOffset = Duration. between(zdt3. withLaterOffsetAtOverlap(), zdt4. withLaterOffsetAtOverlap()); |
那么,durationAtEarlierOffset将是一秒钟,而durationAtLaterOffset将是5分53秒。
此外,这两个偏移量相同:
1 2 3
| // Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset(); |
但这两个不同:
1 2 3 4 5
| // +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();
// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset(); |
在比较1927-12-31 23:59:59和1928-01-01 00:00:00时,您可以看到相同的问题,不过,在这种情况下,产生较长差异的是较早的偏移量,而有两个可能的偏移量的是较早的日期。
另一种方法是检查是否正在进行转换。我们可以这样做:
1 2 3 4 5
| // Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime); |
您可以使用isOverlap()和isGap()方法检查转换是否是重叠(在这种情况下,该日期/时间有多个有效偏移)或间隙(在这种情况下,该日期/时间对该区域ID无效)。
我希望这有助于人们在Java 8变得广泛可用时,或使用Java 7的JSR 310后端的人来处理这类问题。
- 嗨,丹尼尔,我已经运行了您的代码,但它没有按预期提供输出。与DurationateArlierOffset和DurationateAtlaterOffset一样,它们都仅为1秒,Zot3和Zot4都为空。我刚刚在我的机器上复制并运行了这个代码。这里有什么需要做的吗?如果你想看一段代码,请告诉我。这是code tutorialspoint.com/…,你能告诉我这里发生了什么吗?
- 这取决于Java的版本,因为在TZDATA中已经发生了变化,JDK的不同版本捆绑了不同版本的TZDATA。在我自己安装的Java中,Times是EDCOX1,1和EDCOX1,2,但是在您共享的站点上不起作用,所以它们使用的Java版本不同于I。
在Java中普遍存在的隐式本地化是其最大的设计缺陷。它可能是针对用户界面的,但坦率地说,除了某些IDE,基本上可以使用Java来代替用户界面,因为基本上程序员不完全是它的目标受众。您可以通过以下方式修复它(尤其是在Linux服务器上):
- 导出lc_all=c tz=utc
- 将系统时钟设置为UTC
- 除非绝对必要,否则不要使用本地化实现(即仅用于显示)
对于Java社区进程成员,我建议:
- 使本地化方法不是默认方法,而是要求用户显式请求本地化。
- 改为使用utf-8/utc作为固定默认值,因为这只是今天的默认值。除了要生成这样的线程外,没有理由执行其他操作。
我是说,拜托,全局静态变量不是反OO模式吗?其他的都不是由一些基本的环境变量给出的普遍的默认值……
正如其他人提到的,1927年上海的时间发生了变化。
基本上说,江户十一〔11〕在上海,当地的标准时间,过了一会儿,就转到第二天的江户十一〔12〕了。但后来它又回到了23:54:08的地方标准时间。所以,这就是为什么差异是343秒而不是1秒。
这也会让其他地方如美国陷入困境。在美国,他们有夏令时。当夏令时开始时,时间向前移动1小时。但过了一段时间,夏时制结束后,它会倒退1小时回到标准时区。因此,有时在比较美国的时间时,差异大约是3600秒,而不是1秒。
最好是在时间不变的情况下使用UTC,除非需要使用非UTC时间,如显示时间。