处理日期问题
在《编程珠玑 第二版》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年的某一天。
这个问题自己不难,这里主要说的是对数组的应用,以及由它展开的一些技巧,并且第一、3题目很是实用。
源代码以下:
/// <summary>
/// 时间计算
/// 《编程珠玑 第二版》P41编程
/// 编写处理下列日期问题的函数:
/// 给定两个日子,计算这两个日子间的天数;
/// 给定某个日子,返回它在一周中属于第几天;
/// 给定某年某月,以字符数组的形式产生该月的日历
///
/// 平闰年解释
/// 公历闰年的精确计算方法(按一回归年365天5小时48分45.5秒)
/// ①普通年能被4整除且不能被100整除的为闰年。(如2004年就是闰年,1901年不是闰年)
/// ②世纪年能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)
/// ③对于数值很大的年份,这年若是能整除3200,而且能整除172800则是闰年。
/// 如172800年是闰年,86400年不是闰年(由于虽然能整除3200,但不能整除172800)
/// (此按一回归年365天5h48'45.5''计算)。
/// </summary>
public class DateTimeCalculate
{
private DateTimeCalculate() { }
/// <summary>
/// 是不是瑞年
/// </summary>
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;
}
/// <summary>
/// 得到一年中每一个月的天数(这是精华用数组简化if...else...语句)
/// </summary>
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 };
}
/// <summary>
/// 获取输入时间是这年中的第几天
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 给定两个日子,计算这两个日子间的天数
/// </summary>
/// <param name="time1">某日1</param>
/// <param name="time2">某日2</param>
/// <returns>若是是负数,则说明某日2小于某日1</returns>
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);
}
/// <summary>
/// 给定某个日子,返回它在一周中属于第几天;
/// </summary>
/// <returns>
/// 1 Monday
/// 2 Tuesday
/// 3 Wednesday
/// 4 Thursday
/// 5 Friday
/// 6 Saturday
/// 7 Sunday
/// </returns>
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);
}
/// <summary>
/// 给定某年某月,以字符数组的形式产生该月的日历
/// Sunday Monday Tuesday Wednesday Thursday Friday Saturday
/// </summary>
/// <param name="time"></param>
/// <returns>是一个二维数组,(7列,4或5或6行)</returns>
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);
}
}数组
这些代码如此短小除了注释和括号,真正的代码行数屈指可数。但愿你们可以喜欢。ide