"التواريخ والأوقات شيء نُعلّمه للأطفال الصغار. فما مدى صعوبته؟"
ارتكب كثير من المبرمجين هذا الخطأ، وعادةً ما يكون للتجربة التي تلي ذلك تأثير سيئ على صحتهم وسعادتهم.
على أي شخص يكتب برامج غير بسيطة تتعامل مع التواريخ والأوقات أن يكون مستعدًا على الأقل لفهم المشكلات المحتملة والتخفيف من آثارها.
تحاول هذه الوثيقة تقديم نظرة عامة على كيفية تعامل Julia مع التواريخ والأوقات، لكن ستحتاج إلى الرجوع إلى الدليل لمزيد من التفاصيل. يوجد أيضًا ويكيبوك قد يجد بعض القراء شروحه أوضح، ويتضمن مخططًا مفيدًا للتسلسل الهرمي للأنواع.
Dates
توجد معظم الوظائف الأساسية في Dates، وهي وحدة مضمنة في التثبيت الافتراضي لكنها ليست جزءًا من المكتبة القياسية.
لاستخدام هذه الوحدة، لديك خياران:
import Dates، ثم اسبق كل استدعاءات الدوال بـ Dates.
using Dates، وهذا يجلب الوحدة بأكملها إلى النطاق الحالي (باستثناء بعض الأسماء الإشكالية).# option 1
julia> import Dates
julia> Dates.now()
2025-06-21T16:02:39.080
julia> now() # wrong: need to include `Dates.` after an import
ERROR: UndefVarError: `now` not defined in `Main`
# option 2
julia> using Dates
julia> now() # functions are now in the namespace
2025-06-21T16:03:04.161
وحدة Dates كبيرة ومعقدة، لكنها توفر أربعة أنواع رئيسية من الوظائف:
Dates.Date، ويشمل اليوم والشهر والسنة لكن ليس وقت اليوم.Dates.Time، ويشمل وقت اليوم لكن ليس التاريخ.Dates.DateTime، ويجمع بين التاريخ والوقت.Dates.Period، ويتعامل مع الفترات الزمنية والتاريخية.لاحظ أن DateTime عمومًا لا يتضمن معلومات المنطقة الزمنية (كانت Python تسمي هذا النوع من قيم datetime بـ "naive").
والاستثناء الوحيد هو now()، الذي يحصل على الوقت المحلي من ساعة نظامك، ويمكنه تحويله إلى UTC عند الحاجة.
ربما تكون الاحتياجات الأكثر شيوعًا هي:
Dates.Dates.Date أو Time أو DateTime.today()) و/أو الوقت الحالي (باستخدام now()).لتقليل التشتيت البصري، تفترض معظم أمثلة الكود في بقية هذه الوثيقة وجود using Dates، وتتجنب كتابة اسم الوحدة Dates. في استدعاءات الدوال.
هناك طرق كثيرة لكتابة التواريخ والأوقات، وهي غالبًا ما تكون مرتبطة بثقافة معينة. التواريخ المكوّنة من أرقام فقط مثل "7/6/23" غامضة ومربكة، وقد أدت إلى أخطاء مكلفة كثيرة في الشركات متعددة الجنسيات (يقول المؤلف هذا من تجربة شخصية مريرة).
المعيار الدولي معرّف في ISO 8601، وله ميزتان رئيسيتان:
رأينا في مثال سابق أن Date.now() أرجع 2025-06-21T16:03:04.161.
ويتكوّن هذا من عدة أجزاء:
YYYY-MM-DD لجزء التاريخ.hh:mm:ss لجزء الوقت.سنناقش صيغًا نصية أخرى في قسم لاحق.
Dates.Date نوع بسيط نسبيًا للتاريخ فقط، ولا يفهم الأوقات أو المناطق الزمنية.
# constructor with year, month, day arguments
julia> date1 = Date(2025, 06, 23)
2025-06-23
# constructor with ISO 8601 string
julia> date2 = Date("2025-06-23")
2025-06-23
julia> date1 == date2
true
julia> typeof(date1)
Date
توجد دوال وصول لأجزاء التاريخ الفردية، وهي تأتي في أزواج يميّز بعضها عن بعض بحالة الأحرف.
# lowercase returns an integer
julia> month(date1)
6
julia> typeof(month(date1))
Int64
# Propercase returns a Period type
julia> Month(date1)
6 months
julia> typeof(Month(date1))
Month
هناك مجموعة متنوعة من الدوال التي لديها بعض الفهم للتقويم. يعرض المثال التالي بعضًا منها فقط.
julia> date1
2025-06-23
julia> dayofweek(date1) # 1 = Monday, 7 = Sunday
1
julia> dayname(date1)
"Monday"
julia> daysinmonth(date1)
30
julia> isleapyear(date1)
false
julia> lastdayofmonth(date1)
2025-06-30
Dates.Time هو النوع الأساسي للوقت فقط.
إنه لا يفهم التواريخ: تنتقل الأوقات تلقائيًا إلى Time(0, 0, 0) عند منتصف الليل.
صيغة البانية الكاملة هي Time(hour, min, sec, millisec, microsec) أو ما يعادلها بصيغة ISO 8601.
جميع المعاملات اختيارية وقيمتها الافتراضية 0.
julia> Time(14, 57, 25, 142)
14:57:25.142
julia> Time("14:57:25.142")
14:57:25.142
julia> Time(14, 57, 25, 142, 32)
14:57:25.142032
كما هو الحال مع Date، تتوفر الأجزاء الفردية عبر دوال الوصول:
julia> minute(t) # => integer
57
julia> Minute(t) # => Period
57 minutes
يجمع Dates.DateTime معظم مزايا النوعين Date و Time.
julia> dt1 = DateTime(2025, 6, 23, 14, 57, 25, 142)
2025-06-23T14:57:25.142
# date1 and t were defined in previous examples
julia> dt2 = DateTime(date1, t)
2025-06-23T14:57:25.142
القيم الافتراضية لمعاملات السنة والشهر واليوم هي 1، والقيم الافتراضية لجميع معاملات الوقت هي 0.
قد يكون تتبّع كل هذه المعاملات بدقة تحديًا، لذا قد تكون صيغة ISO أفضل للإدخال.
أو بدلًا من ذلك، يمكن إنشاء DateTime من Date و Time، كما في المثال أعلاه.
لكل مكوّن من مكوّنات التاريخ/الوقت نوع Period خاص به، من Year وصولًا إلى Nanosecond، ويمثل فترة زمنية.
وهذه مفيدة بشكل خاص في العمليات الحسابية.
julia> dt1
2025-06-23T14:57:25.142
julia> Year(21)
21 years
julia> Second(61)
61 seconds
julia> dt1 + Year(21) - Second(61)
2046-06-23T14:56:24.142
julia> Date(2025, 07, 03) - Date(2025, 04, 25)
69 days
عند العمل مع أنواع Period، تحاول Julia التعامل بذكاء مع التفاوتات في التقويم مثل طول الشهر، وتقرّب القيم عند الحاجة.
# February has fewer days than January
julia> Date(2025, 1, 31) + Month(1)
2025-02-28
ذكرنا سابقًا معيار ISO 8601، وهو التمثيل الافتراضي للتاريخ/الوقت في Julia عند تنسيق السلاسل النصية أو تحليلها.
من الواضح أن هناك صيغًا أخرى كثيرة شائعة الاستخدام، وغالبًا بطرق مرتبطة بثقافة معينة.
للتعامل معها، توفر Julia النوع DateFormat، الذي يمكن إنشاؤه من سلسلة تنسيق بأي درجة من التعقيد.
# dateformat"" string syntax
julia> dtfmt = dateformat"d/m/y"
dateformat"d/m/y"
julia> typeof(dtfmt)
DateFormat{Symbol("d/m/y"), Tuple{Dates.DatePart{'d'}, Dates.Delim{Char, 1}, Dates.DatePart{'m'}, Dates.Delim{Char, 1}, Dates.DatePart{'y'}}}
# DateFormat constructor
julia> DateFormat("d/m/y")
dateformat"d/m/y"
المثال أعلاه بسيط جدًا، وهو مجرد تاريخ بصيغة اليوم/الشهر/السنة الشائعة في أوروبا. تظهر صيغتان مختلفتان من حيث التركيب، لكن النتيجة متطابقة.
يمكن بعد ذلك استخدام DateFormat في تحليل أي سلسلة نصية مناسبة:
julia> DateTime("14/7/2023", dtfmt)
2023-07-14T00:00:00
يتضمن إنشاء DateFormat قدرًا كبيرًا من العمل الإضافي، لذا فإن القيام بهذه الخطوة مرة واحدة فقط (كما هنا مع dtfmt) مفيد للأداء عند تكرار المرور على سلاسل نصية كثيرة للتاريخ/الوقت.
يمكن أن تكون الصيغ أكثر تعقيدًا بكثير من هذا، فراجع الدليل للتفاصيل.
يمكن أيضًا استخدام نفس DateFormat لإخراج تاريخ/وقت بأي صيغة نصية مرغوبة، باستخدام الدالة Dates.format().
للأسف، يبدو أن هناك تعارضًا في النطاق مع format (على الأقل في Julia 1.11.5)، لذا من الأفضل تضمين البادئة Dates. حتى بعد using Dates.
julia> dt1
2025-06-23T14:57:25.142
julia> Dates.format(dt1, dateformat"e, u d")
"Mon, Jun 23"
كما هو موثّق، تعطي "e, u d" التمثيل النصي المختصر لاسم اليوم والشهر.
التمثيل النصي يكون افتراضيًا بالإنجليزية. يمكن إضافة لغات أخرى، لكن الدعم لذلك دون المستوى الأمثل حاليًا.
في هذا التمرين ستعمل على منظّم مواعيد لصالون تجميل في نيويورك افتُتح في 15 سبتمبر 2012.
لديك خمس مهام، وكلها تتعلق بتواريخ المواعيد. الوقت مكتوب بصيغة 24 ساعة (بدون AM/PM).
نفّذ الدالة schedule_appointment() لتحليل تمثيل نصي لتاريخ موعد إلى صيغة DateTime المقابلة.
لاحظ أن المدخل مكتوب بالترتيب الأمريكي: الشهر/اليوم/السنة.
julia> schedule_appointment("07/25/2023 13:45:00")
2023-07-25T13:45:00
نفّذ الدالة has_passed() التي تأخذ تاريخ موعد وتتحقق مما إذا كان الموعد في وقت ما في الماضي:
julia> has_passed(DateTime(1999, 12, 31, 9, 0, 0))
true
نفّذ الدالة is_afternoon_appointment() التي تأخذ تاريخ موعد وتتحقق مما إذا كان الموعد في فترة بعد الظهر (12:00 فأكثر وأقل من 18:00):
julia> is_afternoon_appointment(DateTime(2023, 3, 29, 15, 0, 0))
true
نفّذ الدالة description() التي تأخذ تاريخ موعد وتُرجع وصفًا لذلك التاريخ والوقت:
julia> describe(DateTime(2023, 3, 29, 15, 0, 0))
"You have an appointment on Wednesday, March 29, 2023 at 15:00"
نفّذ الدالة anniversary_date() التي تُرجع تاريخ الذكرى السنوية لهذا العام، وهو 15 سبتمبر:
julia> anniversary_date() # run during 2025
2025-09-15
سجّل في Exercism لتتعلّم وتتقن Julia عبر 35 مفهومًا128 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.