五、写自已的日期格式化器
昨天看了一篇文章,说目前大家用的“农历”这个术语是文革时期才有的,目的是反封建。这里为了省事,还是继续使用这个术语。而英文名称ChineseLunisolarCalendar太长,我自己的代码中就用ChineseCalendar为相关功能命名,这个名字也还过得去吧。
我原先设想自定义一个类,使得能写出这样的代码:
string
s
=
DateTime.Now.ToString(
new
MyFormatProvider());
就能得出我想要的农历日期字符串,经过测试却失败了,依据我的分析,微软公司在.net框架中把日期时间型的格式写死了,只能依据相关的地区采用固定的几种显示格式,没法再自行定义。而前文已经说过,而所有的相关格式微软公司都放到一个名为culture.nlp的文件中(这个文件在以前的.net框架是一个独立的文件,在.net 2.0被作为一个资源编译到mscorlib.dll中。) (我的这个不能为DateTime写自已的格式化器的观点没有资料佐证,如有不当之处,请大家指正)
虽然不能为DataTime写自定义的格式器,但还有另外一个途径,就是为String类的Format方法写自定义格式化器,我测试了一下,效果还不错,调用方式如下:
string
s
=
String.Format(
new
ChineseCalendarFormatter(),
"
{0:D}
"
,DateTime.Now);
可以得到“二〇〇六年正月初九”
string
s
=
String.Format(
new
ChineseCalendarFormatter(),
"
{0:d}
"
,DateTime.Now);
可以得到“丙戌年正月初九”
虽然没有前面所设想的方便,但也还能接受,全部代码帖出如下:
第一个类,主要是封装了农历的一些常用字符和对日历处理的最基本功能
using
System;
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;
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;
}
}
这段代码中间处理格式那部份稍做改进,就可以支持更多的日期格式。
有了这两段代码为原型,要实现计算和显示一个日期的农历日期及其它功能,基本上就很容易了。