Java التاريخ والوقت

⚡ ملخص ذكي

Java يجمع التعامل مع التاريخ والوقت بين فئتي java.util.Date و SimpleDateFormat القديمتين وواجهة برمجة تطبيقات java.time الحديثة التي تم تقديمها في Java 8، مما يمنح المطورين أنواعًا غير قابلة للتغيير وآمنة للاستخدام في بيئة متعددة الخيوط من أجل عمل دقيق في التقويم.

  • 🕒 تاريخ الإرث: تقوم فئة java.util.Date بتخزين لحظة زمنية بالمللي ثانية وطباعة سلسلة نصية افتراضية من خلال toString.
  • 🔤 حروف نمطية: يستخدم SimpleDateFormat أحرفًا مثل yyyy و MM و dd و HH و mm و ss و zzz لتحديد تنسيقات الإخراج المخصصة.
  • 🇧🇷 مقارنة: تقوم طريقة compareTo بإرجاع عدد صحيح سالب أو صفر أو موجب لترتيب مثيلين من نوع Date بشكل زمني.
  • 🆕 واجهة برمجة التطبيقات الحديثة: تستبدل LocalDate و LocalDateTime و ZonedDateTime و Instant في java.time كلاً من Date و Calendar بكائنات غير قابلة للتغيير.
  • 🧵 سلامة الخيط: DateTimeFormatter غير قابل للتغيير وآمن للمشاركة، بينما يحتاج SimpleDateFormat إلى مزامنة أو غلاف ThreadLocal.
  • 🔁 العمل المشترك: قم بالتحويل بين واجهات برمجة التطبيقات القديمة والجديدة باستخدام أدوات المساعدة Date.toInstant و Date.from و Calendar.toInstant.

Java التاريخ والوقت

تحديد التواريخ والأوقات في Java يُعدّ جزءًا روتينيًا من بناء منطق الجدولة، وتسجيل الطوابع الزمنية، والتقارير، وسجلات التدقيق. قبل كتابة أي كود، من المفيد معرفة المعايير التي تُشكّل التاريخ:

  • استخدم عام (برقمين أو أربعة أرقام).
  • استخدم شهر (كرقمين، أو كأول ثلاثة أحرف من الشهر، أو كاسم الشهر الكامل).
  • استخدم تاريخ (رقم اليوم الفعلي من الشهر).
  • استخدم يوم (اسم اليوم في التاريخ المحدد، مثل الأحد، الاثنين، الثلاثاء).

Java يكشف عن العديد من المعايير الأخرى التي تصف لحظة ما على الخط الزمني، بما في ذلك الحقبة الزمنية، والأسبوع في السنة، والمنطقة الزمنية، والفرق الزمني. وتغطي الأقسام التالية كلًا منها بأمثلة قابلة للتنفيذ.

كيفية استخدام التاريخ في Java

عرض التاريخ في Java

Java ويوفر Date الطبقة في java.util تحتوي هذه الحزمة على فئة تخزن لحظة زمنية محددة بدقة تصل إلى أجزاء من الثانية، وتوفر دوال مساعدة لطباعة تلك اللحظة ومقارنتها وتنسيقها. للحصول على التاريخ والوقت الحاليين، قم بإنشاء نسخة من الفئة باستخدام مُنشئها الافتراضي.

import java.util.Date;

class Date_Ex1 {
    public static void main(String args[]) {
        // Instantiate a Date object by invoking its constructor
        Date objDate = new Date();
        // Display the Date and Time using toString()
        System.out.println(objDate.toString());
    }
}

الإخراج:

Wed Nov 29 06:36:22 UTC 2017

يعرض المثال التاريخ بتنسيق toString الافتراضي. لعرض نفس اللحظة بنمط مختلف، عليك أولاً فهم أحرف التنسيق التي Java يستخدم.

SimpleDateFormat: تحليل التواريخ وتنسيقها

استخدم SimpleDateFormat الطبقة في java.text يحول أ Date تحويل الكائن إلى سلسلة منسقة، ثم تحليل السلسلة المنسقة مرة أخرى إلى Dateيستخدم هذا النظام حروفًا نمطية تُشير إلى عنصر التاريخ أو الوقت. يسرد الجدول أدناه جميع الحروف المدعومة:

