0Pricing
SQL Interview Prep · درس

المناطق الزمنية والطوابع الزمنية

تخزين التوقيت العالمي UTC وتحويل المناطق الزمنية والتنبه إلى المشكلات التي يثيرها المحاورون بشأن الطوابع الزمنية

المناطق الزمنية والطوابع الزمنية درس مجاني في SQL Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في SQL Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة SQL Interview Prep 4 دروس في المجموع.

لماذا تربك المناطق الزمنية المرشحين

تُعد المناطق الزمنية من المواضع التي يتعثر فيها المرشحون الواثقون، ولذلك يختبرهم المحاورون فيها للكشف عن مدى عمق فهمهم. والسؤال الأساسي دائمًا هو: «كيف تخزّن الطوابع الزمنية وتقارنها عبر المناطق المختلفة؟»

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

  • timestamp في مقابل timestamptz
  • التحويل بين المناطق الزمنية
  • UTC بوصفه مصدر الحقيقة

timestamp في مقابل timestamptz

تحتوي PostgreSQL على نوعين من الطوابع الزمنية، والخلط بينهما من أكثر الأخطاء شيوعًا في المقابلات.

  • timestamp (من دون منطقة زمنية): قيمة لساعة الحائط من دون منطقة زمنية مرتبطة بها. وهي تخزّن القيمة التي تعطيها لها كما هي تمامًا.
  • timestamptz (مع منطقة زمنية): تُخزَّن داخليًا بالتوقيت العالمي UTC؛ فعند الإدخال تُحوَّل من منطقة الجلسة، وعند الإخراج تُحوَّل إليها مجددًا.

وعلى الرغم من اسمها، فإن timestamptz لا تخزّن منطقة زمنية، بل تخزّن لحظة زمنية دقيقة بالتوقيت العالمي UTC. وهذه ملاحظة تثير إعجاب المحاورين.

CREATE TABLE events (
  id          bigint,
  occurred_at timestamptz   -- recommended: an absolute instant
);

خزّن بالتوقيت العالمي UTC وحوّل عند الأطراف

هذه هي القاعدة الذهبية. خزّن اللحظات الزمنية بالتوقيت العالمي UTC (باستخدام timestamptz)، وحوّلها إلى منطقة محلية فقط عند عرضها للمستخدم. ويؤدي ذلك إلى تجنب الالتباس المرتبط بالتوقيت الصيفي، ويضمن صحة الترتيب الزمني في كل مكان.

إذا سُئلت «لماذا UTC؟»، فأجب: لا تحدث في UTC تغييرات بسبب التوقيت الصيفي، ولذلك لا تتكرر قيمة ساعة الحائط نفسها ولا تُتخطى، بخلاف التوقيت المحلي.

-- Display a UTC instant in a user's zone (Postgres)
SELECT occurred_at AT TIME ZONE 'America/New_York' AS local_time
FROM events;

المعنى المزدوج لعبارة AT TIME ZONE

تُعدّ AT TIME ZONE ذكيةً ومصدرًا شائعًا للالتباس، لأنها تنفّذ أمرين متعاكسين بحسب نوع الإدخال:

  • عند تطبيقها على timestamptz، تحوّل اللحظة الزمنية المطلقة إلى تلك المنطقة الزمنية، وتُرجع timestamp عاديًا، أي وقت الساعة المحلي هناك.
  • عند تطبيقها على timestamp عادي، فإنها تفسّر وقت الساعة المحلي على أنه يقع في تلك المنطقة الزمنية، وتُرجع timestamptz.

إن معرفة الاتجاه الذي تعمل به هي جوهر المسألة.

-- timestamptz -> local wall clock (returns timestamp)
SELECT TIMESTAMPTZ '2024-03-01 12:00:00+00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 21:00:00

-- plain timestamp interpreted in a zone (returns timestamptz)
SELECT TIMESTAMP '2024-03-01 12:00:00'
         AT TIME ZONE 'Asia/Tokyo';        -- 2024-03-01 03:00:00+00

الحصول على اللحظة الحالية

