處理日期問題
在《編程珠璣 第二版》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