لتحويل تاريخ نشر ومنطقة زمنية مسماة إلى لحظة دقيقة عند منتصف الليل، حدّد التوقيت المحلي 00:00:00 وفق قواعد تلك المنطقة، وتحقّق من وجوده، ثم حوّله إلى UTC. يكتشف سير العمل هذا في «طرفية» Mac أيضًا حالات تكرار منتصف الليل.

لنفترض أن سجل نشر اصطناعيًا يتضمن التاريخ 2025-07-15 والمنطقة America/New_York. مهمتك هي حساب الطابع الزمني الذي يمثّل منتصف الليل في بداية ذلك التاريخ. ينتج سير العمل أدناه طابعًا زمنيًا يمكنك نسخه إلى أداة تقبل UTC، مع إبقاء التاريخ والمنطقة الأصليين ظاهرين للتحقق.

ما المعلومات التي تحدد لحظة منتصف الليل؟

أبقِ هذه القيم الأربع منفصلة طوال الحساب:

القيمة المثال الاصطناعي المعنى
التاريخ المدني 2025-07-15 تاريخ تقويمي ميلادي بلا وقت أو منطقة زمنية
التاريخ والوقت المحليان 2025-07-15T00:00:00 قراءة الساعة المطلوبة
المنطقة الزمنية المسماة America/New_York القواعد المستخدمة لتفسير القراءة المحلية
اللحظة المحددة 2025-07-15T04:00:00Z لحظة بعينها معبّر عنها بتوقيت UTC

يمثّل Python التاريخ التقويمي بواسطة date، ويجمع حقول التاريخ والوقت باستخدام datetime، ويستخدم معلومات المنطقة الزمنية لتفسير كائن datetime واعٍ بالمنطقة الزمنية. تمثّل فئته timezone الإزاحات الثابتة؛ بينما توفر zoneinfo قواعد المناطق المسماة. راجع وثائق datetime الرسمية ووثائق zoneinfo.

إزاحة مثل -04:00 هي الفرق بين التوقيت المحلي وUTC لقراءة معينة. وتوفر المنطقة المسماة قواعد قد تنتج إزاحات مختلفة في تواريخ مختلفة. يشرح RFC 9557 لماذا قد يؤدي استبدال منطقة مسماة بإزاحة ثابتة إلى حسابات لاحقة غير صحيحة. احتفظ باسم المنطقة بدلًا من نسخ إزاحة من سجل نشر آخر. RFC 9557: التاريخ والوقت على الإنترنت: طوابع زمنية مع معلومات إضافية

في هذه المهمة، تعني «منتصف الليل» بالضبط 00:00:00 في بداية التاريخ المحدد. اجعل هذا الشرط صريحًا قبل إجراء أي حساب: فالطلب بتحديد أول لحظة صالحة في يوم تقويمي يحتاج إلى سياسة مختلفة عندما يكون منتصف الليل غير موجود.

لماذا يجب التحقق من منتصف الليل قبل التحويل؟

قد تؤدي تغييرات الساعة المحلية إلى فجوة لا تحدث فيها قراءة معينة على الساعة، أو إلى طيّ تتكرر فيه القراءة مرتين. يعرّف PEP 495 في Python هاتين الحالتين ويوفر السمة fold للتمييز بين القراءات المتكررة. كما يوثّق أن المُنشئات وreplace() لا يرفضان الأوقات المحلية غير الموجودة تلقائيًا. لذلك، فإن إسناد منطقة زمنية لا يتحقق وحده من صحة منتصف الليل المطلوب. PEP 495 – إزالة الالتباس في التوقيت المحلي

استخدم قاعدة القرار التالية لمتطلب نشر يشترط منتصف الليل بالضبط:

عدد لحظات منتصف الليل الصالحة التفسير الإجراء
واحدة منتصف الليل فريد أعد تلك اللحظة
اثنتان منتصف الليل متكرر ارفض إلى أن تُختَر سياسة للأسبق أو للأحدث
لا شيء منتصف الليل الدقيق غير موجود أبلغ بتعذر استيفاء المتطلب لهذه المدخلات

ينفّذ البرنامج أدناه هذه القاعدة بتجربة قيمتي fold، وتحويل كل مرشح إلى UTC، ثم تحويله مجددًا إلى المنطقة المسماة. لا يبقى المرشح إلا إذا تطابقت حقوله المحلية بعد الإرجاع تمامًا مع منتصف الليل المطلوب. وتُدمج نتائج UTC المتطابقة في مرشح واحد. تسلسل التحقق هذا مستمد من سلوك التحويل وfold الموثّق؛ وهو منطق إضافي على مستوى التطبيق. datetime.astimezone()، PEP 495

كيف تُجري الحساب في طرفية Mac؟

