الطابع الزمني Unix UTC موضح: المناطق الزمنية، الإزاحات والتحويلات

خريطة العالم مع خطوط المنطقة الزمنية UTC وطابع زمني Unix يتصل بساعات محلية متعددة

إذا سبق لك تخزين تاريخ في قاعدة بيانات، أو بناء API، أو تتبع خطأ في الجدولة الزمنية يظهر فقط في دول بعينها، فأنت على الأرجح واجهت العلاقة بين Unix timestamp و UTC والتوقيت المحلي. هذا الالتباس حقيقي ويسبب أخطاء فعلية في بيئات الإنتاج. في هذا المقال سنشرح بدقة ما هو UTC، ولماذا تُعرَّف Unix timestamps بتوقيت UTC افتراضياً، وكيف تعمل إزاحات المنطقة الزمنية، وكيف تحوّل الطوابع الزمنية بشكل صحيح في JavaScript و Python و PHP. ستجد أيضاً أكثر الأخطاء شيوعاً التي يقع فيها المطورون وكيفية تجنبها.

أبرز النقاط:

  • Unix timestamp يحسب دائماً الثواني منذ 1 يناير 1970، الساعة 00:00:00 UTC - ولا تحتوي القيمة على أي منطقة زمنية.
  • UTC هو نقطة المرجع العالمية؛ والتوقيت المحلي ليس سوى UTC مضافاً إليه أو مطروحاً منه إزاحة معينة.
  • تحويل طابع زمني إلى التوقيت المحلي بشكل صحيح يتطلب معرفة المنطقة الزمنية المستهدفة، وليس مجرد رقم الإزاحة.
  • التوقيت الصيفي (DST) يغيّر إزاحة المنطقة الزمنية، ولهذا السبب يؤدي تثبيت الإزاحة في الكود إلى أخطاء.

ما هو UTC ولماذا يهمك؟

UTC اختصار لـ Coordinated Universal Time، وهو المعيار الزمني الأساسي الذي يُنظَّم به الوقت على مستوى العالم. على عكس المناطق الزمنية، لا تحمل UTC أي إزاحة - فهي نقطة الصفر. ولا تخضع لتوقيت الصيف (DST)، ولا تتغير لأي دولة أو منطقة.

قبل UTC، كان هناك GMT (توقيت غرينتش)، ولا يزال كثيرون يستخدمون المصطلحين بالتبادل. من الناحية التقنية، يختلف UTC و GMT اختلافاً طفيفاً، لكن في معظم تطبيقات البرمجيات يُعاملان على أنهما متكافئان. تم تعريف UTC رسمياً بموجب توصية ITU-R TF.460 ويُحافَظ عليه باستخدام الساعات الذرية.

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

لماذا Unix timestamps دائماً بتوقيت UTC

Unix timestamp (ويُسمى أيضاً epoch time أو POSIX time) يُعرَّف بأنه عدد الثواني التي مرت منذ 1 يناير 1970، الساعة 00:00:00 UTC. نقطة الارتكاز هذه - منتصف ليل أول يناير 1970 بتوقيت UTC - مدمجة في التعريف ذاته. لا توجد نسخة من Unix time تبدأ بتوقيت نيويورك المحلي أو توقيت طوكيو المحلي.

هذا يعني أن الإجابة على سؤال "هل Unix timestamp دائماً بتوقيت UTC؟" هي نعم، بحكم التعريف. الرقم 1700000000 يمثل نفس اللحظة بالضبط لكل شخص على وجه الأرض. ما يتغير بين المستخدمين هو كيفية عرض تلك اللحظة بحسب منطقتهم الزمنية.

لفهم أساس هذا المفهوم بعمق أكبر، اقرأ مقالنا عن Epoch Time: أساس Unix Timestamps.

