تحليل RFC 3339 / ISO-8601 في Go: لماذا يتعثر حتى المطورون ذوو الخبرة
\n\nهل سبق لك أن كتبت في Go شيئًا مثل time.Parse("2006-01-02T15:04:05Z", input)، وحصلت على خطأ، وشعرت وكأن اللغة تتعمد السخرية منك؟ مألوف. تحليل التواريخ والأوقات هو أحد تلك المواضيع التي تبدو بسيطة ظاهريًا، ولكنها تخفي العديد من الفخاخ في الممارسة العملية.
على Stack Overflow، جمع سؤال "Parsing RFC-3339 / ISO-8601 date-time string in Go" آلاف المشاهدات. يختلط الأمر على الناس في التنسيقات، وينسون النانو ثانية، ويعانون من المناطق الزمنية، ويغضبون من الرقم "السحري" 2006-01-02T15:04:05Z. في هذه المقالة، سنحلل 5 من أكثر الأخطاء شيوعًا ونوضح كيفية تجنبها مرة واحدة وإلى الأبد. هيا بنا!
القليل من النظرية: ما هو RFC 3339 و ISO 8601؟
\n\nقبل الغوص في الكود، دعنا نفهم المصطلحات بسرعة. ISO 8601 هو معيار دولي لتمثيل التاريخ والوقت. RFC 3339 هو نسخة "مبسطة" منه، شائعة في الويب (مثل JSON، API، السجلات).
\n\nتبدو سلاسل RFC 3339 النموذجية هكذا:
\n\n2025-01-15T10:30:00Z\n2025-01-15T10:30:00+03:00\n2025-01-15T10:30:00.123456789Z\n\nفي Go، تُستخدم الدالة time.Parse(layout, value) للتحليل. وهنا يبدأ الجزء المثير للاهتمام.
الخطأ رقم 1: التنسيق (layout) الخاطئ - السبب الرئيسي للصداع
\n\nتستخدم Go نهجًا فريدًا: لا يتم تحديد التنسيق برموز مثل YYYY-MM-DD، بل بتاريخ محدد: Mon Jan 2 15:04:05 MST 2006. من السهل تذكره: 01/02 03:04:05PM '06 -0700 (الشهر، اليوم، الساعة، الدقيقة، الثانية، السنة، المنطقة الزمنية).
غالبًا ما يحاول المبتدئون كتابة شيء مثل:
\n\n// ❌ خطأ!\nt, err := time.Parse("YYYY-MM-DDTHH:MM:SSZ", "2025-01-15T10:30:00Z")\n\nلن تفهم Go هذا التنسيق ببساطة. التنسيق الصحيح لـ RFC 3339 هو time.RFC3339:
// ✅ صحيح\nt, err := time.Parse(time.RFC3339, "2025-01-15T10:30:00Z")\nif err != nil {\n log.Fatal(err)\n}\nfmt.Println(t) // 2025-01-15 10:30:00 +0000 UTC\n\nالثابت time.RFC3339 يساوي السلسلة "2006-01-02T15:04:05Z07:00". لاحظ Z07:00 - هذا هو رمز المنطقة الزمنية.
الخطأ رقم 2: تجاهل النانو ثانية (time.RFC3339 مقابل time.RFC3339Nano)
\n\nالثابت القياسي time.RFC3339 لا يدعم النانو ثانية. إذا كانت السلسلة تحتوي على جزء كسري من الثانية:
2025-01-15T10:30:00.123456789Z\n\n…فإن التحليل باستخدام time.RFC3339 سيعيد خطأ:
// ❌ خطأ: parsing time "2025-01-15T10:30:00.123456789Z" as "2006-01-02T15:04:05Z07:00": cannot parse ".123456789Z" as "Z07:00"\nt, err := time.Parse(time.RFC3339, "2025-01-15T10:30:00.123456789Z")\n\nالحل هو استخدام time.RFC3339Nano:
// ✅ صحيح\nt, err := time.Parse(time.RFC3339Nano, "2025-01-15T10:30:00.123456789Z")\nif err != nil {\n log.Fatal(err)\n}\nfmt.Println(t) // 2025-01-15 10:30:00.123456789 +0000 UTC\n\nنصيحة: إذا لم تكن متأكدًا من وجود نانو ثانية، استخدم time.RFC3339Nano - فهو متوافق مع الإصدارات السابقة وسيعمل في كلتا الحالتين.
الخطأ رقم 3: المنطقة الزمنية - Z أو +00:00 - هل هما نفس الشيء؟
\n\nالسلسلتان 2025-01-15T10:30:00Z و 2025-01-15T10:30:00+00:00 تعنيان نفس الشيء (UTC)، لكن Go قد تعالجهما بشكل مختلف إذا كان التنسيق محددًا بشكل خاطئ.
// ✅ كلا الخيارين يعملان مع time.RFC3339\nt1, _ := time.Parse(time.RFC3339, "2025-01-15T10:30:00Z")\nt2, _ := time.Parse(time.RFC3339, "2025-01-15T10:30:00+00:00")\nfmt.Println(t1.Equal(t2)) // true\n\nولكن إذا كنت تكتب تنسيقك الخاص، كن حذرًا:
\n\n- \