在.Net Framework中,我們常用的時間類型是DateTime。直到.Net6微軟加入了兩個新的時間類型:DateOnly和TimeOnly,才彌補了之前的不足。 DateOnly:表示僅日期。比如:某人的生日,我只關心日期,就適合用DateOnly。 TimeOnly:表示僅時間。比如:每 ...
在.Net Framework中,我們常用的時間類型是DateTime。直到.Net6微軟加入了兩個新的時間類型:DateOnly和TimeOnly,才彌補了之前的不足。
DateOnly:表示僅日期。比如:某人的生日,我只關心日期,就適合用DateOnly。
TimeOnly:表示僅時間。比如:每天定時執行某個任務,我只關心時間,就適合用TimeOnly。
由此可見,DateOnly和TimeOnly都有相應的應用場景。可小編在實際項目中遇到了這樣的業務場景:需要每月給客戶生成月賬單。這裡我所關心的是某個月份,於是我首先想到用DateOnly表示(不考慮字元串)。
var date = new DateOnly(2023, 2, 1); // 代表2023年2月1日
雖然DateOnly可用,但從字面理解和表現形式上還是略顯尷尬。 DateOnly真正表達的是某一天並不是某個月, 在代碼層面也容易混淆,所以並不符合小編的心理期望。經過一番糾結和思考,小編決定自己動手創建一個表示年/月的時間類型:YearMonth。
var ym = new YearMonth(2023, 2); // 代表2023年2月
YearMonth的源碼如下:
1 /// <summary> 2 /// 表示年/月的時間類型 3 /// </summary> 4 [JsonConverter(typeof(YearMonthJsonConverter))] 5 public readonly struct YearMonth 6 { 7 public int Year { get; } 8 9 public int Month { get; } 10 11 public YearMonth(int year, int month) 12 { 13 Year = year; 14 Month = month; 15 } 16 17 public YearMonth AddMonths(int value) 18 { 19 var date = new DateOnly(Year, Month, 1); 20 return FromDateOnly(date.AddMonths(value)); 21 } 22 24 public YearMonth AddYears(int value) 25 { 26 var date = new DateOnly(Year, Month, 1); 27 return FromDateOnly(date.AddYears(value)); 28 } 29 30 public DateOnly FirstDay() 31 { 32 return new DateOnly(Year, Month, 1); 33 } 34 35 public DateOnly LastDay() 36 { 37 var nextMonth = AddMonths(1); 38 var date = new DateOnly(nextMonth.Year, nextMonth.Month, 1); 39 return date.AddDays(-1); 40 } 41 42 public int DaysInMonth() 43 { 44 return DateTime.DaysInMonth(Year, Month); 45 } 46 47 public static YearMonth Current 48 { 49 get { return FromDateTime(DateTime.Now); } 50 } 51 52 public static YearMonth UtcCurrent 53 { 54 get { return FromDateTime(DateTime.UtcNow); } 55 } 56 57 public static YearMonth FromDateOnly(DateOnly dateOnly) 58 { 59 return new YearMonth(dateOnly.Year, dateOnly.Month); 60 } 61 62 public static YearMonth FromDateTime(DateTime dateTime) 63 { 64 return new YearMonth(dateTime.Year, dateTime.Month); 65 } 66 67 public static YearMonth FromString(string s) 68 { 69 if (DateTime.TryParse(s, out var date)) 70 { 71 return FromDateTime(date); 72 } 73 throw new ArgumentException("format is error", nameof(s)); 74 } 75 76 public override string ToString() 77 { 78 return $"{Year.ToString().PadLeft(4, '0')}-{Month.ToString().PadLeft(2, '0')}"; 79 } 80 81 public static bool operator ==(YearMonth left, YearMonth right) 82 { 83 return left.Year == right.Year && left.Month == right.Month; 84 } 85 86 public static bool operator !=(YearMonth left, YearMonth right) 87 { 88 return !(left.Year == right.Year && left.Month == right.Month); 89 } 90 91 public static bool operator >=(YearMonth left, YearMonth right) 92 { 93 return (left.Year > right.Year) || (left.Year == right.Year && left.Month >= right.Month); 94 } 95 96 public static bool operator <=(YearMonth left, YearMonth right) 97 { 98 return (left.Year < right.Year) || (left.Year == right.Year && left.Month <= right.Month); 99 } 100 101 public static bool operator >(YearMonth left, YearMonth right) 102 { 103 return (left.Year > right.Year) || (left.Year == right.Year && left.Month > right.Month); 104 } 105 106 public static bool operator <(YearMonth left, YearMonth right) 107 { 108 return (left.Year < right.Year) || (left.Year == right.Year && left.Month < right.Month); 109 } 110 111 public override bool Equals(object obj) 112 { 113 return base.Equals(obj); 114 } 115 116 public override int GetHashCode() 117 { 118 return base.GetHashCode(); 119 } 120 }
其中特性 [JsonConverter(typeof(YearMonthJsonConverter))]用於Json序列化和反序列化。
1 public class YearMonthJsonConverter : JsonConverter<YearMonth> 2 { 3 public override YearMonth Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 4 { 5 return YearMonth.FromString(reader.GetString()); 6 } 7 8 public override void Write(Utf8JsonWriter writer, YearMonth value, JsonSerializerOptions options) 9 { 10 writer.WriteStringValue(value.ToString()); 11 } 12 }
YearMonth的一些用法示例:
1 var ym = new YearMonth(2023, 2); 2 int n = ym.DaysInMonth(); //n:28 3 DateOnly d1 = ym.FirstDay(); //d1:2023/2/1 4 DateOnly d2 = ym.LastDay(); //d2:2023/2/28 5 string str = ym.ToString(); //str:2023-02 6 YearMonth ym2 = ym.AddMonths(1); //ym2: 2023-03 7 YearMonth ym3 = YearMonth.FromDateOnly(new DateOnly(2023, 2, 8)); //ym3: 2023-02 8 YearMonth ym4 = YearMonth.FromDateTime(new DateTime(2023, 2, 8, 12, 23, 45)); //ym4: 2023-02 9 bool b = new YearMonth(2023, 3) > new YearMonth(2023, 2); //b: true
至此,上面的YearMonth時間類型已經滿足小編的開發需要,當然也可以根據需求繼續擴展其它功能。
本文已同步至作者的微信公眾號:玩轉DotNet
感謝點贊並關註