昨天是四年才出現一次的2/29,也是未滿四歲系統的小關卡,結果在網路上跟生活周遭還真目賭不少程式跌倒~~

最常見的狀況是該顯示民國年的地方出現101/2/28而非101/2/29,究其根源,多半是當初程式在撰寫時使用了DateTime.Today.AddYears(-1911).ToString("yyyy/MM/dd")的簡便寫法,乍看之下比單獨抓出年份額外處理簡潔許多,卻隱藏了閏年判定基準不同的問題(民國101年是西元2012年為閏年,但減去1911為西元101年變成非閏年;另外若需要計算星期幾,這種計算方法也會出錯,只是會立即被揪出來),因此這隻閏年蟲就躲在一些2008/3/1日之後才寫好的系統中,通過測試完成驗收,正確無誤地運作多年,到第一次遇上2/29才揭竿而起(若剛好是假日,說不定還能再躲四年),攪亂一池春水。

以下我整理了幾種顯示民國年日期的寫法: (比起來都不如AddYears(-1911)做法來得簡潔,難怪許多人會誤用這個看起來很漂亮其實有毒的寫法)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime d = new DateTime(2012, 2, 29);
            //幾種顯示民國年的方法
            //1.另外取年減1911
            Console.WriteLine(
                string.Format("民國{0}年{1:MM月dd日}", 
                d.Year - 1911, d)
                );
            //2.借用TaiwanCalender
            //(沒想到.NET Library中有這個吧!! 微軟就甘心A)
            System.Globalization.TaiwanCalendar tc
                = new System.Globalization.TaiwanCalendar();
            Console.WriteLine(
                "{0}/{1:MM/dd}", tc.GetYear(d), d);
            //同場加映,另外有TaiwanLunisolarCalendar可以查農曆哦!
            //可惜目前還不支援看吉凶沖煞,無法得知否宜嫁娶動土上樑(大誤)
            System.Globalization.TaiwanLunisolarCalendar tlc =
                new System.Globalization.TaiwanLunisolarCalendar();
            Console.WriteLine("農曆: {0}年{1}月{2}日",
                tlc.GetYear(d), tlc.GetMonth(d), tlc.GetDayOfMonth(d));
            //3.借用demo寫的擴充方法http://demo.tc/Post/579
            Console.WriteLine(d.ToTaiwanCalendar("yyyy/MM/dd"));
            Console.Read();
        }
 
 
    }
    static class DateExt
    {
        /// <summary>
        /// 轉換為民國年
        /// </summary>
        ///<param name="format">標準格式化語法</param>
        /// <returns></returns>
        static public string ToTaiwanCalendar(this DateTime x, string format)
        {
            DateTime now = x;
            TaiwanCalendar tc = new TaiwanCalendar();
            Regex regex = new System.Text.RegularExpressions.Regex(@"[yY]+");
            format = regex.Replace(format, tc.GetYear(x).ToString("000"));
            return x.ToString(format);
        }
    }
}

執行結果:

民國101年02月29日
101/02/29
農曆: 101年2月8日
101/02/29

今天學到的教訓:

  1. 遇到系統規格中民國年轉換時,記得針對2/29 進行測試
  2. 看到簡潔寫法時,採用前記得推敲是否隱含邏輯瑕疵,顏色鮮豔的蘑菇往往有毒呀~
  3. 手邊如有運作未滿四年還沒上幼稚園中班的新系統,第一次碰到閏年時記得要提高警覺
  4. 3/1起,應該還是會有民國閏年蟲陸續被新同學埋進新系統裡,通過測試驗收等待2016/2/29來臨 XD

 


Comments

# by 風箏

前輩 關於農曆的判定有BUG,會有閏月問題 需要再套用GetLeapMonth來判定指定年度的閏月數另在處理才不會出現異常

# by 風箏

前輩 關於農曆的判定有BUG,會有閏月問題 需要再套用GetLeapMonth來判定指定年度的閏月數另在處理才不會出現異常

# by demo

意外看到自己的玩意XD 同場加映一下,寫成 d.ToTaiwanCalendar("民國yy年MM月dd日") 會更好用哩

# by Jeffrey

to 風箏, 謝謝提醒,原來還需要額外的邏輯處理閏月問題,稍後會再補充。

# by jain

我們之前也是遇到閏月問題,所以不敢用, 沒想到用GetLeapMonth可以解決, 謝謝風箏大。

# by .

微軟也出槌... http://blogs.msdn.com/b/windowsazure/archive/2012/03/09/summary-of-windows-azure-service-disruption-on-feb-29th-2012.aspx

Post a comment