在.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的更多相关文章

  1. Java 之 JDK 1.8 新增日期时间类型

    一.原来的日期时间 Java1.0中包含了一个Date类,但是它的大多数方法已经在Java 1.1引入Calendar类之后被弃用了.而Calendar并不比Date好多少.它们面临的问题是: ① 可 ...

  2. MySQL 日期时间类型怎么选?千万不要乱用!

    构建数据库写程序避免不了使用日期和时间,对于数据库来说,有多种日期时间字段可供选择,如 timestamp 和 datetime 以及使用 int 来存储 unix timestamp. 不仅新手,包 ...

  3. JDK8新增时间类型用在JPA中的问题

    之前数据库存储日期时间类型时一般POJO实体对应属性为java.util.Date,然后通过JPA注解指定它是日期格式或是日期时间格式,JDK8中新增了更好的时间API,如表示本地日期的LocalDa ...

  4. 制作一个SSRS的ORACLE数据库报表,使用了时间类型的参数。

    需求:我们这个报表是以月为单位,呈现的数据为查询为当前月的第一天到最后一天.条件类似于:time_day > 20140601 and  time_day < 20140630 因为是让用 ...

  5. EF6中一个关于时间类型 datetime2 的坑

    在一个访问下位机的程序中,返回的时间戳有时候因断线产生0001年01月01日的时间,而原先使用拼接SQL进行数据存储的操作时,这个问题是可以跳过的. 这次把拼接SQL的部分重新改为EF进行管理,这个坑 ...

  6. MySQL学习分享-->日期时间类型

    日期时间类型 ①如果要用来表示年月日时分秒,一般使用datetime类型: ②如果要用来表示年月日,一般使用date类型: ③如果要表示时分秒,一般使用time类型: ④如果只是表示年份,一般使用ye ...

  7. MySQL日期和时间类型笔记

    最近在看<MySQL技术内幕:SQL编程>并做了笔记,这是一篇笔记类型博客,分享出来方便自己复习,也可以帮助其他人 一.日期时间类型所占空间对比 各种日期时间数据类型所占的空间: 类型 所 ...

  8. Entity Framework 6 Recipes 2nd Edition(10-4)译 -> 从存储过程返回一个复杂类型

    10-4. 从存储过程返回一个复杂类型 问题 想在方法中使用一个返回复杂类型的存储过程 解决方案 假设我们已经有如Figure 10-3.所示的模型,该Employee (雇员)模型包含Employe ...

  9. 关于mysql字段时间类型timestamp默认值为当前时间问题

    今天把应用部署到AWS上发现后台修改内容提交后程序报错,经过排查发现是更新数据的时候,有张数据表中的一个timestamp类型的字段默认值变成了"0000-00-00 00:00:00.00 ...

  10. ASP.MVC时间类型json数据处理

    服务端返回DateTime属性如果用自带的json方法返回的数据如下: 有2种办法解决一种是采用服务端解决方案,一种是使用前端解决方案 1.前端解决方案 第一步:对Date进行扩展 // 对Date的 ...

随机推荐

  1. linux发行版中的i386/i686/x86-64/的区别

    在yum上找32位的i386找不到,看到i686以为是64位呢,原来它也是32位啊 i686 只是i386的一个子集,支持的cpu从Pentium 2 (686)开始,之前的型号不支持. 备注: 1. ...

  2. java镜子之反射篇

    文章目录 注解 内置注解 元注解 反射 类的初始化 类加载器 双亲委派机制 反射方法的使用 调用类的方法.成员变量.构造器等 总结 注解和反射是Java中非常重要的知识,一些优秀开源的框架都是大量运用 ...

  3. 极速进化,光速转录,C++版本人工智能实时语音转文字(字幕/语音识别)Whisper.cpp实践

    业界良心OpenAI开源的Whisper模型是开源语音转文字领域的执牛耳者,白璧微瑕之处在于无法通过苹果M芯片优化转录效率,Whisper.cpp 则是 Whisper 模型的 C/C++ 移植版本, ...

  4. Apache hudi 核心功能点分析

    Hudi 文中部分代码对应 0.14.0 版本 发展背景 初始的需求是Uber公司会有很多记录级别的更新场景,Hudi 在Uber 内部主要的一个场景,就是乘客打车下单和司机接单的匹配,乘客和司机分别 ...

  5. 【H5】Emmet 指令 HTML

    Emmet操作指南 HTML篇 生成带有内容的标签 标签名{内容}可以生成带有内容的标签 div{abc} <div>abc</div> 生成带有属性的标签 生成带有class ...

  6. JavaWeb之day02css与js

    目录: 1.CSS概述和与HTML的结合方式(四种)(*******) 2.CSS的基本选择器(******) 3.CSS的扩展选择器(了解) 4.CSS的盒子模型(了解) 5.CSS的布局(浮动)( ...

  7. 2023-04-19:给定一个非负数组arr 任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 然后新的arr继续,任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 一直到ar

    2023-04-19:给定一个非负数组arr 任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 然后新的arr继续,任何两个数差值的绝对值,如果arr中没有,都要加入到arr里 一直到ar ...

  8. 2021-06-20:已知一个消息流会不断地吐出整数 1~N,但不一定按照顺序依次吐出。如果上次打印的序号为i, 那么当i+1出现时,请打印 i+1 及其之后接收过的并且连续的所有数,直到1~N全部接

    2021-06-20:已知一个消息流会不断地吐出整数 1~N,但不一定按照顺序依次吐出.如果上次打印的序号为i, 那么当i+1出现时,请打印 i+1 及其之后接收过的并且连续的所有数,直到1~N全部接 ...

  9. Django4全栈进阶之路20 项目实战(在线报修):项目需求分析

    为了实现一个在线报修系统,您可以按照以下步骤进行: 创建Django项目和应用 使用Django的命令行工具创建一个Django项目,并在该项目中创建一个名为"RepairApp" ...

  10. Django4全栈进阶之路14 项目实战(用户管理):base.html基础模板设计

    在 Django 中,我们可以使用模板继承来避免代码的重复.模板继承是指我们可以在一个模板中定义一些公共的 HTML 代码,然后在其他模板中继承这个基础模板,并根据需要添加或覆盖一些内容. 通常情况下 ...