شرح datetime في Python يساعدك على اختيار نوع البيانات المناسب بدل جمع تواريخ كنصوص. date يمثل يومًا تقويميًا، وdatetime يجمع اليوم مع الوقت، وtimedelta يمثل فرقًا زمنيًا. حساب العمر بالسنوات المكتملة يحتاج مقارنة يوم الميلاد في السنة الحالية، بينما الفرق بعدد الأيام يخدم سؤالًا آخر. سنقرأ تاريخ ميلاد ونحسب العمر ونطبع وقتًا بتوقيت UTC، دون الاعتماد على قسمة الأيام على 365.
التجهيز والمتطلبات
# datetime is part of the Python standard library.تحويل النص باستخدام strptime
يقرأ strptime النص وفق النمط الذي تحدده. في المثال 2000-10-29 يطابق %Y-%m-%d ثم نحول النتيجة إلى date. يجب أن يوافق النمط ترتيب الأجزاء والفواصل؛ وإلا يظهر ValueError. التاريخ غير الصحيح مثل 31 فبراير يرفض كذلك. عند استقبال إدخال من واجهة، اعرض مثالًا واضحًا للتنسيق بدل مطالبة المستخدم بتخمينه. لا تستخدم مقارنة النصوص المختلفة التنسيق لمعرفة التاريخ الأقدم.
صيغة العرض والفرق بين الشهر والدقيقة
تستخدم strftime لتحويل الكائن إلى نص. %Y للسنة الكاملة و%m للشهر و%d لليوم، بينما %H للساعة بنظام 24 و%M للدقيقة و%S للثانية. اختلاف حالة الحرف هنا يغير المعنى؛ استخدام %m مكان %M يطبع الشهر في موضع الدقائق. يعرض %z الإزاحة الزمنية، ويمكن isoformat إنتاج صيغة مناسبة للتبادل. لا تستخدم عرضًا محليًا غامضًا بدل صيغة متفق عليها بين الخدمات.
المثال البرمجي الكامل
from datetime import date, datetime, timezone
def age_on(birth, today):
if birth > today:
raise ValueError("Birth date cannot be in the future")
# For Feb 29, this convention increments the age on March 1 in a non-leap year.
return today.year - birth.year - ((today.month, today.day) < (birth.month, birth.day))
if __name__ == "__main__":
birthday = datetime.strptime("2000-10-29", "%Y-%m-%d").date()
current = date.today()
print("Age:", age_on(birthday, current))
now = datetime.now(timezone.utc)
print("UTC:", now.strftime("%Y-%m-%d %H:%M:%S %z"))
print("ISO:", now.isoformat())
حساب العمر عند الحدود
نطرح سنة الميلاد ثم ننقص سنة إذا لم يأت شهر ويوم الميلاد بعد. في اليوم السابق للميلاد يبقى العمر السابق، وفي يومه يزيد. نرفض تاريخ ميلاد في المستقبل بدل إنتاج عمر سالب. بالنسبة إلى 29 فبراير، يعتمد المثال سياسة زيادة العمر في 1 مارس في السنة غير الكبيسة؛ قد تتطلب قواعد عمل معينة 28 فبراير. اكتب السياسة بوضوح واختبرها بدل افتراض أن كل التطبيقات تتبع القاعدة نفسها.
UTC والمنطقة الزمنية والتجربة
datetime.now(timezone.utc) ينتج وقتًا يحمل معلومات منطقة زمنية. أما date.today فيعتمد تاريخ الجهاز المحلي، وهو مناسب لهذا العرض البسيط للعمر إذا كان هو التقويم المطلوب. عند تسجيل أحداث عبر بلدان، حدد المنطقة المرجعية واستخدم zoneinfo عند الحاجة، وتجنب خلط كائن واع بالمنطقة مع كائن لا يحملها. احفظ البرنامج باسم dates_demo.py وجرّب يوم الميلاد واليوم السابق وتاريخًا مستقبليًا، ثم قارن الناتج يدويًا.
لماذا لا نقسم فرق الأيام على 365؟
السنة ليست دائمًا 365 يومًا، كما أن العمر المكتمل يتغير في ذكرى الميلاد لا بعد مضاعف ثابت من الثواني. قسمة الأيام تعطي تقريبًا قد يخطئ قرب يوم الميلاد أو في السنوات الكبيسة. إذا كان المطلوب مدة زمنية بالأيام فاستخدم طرح date مباشرة، وإذا كان المطلوب عمرًا بالسنوات فاستخدم قاعدة تقويمية معلنة. لا تستبدل سؤال العمل بعملية حسابية أسهل دون مراجعة معنى النتيجة التي ستعرض للمستخدم.
ماذا يحدث مع المناطق الزمنية؟
التاريخ المحلي قد يكون يومًا مختلفًا عن UTC قرب منتصف الليل. لذلك حدد أي منطقة يعتمد عليها حدث مثل تجديد اشتراك أو ذكرى ميلاد مستخدم، ثم استخرج اليوم فيها. تخزين وقت واع بالمنطقة يسهل تحويله، أما النص الذي لا يحتوي إزاحة فيحتاج اتفاقًا خارجيًا. لا تضف ساعات ثابتة يدويًا لتقليد منطقة قد تتغير قواعدها؛ استخدم zoneinfo عند الحاجة إلى اسم منطقة مدعوم ببيانات النظام.
ما حالات الاختبار المهمة؟
جرّب تاريخ ميلاد اليوم، واليوم السابق واللاحق، وبداية السنة ونهايتها، ويوم 29 فبراير في سنة عادية وكبيسة. اختبر نصًا بتنسيق مختلف وتاريخًا غير صالح، وتأكد أن رسالة الخطأ تميز الإدخال غير الصحيح عن تاريخ مستقبل. يمكنك تمرير today ثابت إلى age_on كما في الدالة بدل الاعتماد على ساعة الجهاز داخل الاختبار. هكذا تظل النتيجة قابلة لإعادة التحقق في أي يوم تنفذ فيه الاختبارات.
المراجع
الصور والفيديو من الشرح السابق
الكود النصي أعلاه هو النسخة المصححة؛ قد تختلف المواد المرئية السابقة في أسماء المتغيرات أو طريقة التشغيل.







