عام 1927: تلك اللحظة التي عاد فيها الزمن إلى الوراء
\n\nتخيل: أنت تكتب سكريبت بسيط بلغة Java لحساب الفرق بين تاريخين بالمللي ثانية. تأخذ عام 1927، وتطرح قيمة من أخرى - وتحصل على رقم غريب بدلاً من 3600 ثانية (ساعة واحدة). تتحقق من الكود، تبحث في Google، وتجد السؤال الأسطوري على StackOverflow بآلاف المشاهدات. وتدرك: أنت لست وحدك.
\n\nهذا الخطأ هو قطعة أثرية حقيقية من التاريخ، لا تزال تخيف المبتدئين حتى اليوم. في هذه المقالة، سنشرح لماذا يعطي طرح epoch-milli times في عام 1927 نتيجة غريبة، ونعرض أمثلة حقيقية من كود Java، والأهم من ذلك، سنقدم طرقاً عملية لتجنب هذا الفخ. إذا كنت تعمل مع بيانات تاريخية أو تريد ببساطة كتابة كود موثوق - فهذه المعلومات لك.
\n\nجوهر المشكلة: سحر 31 ديسمبر 1927
\n\nدعنا نذهب مباشرة إلى الكود. إليك مثالاً كلاسيكياً يحير العقل:
\n\nimport 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 مللي ثانية (ثانية واحدة) ترى 353؟ أو حتى رقماً سالباً؟ تهانينا، لقد واجهت خطأ طرح التواريخ لعام 1927.
\n\nالسبب ليس خطأً في Java (على الرغم من وجود خطأ أيضاً، ولكن سنتحدث عن ذلك لاحقاً). السبب هو تغيير تاريخي في المنطقة الزمنية. في 31 ديسمبر 1927 في شنغهاي (Asia/Shanghai)، حدث أمر غريب: في الساعة 23:54:08، تم إرجاع التوقيت المحلي بمقدار 5 دقائق و52 ثانية. في الواقع، لم تدم الثانية 1000 مللي ثانية، بل 353 مللي ثانية فقط. Java تتعامل بأمانة مع هذه الحقيقة التاريخية، لكن بالنسبة للمطور، يبدو هذا كالسحر.
\n\nأهم الأخطاء التي يرتكبها المطورون
\n\n1. الثقة العمياء في واجهات API القديمة (java.util.Date و SimpleDateFormat)
\n\nالخطأ الأول والأكثر شيوعاً هو استخدام الفئات القديمة java.util.Date و SimpleDateFormat. فهي ليست غير مريحة فحسب، بل تحتوي أيضاً على مفاجآت مخفية، خاصة عند العمل مع التواريخ التاريخية.
كيف تتجنب ذلك: استخدم java.time (Java 8+). على سبيل المثال:
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\n2. تجاهل التغييرات التاريخية للمناطق الزمنية
\n\nيعتقد الكثيرون أن المناطق الزمنية هي مجرد إزاحة ثابتة بالنسبة لـ UTC. لكن في الواقع، لقد تغيرت عشرات المرات خلال المائة عام الماضية.