متوفر أيضًا بلغات أخرى: English · Español · Português · Français
محلّل التاريخ في HTTP
الصيغ الثلاث التي يسمح بها HTTP، ومعنى كل واحدة، والوجوه الستة التي يخطئ فيها Date.parse.
ما تاريخ HTTP؟
كل طابع زمني في ترويسة HTTP — سواء Date أو Expires أو Last-Modified أو If-Modified-Since أو صورة Retry-After ذات التاريخ — يُكتب بصيغة يسميها معيار RFC 9110 تاريخَ HTTP. وهي ثلاث صيغ، لأن الخوادم قبل 1995 كانت تستعمل ثلاثًا، فعرّفها المعيار جميعًا حفاظًا على التوافق.
والقاعدة الحاكمة لها غير متناظرة، وهذا التفاوت هو ما يستحق أن يُحفظ: على المتلقّي الذي يحلّل طابعًا زمنيًا أن «يقبل الصيغ الثلاث»، وعلى المرسِل أن «يولّد تلك الطوابع بصيغة IMF-fixdate». فليس لك إلا كتابة واحدة يجوز أن تنتجها — Sun, 06 Nov 1994 08:49:37 GMT — وثلاث عليك أن تُحسن قراءتها.
والثلاث جميعًا تدل على لحظة بالتوقيت العالمي المنسّق. تقول الأوليان ذلك بالأحرف GMT، أما الثالثة الموروثة عن دالة asctime في لغة C فلا تحمل منطقةً زمنية أصلًا، ويكتفي المعيار بأن قيمها «تُفترض بالتوقيت العالمي». ومن هذه الجملة تحديدًا تبدأ أغلب المتاعب.
كيف تستعمله
- الصق قيمة الترويسة. القيمة وحدها بلا اسم الحقل. وأزرار الأمثلة الثلاثة تغطي صيغةً لكل زر، والرابع يكتب هذه اللحظة كما يلزم المرسِل أن يكتبها.
- اقرأ سطر الصيغة والملاحظات تحته. تحصل على اللحظة بالتوقيت العالمي وبتوقيتك، وأي الصيغ الثلاث هي، وهل يجوز للمرسِل إرسالها، وكل وجه تحيد فيه عن القواعد.
- قارن السطر الأخير بالذي فوقه. فذلك السطر هو ما يفعله Date.parse في متصفحك أنت بالنص نفسه. وحيث يختلفان يظهر الفارق بالثواني.
ستة أخطاء يقع فيها Date.parse
أشدها ضررًا صيغة asctime. فهي بلا منطقة زمنية، ويقول RFC 9110 إن قيمها تُفترض بالتوقيت العالمي، لكن Date.parse يقرأها بالتوقيت المحلي؛ فيخرج الجواب منحرفًا بمقدار فرق توقيت من يشغّله، ولا يصح إلا في غرينتش. وهو أسوأ أنواع العلل: صامت، ومعقول المظهر، ومختلف على كل جهاز. ولهذا يجري سطر المقارنة في هذه الصفحة داخل متصفحك، لترى ما يفعله متصفحك أنت.
ثم السنوات ذات الرقمين. تكتب صيغة RFC 850 المهجورة السنة برقمين، وقاعدة المعيار منسوبة إلى الحاضر: على المتلقّي أن يقرأ قيمةً تبدو أبعد من خمسين سنة في المستقبل على أنها أحدث سنة ماضية تنتهي بهذين الرقمين. أما محرّكات جافاسكربت فتستعمل محورًا ثابتًا عند 50. واليوم تختلف القاعدتان في 27 سنة من أصل 100 — كل القيم من 50 إلى 76 — ولأن حدّ المعيار يتقدم كل يناير، تكبر هذه المجموعة بواحدة كل سنة. فالرقم 68 يعني 2068 بالقاعدة و1968 بالمحور: قرنٌ كامل من الفارق.
والسنة المكتوبة بأربعة أرقام وهي دون المئة يصيبها الأمر نفسه. فالقواعد تقول إن السنة أربعة أرقام، فـ0094 كتابة مشروعة للسنة 94، ومع ذلك يحوّلها Date.parse إلى 1994.
واسم اليوم زائد عن الحاجة، ولذلك ينفع مرتين. فإن ناقض التاريخ دلّ على أن شيئًا ركّب القيمة تركيبًا خاطئًا: Mon, 06 Nov 1994 كان أحدًا، ويقبله Date.parse بلا كلمة. وهو كذلك يفصل في القرن: المثال أعلاه، Tuesday, 06-Nov-68، لا يستقيم إلا إن كانت السنة 2068، لأن السادس من نوفمبر 1968 كان أربعاء. فالحقل الزائد يحسم الحقل الملتبس، وهي مصادفة موفَّقة في صيغة لم تُصمَّم لذلك قط.
وتجيز القواعد الثانية 60 للثانية الكبيسة. أما Date.parse فيحوّل 23:59:60 إلى 23:59:00، فيطرح الثواني ويخسر دقيقة بدل أن يتقدم ثانيةً واحدة.
وأخيرًا التواريخ المستحيلة. فالحادي والثلاثون من نوفمبر لا وجود له، ويعيد Date.parse الأولَ من ديسمبر بدل NaN؛ قيمةٌ ستبدو معقولةً تمامًا في سجلّ.
حدود معلنة
تفحص هذه الصفحة القيمة لا الرسالة التي جاءت منها. فهل كان ينبغي إرسال ترويسة Date أصلًا، وهل يجوز لذاكرة وسيطة أن تستعملها، وهل كانت ساعة المرسِل صحيحة — كل ذلك مما لا يخبر به نصٌّ مجرَّد؛ بل يجيز RFC 9110 صراحةً لمتلقٍّ لديه ساعة أن يستبدل بقيمة Date غير الصحيحة وقتَ وصول الرد.
وهي كذلك تطبّق قاعدة الخمسين سنة بحالها اليوم. وهذا هو الصواب، ويحسن قوله جهرًا: فالطابع الزمني نفسه من صيغة RFC 850 يعني سنواتٍ مختلفة باختلاف وقت قراءته، فقيمةٌ تُحفظ اليوم في سجلّ وتُقرأ بعد عقود قد يتبدّل معناها. ولا سبيل إلى إصلاح ذلك في محلّل، فالصيغة ينقصها رقمان لا غير.
وحكم المطابقة إنما هو على القواعد لا على أن القيمة ستعمل. فـgmt بحروف صغيرة، ويومٌ برقم واحد، ومسافةٌ زائدة، كلها أشياء يمنع المعيار إنتاجها ويقبلها كل محلّل واقعي. والصفحة تخبرك في أي جانب من هذا الخط أنت، بدل أن تتظاهر بأن الأمر نجاح أو رسوب.
وحقل Retry-After يميّز صورتيه — عددًا صحيحًا من الثواني أو طابعًا زمنيًا — لأنه الموضع الوحيد الذي يقبل فيه HTTP الاثنتين، وهو مصدر التباس شائع. أما العدد السالب والعدد الكسري فليسا واحدةً منهما.
لماذا هي مجانية؟
كل شيء يجري في متصفحك، ولا تُرسل قيمة الترويسة إلى أي جهة؛ وهذا مهم في صفحة كهذه، إذ إن قيمة Last-Modified أو If-Modified-Since من نظام حقيقي تقول شيئًا عن ذلك النظام.
ولا كلفة خادم تُستردّ، فلا حساب ولا حدّ ولا علامة مائية.