احرص على معرفة دوال «الآن» لديك. تُرجع NOW() وCURRENT_TIMESTAMP قيمة من النوع timestamptz في Postgres. وهما تُرجعان وقت بدء المعاملة، لا وقت تنفيذ العبارة، وهذا مهم في المعاملات الطويلة.

للحصول على UTC صراحةً، أجرِ التحويل باستخدام: NOW() AT TIME ZONE 'UTC'. أما في MySQL، فتعطيك UTC_TIMESTAMP() وقت UTC مباشرةً.

SELECT
  NOW()                       AS tx_start_tz,
  NOW() AT TIME ZONE 'UTC'    AS utc_walltime;

التوقيت الصيفي هو العدو الحقيقي

يحبّ المحاورون حالات الحواف المتعلقة بالتوقيت الصيفي. فعندما تتقدّم الساعة، لا توجد ساعة محلية معينة على ساعة الحائط؛ وعندما تتراجع، تتكرر ساعة كاملة. ويجعل تخزين الوقت المحلي هذه الأوقات ملتبسة أو غير صالحة.

أما تخزين UTC فيتجاوز هذه المشكلة تمامًا: كل لحظة زمنية فريدة ومتزايدة بترتيب زمني. ويتيح تحديد منطقة باسم إقليمي مثل 'America/New_York'، بدلًا من إزاحة ثابتة مثل -05:00، لقاعدة البيانات تطبيق قواعد التوقيت الصيفي بشكل صحيح لأي تاريخ.

-- Region name applies DST automatically for the given date
SELECT TIMESTAMPTZ '2024-07-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS summer, -- EDT (-04)
       TIMESTAMPTZ '2024-01-01 12:00:00+00'
         AT TIME ZONE 'America/New_York' AS winter; -- EST (-05)

التجميع حسب اليوم المحلي عبر المناطق الزمنية

إليك مشكلة واقعية: «عدد المستخدمين النشطين يوميًا وفق التوقيت المحلي لكل مستخدم». إذا اقتطعت الطابع الزمني UTC مباشرةً، فستكون حدود منتصف الليل خاطئة للمستخدمين الذين لا يستخدمون UTC.

حوّل الوقت إلى المنطقة الزمنية الخاصة بالمستخدم قبل اقتطاعه إلى يوم. إذ يغيّر التحويل وقت الساعة المحلي بحيث تتوافق حدود الأيام مع التوقيت المحلي.

SELECT
  DATE_TRUNC('day', occurred_at AT TIME ZONE u.tz) AS local_day,
  COUNT(DISTINCT e.user_id)                         AS dau
FROM events e
JOIN users u ON u.id = e.user_id
GROUP BY 1
ORDER BY 1;

مقارنة الطوابع الزمنية بأمان

عند التصفية باستخدام عمود من النوع timestamptz، قارنه بلحظة زمنية محددة، ويفضّل أن تكون قيمة UTC حرفية أو قيمة timestamptz تحتوي على إزاحة. أما مقارنة العمود بسلسلة نصية مجردة فقد تؤدي إلى تفسيرها وفق منطقة الجلسة الزمنية، بطريقة يصعب التنبؤ بها.

وهذا يجعل المقارنة غير ملتبسة بغضّ النظر عمّن ينفّذ الاستعلام.

SELECT *
FROM events
WHERE occurred_at >= TIMESTAMPTZ '2024-03-01 00:00:00+00'
  AND occurred_at <  TIMESTAMPTZ '2024-04-01 00:00:00+00';

طوابع Epoch وUnix الزمنية

تخزّن أنظمة كثيرة الوقت على شكل Unix epoch، أي عدد الثواني منذ 1970-01-01 UTC. وقد يعطيك المحاور عمودًا من نوع عدد صحيح ويطلب منك قراءته.

  • Postgres: تُرجع TO_TIMESTAMP(epoch_seconds) قيمة من النوع timestamptz.
  • للتحويل مرة أخرى إلى epoch: EXTRACT(EPOCH FROM occurred_at).
  • MySQL: استخدم FROM_UNIXTIME() وUNIX_TIMESTAMP().

تكون قيم epoch وفق UTC بطبيعتها، وهذا أحد أسباب شيوعها في التخزين.

