ก่อนอื่น ใครที่เปิดมาเจอบทความนี้ เพราะเจอ Bug กับ Software ที่ตัวเองพัฒนา ในวันที่ 29 กุมภาพันธ์บ้าง ขอเสียงหน่อยยยยย ^ ^/

เรื่องก็มีอยู่ว่า จากที่ประเทศไทย ใช้หน่วยปีเป็น พ.ศ. เป็นหลัก ทำให้ Software ส่วนใหญ่ ที่ต้องยุ่งกับวันที่ ก็จะใช้หน่วยปี พ.ศ. ในการแสดงผลด้วย และนอกจากเรื่องของการแสดงผลแล้ว ก็มักจะต้องนำวันที่ไปคำนวณบางอย่างต่อ เช่น การเปรียบเทียบวัน, การคำนวณวันหมดอายุ, หาวันสุดท้ายของเดือน หรือแม้แต่การหาวันจากวันที่ เป็นต้น
บ่อยครั้งที่ผมเห็นปัญหาเกี่ยวกับการคำนวณที่เกี่ยวกับปีอธิกสุรทิน (Leap year) ก็คือปีที่มี 366 วัน หรือปีที่มีวันที่ 29 กุมภาพันธ์นั่นเอง บทความนี้เลยจะพูดถึงการเขียนโปรแกรมเกี่ยวกับ Leap year ซักเล็กน้อย
รู้จักปีอธิกสุรทิน (Leap year) กันก่อน
จากที่เกริ่นมาตอนต้น ปีอธิกสุรทิน คือปีที่มี 366 วัน หรือมีวันที่ 29 ก.พ. ในปีนั้น ๆ ซึ่งก็เป็นที่รู้กันดีว่า 4 ปี จะมีครั้งนึง
แต่มันไม่จบอยู่แค่นั้น เพราะถ้าไปดูรายละเอียดจริง ๆ จะพบว่า ทุก ๆ 100 ปี จะมีข้อยกเว้น (มี 365 วัน) และทุก ๆ 400 ปี จะไม่ต้องยกเว้น
ซึ่งส่วนใหญ่แล้ว โปรแกรมเมอร์จะเขียน Logic โดยการ Mod 4, Mod 100 และ Mod 400 ประมาณนี้
1 2 3 4 5 6 |
function isLeapYear(year) { if (year % 4 !== 0) return false if (year % 100 !== 0) return true return year % 400 === 0 } |
หยุดก่อน! อย่าเพิ่ง Copy code ไปใช้งาน อ่านต่อก่อนนะ~!!
จาก Code ด้านบน จะใช้ตรวจสอบ Leap year ได้ก็จริง แต่อยู่ภายใต้เงื่อนไขว่า จะต้องเป็นปี ค.ศ. เท่านั้น หากเผลอใช้ปี พ.ศ. ล่ะก็ Bug แน่ ๆ
543
น่าจะรู้กันอยู่แล้วว่า เลข 543 คือความต่างของปี ค.ศ. เทียบกับปี พ.ศ. อย่างปีนี้ ค.ศ. 2020 หรือปี พ.ศ. 2563 ซึ่งต่างกันอยู่ 543 ปีนั่นเอง
ซึ่งเราสามารถคำนวณเลขปีข้ามไปมาได้ด้วยการนำปีมาบวกหรือลบได้ แต่สิ่งหนึ่งที่ต้องระวัง หากต้องคำนวณเกี่ยวกับ Leap year เมื่อข้อมูลเป็นปี พ.ศ. ก็จะต้องแปลงเป็นปี ค.ศ. ก่อน ด้วยการลบ 543
มาลองดู Code ด้านล่างกัน เป็น Code ในภาษา JavaScript เมื่อรันใน Console บน Browser
1 2 3 4 5 6 7 8 9 10 |
const day = 29 const month = 2 - 1 // Month start at 0 const year = 2020 const date = new Date(year, month, day) console.log(date) // Sat Feb 29 2020 00:00:00 GMT+0700 (Indochina Time) console.log('Day =', date.getDate()) // Day = 29 console.log('Month =', date.getMonth() + 1) // Month = 2 console.log('Year =', date.getFullYear()) // Year = 2020 console.log('Day of week =', date.getDay()) // Day of week = 6 |
หาก Code ด้านบน ใช้ปี พ.ศ. จะเกิดอะไรขึ้น…
1 2 3 4 5 6 7 8 9 10 |
const day = 29 const month = 2 - 1 // Month start at 0 const year = 2563 const date = new Date(year, month, day) console.log(date) // Tue Mar 01 2563 00:00:00 GMT+0700 (Indochina Time) console.log('Day =', date.getDate()) // Day = 1 console.log('Month =', date.getMonth() + 1) // Month = 3 console.log('Year =', date.getFullYear()) // Year = 2563 console.log('Day of week =', date.getDay()) // Day of week = 2 |
จะเห็นว่า เมื่ออ่านข้อมูลออกมา จะกลายเป็นวันอังคารที่ 1 มี.ค. ซึ่งไม่ถูกต้อง
เทคนิคการตรวจสอบ Leap year อื่น ๆ
จาก function isLeapYear
ในหัวข้อแรก เป็นวิธีการที่สามารถใช้ได้ แต่ถ้าจะจบแค่นี้ก็คงไม่มีอะไรน่าสนใจ ฮาาาาาา
ในการตรวจสอบ Leap year สามารถใช้เทคนิคอื่น ๆ ได้ ดังนี้
- ทดสอบสร้างวันที่ 29 Feb ของปีที่ต้องการตรวจสอบ แล้วอ่านค่าวันที่ออกมา (ในตัวอย่างหัวข้อที่แล้ว)
- หาจำนวนวันของปีนั้น ๆ แล้วตรวจสอบว่าได้ค่าเป็น 366
- ใช้ Method ตรวจสอบ Leap year (มีในบางภาษาโปรแกรม)
แต่ไม่ว่าจะวิธีไหน ก็ต้องใช้ function หรือ class Date
มาใช้ในการตรวจสอบ ซึ่งภาษาโปรแกรมต่าง ๆ เตรียมไว้ให้เรียบร้อยแล้ว ไม่ต้องเขียน Code เองเหมือนอย่างใน function isLeapYear
ในหัวข้อแรก
ทิ้งท้าย
หากต้องการจัดการข้อมูลเกี่ยวกับวันที่ ควรจะใช้ function หรือ class ที่ภาษาโปรแกรมเตรียมไว้ให้ เพื่อความมั่นใจว่าจะสามารถทำงานได้อย่างถูกต้อง ไม่ควรเขียน Logic ขึ้นมาเอง เพราะอาจเกิดความผิดพลาดได้ รวมถึงข้อมูลปี ควรใช้ในรูปแบบ ค.ศ. เท่านั้น
ในบางภาษาโปรแกรม สามารถกำหนดรูปแบบเป็นปี พ.ศ. โดยอ้างอิงจากประเทศได้ แต่จะต้องเขียน Code ซับซ้อนขึ้น อย่าลืมทดสอบให้ดี และที่สำคัญ ควรเขียน Test เพื่อตรวจสอบผลด้วยนะครับ
แล้วก็… ระวังเรื่อง Time Zone ด้วยนะ…