在System命名空间里定义了日期和时间处理的基础资源,如DateTime和TimeSpan结构类型;本课,除了使用这些基础资源,我们还将讨论如何获取中国农历信息,首先,我们从DateTime结构的使用开始。

DateTime结构

DateTime结构中的MinValue和MaxValue两个字段,分别定义了处理日期和时间的最小值和最大值,其中,MinValue表示公元0001年1月1日0时0分0秒。此外,在.NET Framework中处理时间的最小单位是Tick,它表示万之一秒(即100毫微秒),在DateTime中的Ticks属性则表示保存时间距离DateTime.MinValue值的Tick值。

除了Ticks属性,我们常用用的属性还有Year、Month、Day、Hour、Minute、Second、Millisecond等。获取一个时间点时,可以通过构造函数、Now属性、Today属性等,其中:

  • Now属性可以获取计算机的当前日期和时间。
  • Today属性获取计算当前日期,其中的时间设置为0点0分0秒。

使用DateTime结构的构造函数时,可以使用多个版本,常用的有:

  • DateTime(long),使用Tick值初始化时间。
  • DateTime(int,int,int),使用年、月、日初始化时间。
  • DateTime(int,int,int,int,int,int),使用年、月、日、时、分、秒初始化时间。

获取时间值后,还可以使用一些方法显示为常用的格式,如:

  • ToLongDateString(),返回长日期格式字符串。
  • ToShortDateString(),返回短日期格式字符串。
  • ToLongTimeString(),返回长时间格式字符串。
  • ToShortTimeString(),返回短时间格式字符串。

下面的代码演示了这四个方法的输出结果,请注意,其输出格式默认使用计算机系统设置的区域中的日期时间格式。

    static void Main(string[] args)
    {
        DateTime dt = new DateTime(2018, 9, 23, 11, 16, 30);
        Console.WriteLine(dt.ToLongDateString());
        Console.WriteLine(dt.ToShortDateString());
        Console.WriteLine(dt.ToLongTimeString());
        Console.WriteLine(dt.ToShortTimeString());
    }

我的计算机区域设置当然是中国,代码输出结果如下图所示。

enter image description here

除获取日期和时间信息,我们还可以通过DateTime结构进行一些简单的日期和时间推算,如获取某个时间点早一些或晚一些时间点,此时,可以使用一系列的AddXXX()方法,如下面的代码,我们使用AddDays()推算早三天和晚三天的时间点。

    static void Main(string[] args)
    {
        DateTime dt = new DateTime(2018, 9, 23, 11, 16, 30);
        Console.WriteLine(dt.AddDays(-3).ToString());
        Console.WriteLine(dt.AddDays(3.5).ToString());
    }

代码执行结果如下图所示。

enter image description here

我们可以看到,当AddXXX()方法的参数为负数时,就是推算早一些的时间点,而参数为正数时则是推算晚一些的时间点。除了使用DateTime结构中的AddXXX()方法进行日期和时间的推算,我们还可以借助TimeSpan结构进行日期和时间的计算工作。

TimeSpan结构

下面的代码,我们使用TimeSpan结构进行DateTime数据的加、减运算。

    static void Main(string[] args)
    {
        DateTime dt1 = new DateTime(2018, 9, 23, 11, 16, 00);
        TimeSpan ts = new TimeSpan(1, 30, 0);
        DateTime dt2 = dt1 + ts;
        Console.WriteLine(dt2.ToString());
        DateTime dt3 = dt1 - ts;
        Console.WriteLine(dt3.ToString());
    }

本例中,我们定义的TimeSpan为1小时30分,然后,使用dt1分别进行加(晚一些)、减(早一些)运算,执行结果如下图所示。

enter image description here

实际应用中,我们使用两个DateTime数据进行加、减运算时,也会返回一个TimeSpan数据,此时,可以获取两个时间点间隔的小时、分、秒等数据,如下面的代码。

    static void Main(string[] args)
    {
        DateTime dt1 = new DateTime(2018, 9, 23, 11, 16, 00);
        DateTime dt2 = new DateTime(2018, 9, 26, 15, 36, 25);
        TimeSpan ts = dt2 - dt1;
        Console.WriteLine(ts.Days);
        Console.WriteLine(ts.Hours);
        Console.WriteLine(ts.Minutes);
        Console.WriteLine(ts.Seconds);
    }

本例显示了两个时间点分别计算的天数、小时数、分钟数和秒数,执行结果如下图所示。

enter image description here

此外,如果需要两个时间点完整的间隔数据,可以使用TimeSpan结构中的Ticks属性获取。

获取中国农历信息

世界上很多文明都有自己的历法,中国的农历就是其中之一,相信大家在春节时就会感觉到其威力有多大。在.NET Framework类库中,包含了处理各种历法的资源,它们包含在System.Globalization命名空间,以下代码请注意引用此命名空间。

下面的代码,我们首先计算出农历所需要的数据。

/*
 * CChineseCalendar类,处理农历
 * 
 */

using System;
using System.Globalization;