SELECT
  TO_TIMESTAMP(1709294400)               AS as_ts,   -- from epoch
  EXTRACT(EPOCH FROM NOW())::bigint        AS as_epoch; -- to epoch

ملاحظات حول المناطق الزمنية بين اللهجات

إليك خريطة سريعة تساعدك على التحدث بطلاقة في أي بيئة:

  • Postgres: يوفّر timestamptz وAT TIME ZONE، وهو الأكثر شمولًا في الدعم.
  • MySQL: يحوّل TIMESTAMP تلقائيًا عبر time_zone الخاص بالجلسة؛ أما CONVERT_TZ(t, from, to) فيجري التحويل صراحةً. ولا يراعي DATETIME المناطق الزمنية.
  • SQL Server: يخزّن datetimeoffset إزاحةً زمنية؛ وتحوّل AT TIME ZONE 'name' باستخدام أسماء المناطق الزمنية في Windows.
-- MySQL explicit conversion
SELECT CONVERT_TZ(event_dt, 'UTC', 'Europe/Istanbul') AS local_dt
FROM events;

مثال أعمق: جلسات تمتد عبر منتصف الليل

إليك سؤالًا دقيقًا في التقارير: احسب الجلسات لكل يوم تقويمي محلي عندما يمكن أن تمتد الجلسة عبر منتصف الليل. والحل هو اتباع المنهج نفسه: حوّل إلى الوقت المحلي، ثم صنّف.

خزّن وقتَي البدء والانتهاء كقيم من النوع timestamptz؛ ولأغراض إعداد التقارير، استخرج اليوم المحلي من وقت البدء بعد تحويله. وإذا لزم تقسيم الجلسة بين يومين، فستحتاج إلى الربط بجدول تسلسل الأيام، وهذه نقطة ممتازة تطرحها كسؤال متابعة.

SELECT
  DATE_TRUNC('day', started_at AT TIME ZONE 'Europe/Istanbul') AS local_day,
  COUNT(*) AS sessions
FROM sessions
GROUP BY 1
ORDER BY 1;

تحقق سريع

أكّد استراتيجية التخزين الموصى بها وسببها.

مراجعة: المناطق الزمنية والطوابع الزمنية

المنهج الذي ينبغي أن تخرج به من هذا الدرس:

  • خزّن UTC كقيمة من النوع timestamptz؛ وحوّله إلى منطقة زمنية مسماة للعرض فقط.
  • يخزّن timestamptz لحظة زمنية وفق UTC، وليس منطقة زمنية، رغم اسمه.
  • تعمل AT TIME ZONE في الاتجاهين بحسب نوع الإدخال: فهي تحوّل timestamptz إلى وقت ساعة محلي، أو تفسّر timestamp عاديًا على أنه يقع في منطقة زمنية معينة.
  • استخدم أسماء المناطق الإقليمية مثل 'America/New_York' لكي يُطبّق التوقيت الصيفي تلقائيًا، وتجنّب الإزاحات الثابتة.
  • حوّل الوقت إلى الوقت المحلي قبل اقتطاعه إلى يوم، وقارن الأعمدة بلحظات زمنية صريحة وفق UTC.

الأسئلة الشائعة

هل درس «المناطق الزمنية والطوابع الزمنية» مجاني؟

نعم — نص درس «المناطق الزمنية والطوابع الزمنية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة SQL Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة SQL Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «المناطق الزمنية والطوابع الزمنية»؟

تخزين التوقيت العالمي UTC وتحويل المناطق الزمنية والتنبه إلى المشكلات التي يثيرها المحاورون بشأن الطوابع الزمنية تتمرن على SQL Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ SQL Interview Prep؟

لا تُشترط خبرة سابقة. SQL Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «المناطق الزمنية والطوابع الزمنية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس SQL Interview Prep هذا؟

نعم. كل درس في SQL Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الحساب باستخدام التواريخ والفواصل الزمنية
  2. اقتطاع التواريخ وتجميعها في فئات
  3. تحليل السلاسل النصية وتنسيقها
  4. المناطق الزمنية والطوابع الزمنية
← العودة إلى SQL Interview Prep