أوقات المناطق هي كذب (وكيفية التعامل معها في الكود)

تحديث في

تبدو مناطق الوقت مثل تغييرات UTC البسيطة. لكنها ليست كذلك. يوضح هذا الدليل السبب في أن مناطق الوقت تؤثر على الكود — فجوات التغييرات الموسمية، التغييرات النصفية، الأوقات غير المُعرفة — وكيفية التعامل معها بشكل صحيح في JavaScript وPython وPHP وSQL.

أوقات المناطق الزمنية هي كذب (وكيفية التعامل معها في الكود) 1
إعلان · حذف؟

سألتُ خادمك ما هو الوقت. أجاب 14:00. قمت بحفظه. قمت بطلبه مرة أخرى. الآن يُظهر 16:00. لم تُغيّر أي شيء. مرحباً بكم في مناطق الأوقات.

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

لماذا "استخدم فقط UTC" هو مجرد جزء من الحل؟

النصيحة الأكثر شيوعاً التي سمعتها هي: احفظ كل شيء في UTC. هذه النصيحة صحيحة — لكنها غير مكتملة. الحفظ في UTC يحلّ مشكلة التخزين (الثابت) بينما يُبقي مشكلة أكبر غير مُحلّلة: عرض البيانات وتسجيلها. مفتاح واحد. مشكلة (الحفظ الثابت) بينما يُبقي مشكلة أكبر غير مُحلّلة: عرض البيانات وتسجيلها.

يُجدّد مستخدم في طوكيو اجتماعاً في الساعة 9 صباحاً بتوقيت مُناسبه. تُحفظ هذه المعلومة كزمن UTC. لاحقاً، يفتح مستخدم في نيويورك نفس الحدث. يُعرض في وقت مُناسبه. لكن أي وقت يُستخدم عندما يسافر المستخدم؟ عند تحديث جهازه؟ عند تفعيل التوقيت المُختلف؟ "احفظ UTC، عرض في الوقت المحلي" هو سياسة صحيحة — لكنها التنفيذ التي تُسبب أغلب الفرق في الفرق.

المشكلات الحقيقية مع مناطق الأوقات

قبل أن نصل إلى الحلول، يُفيد أن نُسمّي ما نُحارب عليه.

1. مُعَدّل UTC ليس مناطق الأوقات

UTC+5:30 ليس "زمن الهند". إنه مُعَدّل ثابت. زمن الهند هو Asia/Kolkata — مناطق زمنية مُسمّاة تستخدم +5:30 وليست مُتّسعة لفترة التوقيت المُختلف. هذه أمور مختلفة. إذا قمت بوضع مُعَدّل ثابت، فأنت تُخزن رقمًا. إذا قمت بحفظ مناطق زمنية مُسمّاة، فأنت تُخزن نية.

المناطق الزمنية توجد في قاعدة بيانات مناطق الأوقات من IANA (أو تسمى tzdata أو قاعدة بيانات Olson). تأتي نسخة من كل لغة ونظام تشغيل مُعتمد. استخدمها. افضل الطرق هي America/New_York على UTC-5.

2. التوقيت المُختلف يُغيّر الساعات (بشكل غير متوقع)

تُنتج تحولات التوقيت المُختلف حالات خطيرة من نوعين:

  • النقص في "الانطلاق إلى الصيف": تُنتقل الساعات من 2:00 إلى 3:00. لا يوجد وقت 2:30. إذا حاولت تخطيط شيء في 2:30 في ذلك اليوم، ستحصل على خطأ أو سلوك خاطئ حسب مكتبة استخدامك.
  • الساعة المُختلفة في "الانسحاب": تُنتقل الساعات من 2:00 إلى 1:00. الآن، يقع الوقت 1:30 مرتين. بدون سياق إضافي (العلامة المُحددة)، لا يمكنك تحديد أي من الظواهر المقصودة.

وتعود قواعد التوقيت المُختلف. تغيّر الدول والولايات الأمريكية، أو ألغتها، أو تغيّرت في السنوات الأخيرة. يعتمد سلوك كودك على وجود نسخة محدثة من package tzdata — وهي مسؤولية للتشغيل، وليس فقط للتطوير.

3. ليس كل المُعَدّلات تُبنى على الساعات الكاملة

هندوستان هي UTC+5:30. نيبال هي UTC+5:45. أجزاء من أستراليا هي UTC+9:30. إيران هي UTC+3:30 في الشتاء وUTC+4:30 في الصيف. إذا افترضت أن مناطق الأوقات دائمًا على الساعات الكاملة، فسيُفسد التوقيت لعشرات الملايين من المستخدمين.

4. "الوقت المحلي" على الخادم ليس له معنى

