给C#新增一个时间类型: YearMonth

  • 给C#新增一个时间类型: YearMonth已关闭评论
  • 106 次浏览
  • A+
所属分类:.NET技术
摘要

DateOnly:表示仅日期。比如:某人的生日,我只关心日期,就适合用DateOnly。TimeOnly:表示仅时间。比如:每天定时执行某个任务,我只关心时间,就适合用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

感谢点赞并关注

给C#新增一个时间类型: YearMonth