هذا التصميم المرتبط بـ UTC هو من أكثر خصائص Unix time فائدة. لأن الرقم في حد ذاته لا يحمل أي معلومات عن المنطقة الزمنية، يستطيع نظامان في دولتين مختلفتين تبادل طابع زمني وكلاهما يعرف بالضبط أي لحظة يشير إليه، دون أي تفاوض إضافي.

UTC مقابل التوقيت المحلي والمناطق الزمنية

UTC هو المرجع. التوقيت المحلي هو ما تحصل عليه حين تُطبّق قاعدة منطقة زمنية فوقه. المنطقة الزمنية ليست مجرد إزاحة ثابتة - بل هي منطقة مسمّاة تحكمها مجموعة من القواعد التي قد تتغير مع الوقت (في الغالب بسبب التوقيت الصيفي DST).

على سبيل المثال، "التوقيت الشرقي" في الولايات المتحدة هو UTC-5 في الشتاء و UTC-4 في الصيف. اسم المنطقة الزمنية هو "America/New_York" في قاعدة بيانات IANA للمناطق الزمنية، وهي المصدر الموثوق لقواعد المناطق الزمنية المستخدمة في معظم لغات البرمجة وأنظمة التشغيل.

الخلاصة العملية: حين تخزّن Unix timestamp في قاعدة بياناتك، فأنت تخزّن قيمة UTC. وحين تعرضه للمستخدم، تحوّله إلى منطقته الزمنية المحلية. لا تخزّن التوقيت المحلي كرقم خام وتفترض أنه UTC - هنا تبدأ الأخطاء.

كيف تعمل إزاحات UTC

إزاحة UTC تصف مدى بُعد منطقة زمنية عن UTC. وتُكتب بالصيغة +HH:MM أو -HH:MM. إليك بعض الأمثلة:

  • UTC+02:00 - توقيت وسط أوروبا الصيفي (CEST)، يُستخدم في ألمانيا وفرنسا خلال الصيف.
  • UTC-05:00 - التوقيت الشرقي الرسمي (EST)، يُستخدم على الساحل الشرقي الأمريكي في الشتاء.
  • UTC+05:30 - التوقيت الرسمي للهند (IST)، وهو إزاحة بنصف ساعة.
  • UTC+00:00 - UTC نفسه، يُستخدم أيضاً في المملكة المتحدة في الشتاء (GMT).

لتحويل epoch time بتوقيت UTC إلى توقيت محلي يدوياً، تجمع أو تطرح الإزاحة بالثواني. بالنسبة لـ UTC+02:00، هذا يعني 2 * 3600 = 7200 ثانية. فإذا كان Unix timestamp لديك هو 1700000000، فإن التوقيت المحلي في UTC+02:00 يقابل 1700000000 + 7200 قبل التنسيق.

غير أن إجراء هذا الحساب يدوياً محفوف بالمخاطر لأن الإزاحات تتغير مع التوقيت الصيفي. استخدم دائماً مكتبة مناطق زمنية مناسبة بدلاً من تثبيت الإزاحة في الكود. راجع دليلنا حول كيفية تحويل Unix Timestamp إلى تاريخ للاطلاع على طرق التحويل بتفصيل أكبر.

تحويل Unix timestamps إلى التوقيت المحلي

إليك مثال عملي باستخدام Unix timestamp 1700000000، الذي يقابل 14 نوفمبر 2023، الساعة 22:13:20 UTC. سنحوّله إلى توقيت "America/New_York" (UTC-5 في نوفمبر) بثلاث لغات برمجة.

JavaScript

const ts = 1700000000;

// JavaScript Date takes milliseconds
const date = new Date(ts * 1000);

// Display in New York local time
const options = {
  timeZone: 'America/New_York',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  timeZoneName: 'short'
};

console.log(date.toLocaleString('en-US', options));
// Output: November 14, 2023 at 05:13:20 PM EST

