为什么减去这两次(1927年)会得出一个奇怪的结果?

2020-11-02 20:57:12

如果我运行以下程序,该程序解析相隔1秒的两个引用时间的日期字符串,并对它们进行比较:

Public static void main(string[]args)抛出ParseException{SimpleDateFormat SF=new SimpleDateFormat(";yyyy-MM-dd hh:mm:ss";);string str3=";1927-12-31 23:54:07";;string str4=";1927-12-31 23:54:08";;date sDt3=sf.parse(Str3);date sDt4=sf.parse(Str4);long ld3=sDt3.getTime()/1000;long ld4=sD4.Time();/1000。System.out.println(ld4-ld3);}。

为什么ld4-ld3不是1(正如我从1秒的时间差中预计的那样),而是353?

Java版本";1.6.0_22";Java(TM)SE运行时环境(Build 1.6.0_22-b04)动态代码演进客户端VM(Build 0.2-b02-内部,19.0-B04-内部,混合模式)。

真正的答案是始终始终使用从纪元开始的秒数进行日志记录,就像Unix纪元一样,使用64位整数表示(有符号,如果您想允许纪元之前的戳)。任何现实世界的时间系统都有一些非线性、非单调的行为,比如闰时或夏令时。 --约翰·菲尔·H(Phil H)。

另一个来自同一个人,@ThorbjørnRavnAndersen:youtube.com/watch?v=Uqjg8Kk1HXo(Leap Second)。(这一条来自汤姆·斯科特自己的YouTube频道,而不是Computerphile。) -Trig。

@Phil H&34;秒从纪元开始(即Unix时间)也是非线性的,因为POSIX秒不是SI秒并且长度不同 -我还记得莫妮卡吗。

关于1927年上海的详细情况,请看这一页。基本上是在1927年底的午夜,时钟向后拨了5分52秒。因此";1927-12-31 23:54:08";实际上发生了两次,看起来Java正在将其解析为该本地日期/时间的较晚可能的时刻-这就是差异所在。

如果用TZDB的2013a版重新构建,原来的问题将不再表现出完全相同的行为。在2013a,结果为358秒,过渡时间为23:54:03,而不是23:54:08。

我之所以注意到这一点,是因为我正在以单元测试的形式,在野田时间收集这样的问题……。测试现在已经改变了,但它只是显示了-即使是历史数据也不是安全的。

在TZDB 2014f中,更改时间移到了1900-12-31,现在只有343秒的更改(所以t和t+1之间的时间是344秒,如果您明白我的意思的话)。

编辑:要回答关于1900年过渡的问题...。看起来Java时区实现将所有时区视为1900UTC开始之前的任何时刻的标准时间:

Import java.util.TimeZone;public class Test{public static void main(string[]args)]抛出异常{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认为是有效的概念),所以其他库不需要引入这种人工转换。

当当地标准时间即将到达1928年1月1日(星期日)时,时钟被拨回0:05:52小时至31(星期六)。改为当地标准时间1927年12月23:54:08。

这并不是特别奇怪,几乎每个地方都会在某个时间或另一个时间发生,因为时区因政治或行政行动而改变或改变。

如果您无法以UTC显示日期或时间,请始终指明时区。

如果您不能要求以UTC表示的输入日期/时间,则需要明确指定的时区。

递增时间时,应转换回UTC,然后加或减。仅使用当地时间进行显示。

通过这种方式,您将能够经历小时或分钟出现两次的任何时间段。

如果已转换为UTC,请每秒钟添加一次,然后转换为本地时间以进行显示。您将通过晚上11:54:08。LMT-晚上11:59:59。LMT,然后晚上11:54:08。CST-晚上11:59:59。Cst.。

正如其他人解释的那样,那里有一个时间不连续的地方。亚洲/上海可能有两个1927-12-31 23:54:08的时区偏移,但只有一个1927-12-31 23:54:07的时区偏移。因此,根据使用的偏移量,可能存在1秒的差异,也可能存在5分53秒的差异。