افتح Spotlight، واكتب Terminal، ثم افتح التطبيق. أو افتح /Applications/Utilities في Finder وانقر نقرًا مزدوجًا على Terminal. هذه هي الطرق التي توثّقها Apple لفتح نافذة أوامر. فتح «طرفية» على Mac أو إنهاؤها

شغّل الأمر التالي للتحقق من إصدار Python لديك:

python3 --version

يتطلب هذا المثال Python 3.9 أو إصدارًا أحدث، إذ أصبح zoneinfo متاحًا بدءًا منه. إذا لم يكن Python مثبتًا أو كان إصدارك أقدم، فاتبع دليل التثبيت الرسمي لنظام macOS للحصول على مُثبّت Python مدعوم حاليًا من Python.org، ثم أعد التحقق من الإصدار. يستخدم سير العمل وحدات المكتبة القياسية. zoneinfo — دعم المناطق الزمنية لـ IANA، استخدام Python على macOS

انسخ الكتلة كاملة أدناه إلى «طرفية» واضغط Return. إنها أمر كامل يتضمن برنامج Python. لحساب طابع زمني مختلف للنشر، عدّل DATE_TEXT وZONE_NAME قبل اللصق. اترك REPEATED على القيمة reject ما لم تكن قد اخترت عمدًا سياسة لمنتصف الليل المتكرر.

python3 - <<'PY'
from datetime import date, datetime, time, timezone
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

DATE_TEXT = '2025-07-15'
ZONE_NAME = 'America/New_York'
REPEATED = 'reject'  # 'reject', 'earlier', or 'later'


def resolve_midnight(date_text, zone_name, repeated='reject'):
    if repeated not in {'reject', 'earlier', 'later'}:
        raise ValueError('Repeated-time policy must be reject, earlier, or later.')

    day = date.fromisoformat(date_text)
    if day.isoformat() != date_text:
        raise ValueError('Use a date in YYYY-MM-DD format.')

    zone = ZoneInfo(zone_name)
    wall = datetime.combine(day, time.min)
    candidates = set()

    for fold in (0, 1):
        guess = wall.replace(tzinfo=zone, fold=fold)
        instant = guess.astimezone(timezone.utc)
        back = instant.astimezone(zone)
        if back.replace(tzinfo=None) == wall:
            candidates.add(instant)

    ordered = sorted(candidates)
    if not ordered:
        raise ValueError('Exact midnight does not exist on this date in this zone.')

    if len(ordered) == 2 and repeated == 'reject':
        choices = ', '.join(x.isoformat() for x in ordered)
        raise ValueError(
            'Midnight repeats. Choose earlier or later. UTC choices: ' + choices
        )

    chosen = ordered[-1] if repeated == 'later' else ordered[0]
    return chosen.astimezone(zone), chosen


try:
    local, utc = resolve_midnight(DATE_TEXT, ZONE_NAME, REPEATED)
except ZoneInfoNotFoundError:
    raise SystemExit(
        'Zone not found. Check the IANA name and time zone data installation.'
    )
except (ValueError, OverflowError) as exc:
    raise SystemExit(str(exc))

print('Civil date:', DATE_TEXT)
print('Named zone:', ZONE_NAME)
print('Local midnight:', local.isoformat())
print('UTC instant:', utc.isoformat().replace('+00:00', 'Z'))
PY

بالنسبة إلى العينة المقدمة، يكون الناتج:

Civil date: 2025-07-15
Named zone: America/New_York
Local midnight: 2025-07-15T00:00:00-04:00
UTC instant: 2025-07-15T04:00:00Z

اقرأ سطري الطابع الزمني معًا. يؤكد السطر المحلي أن وقت الساعة المطلوب هو منتصف الليل. ويحدد سطر UTC اللحظة نفسها: بطرح الإزاحة المحلية البالغة أربع ساعات سالبة، نحصل على 04:00:00 بتوقيت UTC. يعرّف RFC 3339 الطابع الزمني باستخدام تاريخ ووقت وإما إزاحة رقمية أو Z. RFC 3339: التاريخ والوقت على الإنترنت: الطوابع الزمنية

كيف تتحقق من النتائج العادية والاستثنائية؟

المدخلات التالية أمثلة نشر اصطناعية اختيرت لاختبار البرنامج. حُسبت نتائجها باستخدام أداة الحل أعلاه وقاعدة بيانات المناطق الزمنية المتاحة؛ وهي ليست سجلات لمنشورات فعلية. غيّر قيمتي الإدخال وأعد تشغيل الأمر كاملًا لكل حالة.

