برمجياتقراءة دقيقتين

في Postgres، عبارة AT TIME ZONE 'UTC' تُزيل المنطقة الزمنية بدل أن تحوّل القيمة إلى UTC

المغزى

أسوأ الأخطاء هي التي تنجح على جهاز المطور وتفشل على غيره. فالنتيجة هنا تعتمد على توقيت الـ session، فتمر المراجعة والاختبار المحلي ثم تختلف في الإنتاج.

نُشر في 27 سبتمبر 2026 مقال يشرح خطأً شائعًا في Postgres. فحين يكون العمود من نوع timestamptz، أي وقت مرتبط بمنطقة زمنية، ثم يُكتب عليه AT TIME ZONE ‘UTC’، لا تتحول القيمة إلى UTC كما يظن كثيرون. بل تُزال المنطقة الزمنية منها، ويصبح نوعها timestamp بلا منطقة.

ويبدأ المثال من استعلام عادي: رسم بياني يقارن كل شهر بالشهر السابق عبر شرط مثل: ON a.month_start = b.month_start + INTERVAL ‘1 months’

المشكلة الأولى أن إضافة شهر تُحسب وفق توقيت الخادم. فإذا كان الخادم على توقيت PT والمنتج يعمل بتوقيت UTC، فإن 2026-03-01 00:00 بتوقيت UTC هو نفسه 2026-02-28 16:00 بتوقيت PT. فيضيف Postgres الشهر إلى التاريخ الثاني، وتكون النتيجة 28 مارس.

والحل الذي يبدو بديهيًا هو التحويل إلى UTC أولًا باستخدام AT TIME ZONE ‘UTC’. وهنا تظهر المشكلة الثانية: النتيجة أصبحت timestamp بلا منطقة، وتجري مقارنتها بعمود timestamptz.

وكما أوضح نقاش على Hacker News، يحل Postgres هذه المقارنة بتفسير الـ timestamp وفق توقيت الـ session. فالنتيجة ليست خاطئة دائمًا، بل صحيحة حين يكون توقيت الـ session هو UTC، وخاطئة في غير ذلك. ولهذا يمر الخطأ من المراجعة ومن الاختبار على جهاز المطور، ثم يظهر لدى غيره.

والحل الذي يقترحه المقال هو إعادة المنطقة الزمنية بعد الحساب: ((b.month_start AT TIME ZONE ‘UTC’) + INTERVAL ‘1 months’) AT TIME ZONE ‘UTC’

أي أن العبارة تُكتب مرتين: الأولى تُزيل المنطقة الزمنية، والثانية تعيدها.

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

المصادر

  1. المقال الأصلي
  2. مثال قابل للتشغيل في المتصفح
  3. نقاش Hacker News

من عدد الثلاثاء 18 ربيع الآخر - 29 سبتمبر

أخبار ذات صلة

أخبار الثلاثاء 18 ربيع الآخر - 29 سبتمبر