处理日期问题

在《编程珠玑 第二版》41页的问题四中有这么一道练习:
编写处理下列日期问题的函数:
题目1给定两个日子,计算这两个日子间的天数;
题目2给定某个日子,返回它在一周中属于第几天;
题目3给定某年某月,以字符数组的形式产生该月的日历

先给一个预备知识:
平闰年解释
公历闰年的精确计算方法(按一回归年365天5小时48分45.5秒)   
①普通年能被4整除且不能被100整除的为闰年。(如2004年就是闰年,1901年不是闰年)  
②世纪年能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)   
③对于数值很大的年份,这年如果能整除3200,并且能整除172800则是闰年。
如172800年是闰年,86400年不是闰年(因为虽然能整除3200,但不能整除172800)
(此按一回归年365天5h48'45.5''计算)。
这里的第三点就不考虑的,也没有谁会纠结公元172800年的某一天。
这个问题本身不难,这里主要说的是对数组的应用,以及由它展开的一些技巧,而且第1、3题目非常实用。
源代码如下:
///


/// 时间计算
/// 《编程珠玑 第二版》P41

/// 编写处理下列日期问题的函数:
/// 给定两个日子,计算这两个日子间的天数;
/// 给定某个日子,返回它在一周中属于第几天;
/// 给定某年某月,以字符数组的形式产生该月的日历
///
/// 平闰年解释
/// 公历闰年的精确计算方法(按一回归年365天5小时48分45.5秒)   
/// ①普通年能被4整除且不能被100整除的为闰年。(如2004年就是闰年,1901年不是闰年)  
/// ②世纪年能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)   
/// ③对于数值很大的年份,这年如果能整除3200,并且能整除172800则是闰年。
///     如172800年是闰年,86400年不是闰年(因为虽然能整除3200,但不能整除172800)
///     (此按一回归年365天5h48'45.5''计算)。
///


public class DateTimeCalculate
{
private DateTimeCalculate() { }

///
/// 是否是瑞年
///

private static bool IsRuiYear(int year)
{
bool divisionOff4 = (year % 4) == 0;
bool divisionOff100 = (year % 100) == 0;
bool divisionOff400 = (year % 400) == 0;
return (divisionOff4 && !divisionOff100) || divisionOff400;
}

///
/// 获得一年中每个月的天数(这是精华用数组简化if...else...语句)
///

private static int[] GetEveryMonthDaysOfYear(int year)
{
int february = IsRuiYear(year) ? 29 : 28;
return new[] { 31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
}

///
/// 获取输入时间是这年中的第几天
///

///
///
private static int GetDayOfYear(DateTime time)
{
int[] days = GetEveryMonthDaysOfYear(time.Year);
int dayNumber = 0;
for (int i = 0; i < time.Month - 1; i++)
{ dayNumber += days[i]; }
return dayNumber + time.Day;
}

///
/// 给定两个日子,计算这两个日子间的天数
///

/// 某日1
/// 某日2
/// 如果是负数,则说明某日2小于某日1
public static int GetDateTimeDifference(DateTime time1, DateTime time2)
{
bool moreThan = (time2.Year >= time1.Year) || (time2.Month >= time1.Month) || (time2.Day >= time1.Day);
DateTime beginDay = moreThan ? time1 : time2;
DateTime endDay = moreThan ? time2 : time1;
int day = 0;
for (int i = beginDay.Year; i < endDay.Year; i++)
{ day += IsRuiYear(i) ? 366 : 365; }
return (day + GetDayOfYear(endDay) - GetDayOfYear(beginDay)) * (moreThan ? 1 : -1);
}

///
/// 给定某个日子,返回它在一周中属于第几天;
///

///
/// 1   Monday
/// 2   Tuesday
/// 3   Wednesday
/// 4   Thursday
/// 5   Friday
/// 6   Saturday
/// 7   Sunday
///

public static int GetDayOfWeek(DateTime time)
{
int todayOfWeek = Convert.ToInt32(DateTime.Now.DayOfWeek);
int difference = GetDateTimeDifference(DateTime.Now, time);
int days = todayOfWeek + difference;
return (days % 7) + (days < 0 ? 7 : 0);
}

///
/// 给定某年某月,以字符数组的形式产生该月的日历
/// Sunday Monday Tuesday Wednesday Thursday Friday Saturday
///

///
/// 是一个二维数组,(7列,4或5或6行)
public static int[,] GetMonthCalendar(int year, int month)
{
int[,] calendar = new int[6, 7];
int day1OfColumn = GetDayOfWeek(new DateTime(year, month, 1));
int daysOfLastMonth =
month == 1 ?
31 : GetEveryMonthDaysOfYear(year)[month - 2];
int daysOfCurrentMonth = GetEveryMonthDaysOfYear(year)[month - 1];

int dayOfMonth = 1;
int dayOfNextMonth = 1;
for (int row = 0; row < 6; row++)
{
for (int column = 0; column < 7; column++)
{
if (row == 0 && column < day1OfColumn)
{ calendar[row, column] = daysOfLastMonth + 1 - day1OfColumn + column; }
else if (dayOfMonth <= daysOfCurrentMonth)
{ calendar[row, column] = dayOfMonth++; }
else
{ calendar[row, column] = dayOfNextMonth++; }
}
}
return calendar;
}
}
单元测试如下:
[TestMethod()]
public void DateTimeCalculate_Test()
{
DateTime dt = new DateTime(2004, 3, 1);
Assert.AreEqual(DateTimeCalculate_Accessor.GetDayOfYear(dt), 61);

DateTime dt2 = new DateTime(2013, 11, 28);
Assert.AreEqual(DateTimeCalculate_Accessor.GetDayOfYear(dt2), 332);

Assert.AreEqual(DateTimeCalculate_Accessor.GetDateTimeDifference(dt2, dt), -3559);

/// 2019.4.25  星期四
DateTime dt3 = new DateTime(2019, 4, 25);
Assert.AreEqual(DateTimeCalculate_Accessor.GetDayOfWeek(dt3), 4);

int[,] days = DateTimeCalculate_Accessor.GetMonthCalendar(2013, 11);
for (int i = 0; i < 6; i++)
{
string rowString = string.Empty;
for (int j = 0; j < 7; j++)
{
rowString += days[i, j] + (j < 6 ? "\t" : "\r\n");
}
System.Diagnostics.Debug.Write(rowString);
}
}

这些代码如此短小除了注释和括号,真正的代码行数屈指可数。希望大家能够喜欢。