为什么1927年的日期差会破坏代码?Java常见错误TOP

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

1927年:时间倒流的那一刻

\n\n

想象一下:你正在用Java编写一个简单的脚本,用于计算两个日期之间的毫秒差。你取1927年,从一个值中减去另一个值——结果得到的不是3600秒(一小时),而是一个奇怪的数字。你反复检查代码,上网搜索,找到了StackOverflow上那个拥有数千次浏览的传奇问题。然后你明白了:你并不孤单。

\n\n

这个Bug是一个真正的历史产物,至今仍让新手感到困惑。在本文中,我们将分析为什么1927年的epoch毫秒时间减法会得出奇怪结果,通过真实的Java代码示例进行展示,最重要的是,给出避免这个陷阱的有效方法。如果你在处理历史数据,或者只是想编写可靠的代码——这些信息对你来说很有用。

\n\n

问题本质:1927年12月31日的魔法

\n\n

让我们直接看代码。这是一个经典的让人头疼的例子:

\n\n
import java.util.TimeZone;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\npublic class StrangeDateDifference {\n    public static void main(String[] args) throws Exception {\n        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");\n        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");\n        sdf.setTimeZone(tz);\n\n        Date date1 = sdf.parse("1927-12-31 23:54:07");\n        Date date2 = sdf.parse("1927-12-31 23:54:08");\n\n        long diff = date2.getTime() - date1.getTime();\n        System.out.println("毫秒差:" + diff);\n        // 预期1000,但得到……\n    }\n}
\n\n

运行后——你看到的不是1000毫秒(1秒),而是353?甚至是负数?恭喜你,你遇到了1927年日期减法错误。

\n\n

原因不在于Java的Bug(虽然也有关系,但稍后再说)。原因在于历史时区变更。1927年12月31日,在上海(Asia/Shanghai)发生了一件奇怪的事:当地时间23:54:08被调慢了5分52秒。实际上,那一秒并非1000毫秒,而只有353毫秒。Java忠实地反映了这一历史事实,但对开发者来说,这看起来就像魔法。

\n\n

开发者常犯的常见错误

\n\n

1. 盲目信任旧API(java.util.Date 和 SimpleDateFormat)

\n\n

第一个也是最常见的错误是使用过时的类java.util.Date和SimpleDateFormat。它们不仅不方便,而且在处理历史日期时还隐藏着意外情况。

\n\n

如何避免:使用java.time(Java 8+)。例如:

\n\n
import java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\n\npublic class SafeDateDifference {\n    public static void main(String[] args) {\n        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");\n        ZoneId zone = ZoneId.of("Asia/Shanghai");\n\n        LocalDateTime ldt1 = LocalDateTime.parse("1927-12-31 23:54:07", formatter);\n        LocalDateTime ldt2 = LocalDateTime.parse("1927-12-31 23:54:08", formatter);\n\n        ZonedDateTime zdt1 = ldt1.atZone(zone);\n        ZonedDateTime zdt2 = ldt2.atZone(zone);\n\n        long diff = zdt2.toInstant().toEpochMilli() - zdt1.toInstant().toEpochMilli();\n        System.out.println("毫秒差:" + diff);\n        // 仍然会得到奇怪数字,但至少你会知道原因!\n    }\n}
\n\n

2. 忽略时区的历史变更

\n\n

许多人认为时区只是相对于UTC的固定偏移。但实际上,在过去100年里,时区已经变更过数十次。

博客

书籍推荐