المُدد الزمنية: Duration و Period
المُدد الزمنية: Duration و Period
تناول الدرس السابق LocalDate وLocalTime وLocalDateTime بوصفها نقاطًا على خط الزمن. هذا الدرس يتناول كميات الزمن — الفجوة بين نقطتين، أو مقدارًا من الوقت تريد إضافته أو طرحه. توفّر واجهة java.time كلاستين مختلفتين لهذا الغرض، كلٌّ منهما تُمثّل مفهومًا مغايرًا:
Duration— كمية زمنية تُقاس بالثواني والنانو ثانية. استخدمها معLocalTimeوLocalDateTimeوInstantأو أي كلاس يحمل مكوّن وقت.Period— كمية زمنية تُقاس بالسنوات والأشهر والأيام. استخدمها معLocalDateأو مكوّن التاريخ فيLocalDateTime.
Period بوحدات تقويمية وتتعامل مع هذا التباين بشكل صحيح، في حين تعمل Duration بثوانٍ دقيقة لا علاقة لها بالتقويم. الخلط بينهما يُفضي إلى أخطاء خفيّة — يُجبرك التصميم على الاختيار الواعي.
Duration
تُخزَّن Duration دائمًا على شكل عدد إجمالي من الثواني مع تعديل بالنانو ثانية. تُتيح لك توابع المصنع التعبير عن المقدار بوحدة مقروءة:
أقوى توابع المصنع هو Duration.between() الذي يحسب الفجوة بين كائنَين زمنيَّين من النوع ذاته:
يُعيد toMinutesPart() (Java 9) مكوّن الدقائق (0–59) لا المجموع الكلي. هذا هو الأسلوب الاصطلاحي لتنسيق المدة دون إجراء حسابات يدوية.
toHoursPart() وtoMinutesPart() وtoSecondsPart() وtoNanosPart() تُقسّم Duration إلى مكوّناتها المقروءة. قبل Java 9 كان عليك إجراء الحساب يدويًا (مثلًا minutes % 60).
عمليات Duration الحسابية
كلاس Duration غير قابل للتغيير (immutable). كل تابع يُعدّل القيمة يُعيد نسخة جديدة:
Period
تُخزّن Period السنوات والأشهر والأيام في ثلاثة حقول صحيحة منفصلة — ولا تُحوّل بينها. إضافة مدة شهر واحد إلى 31 يناير تُعطي 28 فبراير (أو 29 في السنة الكبيسة)، لا 31 يومًا لاحقًا، لأن الحساب التقويمي يُطبَّق بشكل صحيح لحظة التطبيق.
Period.between()
مثل Duration.between()، يحسب Period.between() الفرق بين قيمتَي LocalDate بوحدات تقويمية:
LocalDate مكوّن وقت، لذا تمريره إلى Duration.between() يُطلق استثناء UnsupportedTemporalTypeException. دائمًا طابق الكلاس: Duration للكائنات الحاملة للوقت، وPeriod لكائنات التاريخ فقط.
عمليات Period الحسابية
لاحظ أن toTotalMonths() يتجاهل حقل الأيام. لا يوجد toTotalDays() لأن ذلك يستلزم معرفة التقويم (الشهر ليس عددًا ثابتًا من الأيام). إذا أردت إجمالي الأيام بين تاريخين، استخدم ChronoUnit.DAYS.between(start, end) عوضًا عن ذلك.
ChronoUnit.between() — بديل أبسط
أحيانًا تحتاج رقمًا واحدًا فحسب: "كم أسبوعًا كاملًا بين هذين التاريخين؟" يجعل ChronoUnit هذا سطرًا واحدًا:
يُعيد هذا دائمًا long واحدًا مُقتطعًا إلى وحدات كاملة. استخدمه حين تحتاج رقمًا قياسيًا لا Period أو Duration منظَّمة.
تطبيق الكميات على كائنات التاريخ والوقت
تُنفّذ كلتا الكلاستين واجهة TemporalAmount، ما يتيح تمريرهما إلى plus() وminus() على أي كائن زمني متوافق:
LocalDate، استخدم Period. وإذا احتوى على مكوّن وقت (LocalTime أو LocalDateTime أو Instant)، استخدم Duration. قد يتجاوز المُترجم الخلط بينهما أحيانًا، غير أن وقت التشغيل يُطلق استثناءً — لذا يجب أن يصدر الانضباط منك أنت.
الخلاصة
تُمثّل Duration وقت الآلة — عددًا دقيقًا من الثواني والنانو ثانية، مستقلًّا عن التقويم. وتُمثّل Period وقت الإنسان — سنواتٌ وأشهرٌ وأيامٌ تحترم عدم انتظام التقويم. كلتاهما كائنات قيمة غير قابلة للتغيير. استخدم between() لقياس الفجوة، وplus() / minus() لتطبيقهما. وحين تحتاج عددًا قياسيًا واحدًا فحسب، الجأ إلى ChronoUnit.