Instant والوقت الآلي
Instant والوقت الآلي
يدور معظم واجهة برمجة java.time حول الوقت البشري — التقاويم والمناطق الزمنية والأشهر وأيام الأسبوع. لكن الحواسيب والأنظمة الموزّعة تحتاج في الغالب إلى شيء أبسط: رقم واحد يُحدّد نقطة بلا غموض على الجدول الزمني العالمي. هذا بالضبط ما توفّره Instant.
حقبة يونيكس ومعنى "الوقت الآلي"
بالاتفاق الدولي، يُقاس الوقت على الحواسيب من حقبة يونيكس: منتصف الليل بتوقيت UTC في الأول من يناير 1970 (1970-01-01T00:00:00Z). كل لحظة في التاريخ تُعبَّر عنها كعدد موقّع من الثواني (وأجزائها) نسبةً إلى هذه النقطة المرجعية.
الوقت الآلي خالٍ من المناطق الزمنية ومن اللغات المحلية ومن التقاويم. لا يوجد فيه مفهوم "الثلاثاء" أو "مارس" — مجرّد رقم على خط الأرقام. هذا يجعله مثاليًا لـ:
- ختم سجلات التتبع وسجلات التدقيق بالطابع الزمني
- قياس الوقت المنقضي في اختبارات الأداء
- مقارنة الأحداث عبر الأنظمة الموزّعة في مناطق جغرافية مختلفة
- تخزين الوقت ونقله في قواعد البيانات وواجهات JSON البرمجية
الصنف Instant
تمثّل java.time.Instant نقطة على الجدول الزمني بدقة تصل إلى النانوثانية. داخليًا تحتفظ بحقلين: long epochSecond (الثواني منذ 1970-01-01T00:00:00Z) وint nanos (التعديل بالنانوثانية داخل تلك الثانية، من 0 إلى 999,999,999).
toString() لأي Instant بالحرف Z (زولو، الرمز الصوتي لحرف Z في مقياس NATO)، وهو يعني UTC. هذا هو تمثيل ISO-8601 لطابع زمني بتوقيت UTC. حين تُرسل أو تستقبل طوابع زمنية في واجهات برمجية، يُشير Z إلى أن التوقيت بالفعل بتوقيت UTC ولا يحتاج إلى تحويل.
إنشاء كائنات Instant
إضافةً إلى Instant.now() توجد عدة توابع مصنع:
الاستعلام عن Instant
toEpochMilli() عند التكامل مع الكود القديم (طوابع JDBC الزمنية، Date، المكتبات الخارجية) لأن دقة الميلي ثانية هي القاسم المشترك. لا تلجأ إلى getNano() إلا حين تحتاج فعلًا إلى دقة أدق من الميلي ثانية.
الوقت الآلي مقابل الوقت البشري: لماذا يهم الفرق؟
فكّر في تخزين موعد لمستخدم. إذا خزّنته كـInstant التقطت نقطة الوقت بتوقيت UTC، لكنك ستفقد القصد: المستخدم قال "9 صباحًا في نيويورك" — وإذا حدث تحوّل في التوقيت الصيفي قبل الموعد، فإن 9 صباحًا بتوقيت UTC لن يكون بعدها 9 صباحًا بالتوقيت المحلي. هذه مشكلة وقت بشري.
في المقابل، إذا خزّنت حدثًا في سجل موزّع كـZonedDateTime بالتوقيت المحلي للخادم، وكان هذا الخادم في منطقة مختلفة عن خادم آخر، يصبح ربط الأحداث بين السجلَّين تمرينًا مؤلمًا في تحويل المناطق الزمنية. هذه مشكلة يحلّها Instant بسهولة تامة.
القاعدة العملية:
- استخدم
Instantلـ: طوابع السجلات والتدقيق وترتيب الأحداث بين الخدمات وقياس المدد وتخزين "متى حدث هذا بمصطلحات مطلقة". - استخدم
ZonedDateTime/LocalDateTimeلـ: التقاويم والجدولة وكل ما يراه المستخدم ويستدلّ عليه بتوقيته المحلي.
العمليات الحسابية على Instant
تدعم Instant الجمع والطرح عبر Duration (سيأتي في الدرس التالي)، إضافةً إلى توابع مختصرة للوحدات الشائعة:
Instant لاختبارات الأداء الدقيقة. تقرأ Instant.now() ساعة الحائط للنظام، التي يمكن أن تقفز للخلف أثناء تصحيحات NTP. لاختبار الأداء عالي الدقة استخدم System.nanoTime() — فهو رتيب (لا يرجع أبدًا للخلف) لكنه لا يحمل معنى حقبة ولا يمكن تحويله إلى تاريخ. استخدم Instant حين تحتاج طابعًا زمنيًا حقيقيًا لساعة الحائط؛ واستخدم System.nanoTime() حين تحتاج فقط قياس مدة.
التحويل بين Instant والأنواع المقروءة للإنسان
ستحتاج كثيرًا إلى تحويل Instant إلى تمثيل زمني محلّي أو بمنطقة زمنية محدّدة للعرض، أو التحويل في الاتجاه المعاكس بعد إدخال المستخدم:
Instant وواجهات برمجة الإصدارات القديمة
قبل Java 8 كان العالم يستخدم java.util.Date وjava.sql.Timestamp. ستصادف هذين الصنفين في قواعد الكود القديمة وتعريفات JDBC وتعيينات ORM. الجسر بينهما مباشر:
الخلاصة
Instant هو تمثيل java.time للوقت الآلي: نقطة على الجدول الزمني بتوقيت UTC خالية من المناطق الزمنية والتقاويم، مقيسة من حقبة يونيكس. استخدمها في كل مكان تحتاج فيه طابعًا زمنيًا عالميًا لا لبس فيه — التسجيل وترتيب الأحداث والتخزين والتواصل بين الخدمات. لأي شيء يقرأه الإنسان أو يجدوله بتوقيته المحلي، حوّل إلى ZonedDateTime. سيقدّم الدرس التالي Duration وPeriod، الصنفَين اللذَين يمثّلان امتداد الزمن.