لاحظ أن كائن Date في JavaScript يخزّن الوقت داخلياً كـ milliseconds بتوقيت UTC. تتولى دالة toLocaleString مع خيار timeZone تطبيق الإزاحة وقواعد التوقيت الصيفي تلقائياً.

Python

from datetime import datetime, timezone
import zoneinfo  # Python 3.9+

ts = 1700000000

# Create a UTC-aware datetime from the timestamp
utc_dt = datetime.fromtimestamp(ts, tz=timezone.utc)

# Convert to New York local time
ny_tz = zoneinfo.ZoneInfo('America/New_York')
local_dt = utc_dt.astimezone(ny_tz)

print(local_dt.strftime('%Y-%m-%d %H:%M:%S %Z'))
# Output: 2023-11-14 17:13:20 EST

النقطة الأساسية هنا هي استخدام datetime.fromtimestamp(ts, tz=timezone.utc). إذا أغفلت معامل tz، سيستخدم Python المنطقة الزمنية المحلية للخادم لتفسير الطابع الزمني - وهذا مصدر شائع للأخطاء.

PHP

$ts = 1700000000;

// Create a DateTime object from the Unix timestamp (always UTC)
$dt = new DateTime('@' . $ts);

// Set the target timezone
$dt->setTimezone(new DateTimeZone('America/New_York'));

echo $dt->format('Y-m-d H:i:s T');
// Output: 2023-11-14 17:13:20 EST

في PHP، يخبر البادئة @ عند إنشاء كائن DateTime أن تتعامل مع القيمة كـ Unix timestamp بتوقيت UTC. بدونها، قد يفسّر PHP النص باستخدام إعداد المنطقة الزمنية الافتراضي للخادم.

أكثر الأخطاء شيوعاً عند المطورين

حتى المطورون ذوو الخبرة يقعون في فخ التعامل مع المناطق الزمنية. إليك أكثر المشكلات تكراراً:

1. افتراض أن التوقيت المحلي للخادم هو UTC

إذا كان خادمك مضبوطاً على "Europe/Berlin" واستدعيت time() في PHP أو datetime.now() في Python دون تحديد منطقة زمنية، ستحصل على التوقيت المحلي وليس UTC. كن صريحاً دائماً في تحديد UTC عند تسجيل الطوابع الزمنية.

2. تثبيت إزاحات UTC بدلاً من استخدام أسماء المناطق الزمنية

استخدام +02:00 كإزاحة ثابتة لألمانيا سيتسبب في خطأ في الشتاء حين تتحول ألمانيا إلى +01:00. استخدم دائماً أسماء المناطق الزمنية مثل Europe/Berlin حتى تتمكن المكتبة من تطبيق قواعد التوقيت الصيفي تلقائياً.

3. تخزين الطوابع الزمنية كنصوص بالتوقيت المحلي دون بيانات المنطقة الزمنية

نص مثل 2023-11-14 17:13:20 في قاعدة بيانات غامض المعنى. هل هو UTC؟ EST؟ IST؟ إذا خزّنت الطوابع الزمنية كأعداد صحيحة بصيغة Unix أو كنصوص ISO 8601 مع إزاحة UTC (مثلاً 2023-11-14T22:13:20Z)، يصبح المعنى لا لبس فيه. راجع مقارنتنا بين صيغة Unix Timestamp مقابل ISO 8601 للاسترشاد بالخيار الأنسب.

4. تجاهل التوقيت الصيفي عند الجدولة

إذا جدولت مهمة لتعمل "الساعة 09:00 كل يوم" بإضافة 86400 ثانية إلى وقت التشغيل الأخير، ستنحرف المهمة بساعة في الأيام التي يتغير فيها التوقيت الصيفي. استخدم مكتبة cron أو جدولاً زمنياً يفهم قواعد المناطق الزمنية.

5. الخلط بين milliseconds و seconds

