تحتوي JavaScript على كائن مدمج هو Date يخزّن التاريخ والوقت، ويوفّر طرقًا لإدارتهما.
كان مبنيًّا على صنف java.util.Date في Java، الذي استُبدل في مطلع العقد 2010، لكن Date في JavaScript لا يزال موجودًا من أجل التوافق مع الإصدارات السابقة.
ولأنّ العمل مع التواريخ صعب عمومًا، ولأنّ التعامل مع المناطق الزمنية سيّئ أو شبه معدوم، ظهرت مكتبات كثيرة مثل moment.js وday.js وdate-fns وluxon.
لا تتوفّر أي من هذه المكتبات على Exercism.
في مشاريعك الخاصة، لا تستخدم حزمة مهجورة أو غير مدعومة مثل moment.js، بل اعتمد على بدائل أحدث مثل luxon، أو على Temporal الذي لم ينتشر بعد على نطاق واسع.
يركّز هذا التمرين على Date، الذي سيبقى ذا صلة إلى نهاية JavaScript.
كائن Date هو نسخة من صنف Date.
يمكن إنشاؤه دون تمرير أي وسائط إلى دالة البناء.
ينتج عن ذلك كائن Date يمثّل التاريخ والوقت الحاليين:
const now = new Date();
// => Thu Apr 14 2022 11:46:08 GMT+0530 (India Standard Time)
// Shows current day, date and time (in your time zone).
إذا مُرّر عدد، فسيُفسَّر على أنه timestamp.
الطابع الزمني عدد صحيح يمثّل عدد الميلي ثانية التي مرّت منذ 1 يناير 1970 UTC+0.
const epoch = new Date(0);
// Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)
const another = new Date(1749508766627);
// Tue Jun 10 2025 00:39:26 GMT+0200 (Central European Summer Time)
قد يتوقّع المرء أن new Date(0) ينشئ كائن التاريخ "الأقدم"، لكن JavaScript ستحوّل التاريخ إلى منطقتك الزمنية المحلية، ما يعني أن من هم في نطاق المنطقة الزمنية GMT / UTC+0 فقط هم من سيحصلون فعلًا على قيمة حقبة Unix.
يمكنك تمرير قيمة نصية تمثّل تاريخًا إلى دالة البناء Date.
الصيغة الوحيدة المتّسقة بين مختلف التطبيقات هي النسخة المبسّطة مما يُعرف بسلاسل الطابع الزمني ISO 8601، وهي معترف بها دوليًا وموحّدة.
تبدو لحظة زمنية معيّنة بتوقيت UTC هكذا:
YYYY-MM-DDTHH:mm:ss.mssZ
YYYYMMDDTHHmmss.mssZ
حيث تُجرى الاستبدالات التالية:
| المفتاح | الوصف | القيمة الافتراضية |
|---|---|---|
| YYYY | السنة التقويمية، بصيغة 4 أرقام | |
| MM | الشهر التقويمي، بصيغة رقمين | 01 |
| DD | يوم الشهر التقويمي، بصيغة رقمين | 01 |
| T | حرف T نفسه، يفصل التاريخ عن الوقت | |
| HH | الساعات بنظام 24 ساعة، بصيغة رقمين | 00 |
| mm | الدقائق، بصيغة رقمين | 00 |
| ss | الثواني، بصيغة رقمين | 00 |
| mss | الميلي ثانية، بصيغة 3 أرقام | 000 |
| Z | حرف Z نفسه، أو إزاحة +/-HH:mm
|
يشير الحرف Z نفسه إلى UTC (بدون منطقة زمنية، وبدون توقيت صيفي).
ولأنّ لمعظم المكوّنات قيمًا افتراضية، فمن الصحيح حذف أجزاء من النهاية:
YYYY-MM-DD
القيمة الافتراضية للوقت هي 00:00:00.000
إذا لم ينتهِ الطابع الزمني بـ Z، ولم ينتهِ بـ +HH:mm أو -HH:mm (ما يشير إلى إزاحة المنطقة الزمنية)، فبسبب أسباب تاريخية ينطبق ما يلي:
عندما تكون إزاحة المنطقة الزمنية غائبة، تُفسَّر الصيغ التي تتضمّن التاريخ فقط على أنها بتوقيت UTC، وتُفسَّر الصيغ التي تتضمّن التاريخ والوقت على أنها بالتوقيت المحلي. ويرجع تفسيرها على أنها بتوقيت UTC إلى خطأ تاريخي في المواصفة لم يكن متّسقًا مع ISO 8601، لكن تعذّر تغييره بسبب التوافق مع الويب. راجع المحلّل المعطوب: مشكلة واقعية على الويب.
قد تعمل صيغ أخرى يقبلها Date.parse وقد لا تعمل.
عند التعامل مع التواريخ في JavaScript، استخدم دائمًا طابعًا زمنيًا بصيغة ISO 8601 عند التحويل من string إلى Date.
الصيغ التي تتضمّن التاريخ فقط مسموح بها، لكن ليس كل صيغ ISO 8601 مدعومة. راجع صفحة شرح النسخة المبسّطة على MDN.
Dateيمكن أيضًا استخدام كائن تاريخ موجود كوسيط لدالة البناء.
ينشئ ذلك نسخة من كائن Date الموجود بالتاريخ والوقت نفسيهما.
const t1 = new Date();
const t2 = new Date(t1);
// Values of t1 and t2 will be the same.
يمكن إنشاء تاريخ يمثّل يومًا بتمرير ثلاثة أعداد. ويمكن إنشاء تاريخ يمثّل يومًا ووقتًا بتمرير 6 أعداد.
const date1 = new Date(95, 11, 17);
// Creates Date for Dec 17 1995 00:00 if your local timezone is equivalent to UTC.
const date2 = new Date(2013, 12, 5, 13, 24, 0);
// Creates Date for Jan 5 2014 13:24 if your local timezone is equivalent to UTC.
القيمة الثانية هي month، وتبدأ من 0 لشهر يناير، حتى 11 لشهر ديسمبر.
Date.parse()قد تصادف إشارات أو إحالات إلى دالة تحليل التواريخ Date.parse.
ولأنّ هذه الدالة لا تضمّ سوى قليل من الثوابت (الحقائق المؤكدة)، ولأنّ بقية تنفيذها غير محدّدة (وبالتالي غير قياسية)، فلا ينبغي استخدامها.
Date
هناك طرق متعددة على كائنات التاريخ تُرجع مكوّنات التاريخ:
getFullYear(); // Get the year (4 digits)
getMonth(); // Get the month, from 0 to 11.
getDate(); // Get the day of month, from 1 to 31.
getHours(); // Get the hour in a 24 clock, from 0 to 23
getMinutes(); // Get the minutes, from 0 to 59
getSeconds(); // Get the seconds, from 0 to 59
getMilliseconds(); // Get the milliseconds, from 0 and 999
getDay(); // Get the day of week, from 0 (Sunday) to 6 (Saturday).
ولكلٍّ من هذه الطرق صيغة set مناظرة (مثل setFullYear) لتحديث القيمة.
وأي قيمة تتجاوز الحد تنتقل إلى المكوّن التالي:
const date = new Date('2025-02-28T12:42:00Z');
// => Fri Feb 28 2025 13:42:00 GMT+0100 (Central European Standard Time)
date.setDate(29);
// there was no February 29th in 2025.
date.getDate();
// => 1
date.toString();
// => Sat Mar 01 2025 13:42:00 GMT+0100 (Central European Standard Time)
توجد صيغ بتوقيت UTC لجميع الطرق تتجاهل المنطقة الزمنية المحلية.
تمتلك كائنات التاريخ طريقة getTime() تُرجع الطابع الزمني UNIX بالميلي ثانية، أي عدد الميلي ثانية منذ منتصف ليل 1 يناير 1970 بتوقيت UTC.
إضافةً إلى ذلك، تتوفّر طريقة toISOString() للتحويل من كائن تاريخ إلى سلسلة طابع زمني بصيغة ISO 8601.
يمكن استخدام أكبر من (>) وأكبر من أو يساوي (>=) وكذلك أصغر من (<) وأصغر من أو يساوي (<=) مباشرةً بين تاريخين، أو بين تاريخ وعدد.
يعمل هذا لأنّ JavaScript ستحاول تحويل التاريخ إلى قيمة أولية.
عند إجراء مقارنة بين تاريخين أو بين تاريخ وعدد، تستدعي JavaScript [Symbol.toPrimitive]("number") التي تستدعي بدورها date.valueOf().
وهذه الأخيرة مماثلة لاستدعاء date.getTime().
إذا كنت لا تريد الاعتماد على هذا السلوك، فحوّل التاريخ إلى عدد باستخدام getTime() أولًا.
لا يمكن مقارنة التواريخ باستخدام المساواة (== و===)، لكن يمكن مقارنة ناتج .getTime().
في هذا التمرين ستعمل على بعض الدوال لإدارة المواعيد. يخزّن النظام كل شيء في سلاسل نصية بتنسيق ISO 8601، لكن هذه ليست الطريقة التي يستخدم بها الناس التقويم. نحتاج إلى دوال متعددة للتحويل بين التنسيقات المختلفة.
أنشئ موعدًا بعد n يومًا من الآن في الوقت الحالي.
الدالة تأخذ عدد الأيام n وتُرجع وقت الموعد بعد n يومًا من الآن.
createAppointment(4, now);
// Given now is Sun Oct 05 2022 23:28:43 GMT+0600 (Bangladesh Standard Time)
// => Sun Oct 09 2022 23:28:43 GMT+0600 (Bangladesh Standard Time)
إذا لم يُستخدم المعامل الثاني now، فينبغي استخدام الوقت الحالي بدلًا منه.
أدوات متعددة لا تعمل إلا بالتنسيق القياسي الدولي ISO 8601.
اكتب الدالة getAppointmentTimestamp لتأخذ تاريخًا وتُرجع سلسلة نصية بهذا التنسيق.
const appointment = new Date(Date.UTC(2010, 6, 16, 12, 42, 0, 0));
getAppointmentTimestamp(appointment);
// => '2010-07-16T12:42:00.000Z'
يصعب قراءة الطوابع الزمنية، ولذلك ستساعد دالة للحصول على تفاصيل الموعد في ذلك.
الدالة getAppointmentDetails تأخذ طابعًا زمنيًا بتنسيق ISO 8601، وتُرجع السنة والشهر واليوم والساعة والدقيقة.
getAppointmentDetails('2022-04-24T08:15:00.000');
// => { year: 2022, month: 3, date: 24, hour: 8, minute: 15 }
الدالة تستقبل الوسيط الأول وقت الموعد، والوسيط الثاني كائنًا يحمل بعض الخيارات. عليك تحديث الموعد وفقًا للخيارات الموجودة في الكائن، وإرجاع تاريخ الموعد الجديد. قد يحتوي كائن الخيارات على خيارات متعددة.
updateAppointment('2022-02-09T09:20:00.000', { month: 6 });
// => { year: 2022, month: 6, date: 9, hour: 10, minute: 20 }
الدالة تستقبل موعدين (طابعين زمنيين) كوسائط. عليك إرجاع الفرق بين هذين الوقتين بالثواني.
ولأن نصف ثانية لا يعني شيئًا تقريبًا، قرّب العدد قبل إرجاعه.
timeBetween('2022-12-12T09:20:00.000', '2022-12-18T08:30:00.000');
// => 515400
أخيرًا، عندما يُحدَّد الموعد، يحتاج النظام إلى التحقق مما إذا كان صالحًا. بعبارة أخرى، يجب أن يكون الموعد في المستقبل، لا في الماضي.
اكتب الدالة isValid التي تأخذ وسيطين: طابعًا زمنيًا للموعد (سلسلة نصية)، والوقت الحالي كطابع زمني (سلسلة نصية)، وتُرجع true إذا كان الموعد في المستقبل بالنظر إلى الوقت الحالي.
isValid('2022-02-11T23:00:00.000', '2022-02-08T23:00:00.000');
// => true
سجّل في Exercism لتتعلّم وتتقن JavaScript عبر 37 مفهومًا159 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.