الخوادم لها أوقات نظامها. هذه الأوقات لها مناطق زمنية مُحددة — غالبًا ما تكون UTC، أحيانًا ما تُحدّده مزود الخدمة، أو ما تُحدّده مُدير النظام منذ سنوات. الكود الذي يُستخدم new Date() أو datetime.now() بدون تحديد مناطق زمنية يعتمد بشكل ضمني على هذه الإعدادات. ستُعطي نتائج مختلفة في بيئات مختلفة. هذه خطأ ينتظر أن يظهر في كل تطبيق.

النهج الصحيح، حسب اللغة

JavaScript / TypeScript

الناتج المُدمج في JavaScript هو عبارة عن علامة خفيفة حول توقيت مللي ثانية UTC. يبدو مريحًا؛ لكنه ليس كذلك. تجنب تنسيقها يدويًا — استخدم Date واجهة للعرض، أو ابحث عن مكتبة. Intl.DateTimeFormat النهج الحديث هو Temporal

— اقتراح من TC39 الذي تم تطبيقه في Chrome 121 وستُضاف إلى جميع الأنظمة الرئيسية. يدعم مناطق الأوقات بشكل مُميز ويُعدّ الحل الصحيح على المدى الطويل: إذا لم تتمكن من استخدام Temporal بعد،

// Store an instant (UTC-equivalent)
const meeting = Temporal.Instant.from("2025-06-15T14:00:00Z");

// Display in a specific zone
const nyTime = meeting.toZonedDateTimeISO("America/New_York");
console.log(nyTime.toString()); // 2025-06-15T10:00:00-04:00[America/New_York]

// Convert to Tokyo time
const tokyoTime = meeting.toZonedDateTimeISO("Asia/Tokyo");
console.log(tokyoTime.toString()); // 2025-06-16T23:00:00+09:00[Asia/Tokyo]

date-fns-tz مُزود مع date-fns هو خيار موثوق. Luxon هي خيار ممتاز آخر. Moment.js هو مكتمل الوظائف لكنه لم يُطور منذ زمن بعيد — انتقل بعيدًا عنه. مُودول يُميز بين "الوقت البدائي" (لا يحتوي على معلومات مناطق الأوقات) و"الوقت المُدرك" (يحتوي على tzinfo). القيم البدائية هي فخ — تبدو صالحة لكنها لا تحمل معنى عبر الأنظمة.

بايثون

في بيئة بيئة بيثون datetime استخدم دائمًا القيم المُدركة. استخدم مُودول

(Python 3.9+) لدعم مناطق الأوقات من IANA: zoneinfo لـ Python 3.8 وقبلها، استخدم مكتبة — لكن احذر من

from datetime import datetime
from zoneinfo import ZoneInfo

# Aware datetime — always do this
utc_time = datetime(2025, 6, 15, 14, 0, 0, tzinfo=ZoneInfo("UTC"))

# Convert to New York time
ny_time = utc_time.astimezone(ZoneInfo("America/New_York"))
print(ny_time)  # 2025-06-15 10:00:00-04:00

# Never do this — naive datetime, meaningless
bad = datetime(2025, 6, 15, 14, 0, 0)  # what zone is this?

الوظائف المُحددة لـ pytz التي تحتوي على مخاطر. pytzالأنواع تدعم مناطق الأوقات من IANA بشكل مُدمج من خلال zoneinfo . افضل الطرق هي

PHP

مع DateTime و DateTimeImmutable — لأنها أمانة لأنها تُعيد إنشاء الكائنات بدلًا من تعديلها في المكان. DateTimeZoneالقواعد في قواعد قواعد البيانات DateTimeImmutable معالجة الأوقات في قواعد البيانات هي مسار مُحتمل.

$utc = new DateTimeImmutable('2025-06-15T14:00:00', new DateTimeZone('UTC'));

// Convert to Sydney time
$sydney = $utc->setTimezone(new DateTimeZone('Australia/Sydney'));
echo $sydney->format('Y-m-d H:i:s T'); // 2025-06-16 00:00:00 AEST

// Store timestamps as ISO 8601 strings or Unix timestamps
echo $utc->getTimestamp(); // 1749996000

PostgreSQL:

(timestamp with time zone) — يُخزن كل شيء كUTC ويُحوّل عند الإخراج. لا تستخدم

  • (بدون مناطق زمنية) للبيانات المُعرضة للمستخدمين؛ لأنه يُخزن ما تُعطيه دون تحويل. استخدم TIMESTAMPTZ MySQL: TIMESTAMP نوع هو بسيط (لا يحتوي على مناطق زمنية). استخدم
  • إذا كنت ترغب في تخزين UTC، لكن لاحظ أن مساحة التخزين محدودة إلى 2038. في المخططات الجديدة، التخزين كـ ال DATETIME بصيغة ISO 8601 أو كزمن Unix في TIMESTAMP أفضل طريقة. VARCHAR SQLite: BIGINT لا يحتوي على نوع زمني مُدمج. احفظ كنص ISO 8601 (
  • ) أو كعدد Unix. اجعل التحويل في كود التطبيق. بمجرد أن تختار قاعدة بيانات، اضبط مناطق الأوقات على جهاز الخادم بشكل صريح بدل الاعتماد على القيم الافتراضية.2025-06-15T14:00:00Zقواعد عملية تُعمل فعليًا

