كائن Date في JavaScript: لحظة زمنية واحدة
كائن Date في JavaScript يمثّل لحظة زمنية واحدة بعينها، وهو في حقيقته مجرد رقم يدلّ على عدد الميلي ثانية المنقضية منذ الأول من يناير 1970 بتوقيت UTC (وهو ما يُعرف بـ"حقبة يونكس" أو Unix epoch). أما باقي التفاصيل — من سنوات وشهور وأيام ومناطق زمنية وصيغ عرض — فما هي إلا طبقات تُبنى فوق هذا الرقم.
now.getTime() هو ببساطة عدد الميلي ثانية الخام. كل ما يقوم به كائن Date في javascript — من مقارنة التواريخ، وإضافة الأيام، وتنسيقها — يعود في النهاية إلى التعامل مع هذا الرقم ثم إعادة تفسيره.
احتفظ بهذا التصور في ذهنك دائماً. كائن Date لا يعني "14 مارس في باريس"، بل هو لحظة زمنية كونية يمكن عرضها على أنها 14 مارس في باريس، أو 13 مارس في لوس أنجلوس، وذلك حسب المنطقة الزمنية التي تنظر إليها من خلالها.
إنشاء كائن Date في javascript
هناك أربع طرق رئيسية لإنشاء كائن Date:
شيئان ينبغي الانتباه لهما:
- باني الأجزاء (parts constructor) يستخدم فهرسة الأشهر من الصفر. الرقم
2يعني مارس، ويناير هو0. هذا مصدر دائم لأخطاء الـ off-by-one — لكن مع ذلك، كل الـ API يتعامل مع الأشهر بالفهرسة من الصفر، فعلى الأقل هو متسق مع نفسه. new Date("2026-03-14")(بدون وقت) يُفسَّر على أنه منتصف الليل بتوقيت UTC. أماnew Date("2026-03-14T09:30")(بدونZ) فيُفسَّر على أنه توقيت محلي. هذا التفاوت من أشهر المطبات الكلاسيكية.
لو احتجت "اللحظة الحالية" كرقم فقط، استخدم Date.now() لأنه يتجنّب إنشاء الكائن من الأساس:
Date.now() هي الأداة المناسبة لقياس الزمن المنقضي، ومعالجة المهل الزمنية، وكل ما لا يحتاج إلى حسابات تقويمية.
استخراج مكونات التاريخ في javascript
متى ما كان لديك كائن Date، يمكنك الحصول على مكوناته عبر دوال getter. ولكل واحدة منها نسختان: واحدة بالتوقيت المحلي، وأخرى بالتوقيت العالمي UTC.
التوابع المحلية تعتمد على جهاز الشخص الذي يُشغِّل الكود. فإذا كنت تحفظ التواريخ أو تقارن بينها بين المستخدمين والخوادم، فاحسم أمرك واستخدم UTC بشكل صريح، وإلا ستطارد أخطاءً وهمية لا تنتهي. القاعدة العامة: استعمل دوال UTC لكل ما يذهب إلى قاعدة بيانات أو ملفات السجلّات، واستعمل الدوال المحلية لكل ما ستعرضه أمام المستخدم مباشرة.
تجنّب استخدام getYear()؛ فهي طريقة قديمة تُرجع year - 1900 وموجودة لأغراض التوافق فقط. اعتمد دائمًا على getFullYear().
تنسيق التاريخ للعرض على المستخدم
ابتعد عن date.toString() في أي شيء يهمّك فعلًا، لأن مُخرجاتها تتغيّر حسب اللغة ومحرّك الـ JavaScript. وهناك أداتان رئيسيتان تستحقّان المعرفة.
للحصول على صيغة نصية قياسية يقرؤها الحاسوب، استخدم toISOString():
هذا هو التنسيق المناسب للاستخدام عند تسجيل الأحداث في السجلات، أو التخزين في JSON، أو الإرسال عبر الشبكة. فهو دائمًا بتوقيت UTC، ودائمًا واضح لا لبس فيه.
أما إذا كنت تريد عرض التاريخ للمستخدم، فاستخدم Intl.DateTimeFormat أو التوابع التي تبدأ بـ toLocale، وهي في الأساس غلاف حوله:
Intl.DateTimeFormat يتكفّل بالتعامل مع اللغات المحلية والمناطق الزمنية وأي تركيبة من الحقول قد تحتاجها. اعتمد عليه قبل أن تلجأ إلى تنسيق يدوي من نوع ${year}-${month}-${day} — فهذا النوع من بناء السلاسل هو المكان الذي تتكاثر فيه أخطاء الشهر المُزاح بواحد.
مقارنة التواريخ في javascript
كائنا Date اللذان يمثّلان نفس اللحظة الزمنية ليسا متساويَين بـ === — لأن === يقارن هوية الكائن لا قيمته. قارن بين الطابعَين الزمنيَّين بدلاً من ذلك:
للترتيب، يمكنك استخدام عوامل المقارنة مباشرةً، لأنها تُحوِّل التاريخ إلى رقم تلقائيًا:
الطرح يعطيك الفرق بالميلي ثانية. اقسمه على 1000 * 60 * 60 * 24 لتحصل على عدد الأيام. اكتب هذا الثابت بشكل صريح في المرة الأولى، ومع الوقت ستتعرّف على 86_400_000 بمجرد رؤيته.
الحساب على التواريخ في javascript
لا توجد دالة جاهزة باسم addDays. الطريقة المتعارف عليها هي استخدام setDate وsetMonth وما شابهها، فهي تقبل قيمًا خارج النطاق المعتاد وتتعامل مع الترحيل تلقائيًا:
نقطتان تستحقّان التنويه:
new Date(date)يُنشئ نسخة من التاريخ. أمّاsetDateفهو يُعدِّل القيمة الأصلية مباشرةً، لذا انسخ التاريخ أولاً وإلا ستُعدِّل قيمة المُستدعي دون أن تشعر.- استدعاء
setDate(35)في شهر من 31 يوماً ينتقل تلقائياً إلى الشهر التالي. والشيء نفسه ينطبق علىsetMonth(14)فهو يُقدِّم السنة. هذا السلوك يُبسِّط العمليات الحسابية على التواريخ أكثر ممّا يبدو للوهلة الأولى.
أمّا في الحالات المعقّدة — كأيّام العمل، والأحداث المتكرّرة، والمُدد التي تأخذ عدد أيام الشهر بعين الاعتبار — فالأفضل الاستعانة بمكتبة جاهزة مثل date-fns أو Luxon أو واجهة Temporal القادمة. فكتابة حسابات تقويمية أعقد من "إضافة بضعة أيام" يدوياً هي مستنقع لا ينتهي.
مواجهة واقع المنطقة الزمنية في javascript
المنطقة الزمنية هي المصدر الأول لأخطاء التواريخ. وهذه القواعد التي يجب أن ترسخ في ذهنك:
- كائن
Dateيُخزِّن لحظة زمنية بتوقيت UTC. ولا تُطبَّق المنطقة الزمنية إلّا عند قراءة أجزاء منه أو تنسيقه. - المنطقة الزمنية التي تعتمد عليها دوال مثل
getHours()وgetDate()هي المنطقة الزمنية المحلّية للجهاز الذي يُشغِّل الكود. وكثيراً ما تختلف الخوادم عن المتصفحات في هذا الأمر. new Date("2026-03-14")(بدون وقت) يُحلَّل على أنه UTC. بينماnew Date("2026-03-14T00:00")(بوقت بدون منطقة زمنية) يُحلَّل بالتوقيت المحلّي. وكذلكnew Date(2026, 2, 14)(بأجزاء منفصلة) هو محلّي.
عندما تحتاج إلى عرض التاريخ بمنطقة زمنية محددة، مرّر الخيار timeZone إلى Intl.DateTimeFormat:
نفس اللحظة، لكن بعرضَين مختلفَين. كائن Date نفسه لم يتغيّر.
مثال عملي بسيط
خليني أجمع اللي فات في مثال واحد: دالة تُنسّق الفترة الزمنية منذ وقوع حدث ما (منذ كم من الوقت حصل شيء معيّن):
طوابع زمنية تدخل، ونص مقروء يخرج. هذا هو شكل 90% من كود التواريخ في المشاريع الحقيقية: تطرح لحظتين، تقسم على وحدة زمنية، تقرّب، ثم تنسّق.
أبرز ما تخرج به
- كائن
Dateيمثّل لحظة بتوقيت UTC. المناطق الزمنية تظهر فقط عند القراءة أو التنسيق. - استخدم
Date.now()للطوابع الزمنية، وnew Date()للتعامل مع التقويم. - استخدم
toISOString()للتخزين والسجلات، وIntl.DateTimeFormatلعرض التاريخ للمستخدم. - قارن التواريخ باستخدام
getTime()أو<و>. لا تستخدم===أبدًا. - الأشهر تبدأ من الصفر. وانتبه لفخ تحليل السلاسل التي تحتوي على التاريخ فقط.
- لحسابات التواريخ الجدّية، استعن بمكتبة متخصصة.
التالي: الروابط و سلاسل الاستعلام
كثيرًا ما تظهر التواريخ داخل الروابط — كفلترة حسب نطاق زمني، أو تمرير طابع زمني كمعامل استعلام. وبناء الروابط وتحليلها يدويًا لا يقل خطورة عن تنسيق التواريخ يدويًا، لكن المكتبة القياسية توفّر كائن URL يتولى هذه المهمة بأناقة. هذا ما سنتناوله في الفصل التالي.
الأسئلة الشائعة
كيف أحصل على التاريخ الحالي في JavaScript؟
ببساطة استدعِ new Date() بدون أي وسائط، وسيُرجع لك كائن Date يمثّل اللحظة التي نُفّذ فيها المُنشئ. أما إذا كنت تريد الطابع الزمني بالأرقام فقط (عدد المللي ثانية منذ 1970)، فاستخدم Date.now() — فهو أسرع ولا يُنشئ كائنًا كاملًا في الذاكرة.
كيف أقارن بين تاريخين في JavaScript؟
قارن بين الطوابع الزمنية وليس بين كائنات Date مباشرةً. الطريقة a.getTime() < b.getTime() تعمل، وكذلك a < b لأن عامل المقارنة < يُحوّل التواريخ إلى أرقام تلقائيًا. لكن a === b لن يعمل — لأن === يقارن هوية الكائن، فكائنان يمثّلان اللحظة نفسها لن يتساويا أبدًا بالمقارنة الصارمة.
ما أفضل طريقة لتنسيق التاريخ في JavaScript؟
لأي شيء يظهر للمستخدم، استخدم Intl.DateTimeFormat أو date.toLocaleDateString() — فهما يتعاملان مع اللغات والمناطق الزمنية بشكل صحيح. أما إذا كنت تحتاج صيغة يقرأها الجهاز (للتخزين مثلًا)، فـ date.toISOString() يعطيك نصًا قياسيًا مثل 2026-03-14T09:30:00.000Z. وتجنّب date.toString() للتخزين لأن صيغته تتغيّر حسب اللغة.
لماذا يظهر التاريخ في JavaScript متأخرًا بيوم واحد؟
في الغالب السبب هو المنطقة الزمنية. فعند كتابة new Date('2026-03-14') يُفسَّر التاريخ على أنه منتصف الليل بتوقيت UTC، لكن date.getDate() يُرجع اليوم بالتوقيت المحلي — وقد يكون اليوم السابق. الحل: استخدم getUTCDate() للحصول على اليوم بتوقيت UTC، أو أنشئ التاريخ عبر new Date(year, month, day) الذي يعتمد على التوقيت المحلي منذ البداية.