التاريخ المنطقة الزمنية المسماة نتيجة أداة الحل
2025-01-15 America/New_York لحظة واحدة: 2025-01-15T05:00:00Z
2025-07-15 America/New_York لحظة واحدة: 2025-07-15T04:00:00Z
2018-11-04 America/Sao_Paulo منتصف الليل الدقيق غير موجود؛ يُرفض
2020-11-01 America/Havana منتصف الليل متكرر؛ يُرفض افتراضيًا
2011-12-30 Pacific/Apia منتصف الليل الدقيق غير موجود؛ يُرفض

يوضح الصفان الأولان سبب إدراج التاريخ في الحساب: تحوّل المنطقة المسماة نفسها منتصف الليل إلى ساعات UTC مختلفة. في مثال هافانا ذي منتصف الليل المتكرر، يؤدي تعيين REPEATED = 'earlier' إلى اختيار 2020-11-01T04:00:00Z؛ بينما يؤدي تعيينه إلى later إلى اختيار 2020-11-01T05:00:00Z. وتشير الاسمان إلى الترتيب الزمني على خط UTC.

تتطلب نتيجة منتصف الليل غير الموجود قرارًا بشأن متطلب النشر. إذا كان منتصف الليل الدقيق أساسيًا، فاختر تاريخًا أو منطقة أخرى صراحةً. أما إذا كان المقصود هو أول وقت صالح في ذلك التاريخ، فعرّف هذه العملية ونفّذها على نحو منفصل، مع تحديد ما يجب فعله إذا كان اليوم بأكمله غير موجود. تُبلغ أداة الحل هذه عمدًا عن الإخفاق بدلًا من استبدال وقت محلي آخر بصمت.

ماذا تفعل عندما يفشل أحد المدخلات؟

إذا كان الخطأ متعلقًا بالتاريخ، فأدخل تاريخًا ميلاديًا صحيحًا بصيغة YYYY-MM-DD، مثل 2025-07-15. يتحقق البرنامج من صحة التاريخ التقويمية ومن مطابقته لهذه الصيغة تحديدًا. يفشل إدخال مثل 2025-02-30 بدلًا من ترحيله إلى شهر آخر.

عند ظهور Zone not found، تحقّق من معرّف IANA كاملًا، بما في ذلك التهجئة وحالة الأحرف. توثّق Python أن هذا الاستثناء قد يعني مفتاحًا غير صالح أو عدم توفر بيانات المنطقة الزمنية. يبحث Python القياسي أولًا في بيانات النظام، ثم يستخدم حزمة الطرف الأول tzdata كخيار احتياطي. إذا كانت البيانات غير موجودة، فثبّت هذه الحزمة في بيئة Python التي تستخدمها، ثم أعد تشغيل أداة الحل. PEP 615 – دعم قاعدة بيانات المناطق الزمنية لـ IANA في المكتبة القياسية

احتفظ بالتاريخ الأصلي واسم المنطقة وخيار الوقت المتكرر وطابع UTC الزمني المحدد معًا في ملاحظات عملك. إذا اختلفت الحسابات بين أجهزة الكمبيوتر، فقارن مصادر بيانات المناطق الزمنية لديها. تثبيت حزمة احتياطية لا يستبدل تلقائيًا بيانات النظام التي يعثر عليها Python بالفعل. مصادر بيانات zoneinfo

أي قيمة ينبغي نسخها إلى أداة أخرى؟

عندما تقبل الوجهة طابعًا زمنيًا بتوقيت UTC، انسخ قيمة UTC instant مع Z الخاص بها. تحقّق من صيغة الإدخال الموثّقة للوجهة قبل إدخالها. لا يستطيع حقل تاريخ مجرد الاحتفاظ بكل المعلومات التي يمثلها ذلك الطابع الزمني المحدد؛ لذا احتفظ بالتاريخ الأصلي والمنطقة المسماة إلى جانبه عندما تحتاج إلى حفظ قصد الجدولة.

يعرّف RFC 9557 صيغة طابع زمني موسعة يمكن أن تتضمن لاحقة منطقة مسماة، لكن هذه الصيغة تظل تمثل لحظة محددة. كما يقر بأن تحديثات بيانات المناطق قد تؤدي إلى تناقضات بين الإزاحات المخزنة ومعلومات المنطقة. استخدم تمثيلًا موسعًا فقط عندما توثّق الأداة المستقبلة دعمها له. RFC 9557، النطاق والإزاحات غير المتسقة

أكمل بالتحقق من أن الناتج المحلي يعرض التاريخ المقصود عند 00:00:00، وأن المنطقة المسماة تطابق إدخالك، وأن أي اختيار لمنتصف الليل المتكرر كان مقصودًا. بعد اجتياز هذه الفحوص، يكون ناتج UTC هو لحظة منتصف الليل المحددة وفق القواعد المستخدمة في ذلك الحساب.

المصادر