التحقق من تاريخ الميلاد له سمعة بأنه سهل، وهذه السمعة غير مستحقة. الحقل يستقبل قيمة واحدة، والقيمة تاريخ، والتاريخ مسألة محلولة—إلا أنها ليست كذلك، لأن تاريخ الميلاد مدخل لحساب العمر أيضا، والعمر يقارن بحد قانوني، وكل من العمر والحد يعتمد على اليوم الذي تجري فيه المقارنة وفي أي مكان.
تجمع هذه المقالة الحالات التي تعطي فيها التطبيقات الصحيحة المظهر أجوبة خاطئة، وتشرح أي منها ينتمي إلى مجموعة الاختبارات قبل الإصدار لا بعد شكوى عميل.
ما الذي يجعل تاريخ الميلاد مختلفا عن بقية التواريخ؟
تاريخ الحجز وتاريخ الفاتورة وتاريخ التسليم كلها قريبة من الحاضر، ولا يحتاج أي منها إلى التحويل إلى عدد من السنوات. وتاريخ الميلاد مختلف بثلاثة أوجه في وقت واحد. فهو بعيد في الماضي، فيمتد على عشرات من دورات السنوات الكبيسة وعلى تغيير واحد في القاعدة على الأقل. وهو يستخدم لحساب عمر الشخص، أي لحساب لا لتنسيق. وكثيرا ما يقارن بحد قانوني، أي أن للجواب تبعات.
والنتيجة عن الخطأ غير متكافئة. فرفض تاريخ ميلاد صالح يطرد مستخدما مشروعا؛ وقبول تاريخ كان ينبغي رفضه يمرر شخصا عبر بوابة وجدت لحمايته. وكلاهما فشل حقيقي، وكلاهما يصل عبر مسار برمجي مختلف.
أي التواريخ لا وجود لها أصلا؟
أي تاريخ لا وجود له على التقويم ينبغي أن يرفض، وهذا يبدو بديهيا حتى تكتب قاعدة السنة الكبيسة. فالسنة كبيسة حين تقبل القسمة على أربعة، إلا أن سنوات القرن يجب أن تقبل القسمة على أربع مئة، لذلك هناك سنة من سنوات القرن تبدو قابلة للقسمة على أربعة وليست كبيسة، وأخرى كذلك.
والحالات العملية المهمة هي سنتا القرن على جانبي الحاضر. إحداهما ليست كبيسة، فاليوم التاسع والعشرون من شهرها الثاني ليس تاريخا صالحا؛ والأخرى كبيسة، فاليوم نفسه صالح. والتطبيقات التي تطبق قاعدة القسمة على أربعة فقط تقبل تاريخا مستحيلا وترفض تاريخا حقيقيا، ويبقى الخطأ غير مرئي معظم أيام السنة.
ويمتد المنطق نفسه إلى بقية التقويم: الأشهر مختلفة الأطوال، وبعض المدخلات تصل بترتيب اليوم أولا وبعضها بترتيب الشهر أولا، والسنة المكونة من رقمين غامضة بطبيعتها. والحقل الذي يقبل سنة من رقمين سيضع بعض الأشخاص على بعد قرن من عمرهم الحقيقي.
كيف يحسب العمر فعلا؟
العمر فرق بالسنوات الكاملة، يحسب بمقارنة تاريخ الميلاد بالتاريخ الحالي مكونا مكونا: اطرح سنوات الميلاد، ثم اطرح سنة أخرى إن لم يكن عيد الميلاد قد حل هذا العام بعد. وعيد الميلاد نفسه هو الحد، والعرف الشائع أن الشخص يكبر سنة في اليوم نفسه لا في اليوم التالي.
ولهذا التعريف دقة تستحق النص، لأنها موضع معظم أخطاء الواحد. فالمقارنة بين تاريخين تقويميين لا بين لحظتين. وشخصان ودا في اليوم التقويمي نفسه عمرهما واحد في ذلك اليوم حتى لو ودا في وقتين مختلفين، ومن ولد في آخر الليل لا يزيد عمره سنة عند ذلك الوقت من اليوم.
وتحويل أي من التاريخين إلى عدد ثوان ثم القسمة هو التطبيق الخاطئ المعتاد. إنه ينحرف عبر السنوات الكبيسة، ويجعل الجواب معتمدا على وقت اليوم، وينتج عمرا يتغير في ساعة عشوائية لا عند منتصف الليل.
أي يوم يحسبه الفحص؟
اليوم الذي تصادف أن ساعة الخادم عليه، إلا إذا قرر أحد غير ذلك. وهذا أصل فئة من الأخطاء لا تظهر إلا قرب منتصف الليل ولا تظهر إلا لمستخدمين في مناطق زمنية معينة: تاريخ ميلاد يحقق حد العمر في اليوم المحلي للمستخدم يفشل في يوم الخادم، أو العكس.
والطريقة النظيفة للتفكير أن تاريخ الميلاد تاريخ لا يحمل وقتا على الإطلاق. خزنه كتاريخ مجرد بلا منطقة زمنية مرتبطة به، وأجر مقارنة العمر مقابل تاريخ مستخرج من سياسة معلنة—عادة التاريخ المحلي للمستخدم في الفحص التفاعلي، وتاريخ مرجعي ثابت في كل ما يجب أن يكون قابلا للتكرار. وخلط تاريخ ميلاد بلا منطقة زمنية بلحظة حالية ذات منطقة زمنية هو الطريق الذي يتسرب منه الغموض.
وهناك فخ قريب يخص الاختبارات. فالاختبار الذي يحسب توقعه من ساعة النظام ينجح اليوم ويفشل في عيد ميلاد أحدهم، أو في سنة كبيسة، أو بعد تغيير سياسة. والاختبارات التي تؤكد على العمر تحتاج إلى التاريخ الحالي محقونا لا مقروءا من الساعة.
هل تتفق كل التقاويم على اليوم نفسه؟
لا. فالحساب الذي يستخدمه التقويم الافتراضي العالمي ليس الوحيد المستعمل، وعدة مناطق تحتفظ بأنظمة خاصة بها للأغراض المدنية. والتاريخ المكتوب في تقويم لا يقابل التاريخ المكتوب نفسه في تقويم آخر، ويمكن أن تظهر اللحظة نفسها بسنة وشهر ويوم مختلفين بحسب النظام الذي يتوقعه النموذج.
وقاعدة الممارسة لباني النماذج أن يكون صريحا لا بارعا: حدد أي تقويم يتوقعه الحقل، واقبل المكونات بترتيب معلن، وإن كان تقويم منطقة محلية مهما لمستخدميك فتعامل مع التحويل كقرار منتج له حقله الخاص لا كتحويل تلقائي لا يراه أحد. فالتحويل الصامت أسوأ من عدم التحويل، لأن القيمة الخاطئة لا تفرق عن قيمة مكتوبة بيد المستخدم.
أين تسبب التواريخ النائبة المتاعب؟
ثلاث قيم افتراضية تسبب ضررا قابلا للقياس. الأول من يناير في سنة مستديرة هو أشيع نص نائب في العالم، والنموذج الذي يعامله بصمت كتاريخ ميلاد حقيقي سيجمع لديه حزمة من المستخدمين يتبين أنهم يشتركون في يوم الميلاد نفسه. والقيمة الصفرية أو الفارغة المتنكرة في هيئة تاريخ أسوأ، لأنها قد تحسب إلى عمر يتجاوز القرون وتمر عبر فحص تجاوز الثالثة عشرة بينما تفشل في كل فحص آخر بصمت.
والثالث هو النص النائب الصالح تقنيا والخاطئ بوضوح، مثل أقرب تاريخ يسمح به منتقي التاريخ. والثلاثة تشترك في عرض واحد: أنها تجعل السجل السيئ يبدو مكتملا، فلا تجد الشيفرة اللاحقة فرصة لرفضه.
وهذا هو الحجة لصالح السجلات المنتجة في الاختبارات. فالقيم التي تنتجها أداة الهوية وبيانات الاختبار موزعة لا متكدسة، أي أن النموذج تحت الاختبار يرى مدى من التواريخ ذات شكل حقيقي بدلا من النص النائب نفسه مرارا. وهي قيم وهمية للاختبار البرمجي ولا شيء غير ذلك—ليست تاريخ ميلاد حقيقي لأحد، وليست وثيقة يمكن استخدامها كهوية شخص.
للمطورين: الحدود التي تستحق التوكيد
ثبت التاريخ المرجعي في الاختبار، ثم أكد على الحد بالضبط لا على قيمة قريبة منه. وأربع قيم تغطي معظم المخاطرة في أي حد: تاريخ الميلاد الذي يجعل الشخص في سن الحد بالضبط اليوم، والذي ينقص يوما واحدا، والذي يزيد يوما واحدا، ومن ولد في يوم كبيس.
وما بعد ذلك، أبق قواعد التخزين والمقارنة ضيقة: خزن تاريخا تقويميا بلا منطقة زمنية، واستخرج العمر عند الطلب بدلا من تخزينه، ولا تدع تاريخا نائبا يصبح غير مميز عن تاريخ حقيقي. وقيمة حارسة خارج المدى المعقول ويرفضها التحقق أفضل من قيمة افتراضية معقولة تتسلل عبر الفحوص. وأبق أرقام السياسة في الإعدادات، لأن الحدود تتغير والحد المرمز في الشيفرة على بعد إصدار واحد من أن يصبح خاطئا.
الخطوات التالية
خذ فحص العمر في منتجك ومرر عليه قيم الحد الأربع بتاريخ مرجعي ثابت؛ فإن أعاد أي منها جوابا خاطئا فالخطأ في المقارنة لا في النموذج. ثم اسحب تشكيلة من تواريخ الميلاد المنتجة من أداة الهوية وتأكد من أن الحقل يخزنها دون تغيير، ومنها تاريخ في أول الشهر وتاريخ بترتيب تقويمي غير افتراضي، كي لا يعيد تغيير اللغة ترتيبها بصمت. وتغطي مقالة اختبار التحقق من العمر الحدود التي تقارن بها هذه التواريخ عادة، وهي المتابعة الطبيعية لهذا الدليل.