OMGمكون التاريخ أو الوقتمثال
Gمُحدّد العصرAD
yالسنة2026
Mالشهر في السنةيوليو أو يوليو أو 07
wأسبوع في السنة27
Wأسبوع في الشهر2
Dاليوم في السنة189
dاليوم في الشهر10
Fيوم من الأسبوع في الشهر2
Eاسم اليوم في الأسبوعالثلاثاء أو الثلاثاء
uعدد أيام الأسبوع (1 = Monday7 = الأحد)1
aعلامة صباحية أو مسائيةPM
Hساعة في اليوم (0-23)0
kساعة في اليوم (1-24)24
Kالساعة صباحًا أو مساءً (0-11)0
hالساعة صباحًا أو مساءً (1-12)12
mدقيقة بالساعة30
sالثانية في دقيقة55
Sميلي ثانية واحدة978
zالمنطقة الزمنية (الاسم)الوقت الباسيفيكي القياسي؛ PST؛ GMT-08: 00
Zالمنطقة الزمنية (فرق التوقيت RFC 822)-0800
Xالمنطقة الزمنية (فرق التوقيت وفقًا لمعيار ISO 8601)-08 أو -0800 أو -08:00

لستَ بحاجةٍ إلى حفظ الجدول بأكمله. ارجع إليه كلما أنشأتَ نمطًا جديدًا.

كيفية استخدام تنسيق التاريخ البسيط

SimpleDateFormat هي فئة فرعية من DateFormat يتيح لك ذلك تنسيق وتحليل التواريخ باستخدام الأحرف النمطية المذكورة أعلاه. اجمع الأحرف لوصف السلسلة النصية التي تريد إنتاجها أو قراءتها بدقة.

فمثلا:

  1. تنسيق التاريخ المطلوب: 2012.10.23 20:20:45 بتوقيت المحيط الهادئنمط التنسيق المناسب هو yyyy.MM.dd HH:mm:ss zzz.
  2. تنسيق التاريخ المطلوب: 09:30:00 AM 23-May-2012نمط التنسيق المناسب هو hh:mm:ss a dd-MMM-yyyy.

تلميح: انظر إلى حالة الأحرف. أحرف كبيرة M الشهر، بأحرف صغيرة m دقيقة. أحرف كبيرة H هو نظام الساعة ذو 24 ساعة، بأحرف صغيرة h هو نظام توقيت 12 ساعة. خلطهما ينتج عنه مخرجات خاطئة صامتة.

يقوم البرنامج أدناه بطباعة التاريخ الحالي مرتين، مرة بالتنسيق الافتراضي ومرة ​​أخرى من خلال نمط SimpleDateFormat:

import java.text.SimpleDateFormat;
import java.util.Date;

class TestDates_Format {
    public static void main(String args[]) {
        // Current system date and time is assigned to objDate
        Date objDate = new Date();
        System.out.println(objDate);

        // Build the target pattern and format the date
        String strDateFormat = "hh:mm:ss a dd-MMM-yyyy";
        SimpleDateFormat objSDF = new SimpleDateFormat(strDateFormat);
        System.out.println(objSDF.format(objDate));
    }
}

الإخراج:

Wed Nov 29 06:31:41 UTC 2017
06:31:41 AM 29-Nov-2017

مقارنة التواريخ مثال

مقارنة التواريخ مثال

الطريقة الأكثر موثوقية لطلب اثنين Date الأشياء هي compareTo() تُعيد هذه الدالة عددًا صحيحًا سالبًا عندما يكون التاريخ المُستدعي أسبق، وصفرًا عندما يكون التاريخان متساويين، وعددًا صحيحًا موجبًا عندما يكون التاريخ المُستدعي أحدث. يُحلل المقتطف البرمجي أدناه سلسلتي تاريخ ويطبع ترتيبهما النسبي:

import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;

class TestDates_Compare {
    public static void main(String args[]) throws ParseException {

        SimpleDateFormat objSDF = new SimpleDateFormat("dd-MM-yyyy");
        Date dt_1 = objSDF.parse("20-08-1981");
        Date dt_2 = objSDF.parse("12-10-2012");

        System.out.println("Date1 : " + objSDF.format(dt_1));
        System.out.println("Date2 : " + objSDF.format(dt_2));

        // compareTo returns > 0 when dt_1 is after dt_2
        if (dt_1.compareTo(dt_2) > 0) {
            System.out.println("Date 1 occurs after Date 2");
        }
        // compareTo returns < 0 when dt_1 is before dt_2
        else if (dt_1.compareTo(dt_2) < 0) {
            System.out.println("Date 1 occurs before Date 2");
        }
        // compareTo returns 0 when both dates are equal
        else {
            System.out.println("Both are the same date");
        }
    }
}

