2013年9月17日 星期二

[轉貼] 顯示民國年與閏年蟲


來源:http://blog.darkthread.net/post-2012-03-01-leap-year-and-taiwancalendar.aspx

昨天是四年才出現一次的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);
        }
    }
}
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 

沒有留言:

張貼留言