namespace ConsoleTest
{
    public class CChineseCalendar
    {
        private DateTime myDate;
        // 构造函数
        public CChineseCalendar(DateTime dt)
        {
            myDate = dt;
            SetLunar();
        }
        //
        public CChineseCalendar() : this(DateTime.Now)
        { }
        //
        public CChineseCalendar(int year, int month, int day)
            :this(new DateTime(year, month, day))
        { }
        // 计算农历信息
        private void SetLunar()
        {
            ChineseLunisolarCalendar cale = 
                new ChineseLunisolarCalendar();
            // 甲子年份
            Year = 
                cale.GetSexagenaryYear(myDate);
            // 地支
            TerrestrialBranch = 
                cale.GetTerrestrialBranch(Year);
            // 天干
            CelestialStem = 
                cale.GetCelestialStem(Year);
            // 月份(1-13),注意处理闰月情况
            Month = cale.GetMonth(myDate);
            // 闰月是第几个月,注意冬月和腊月在阳历第二年的情况
            // 如是8就是闰七月
            if (Month > 10 && myDate.Month < 3)
                LeapMonth = cale.GetLeapMonth(myDate.Year - 1);
            else
                LeapMonth = cale.GetLeapMonth(myDate.Year);
            // 日子
            Day = cale.GetDayOfMonth(myDate);
        }
        //
        public int Year { get; private set; }
        public int Month { get;private set; }
        public int LeapMonth { get; private set; }
        public int Day { get; private set; }
        public int TerrestrialBranch { get; private set; }
        public int CelestialStem { get; private set; }
        //
    }// end class CChineseCalendar
}

代码中,我们定义了一些属性,如:

  • Year属性,农历年份,表示甲子中的第几年,取值从1到60。
  • Month属性,农历月份值,取值从1到13,注意农历有闰月的情况。
  • LeapMonth属性,闰月值,如年份中没有闰月为0,如为8则表示闰7月,以此类推。如果阳历为一月或二月,则应注意是不为前一年的冬月或腊月,此时,计算闰月值需要使用前一年的阳历年份。
  • Day属性,农历日期,如初一为1,取值从1到30。
  • TerrestrialBranch属性,地支值,1到12,同时分别对应了十二生肖。
  • CelestialStem属性,天干值,1到10。

关键的数据计算都放在SetLunar()方法中,其中关于各项数据的获取请注意参数的不同,有的是使用阳历日期(DateTime类型),有的是使用甲子年份,有的是使用阳历年份。私有字段myDate则用于保存对象中处理的阳历日期值。

此外,在CChineseCalendar类中还定义了三个构造函数,分别是:

  • CChineseCalendar(DateTime dt),通过DateTime数据构建对象。
  • CChineseCalendar(),通过计算机当前时间构建对象。
  • CChineseCalendar(int year, int month, int day),通过年、月、日构建对象。

接下来,我们可以通过获取的相关数据,以合理的形式显示出农历信息,如下面的代码,首先是年份名称的显示。

    /* 年份名称 */
    private static string[] yearNames = {"",
        "甲子","乙丑","丙寅","丁卯","戊辰","己巳","庚午","辛未","壬申","癸酉",
        "甲戌","乙亥","丙子","丁丑","戊寅","己卯","庚辰","辛巳","壬午","癸未",
        "甲申","乙酉","丙戌","丁亥","戊子","己丑","庚寅","辛卯","壬辰","癸巳",
        "甲午","乙未","丙申","丁酉","戊戌","己亥","庚子","辛丑","壬寅","癸卯",
        "甲辰","乙巳","丙午","丁未","戊申","己酉","庚戌","辛亥","壬子","癸丑",
        "甲寅","乙卯","丙辰","丁巳","戊午","己未","庚申","辛酉","壬戌","癸亥"};
    //
    public string YearName
    {
        get { return yearNames[Year]; }
    }

代码中,我们定义了yearNames数据,用于保存甲子的60个年份名称;请注意,数组的第一个成员定义为空字符中,这里只是一个占位,这样就可以直接使用年份的值(1到60)作为索引值使用了。

月份和日期的名称获取与年份名称类似,只是需要注意,月份要处理闰月的情况,如下面的代码。

    /* 月份名称 */
    private static string[] monthNames =
        {"", "正月", "二月", "三月", "四月", "五月", "六月",
            "七月", "八月", "九月", "十月", "冬月", "腊月"};
    // 给出当前月份名称
    public string MonthName
    {
        get
        {
            if (LeapMonth <= 0 || Month < LeapMonth)
                return monthNames[Month];
            else if (LeapMonth == Month)
                return CStr.Append("闰", monthNames[Month - 1]);
            else
                return monthNames[Month - 1];
        }
    }
    /* 日子名称 */
    private static string[] dayNames =
        {"", "初一", "初二", "初三", "初四", "初五",
            "初六", "初七", "初八", "初九", "初十",
            "十一", "十二", "十三", "十四", "十五",
            "十六", "十七", "十八", "十九", "二十",
            "廿一", "廿二", "廿三", "廿四", "廿五",
            "廿六", "廿七", "廿八", "廿九", "三十"};
    // 当前日期名称
    public string DayName
    {
        get { return dayNames[Day]; }
    }

下面是通过地支的值获取十二生肖名称的代码。

    // 生肖对应地支
    private static string[] animalNames = {"",
        "鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪" };
    //
    public string AnimalName
    {
        get { return animalNames[TerrestrialBranch]; }
    }
    //
    public int Animal
    {
        get { return TerrestrialBranch; }
    }

最后,我们定义一个ToStr()方法,用于显示完成的农历信息,如下面的代码。

    //
    public string ToStr()
    {
        return "{0}({1})年 {2}{3}".Combine(
            YearName, AnimalName, MonthName, DayName);
    }

下面,我们测试CChineseCalendar类的使用。

    static void Main(string[] args)
    {
        DateTime dt1 = new DateTime(2018, 9, 23, 11, 16, 00);
        CChineseCalendar cale = new CChineseCalendar(dt1);
        Console.WriteLine(cale.ToStr());
    }

代码执行结果如下图所示。

enter image description here

实际应用中,大家还可以根据需要扩展CChineseCalendar类的功能。

CHY软件小屋原创作品!