الإخراج:

Date1 : 20-08-1981
Date2 : 12-10-2012
Date 1 occurs before Date 2

ملاحظة: استخدم الكود الأصلي النمط dd-mm-yyyy. صغيرة mm الحرف يشير إلى الدقيقة وليس الشهر، لذا قام المحلل اللغوي بإنتاج قيمة الشهر الخاطئة دون أن يلاحظ المستخدم. الحل المذكور أعلاه يستخدم الأحرف الكبيرة. MM بالنسبة للشهر، وهو الحرف الصحيح في النمط.

Java 8. واجهة برمجة تطبيقات التاريخ والوقت الحديثة (java.time)

Java 8 تم تقديمها java.time حزمة برمجية، واجهة برمجة تطبيقات مُعاد تصميمها مستوحاة من مكتبة Joda-Time. الفئات الجديدة غير قابلة للتغيير، وآمنة للاستخدام في بيئات متعددة الخيوط، وتفصل التقويم البشري عن الجدول الزمني الآلي. أكثر الأنواع شيوعًا هي:

  • LocalDate لتاريخ بدون وقت أو منطقة زمنية (على سبيل المثال، عيد ميلاد).
  • LocalTime لعرض الوقت على ساعة حائط بدون تاريخ أو منطقة زمنية.
  • LocalDateTime لتاريخ ووقت بدون تحديد منطقة زمنية.
  • ZonedDateTime لتاريخ ووقت محددين ضمن منطقة زمنية معينة.
  • OffsetDateTime لتاريخ ووقت مع فرق التوقيت عن التوقيت العالمي المنسق ولكن بدون منطقة محددة.
  • Instant بالنسبة للطابع الزمني للجهاز الذي تم قياسه من حقبة يونكس.
  • Duration و Period بالنسبة للمبالغ المستندة إلى الوقت والتاريخ.

يعرض المثال أدناه التاريخ والوقت والطابع الزمني والمنطقة الزمنية الحالية باستخدام واجهة برمجة التطبيقات الحديثة:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.Instant;
import java.time.ZoneId;

class ModernDateDemo {
    public static void main(String args[]) {
        LocalDate today = LocalDate.now();
        LocalDateTime now = LocalDateTime.now();
        ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
        Instant epoch = Instant.now();

        System.out.println("LocalDate     : " + today);
        System.out.println("LocalDateTime : " + now);
        System.out.println("ZonedDateTime : " + zoned);
        System.out.println("Instant       : " + epoch);
    }
}

لكل نوع طرق تصنيع (now, of, parse) والعمليات غير القابلة للتغيير (plusDays, minusHours, withYear) التي تُعيد قيمة جديدة بدلاً من تغيير الكائن الأصلي.

مُنسِّق التاريخ والوقت: تنسيق التواريخ في الوضع الحديث Java

DateTimeFormatter in java.time.format يستبدل SimpleDateFormatيستخدم نفس نمط الأحرف النمطية، وهو غير قابل للتغيير، ويمكن مشاركته بأمان بين الخيوط دون الحاجة إلى مزامنة إضافية. كما يوفر هذا الصنف ثوابت جاهزة مثل ISO_LOCAL_DATE و ISO_ZONED_DATE_TIME للتنسيقات القياسية.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class FormatterDemo {
    public static void main(String args[]) {
        LocalDateTime now = LocalDateTime.now();

        DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        DateTimeFormatter human = DateTimeFormatter.ofPattern("hh:mm:ss a dd-MMM-yyyy");

        System.out.println(now.format(iso));
        System.out.println(now.format(human));

        LocalDateTime parsed = LocalDateTime.parse("2026-07-04T09:30:00", iso);
        System.out.println("Parsed back : " + parsed);
    }
}

لمقارنة تاريخين في واجهة برمجة التطبيقات الحديثة، اتصل بـ isBefore, isAfter أو isEqualتُقرأ هذه الأساليب بشكل أوضح من compareTo والعمل مع الجميع java.time فئة.

سلامة الخيط وأفضل الممارسات لـ Java التاريخ والوقت

