测试(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;
}
}
代码:
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);
}
}
辅助方法:
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);
}
}