这种微小的偏移,而不是我们通常习惯的一小时夏令时(夏令时),有点掩盖了这个问题。

请注意,时区数据库的2013a更新将这种中断提前了几秒钟,但效果仍可观察到。

Java8上的新java.time包让用户更清楚地看到了这一点,并提供了处理它的工具。给予:

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(";亚洲/上海";);string str3=";1927-12-31 23:54:07";;string str4=";1927-12-31 23:54:08";ZonedDateTime zdt3=LocalDateTime.parse(str3,dtf).atZone(上海);ZonedDateTime zdt4=LocalDateTime.parse(str4,dtf).atZone(上海);时长Duration AtEarlierOffset=Duration.between(zdt3.withEarlierOffsetAtOverlap(),zdt4.with EarlierOffsetAtOverlay());时长Duration ationAtLaterOffset=Duration.between(zdt3.withLaterOffsetAtOverlap(),zdt4.with LaterOffsetAtOverlay();

则durationAtEarlierOffset将为1秒,而durationAtLaterOffset将为5分53秒。

将1927-12-31 23:59:59与1928-01-01 00:00:00进行比较可以看到相同的问题,但在本例中,较早的偏移量会产生较长的偏差,而较早的日期可能有两个偏移量。

处理这一问题的另一种方法是检查是否有过渡正在进行。我们可以这样做:

通过使用zot4上的isOverlay()和ISGAP()方法,您可以检查转换是重叠(该日期/时间存在多个该日期/时间的有效偏移量)还是间隙(该日期/时间对于该区域ID无效)。

我希望一旦Java8广泛可用,或者对于那些使用Java7并采用JSR310后端口的人来说,这能帮助人们处理这类问题。

我认为Java中普遍存在的隐式本地化是其最大的设计缺陷。它可能是为用户界面设计的,但坦率地说,除了一些IDE,您基本上可以忽略本地化,因为程序员并不是它的目标受众,今天谁真正将Java用于用户界面。您可以通过以下方式修复它(特别是在Linux服务器上):

改为使用UTF-8/UTC作为固定默认值,因为那只是今天的默认设置。没有理由做其他事情,除非您想要像这样生产线程。

我的意思是,拜托,全局静态变量难道不是反面向对象模式吗?其他的都不是由一些基本的环境变量给出的普遍缺省值.。

上海当地标准时间23:54:07,但过了5分52秒,转到次日00:00:00,之后当地标准时间又改回23:54:08。因此,这就是为什么这两个时间之间的差异是343秒,而不是你预期的1秒。

在美国等其他地方,时间也可能会搞砸。美国有夏令时。夏令时开始时,时间向前推进1小时。但过了一段时间,夏令时结束了,它会倒退1小时回到标准时区。所以有时候,当比较美国的时间时,相差大约是3600秒,而不是1秒。

但这两次改变有一些不同之处。后者是不断变化的,而前者只是一个变化。它既没有变回来,也没有再变同样的量。

在时间不变的情况下,最好使用UTC,除非需要使用非UTC时间(如显示器中的时间)。

之所以会这样,是因为这是上海1927年12月31日的时区规则。如果您转到此页并选择1900-1924&34;的时区更改,您会看到1900年的日期和时间是UTC+8:05:43小时,这段时间内的所有时间都是";UTC+8:05;:43;,这是因为这是上海1927年31月31日的时区规则。如果您转到此页并选择1900-1924&34;的时区更改,您将看到1900年的日期和时间是整个时间段的8:05:43小时。

请注意,时区从CST(中国标准时间,相当于3个字母的亚洲/上海时间)改为HKT(香港时区的3个字母名称)。

但是改变时区并不是很好的解决方案。因此,请尽可能使用系统UTC时间。它总是在转换后给出当地时间。

不是你想要的答案吗?浏览标记的其他问题或提出您自己的问题。