處理日期問題

             處理日期問題

在《編程珠璣 第二版》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

相關文章
相關標籤/搜索