بعد تحليل النظرية، إليك القواعد العملية التي تمنع معظم أخطاء مناطق الأوقات:

احفظ UTC في كل مكان.

يجب أن يكون كل توقيت في قاعدة البيانات في UTC. لا استثناءات لـ "يُستخدم فقط داخليًا".

  1. استخدم أسماء مناطق الأوقات، لا المُعَدّلات. احتفظ بـ
  2. بجانب توقيت UTC عند الحاجة لإعادة بناء الوقت المحلي الأصلي. المُعَدّل وحده لا يمكن استرجاعه بعد تحول التوقيت المُختلف. لا تُدعِي "الآن" بدون مناطق زمنية. America/Chicago — اجعل تعيين مناطق زمنية صريحًا أو اربطها فورًا.
  3. أعد التحويل إلى الوقت المحلي في اللحظة الأخيرة. datetime.now(), new Date(), time() أعد جميع حسابات الأوقات في UTC. فقط أعد التحويل إلى وقت مناطق الأوقات المحلية قبل العرض.
  4. احتفظ بـ tzdata محدثًا. تتغير قواعد الأوقات. جعل تحديثات package tzdata جزءًا من إدارة الاعتمادات المنتظمة.
  5. أعد اختبارات حول تحولات التوقيت المُختلف. الساعتين الخطرتين كل عام (الانطلاق إلى الصيف، الانسحاب) يجب أن تكون في مجموعات الاختبارات إذا كنت تتعامل مع تخطيطات أو بيانات متعلقة بالوقت.
  6. اطلب من المستخدمين مناطق الأوقات بشكل صريح. لا تعتمد على التوقيت المُحدد من خلال الـ IP أو التقدير من المتصفح لأي شيء مهم. أظهر مُحدد مناطق الأوقات؛ احفظ النتيجة.
  7. ملاحظة حول توقيتات Unix توقيتات Unix — ثوانٍ منذ 1970-01-01T00:00:00Z — لا تُحمل أي تأثير زمني بذاته. فهي صيغة مقبولة للتخزين وذات فائدة خاصة في السجلات، وواجهات الخدمة، والكشوفات حيث ترغب في رقم واحد واضح.

النقطة: توقيتات Unix لا تحمل نية المستخدم الأصلية.

إذا حجز شخص رحلة في "الصباح من يوم الجمعة في لندن"، واحتفظت بتوقيت Unix فقط، فقد فقدت الحقيقة أنهم كانوا يقصدون لندن. عندما يُعاد الحجز بعد 3 أشهر وتم تغيير لندن في التوقيت المُختلف، قد تُعرض الوقت الخاطئ. احتفظ بالمنطقة مع التوقيت عند احتفاظ بنية المستخدم.

الحالة الصعبة: الأحداث المتكررة الأحداث المتكررة — مثل اجتماعات أسبوعية، دوائر سداد شهرية، أو تذكيرات يومية — تُظهر كل حالة من أشكال مناطق الأوقات.أعدّ قاعدة "كل يوم الاثنين في الساعة 9 صباحًا" لمستخدم في لوس أنجلوس. هل تُحفظ الساعة 9 صباحًا في منطقة باريس؟ ماذا يحدث عندما يسافر إلى طوكيو؟ ماذا يحدث عندما تنتقل الساعات إلى الصيف وينتقل يوم الاثنين إلى تاريخ التحول؟

النموذج الصحيح هو:

احتفظ بالقاعدة المتكررة كوقت مُحدد على الساعات + اسم مناطق الأوقات: "الإثنين 09:00 America/Los_Angeles"

احسب الواقعة التالية في وقت التخطيط، وليس عند إنشاء القاعدة

أعد الحساب بعد أي تحول في التوقيت المُختلف الذي يقع داخل فترة التكرار

  • مكتبات مثل
  • (JS) و
  • (Python) تتعامل مع هذا بشكل صحيح عند وجود سياق مناطق الأوقات. تطوير هذا التحديد يُعدّ طريقًا موثوقًا إلى أخطاء خفية تظهر بعد شهور.

مناطق الأوقات هي كذبة (وكيفية التعامل معها في الكود) 2 rrule.js مناطق الأوقات هي كذبة (وكيفية التعامل معها في الكود) 1 dateutil.rrule مناطق الأوقات تبدو كمُعَدّل بسيط للزمن المُستَنَد إلى UTC. لكنها ليست كذلك. هذا الدليل يحلّ سبب كيف تُفسد الأوقات في الكود — فجوات التوقيت المُختلف، مُعَدّلات نصف ساعة، توقيتات بسيطة — وكيفية التعامل معها بشكل صحيح في JavaScript، Python، PHP، وقواعد البيانات.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