一个代表年月的类YearMonth
测试(VS自带框架):
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; /// <summary> ///This is a test class for YearMonthTest and is intended ///to contain all YearMonthTest Unit Tests ///</summary> [TestClass()] public class YearMonthTest { /// <summary> ///A test for YearMonth Constructor ///</summary> [TestMethod()] public void YearMonthConstructorTest() { var date = new DateTime(2000, 1, 1); var target = new YearMonth(date); Assert.AreEqual(2000, target.Year); Assert.AreEqual(1, target.Month); } /// <summary> ///A test for YearMonth Constructor ///</summary> [TestMethod()] public void YearMonthConstructorTest1() { YearMonth target = new YearMonth(2010, 4); Assert.AreEqual(2010, target.Year); Assert.AreEqual(4, target.Month); } /// <summary> ///A test for GetDate ///</summary> [TestMethod()] public void GetDateTest() { var target = new YearMonth(2010, 5); var expected = new DateTime(2010, 5, 3); var actual = target.GetDate(3); Assert.AreEqual(expected, actual); } [TestMethod] public void GetDateThrows() { Assert.IsTrue(Throws( typeof (ArgumentOutOfRangeException), () => new YearMonth(2012, 12).GetDate(32))); } /// <summary> ///A test for ToString ///</summary> [TestMethod()] public void ToStringTest() { var target = new YearMonth(2008, 3); Assert.AreEqual( "200803" , target.ToString()); } /// <summary> ///A test for IsValid ///</summary> [TestMethod()] public void IsValidTest() { Assert.IsTrue( new YearMonth(2008, 12).IsValid); Assert.IsFalse( new YearMonth(2010, 13).IsValid); Assert.IsFalse( new YearMonth(2010, 0).IsValid); Assert.IsFalse( new YearMonth(2010, -3).IsValid); Assert.IsFalse( new YearMonth(0, 3).IsValid); Assert.IsFalse( new YearMonth(-2, 3).IsValid); Assert.IsTrue( new YearMonth(9999, 12).IsValid); Assert.IsFalse( new YearMonth(10000, 1).IsValid); Assert.IsFalse( new YearMonth(-2, -3).IsValid); } /// <summary> ///A test for Equals ///</summary> [TestMethod()] public void EqualsTest() { var target = new YearMonth(2010, 4); Assert.IsTrue(target.Equals( new YearMonth(2010, 4))); Assert.IsFalse(target.Equals( new YearMonth(2010, 3))); Assert.IsFalse(target.Equals( string .Empty)); } /// <summary> ///A test for TryParse ///</summary> [TestMethod()] public void TryParseTest() { YearMonth result; var success = YearMonth.TryParse( "201012" , out result); Assert.IsTrue(success); Assert.AreEqual( new YearMonth(2010, 12), result); success = YearMonth.TryParse( "" , out result); Assert.IsFalse(success); Assert.AreEqual(result, null ); } /// <summary> ///A test for Parse ///</summary> [TestMethod()] public void ParseTest() { Assert.AreEqual( new YearMonth(2008, 12), YearMonth.Parse( "200812" )); } [TestMethod] public void ParseThrows() { Assert.IsTrue(Throws( typeof (ArgumentNullException), () => YearMonth.Parse( null ))); Assert.IsTrue(Throws( typeof (ArgumentException), () => YearMonth.Parse( "" ))); Assert.IsTrue(Throws( typeof (ArgumentException), () => YearMonth.Parse( "aaaaaa" ))); Assert.IsTrue(Throws( typeof (ArgumentException), () => YearMonth.Parse( "20121221" ))); } /// <summary> ///A test for AddYears ///</summary> [TestMethod()] public void AddYearsTest() { Assert.AreEqual( new YearMonth(2011, 11), new YearMonth(2008, 11).AddYears(3)); Assert.AreEqual( new YearMonth(2011, 11), new YearMonth(2011, 11).AddYears(0)); Assert.AreEqual( new YearMonth(2011, 11), new YearMonth(2012, 11).AddYears(-1)); } /// <summary> ///A test for AddMonths ///</summary> [TestMethod()] public void AddMonthsTest() { Assert.AreEqual( new YearMonth(2010, 4), new YearMonth(2010, 3).AddMonths(1)); Assert.AreEqual( new YearMonth(2010, 4), new YearMonth(2010, 4).AddMonths(0)); Assert.AreEqual( new YearMonth(2010, 4), new YearMonth(2010, 8).AddMonths(-4)); Assert.AreEqual( new YearMonth(2010, 1), new YearMonth(2009, 11).AddMonths(2)); Assert.AreEqual( new YearMonth(2009, 11), new YearMonth(2010, 1).AddMonths(-2)); Assert.AreEqual( new YearMonth(2009, 12), new YearMonth(2010, 1).AddMonths(-1)); Assert.AreEqual( new YearMonth(2010, 1), new YearMonth(2009, 12).AddMonths(1)); } static bool Throws(Type exceptionType, Action action) { try { action(); } catch (Exception ex) { if (ex.GetType() == exceptionType || ex.GetType().IsSubclassOf(exceptionType)) return true ; throw ; } return false ; } } |
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | using System; using System.Linq; /// <summary> /// 代表一个年份中的某个指定月份. 如:2008年10月. /// </summary> public sealed class YearMonth { private readonly int _year; private readonly int _month; /// <summary> /// 用指定的年份和月份构造一个 YearMonth 对象. /// </summary> /// <param name="year">年号,如2012.</param> /// <param name="month">月份,1代表1月,12代表12月.</param> public YearMonth( int year, int month) { _year = year; _month = month; } /// <summary> /// 根据指定日期所在的月份构造一个 YearMonth 对象. /// </summary> /// <param name="date">指定的日期.它的年份和月份部分会作为构造的YearMonth的值使用.</param> public YearMonth(DateTime date) { _year = date.Year; _month = date.Month; } public int Year { get { return _year; } } public int Month { get { return _month; } } /// <summary> /// 是否是有效的值.无效的值可能是年份小于1, 或年份大于9999, 或月份小于1, 或月份大于12. /// </summary> public bool IsValid { get { return 0 < Year && Year < 10000 && 0 < Month && Month < 13; } } /// <summary> /// 获取代表该月指定日期的 DateTime 对象 /// </summary> /// <param name="day">日期,范围1到31.</param> /// <exception cref="ArgumentOutOfRangeException">day大于本月的天数.</exception> public DateTime GetDate( int day) { return new DateTime(Year, Month, day); } /// <summary> /// 返回一个新YearMonth对象,其值为当前对象的值加上指定个年份 /// </summary> /// <param name="value">要在当前年月上增加多少年</param> public YearMonth AddYears( int value) { return new YearMonth(Year + value, Month); } /// <summary> /// 返回一个新YearMonth对象,其值为当前对象的值加上指定个月份 /// </summary> /// <param name="value">要在当前年月上增加多少个月</param> public YearMonth AddMonths( int value) { var totalMonths = Year * 12 + Month + value; var year = totalMonths / 12; var month = totalMonths % 12; if (month == 0) { month = 12; year--; } return new YearMonth(year, month); } /// <summary> /// 返回数字代表的年月份值,共6位数字,前4位代表年份,后两位代表月份,如:200805 /// </summary> public override string ToString() { return "{0:D4}{1:D2}" .FormatWith(Year, Month); } /// <summary> /// 判断一个是否与当前对象代表相同的值的YearMonth对象. /// </summary> public override bool Equals( object obj) { return Equals(obj as YearMonth); } /// <summary> ///判断另一个YearMonth对象是否与当前对象代表相同的值. /// </summary> public bool Equals(YearMonth rhs) { if (ReferenceEquals( null , rhs)) return false ; if (ReferenceEquals( this , rhs)) return true ; return _year == rhs._year && _month == rhs._month; } public override int GetHashCode() { unchecked { return (_year * 397) ^ _month; } } public static bool operator ==(YearMonth lhs, YearMonth rhs) { return Object.Equals(lhs, rhs); } public static bool operator !=(YearMonth lhs, YearMonth rhs) { return !(lhs == rhs); } public static bool TryParse( string s, out YearMonth result) { try { result = Parse(s); return true ; } catch (ArgumentException) { result = null ; return false ; } } /// <summary> /// 通过解析字符串构造一个YearMonth对象. /// </summary> /// <param name="s">要解析的字符串.</param> /// <exception cref="ArgumentNullException">s为null.</exception> /// <exception cref="ArgumentException">s包含非数字字符,或s的长度不为6.</exception> public static YearMonth Parse( string s) { if (s == null ) throw new ArgumentNullException( "s" ); if (s.Length != 6 || s.Any(x => x < '0' || x > '9' )) throw new ArgumentException( "s应该是6位数字." ); var year = int .Parse(s.Substring(0, 4)); var month = int .Parse(s.Substring(4)); return new YearMonth(year, month); } } |
辅助方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static class StringExtensions { public static string FormatWith( this string format, object arg0) { return string .Format(format, arg0); } public static string FormatWith( this string format, object arg0, object arg1) { return string .Format(format, arg0, arg1); } public static string FormatWith( this string format, object arg0, object arg1, object arg2) { return string .Format(format, arg0, arg1, arg2); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
2007-04-21 一些感想,欢迎拍砖