يستخدم JavaScript الـ milliseconds في كائن Date، لكن معظم Unix timestamps مقاسة بالثواني. تمرير طابع زمني بالثواني إلى new Date() دون ضربه في 1000 سيعطيك تاريخاً في يناير 1970. هذا مشروح بتفصيل في مقالنا عن Seconds مقابل Milliseconds مقابل Microseconds.

خلاصة

Unix timestamps و UTC مترابطان بحكم التصميم. الطابع الزمني هو دائماً قيمة UTC - عدد من الثواني منذ نقطة ارتكاز UTC ثابتة. التوقيت المحلي ليس سوى صيغة عرض، وليس نوعاً مختلفاً من الطوابع الزمنية. النهج الأكثر أماناً هو تخزين كل شيء بتوقيت UTC، والتحويل إلى التوقيت المحلي فقط عند نقطة العرض، واستخدام أسماء المناطق الزمنية دائماً بدلاً من الإزاحات المثبتة في الكود. اتبع هذه القواعد وستتوقف غالبية الأخطاء المرتبطة بالمناطق الزمنية عن الظهور. للاطلاع على أفضل الممارسات في تخزين هذه القيم، راجع دليلنا حول Unix Timestamps في قواعد البيانات.

تحويل Unix timestamp إلى UTC أو التوقيت المحلي مع unixtimestamp.app

حوّل أي Unix Timestamp إلى UTC أو التوقيت المحلي - فوراً

الصق أي Unix timestamp وشاهده يتحول إلى UTC وإلى منطقتك الزمنية المحلية بنقرة واحدة. مجاني، لا يتطلب تسجيلاً، ويعمل مع الثواني والـ milliseconds.

جرّب الأداة المجانية ←

نعم. بحكم التعريف، يحسب Unix epoch timestamp الثواني منذ 1 يناير 1970، الساعة 00:00:00 UTC. الرقم في حد ذاته لا يحمل أي منطقة زمنية - فهو قيمة UTC. تصبح المنطقة الزمنية ذات صلة فقط عند تحويل الطابع الزمني إلى تاريخ ووقت مقروء بصرياً لأغراض العرض.

استخدم مكتبة المناطق الزمنية المدمجة في لغتك. في JavaScript، استخدم toLocaleString مع خيار timeZone. في Python، استخدم datetime.fromtimestamp(ts, tz=timezone.utc).astimezone() مع منطقة زمنية مسمّاة. في PHP، أنشئ كائن DateTime من الطابع الزمني واستدعِ setTimezone(). استخدم دائماً أسماء المناطق الزمنية، لا الإزاحات الخام.

يحدث هذا عادةً لأن دالة ما تستخدم المنطقة الزمنية الافتراضية للخادم بدلاً من UTC عند تفسير الطابع الزمني. في Python، حذف معامل tz في datetime.fromtimestamp() يستخدم التوقيت المحلي للنظام. في PHP، عدم استخدام البادئة @ له نفس الأثر. كن صريحاً دائماً في تحديد UTC عند الإنشاء.

إزاحة UTC هي عدد الساعات (وأحياناً الدقائق) التي تتقدم بها منطقة زمنية على UTC أو تتأخر عنه. على سبيل المثال، UTC-05:00 تعني خمس ساعات خلف UTC. عند تحويل Unix timestamp إلى توقيت محلي، تُطبَّق الإزاحة على قيمة UTC. ولأن الإزاحات تتغير مع التوقيت الصيفي، يجب استخدام منطقة زمنية مسمّاة بدلاً من إزاحة مثبتة في الكود.

نعم، وهذه إحدى المزايا الرئيسية لـ Unix timestamps. لأن كل طابع زمني هو قيمة UTC، يمكنك مقارنة أي طابعين زمنيين مباشرةً كأعداد صحيحة. الرقم الأكبر يعني دائماً لحظة أحدث، بغض النظر عن مكان إنشاء الطابعين الزمنيين. لا يلزم أي تحويل للمنطقة الزمنية عند المقارنة.