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


    在.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  /// 
      2  /// 表示年/月的时间类型
      3  /// 
      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
     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时间类型已经满足小编的开发需要,当然也可以根据需求继续扩展其它功能。

  • 相关阅读:
    千字文||无聊又数了一下千字文字数
    精彩回顾|云原生 Meetup 广州站
    Python的异常处理机制 ​
    最详细的Xshell连接Linux
    前端 :用HTML ,JS写一个 双色球彩票中将机制,因为时间不够,加上本人懒没有用CSS美化界面,多包涵
    k8s最小镜像
    4.如何终止线程
    通过xhr实现文件上传功能,使用jQuery实现文件上传功能
    docker部署redis集群 删除节点(缩容)
    鸿蒙ArkTS声明式开发:跨平台支持列表【Z序控制】 通用属性
  • 原文地址:https://www.cnblogs.com/fengjq/p/17578772.html