看懂AI寫C#的日期時間,白話說常用實戰範例,看這一篇就夠!!

陳國仁
18 min readDec 19, 2021

每次看到微軟官網說明C#的時間日期DateTime,看到頭會暈@@之外,還找不到說人話的內容,乾脆自己加上實戰使用經驗,整理一篇常用範例+說明完整一點的版本,自己看得舒服,也方便日後日期時間問題快速查詢。

DateTime

一、定義

官網得知:
1.表示時間的瞬間,通常以一天的日期和時間表示。
2.不是一個類別(Class),而是一個結構(Struct)
可以把結構想成類別(Class)的一種,
只是Class類別能支援繼承和多型,結構不能,結構主要用途是儲存資料

二、宣告

常見以下二種宣告:

1.使用new,含指定日期,或日期時間(年,月,時,分,秒,毫秒)的宣告

DateTime dt = new DateTime(2000, 1, 01);DateTime dt = new DateTime(2000, 1, 01, 12, 00, 00);

2.不使用new

DateTime now = DateTime.Now; //傳回電腦目前的日期與時間DateTime today = DateTime.Today; //傳回電腦目前的日期,時間預設是
凌晨00:00:00 (=上午 12:00:00)

三、欄位

MaxValue :readonly欄位,DateTime的最大值,值等於西元9999年12月31日23:59:59.999

MinValue:readonly欄位,DateTime的最小值,值等於西元0001年1月1日00:00:00.000

四、屬性

Date:DateTime是日期+時間,Date就是取DateTime的日期部分,並將時間值設定為午夜 12:00:00 (00:00:00)(取 DateTime的時間部分是TimeOfDay),
Date屬性常用在判斷只能輸入日期,不能輸入時間的情境:

if (inputDate > normalDate.Date) 
{
Console.WriteLine(“可能有輸入了時間!!請不要輸入時間,輸入日期就好”);
}

假如只想顯示日期,不包含時間,可以用以下二種方式:
a.使用ToShortDateString()方法

DateTime dt=DateTime.Now;
Console.WriteLine(dt.ToShortDateString());

b.使用.ToSting(字串格式)方法

DateTime dt=DateTime.Now;
Console.WriteLine(dt.ToString(“yyyy/MM/dd”));

Day:取得該月的第幾天(DayOfMonth),以 1 ~31 的值表示。
DayOfWeek:取得該周內的星期幾(Enum列舉型別),以Sunday,Monday~Saturday表示, 轉成整數以0~6表示。
DayOfYear:取得該年中的第幾天,以 1 ~366的值表示(366為閏年)。
Hour:取得小時的部分。

Kind:指出這個執行個體表示的時間是根據本地時間、國際標準時間 (UTC),或兩者皆非,以"Local","Utc","Unspecified"的字串表示。

Millisecond:取得毫秒的部分,以 0 ~ 999 的值表示。
Minute:取得分鐘的部分。
Month:取得月的部分。
Now:取得電腦現在的日期與時間。
Second:取得秒的部分。

Ticks:取得日期和時間的刻度數,單位是以100奈米秒為一個間隔刻度,值介於 DateTime.MinValue.Ticks 和 DateTime.MaxValue.Ticks 之間,這邊解釋一下秒以下的單位有:秒、毫秒、微秒、奈米秒(奈秒,納秒),彼此是1000倍關係,所以得知,
1毫秒=1百萬 奈米秒=1000000/100 Ticks =10000 Ticks = 1萬Ticks
1秒=1000 *1萬Ticks=1千萬Ticks

TimeOfDay:TimeOfDay就是取DateTime的時間部分(取DateTime的日期部分是Date)。

Today:取得電腦今天日期,但有包含時間,時間設定是00:00:00,但顯示時會類似 "2000/12/31 上午 12:00:00"、"12/31/2000 12:00:00 AM"表示。

UtcNow:取得電腦上目前的日期和時間,以國際標準時間 (UTC) 表示。

Year:取得年的部分。

五、方法

了解DateTime方法前,這邊注意3個重點,都跟時間間隔TimeSpan有關:

1.DateTime的方法,主要包含a.運算、b.比較、c.與字串互轉,運算會用到另一個時間結構-TimeSpan,TimeSpan表示時間間隔,用天數、時數、分鐘數、秒數和毫秒數表示成(天數 . 小時:分鐘:秒數 . 毫秒),例如相差200天16小時34分鐘7.18秒,TimeSpan輸出字串表示成:

