لماذا الوقت في Java هو ألم (وكيفية علاجه)
\nكل مطور واجه مرة واحدة على الأقل مهام عولمة التطبيق يعلم: العمل مع المناطق الزمنية هو حقل ألغام. يبدو الأمر بسيطًا؟ تأخذ تاريخًا، وتحوله إلى منطقة زمنية أخرى، وينتهي الأمر. لكن لا. واجهات البرمجة القديمة مثل java.util.Date و java.util.Calendar تركت وراءها الكثير من الأخطاء، والارتباك مع التوقيت الصيفي، والشيب المبكر.
لحسن الحظ، مع إصدار Java 8 تغير العالم. الحزمة الجديدة java.time (JSR-310) جلبت النظام والوضوح. اليوم سنشرح كيفية العمل بكفاءة مع المناطق الزمنية في Java الحديثة، وتجنب الأخطاء النمطية، وجعل الكود قابلاً للقراءة وموثوقًا.
\nإذا كنت تكتب بلغة Java وكان تطبيقك يتعامل مع مستخدمين من دول مختلفة - فهذه المقالة لك. هيا بنا!
\n\nالتعرف على API الجديد: java.time
\nقبل الغوص في التفاصيل، دعنا نفهم الفئات الرئيسية. ليست كثيرة، وكل منها مسؤول عن مهمته.
\n\nLocalDateTime - الوقت بدون ربط بمنطقة
\nهذه الفئة تخزن التاريخ والوقت، لكنها لا تأخذ في الاعتبار المنطقة الزمنية. من المناسب استخدامها لتسجيل الأحداث التي تحدث في نفس المكان (مثل السجلات على الخادم).
\nLocalDateTime local = LocalDateTime.now();\nSystem.out.println(local); // 2025-03-15T14:30:00.123\nولكن إذا حاولت إرسال هذا الوقت إلى مستخدم في نيويورك، بينما الخادم لديك في موسكو - ستحصل على ارتباك. لذلك، للأنظمة العالمية تحتاج إلى أدوات أخرى.
\n\nZonedDateTime - الوقت مع معلومات كاملة عن المنطقة
\nهذا هو "المعيار الذهبي" للعمل مع المناطق الزمنية في Java. يخزن التاريخ والوقت ومعرف المنطقة الزمنية (ZoneId). هذه هي الفئة التي يجب استخدامها عندما تريد معرفة المنطقة التي يقع فيها الحدث بدقة.
ZonedDateTime nowInMoscow = ZonedDateTime.now(ZoneId.of("Europe/Moscow"));\nSystem.out.println(nowInMoscow); // 2025-03-15T14:30+03:00[Europe/Moscow]\n\nOffsetDateTime - الوقت مع الإزاحة
\nيشبه ZonedDateTime، لكنه يخزن فقط الإزاحة من UTC (مثل +03:00)، بدون اسم المنطقة. مفيد لواجهات API حيث يكون الفرق مع غرينتش هو المهم فقط.
\n\nInstant - لحظة زمنية في UTC
\nتنسيق قابل للقراءة آليًا. Instant هي نقطة على الخط الزمني، دائمًا في UTC. مثالية للتخزين في قواعد البيانات والتبادل بين الخدمات.
Instant instant = Instant.now();\nSystem.out.println(instant); // 2025-03-15T11:30:00.123Z\n\nكيفية اختيار المنطقة الزمنية بشكل صحيح: ZoneId
\nلا تستخدم الاختصارات المكونة من ثلاثة أحرف مثل "PST" أو "CET" - فهي غامضة ولا تراعي التوقيت الصيفي. بدلاً من ذلك، استخدم المعرفات الكاملة من قاعدة بيانات IANA (Time Zone Database).
أمثلة على ZoneId صحيحة:
\n- \n
"Europe/Moscow"\n "America/New_York"\n "Asia/Tokyo"\n "UTC"\n
يمكن الحصول على جميع المناطق المتاحة كالتالي:
\nSet<String> zoneIds = ZoneId.getAvailableZoneIds();\nzoneIds.stream().sorted().limit(10).forEach(System.out::println);\n\nالتحويل بين المناطق الزمنية: أمثلة عملية
\nالمهمة الأكثر شيوعًا هي أخذ وقت في منطقة وإظهاره للمستخدم في منطقة أخرى. يتم ذلك ببساطة.
\n\nمثال 1: تحويل الوقت من موسكو إلى نيويورك
\nZonedDateTime moscowTime = ZonedDateTime.now(ZoneId.of("Europe/Moscow"));\nZonedDateTime newYorkTime = moscowTime.withZoneSameInstant(ZoneId.of("America/New_York"));\n\nSystem.out.println("موسكو: " + moscowTime);\nSystem.out.println("نيويورك: " + newYorkTime);\nلاحظ الطريقة withZoneSameInstant() - فهي تغير فقط عرض الوقت، مع الحفاظ على اللحظة الفيزيائية.
مثال 2: من UTC إلى الوقت المحلي للمستخدم
\n
المدونات
توصيات الكتب
اقرأ أيضًا