過年是中國(以及日本、韓國等國)人民的第一大節日。你怎麼知道哪天過年?查日曆或者聽別人說?程序員當然有程序員的辦法,就是寫程序啦。
雖然公曆(俗稱的“陽曆”)已經成了全世界的通用標準,而且也具有多方面的優越性。但在東亞地區,還是離不開“農曆”,春節、元宵、端午、中秋、重陽這些節日是農曆的,大部份人的老爸老媽的生日也是農曆的。
早在1.0框架出來的時候,我就認爲微軟公司不應該“厚彼薄此”,在.net框架中提供了希伯來曆等,卻沒有提供更廣泛使用的“農曆”。
而在.net 2.0中,微軟公司終於做出了這個小小的改進。
.net 2.0在System.Globalization命名空間中新增加了EastAsianLunisolarCalendar 類及以繼承它的ChineseLunisolarCalendar, JapaneseLunisolarCalendar, KoreanLunisolarCalendar, TaiwanLunisolarCalendar等幾個類。LunisolarCalendar顧名思義應爲“陰陽曆”,我的理解是因爲我們所用的農曆雖然按照月亮公轉來編月份,但用“閏月”的方式來調整年份與地球公轉的誤差,嚴格意義上來說是結合了月亮公轉和地球公轉的成份,因此屬於“陰陽曆”。但我這裏還是按照習慣稱之爲“農曆” 。
二、新的農曆類還是沒有公民待遇
爲了測試新的日曆類,我興沖沖地寫了幾句代碼:(省略了調用這個方法的其它代碼)
爲了說明問題,繼續測試
日曆類 | MinSupportedDateTime | MaxSupportedDateTime |
ChineseLunisolarCalendar | 公元1901年1月初1 | 公元2100年12月29 |
TaiwanLunisolarCalendar | 民國1年1月初1 | 民國139年12月29 |
JapaneseLunisolarCalendar | 昭和35年1月初1 | 平成61年12月29 |
KoreanLunisolarCalendar | 公元918年1月初1 | 公元2050年12月29 |
第一個類,主要是封裝了農曆的一些常用字符和對日曆處理的最基本功能
using System.Collections.Generic;
using System.Text;
using System.Globalization;
public static class ChineseCalendarHelper
{
public static string GetYear(DateTime time)
{
StringBuilder sb = new StringBuilder();
int year = calendar.GetYear(time);
int d;
do
{
d = year % 10;
sb.Insert(0, ChineseNumber[d]);
year = year / 10;
} while (year > 0);
return sb.ToString();
}
public static string GetMonth(DateTime time)
{
int month = calendar.GetMonth(time);
int year = calendar.GetYear(time);
int leap = 0;
//正月不可能閏月
for (int i = 3; i <= month; i++)
{
if (calendar.IsLeapMonth(year, i))
{
leap = i;
break; //一年中最多有一個閏月
}
}
if (leap > 0) month--;
return (leap == month + 1 ? "閏" : "") + ChineseMonthName[month - 1];
}
public static string GetDay(DateTime time)
{
return ChineseDayName[calendar.GetDayOfMonth(time) - 1];
}
public static string GetStemBranch(DateTime time)
{
int sexagenaryYear = calendar.GetSexagenaryYear(time);
string stemBranch = CelestialStem.Substring(sexagenaryYear % 10 - 1, 1) + TerrestrialBranch.Substring(sexagenaryYear % 12 - 1, 1);
return stemBranch;
}
private static ChineseLunisolarCalendar calendar = new ChineseLunisolarCalendar();
private static string ChineseNumber = "〇一二三四五六七八九";
public const string CelestialStem = "甲乙丙丁戊己庚辛壬癸";
public const string TerrestrialBranch = "子醜寅卯辰巳午未申酉戌亥";
public static readonly string[] ChineseDayName = new string[] {
"初一","初二","初三","初四","初五","初六","初七","初八","初九","初十",
"十一","十二","十三","十四","十五","十六","十七","十八","十九","二十",
"廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十"};
public static readonly string[] ChineseMonthName = new string[] { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二" };
}
第二個類爲自定義格式化器:
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Threading;
public class ChineseCalendarFormatter : IFormatProvider, ICustomFormatter
{
//實現IFormatProvider
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return Thread.CurrentThread.CurrentCulture.GetFormat(formatType);
}
//實現ICustomFormatter
public string Format(string format, object arg, IFormatProvider formatProvider)
{
string s;
IFormattable formattable = arg as IFormattable;
if (formattable == null)
s = arg.ToString();
else
s = formattable.ToString(format, formatProvider);
if (arg.GetType() == typeof(DateTime))
{
DateTime time = (DateTime)arg;
switch (format)
{
case "D": //長日期格式
s = String.Format("{0}年{1}月{2}",
ChineseCalendarHelper.GetYear(time),
ChineseCalendarHelper.GetMonth(time),
ChineseCalendarHelper.GetDay(time));
break;
case "d": //短日期格式
s = String.Format("{0}年{1}月{2}", ChineseCalendarHelper.GetStemBranch(time),
ChineseCalendarHelper.GetMonth(time),
ChineseCalendarHelper.GetDay(time));
break;
case "M": //月日格式
s = String.Format("{0}月{1}", ChineseCalendarHelper.GetMonth(time),
ChineseCalendarHelper.GetDay(time));
break;
case "Y": //年月格式
s = String.Format("{0}年{1}月", ChineseCalendarHelper.GetYear(time),
ChineseCalendarHelper.GetMonth(time));
break;
default:
s = String.Format("{0}年{1}月{2}", ChineseCalendarHelper.GetYear(time),
ChineseCalendarHelper.GetMonth(time),
ChineseCalendarHelper.GetDay(time));
break;
}
}
return s;
}
}
有了這兩段代碼爲原型,要實現計算和顯示一個日期的農曆日期及其它功能,基本上就很容易了。
{
CultureInfo ci = new CultureInfo("zh-TW");
ci.DateTimeFormat.Calendar = new TaiwanCalendar();
return dt.ToString("D",ci);
}
{
CultureInfo ci = new CultureInfo("zh-CN");
ci.DateTimeFormat.Calendar = new ChineseLunisolarCalendar();
return dt.ToString("D",ci);
}