"200.16:34:7.18"
. . 毫秒

2.TimeSpan會用最大時間單位是,就是以天為單位來表示一致性,原因是,月跟年的週期天數會不同,月有二月大小月,年有閏年,表示遇到計算下個月的今天其實會不同日期,例如1/31、2/28、2/29、5/31,
1/31的下個月的今天不會是2/31,2/28的下個月的今天不會是3/31,
5/31的下個月的今天不會是6/31,8/31的下個月的今天不會是9/31,
同理,四年一次的閏年2024/02/29的隔年的今天,不會是2025/02/29,
原本自認很厲害日期用手算,但是不小心就會算錯,一定要讓程式幫忙加1個月或加1年的日期才準確。

例如:5/31加一個月,用AddMonths(1),正確是6/30

DateTime today = new DateTime(2021, 5, 31);
DateTime todayAddMonths= today.AddMonths(1);
//答案是 6/30/2021 12:00:00 AM

例如:四年一次閏年2024/2/29加一年,用AddYears(1),正確是2025/2/28

DateTime today = new DateTime(2024, 2, 29);
DateTime todayAddYears= today.AddYears(1);
//答案是 2/28/2025 12:00:00 AM

3.TimeSpan值的精準度
假如有二天做相減(2021/11/22 上午1點、2021/11/23 上午2點30分)

DateTime dt1 = new DateTime(2021, 11, 22, 1, 0, 0); 
DateTime dt2 = new DateTime(2021, 11, 23, 2, 30, 0);
TimeSpan duration = dt2-dt1; //duration=1.01:30:00

上面程式碼看到dt1與dt2的相減結果=duration=1天 . 01小時30分00秒
假如我們想取天數,直覺用duration.Days,發現得到一個整數=1天,
想得到包含完整天數+時間的1.多天該怎辦???
就改用double型別的duration.TotalDays,就會得到1.0625天。

而TotalDays背後計算原理,就是把前面提到的時間最小單位Ticks轉成天數,舉例:
1天=24小時=24*60分鐘=24*60*60秒=86400秒=864000000000 Ticks
duration=1天 . 01小時30分00秒 轉成Ticks =918000000000 Ticks
918000000000 / 864000000000 = 1.0625 = duration.TotalDays

以下列出常用方法
Add():加上以TimeSpan型別的天數或時間。

DateTime today = DateTime.Today;
TimeSpan duration = new TimeSpan(1, 0, 0, 0); //增加1天
DateTime answer = today.Add(duration);
Console.WriteLine(today); //2021/12/14 上午12:00:00
Console.WriteLine(answer); //2021/12/15 上午12:00:00

AddDays():加上以Double型別的天數。
AddHours():加上以Double型別的小時數。
AddMilliseconds():加上以Double型別的毫秒數。
AddMinutes():加上以Double型別的分鐘數。
AddMonths():加上以Int32整數型別的月數。
AddSeconds():加上以Double型別的秒數。
AddTicks():加上以Int64,就是Long長整數型別的Tick數。
AddYears():加上以Int32整數型別的年數。

Compare(t1,t2)比較t1,t2二者皆是DateTime型別的時間早晚關係,比較後回傳大於零、等於零、小於零的整數。

  條件      回傳
t1早於t2 小於0帶負號的整數
t1等於t2 等於0
t1晚於t2 大於0帶正號的整數

CompareTo(t)與DateTime型別的t時間比較早晚關係,比較後跟Compare(t1,t2)一樣,回傳大於零、等於零、小於零的整數。

DaysInMonth(year,month):回傳指定某年(整數)某個月(整數)的當月總天數(整數)。

Equals(t1,t2)比較t1,t2二者皆是DateTime型別的時間是否相同,回傳Boolean型態的true,false。

IsLeapYear(year)回傳指定年份(整數)是否為閏年,回傳Boolean型態的true,false。

Parse(string):把字串轉換成DateTime型別的日期與時間預設是傳入電腦本機的日期時間設定格式,
C#內建能轉成DateTime型別格式的字串格式,有以下幾種:

12/17/2021 08:11:23 => 2021/12/17 上午 08:11:23
12/17/2021 => 2021/12/17 上午 12:00:00
12/2021 => 2021/12/1 上午 12:00:00
12/17 => 2021/12/17 上午 12:00:00
08:11:23 => 2021/12/17 上午 08:11:23
08 PM => 2021/12/17 下午 08:00:00
2021–12–17T08:11:23 => 2021/12/17 上午 08:11:23
17 Dec. 2021 08:11:23 GMT => 2021/12/17 下午 04:11:23

Parse(string)一般會搭配.ToSting(string format)做字串輸出的格式設定,實作來說,.ToSting(string format)可取代string.Format()大部分寫法,例如:

DateTime.Parse(“2021–09–30”).ToString(“yyyy/MM/dd”)
=>2021/09/30
DateTime.Parse(“2021–09–30”).ToString(“yyyy-MM-dd”)
=>2021-09-30
DateTime.Parse(“2021–09–30”).ToString(“yyyy.MM.dd”)
=>2021.09.30
DateTime.Parse(“2021–09–30”).ToString(“dd/MM/yyyy”)
=>30/09/2021
DateTime.Parse(“2021–09–30”).ToString(“dd-MM-yyyy”)
=>30-09-2021
DateTime.Parse(“2021–09–30”).ToString(“yyyy年MM月dd日dddd”)
=>2021年09月30日星期四,
=>四個字dddd的英文是Thursday,中文是星期四,,三個字ddd是簡寫的Thu,中文不變。
DateTime.Parse(“2021–09–30”).ToString(“yyyy年MMMM月dd日dddd”)
=>2021年九月30日星期四
=>四個字MMMM的英文是September,中文是九月,三個字MMM是簡寫的Sep,中文不變。
DateTime.Parse("2021-09-30").ToString("yyyy年MMMM月dd日hh時mm分ss秒")
=>2021年九月30日12時00分00秒,小寫hh是12小時制,大寫HH是24小時制
註:
1.用二個字的寫法(MM、dd、hh...)會補零,排版比較整齊。
2.C#日期時間格式中,只有提供中文的幾月與星期幾,沒有中文的幾日。

不過,Parse()只適用於規則單純的情境,實際上允許使用者手動輸入日期時間時,常有意外的日期時間字串格式傳入,甚至也常傳入null,導致頻繁發生例外狀況並造成效能衝擊!!!用Parse()久了會讓你有懷疑人生的感覺......

所以實務上建議不要用Parse()!!!

實務上會常用TryParse(),更特別的格式處理會用ParseExact(),再搭配Regex正則運算式,才比較能完整寫好日期時間的轉換程式。
要了解更詳細的用DateTime.TryParse()取代Parse()與Convert.DateTime()原因,可以參考這篇

TryParse(String, out DateTime):把字串轉換成DateTime型別的日期與時間,並傳回一個Boolean值表示轉換是否成功。

使用TryParse()有幾個重點要注意:
1.TryParse()正常回傳一個True或False的Boolean值之外,參數內有設計加入"out"關鍵字,表示日期時間字串可以經由TryParse()轉換輸出後,取得一個DateTime參數型別的值。

2.因為TryParse()會更新此DateTime參數型別的值,所以DateTime此參數的宣告,用未初始化的狀態來傳遞即可,也就是用:

DateTime dt; 

這樣宣告就好。

3.日期時間字串用TryParse()如果傳回true轉換成功,字串會轉成對應的DateTime值,如果字串不符合日期時間格式、或=null、或為空字串,會傳回false而轉換失敗,此時 DateTime值=MinValue=西元0001年1月1日00:00:00.000,舉例如下:

DateTime dt ;
DateTime.TryParse(“123456”, out dt);
Console.WriteLine(dt); //輸出 0001/1/1 上午 12:00:00

ParseExact(InputString, StringFormat, IFormatProvider, DateTimeStyles)
指定一個特殊日期時間字串格式後,把輸入的特殊日期時間字串,轉成正常的DateTime型別的日期時間格式。
可以參考本篇文末的程式範例。

ParseExact()厲害的地方就是傳入特別自訂的日期時間格式,都能解析並轉成系統DateTime格式,不過自由度高,表示設定不會簡單,剛入門可能還用不到,但是最終還是要會使用,以下列出網路上大大們寫的文章做參考:
https://blog.darkthread.net/blog/tips-net-datetime-formating
http://www.webapp.com.tw/EBook5/view.aspx?TreeNodeID=149&id=222
https://blog.darkthread.net/blog/flex-datetime-string-parsing/
https://ithelp.ithome.com.tw/articles/10133617
https://dotblogs.com.tw/chhuang/2008/03/18/1921

Subtract(t):減去 t 時間日期(DateTime型別) 得到二日期的天數差,或減去 t 時間間隔(TimeSpan型別)得到幾日後的日期。

DateTime dt1 = new DateTime(2021, 9, 15, 0, 0, 0); //2021年9月15日
DateTime dt2 = new DateTime(2021, 9, 14, 0, 0, 0); //2021年9月14日
DateTime dt3 = new DateTime(2021, 8, 13, 0, 0, 0); //2021年8月13日
TimeSpan diff1 = dt2.Subtract(dt1); //diff1=-1.00:00:00,就是-1天
DateTime dt4 = dt3.Subtract(diff1); //dt4=2021/8/14 上午 12:00:00
//8月13日減(-1)天=加1天=8月14日

ToLongDateString():將DateTime轉換成完整日期的字串。
ToLongTimeString():將DateTime轉換成完整時間的字串。
ToShortDateString():將DateTime轉換成短日期的字串。
ToShortTimeString():將DateTime轉換成短時間的字串。

可以在官網看相關程式範例

六、其他應用

上面都了解後,額外一些應用就不難,以下列出常見的應用,不過這邊不寫太多程式碼,主要列出思考邏輯:

1.每週、月、季、年的第一天。
每週第一天可以用DayOfWeek取整數=0做判斷
每月第一天可以用Day=1做判斷
每季第一天可以用Month=1、4、7、10加上Day=1做判斷,或者用當月月份,來推算出第幾季:

DateTime.Now.Month-(DateTime.Now.Month-1)%3
//假如當月是6月,計算 6-(6-1)%3=6-(5%3)=6-2=4月,4月等於第二季

每年第一天可以用DayOfYear=1做判斷。

2.每週、月、季、年的最後一天
每週最後一天可以用DayOfWeek取整數=6做判斷

每月最後一天可以用每月第一天加一個月AddMonths(1),再減一天
AddDays(-1)做判斷

每季最後一天可以用Month=3、6、9、12加上上面[每月最後一天]做判
斷,或者如上方,用當月月份,來推算出第幾季後再加以計算。

每年最後一天可以用每年第一天加一年AddYears(1),再減一天
AddDays(-1)
做判斷。

3.計算程式執行時間
除了常見用Stopwatch()之外,也可以很直覺的用[結束時間 - 開始時間]取得程式執行時間,範例程式如下:

DateTime startTime = DateTime.Now;long total = 0;
for (int i = 0; i < 100000000; i++)
{
total += 1;
}
DateTime endTime = DateTime.Now;
TimeSpan duration = endTime.Subtract(startTime);
Console.WriteLine(duration.TotalMilliseconds);

4.民國年轉西元年

主要用2個東西:
a.把字串轉日期,用DateTime.ParseExact()方法,
b.把自訂的民國日期格式轉成DateTime後,記住不要直接對整個民國的DateTime作.AddYears(1911),會導致因為閏年而日期跑掉,而是只針對"年"做+1911的動作,
下面程式就是把民國"880627"轉成西元"1999/06/27"

string rocDate = “880627”;//將民國的日期字串寫成8個字元,不到8個字元的話左邊補"0"
//也就是 eightCharROCDate = "00880627"
string eightCharROCDate = rocDate.PadLeft(8,’0');
//用.ParseExact()方法,將自訂的yyyyMMdd轉成DateTime格式的yyyy/MM/dd
DateTime rocDateTime = DateTime.ParseExact(eightCharROCDate, “yyyyMMdd”, CultureInfo.InvariantCulture, DateTimeStyles.None);
//分別取DateTime的年份+1911,再用"+"號將"年"字串與"月日"字串合併起來
string adString = rocDateTime.AddYears(1911).Year.ToString() + rocDateTime.ToString(“/MM/dd”);
Console.WriteLine(adString);

以上!!

看完文章覺得對您有幫助⋯

歡迎鼓掌 👏 與Follow,各位大大的鼓勵,會給我很大的創作動力,十分感謝!!

--

--