يبلغ قطر كلاً من java.util.Date و SimpleDateFormat تسبق الحداثة المعاصرة Javaولكل منها عيوبها المعروفة. اتبع الممارسات التالية للحفاظ على معالجة التاريخ بشكل صحيح في كود الإنتاج:

  • يُفضّل استخدام java.time بدلاً من Date و Calendar. الأنواع الجديدة غير قابلة للتغيير وآمنة للاستخدام في بيئات متعددة الخيوط، وهي تمثل المجال بدقة أكبر (التواريخ بدون أوقات، والأوقات بدون مناطق زمنية، وما إلى ذلك).
  • لا تشارك تنسيق التاريخ البسيط بين سلاسل المحادثات. يخزن هذا الصنف حالة التحليل الداخلية وهو غير آمن للاستخدام في بيئات متعددة الخيوط. قم بتغليفه في ThreadLocalقم بالمزامنة على المثيل، أو التبديل إلى DateTimeFormatter.
  • حدد اللغة دائمًا. تتغير أسماء الأشهر والأيام بتغير اللغة الافتراضية لـ JVM، مما يجعل أنماطًا مثل MMM و EEE غير قابل للنقل. مرر اللغة بشكل صريح (على سبيل المثال، Locale.ENGLISH).
  • كن واضحاً بشأن المناطق الزمنية. LocalDateTime لا يوجد بها منطقة، لذا قم بالتحويل إلى ZonedDateTime or Instant عندما تكون اللحظة الدقيقة مهمة، خاصة بالنسبة للسجلات وأعمدة قواعد البيانات وحمولات الشبكة.
  • قم بالتحويل بين واجهتي برمجة التطبيقات بعناية. استعمل date.toInstant(), Date.from(instant)و calendar.toInstant().atZone(zone) على الحدود بين الكود القديم والكود الحديث.
  • قم بتخزين الطوابع الزمنية بتوقيت UTC. استمر و transmit التواريخ بتوقيت UTC (و Instant أو سلسلة ISO 8601) وتحويلها إلى منطقة المستخدم فقط عند العرض.

الأسئلة الشائعة

استخدم الدالة LocalDateTime.now() من حزمة java.time للحصول على التاريخ والوقت في المنطقة الزمنية للنظام، أو استخدم LocalDate.now() للحصول على التاريخ فقط. في الإصدارات الأقدم Javaاستخدم new java.util.Date() للحصول على اللحظة الحالية.

يمثل java.util.Date لحظة زمنية بالمللي ثانية منذ بداية الحقبة، وهو قابل للتغيير. أما LocalDate فيمثل تاريخًا تقويميًا بدون وقت أو منطقة زمنية، وهو غير قابل للتغيير وآمن للاستخدام في بيئات متعددة الخيوط، ويوفر طرقًا أكثر وضوحًا مثل plusDays و isBefore.

لا. يحتفظ SimpleDateFormat بحالة تحليل قابلة للتغيير، وينتج نتائج خاطئة عند مشاركته بين الخيوط. لذا، قم بتغليفه في ThreadLocal، أو قم بالمزامنة على نفس المثيل، أو انتقل إلى DateTimeFormatter، فهو غير قابل للتغيير وآمن.

أدخل السلسلة النصية "yyyy-MM-dd" كما هي. يُمثل الحرف MM الشهر، بينما يُمثل الحرف mm الدقيقة. لعرض التاريخ والوقت كاملين، استخدم "yyyy-MM-dd HH:mm:ss". حدد اللغة المحلية دائمًا لضمان ثبات أسماء الأشهر.

مع دالة Date القديمة، استخدم compareTo التي تُرجع قيمة عددية سالبة أو صفرية أو موجبة. مع دالة java.time، استدعِ isBefore أو isAfter أو isEqual على LocalDate أو Instant. كلا الطريقتين تحافظان على الترتيب الزمني.

استخدم الدالة `date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()`. وللرجوع إلى تاريخ سابق، استخدم الدالة `Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())`. حدد المنطقة الزمنية بدقة كلما كان تحديد اللحظة الزمنية أمرًا بالغ الأهمية.

تستنتج نماذج الذكاء الاصطناعي المدربة على سجلات ووثائق البيانات النمط المحتمل من سلاسل نصية نموذجية، وتعيد نمط DateTimeFormatter مطابقًا. كما تُشير مصنفات التعلم الآلي إلى الحقول الغامضة، مثل ترتيب الشهر واليوم مقابل ترتيب اليوم والشهر.

نعم. يقترح GitHub Copilot أنماط DateTimeFormatter، وتحويلات ZonedDateTime، و JUnit اختبارات للمخرجات الحساسة للغة المحلية. Revراجع كل اقتراح للتأكد من صحة أحرف النمط واللغة والمنطقة الزمنية قبل الالتزام.

تلخيص هذه التدوينة بـ: