一个代表年月的类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;
    }
}


代码:

 

 

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);
    }
}

posted on 2010-04-21 14:46  deerchao  阅读(1123)  评论(0编辑  收藏  举报