Monthly數據類型

来源:https://www.cnblogs.com/Hollson/archive/2018/06/05/9138531.html
-Advertisement-
Play Games

Monthly由來 最近在做關於智能財稅的項目,大量用到了賬期相關的數據操作。項目已有兩年曆史了,對於賬期數據,前輩們用的是DateTime數據類型,即每個月的最後一天就是賬期。而用DateTime來表達賬期數據,確實讓我人很困惑: 1. 概念不統一: DateTime是時間類型,而賬期只跟年月相關 ...


Monthly由來

最近在做關於智能財稅的項目,大量用到了賬期相關的數據操作。項目已有兩年曆史了,對於賬期數據,前輩們用的是DateTime數據類型,即每個月的最後一天就是賬期。而用DateTime來表達賬期數據,確實讓我人很困惑:

  1. 概念不統一:
    DateTime是時間類型,而賬期只跟年月相關,DateTime用在這裡確實有點殺雞用了宰牛刀,而且給人的理解和溝通造成了額外的誤解。
  2. 格式不統一:
    為了在數據傳輸和存儲中達到數據的統一性,需要大量的字元串與日期的轉換、日期格式的轉換。
  3. 浪費性能:
    DateTime的精確度是可以到毫秒級的,而我們的賬期數據只需要精確到月,如:2018年1月賬期。 所以DateTime是很影響運算性能和存儲空間的。
  4. 操作異常:
    由於賬期是取月末日期,所以對每次接收了賬期參數都要取月末值,以確保數據的準確性。而在實際開發中,任何一個疏忽都會引發表達偏差。


Monthly簡介

Monthly是一個跟Datetime類似的,與月份相關的數據類型,適用於表達年月數據,如賬單、賬期、月刊等信息。


Monthly源碼

using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

namespace System
{
    /// <summary>
    /// 與月份相關的對象,如賬單、賬期、月刊、月報等
    /// </summary>
    [Serializable]
    [StructLayout(LayoutKind.Auto)]
    public struct Monthly : IComparable<Monthly>, IEquatable<Monthly>
    {
        private int _year;
        private int _month;

        #region Property
        /// <summary>
        ///  獲取當前實例的年
        /// </summary>
        public int Year => _year;

        /// <summary>
        ///  獲取當前實例的月
        /// </summary>
        public int Month => _month;

        /// <summary>
        /// 獲取當前實例的年月標記值,如2018年1月記為 : 201801
        /// </summary>
        public int Dot => this._year * 100 + this._month;

        /// <summary>
        /// 獲取當前實例從公元零年一月開始的月份累計值
        /// </summary>
        public int Tickes => this._year * 12 + this._month;

        /// <summary>
        ///  獲取當前實例所在的季度
        /// </summary>
        public int Quarter => (this._month - 1) / 3 + 1;
        #endregion

        #region Ctor
        /// <summary>
        ///  以指定的年和月初始化Monthly實例。
        /// </summary>
        /// <param name="year"> 年(0 到 9999)</param>
        /// <param name="month"> 月(1 到 12)</param>
        public Monthly(int year, int month)
        {
            CheckYear(year);
            CheckMonth(month);
            this._year = year;
            this._month = month;
        }

        /// <summary>
        /// 獲取以當前時間點為依據的新實例
        /// </summary>
        public static Monthly Current => new Monthly() { _year = DateTime.Now.Year, _month = DateTime.Now.Month };

        /// <summary>
        /// 獲取當前時間點的上月為依據的新實例
        /// </summary>
        public Monthly Previous => Monthly.fromTickes(this.Tickes - 1);

        /// <summary>
        /// 獲取當前時間點的下月為依據的新實例
        /// </summary>
        public Monthly Next => Monthly.fromTickes(this.Tickes + 1);

        /// <summary>
        /// 獲取當前年份的一月為依據的新實例  
        /// </summary>
        public Monthly First => new Monthly() { _year = this._year, _month = 1 };

        /// <summary>
        /// 獲取當前年份的十二月為依據的新實例
        /// </summary>
        public Monthly Last => new Monthly() { _year = this._year, _month = 12 };

        /// <summary>
        /// 獲取Monthly的最小值實例
        /// </summary>
        public static Monthly MinValue => new Monthly() { _year = 0, _month = 1 };

        /// <summary>
        /// 獲取Monthly的最大值實例
        /// </summary>
        public static Monthly MaxValue => new Monthly() { _year = 9999, _month = 12 };
        #endregion

        #region Method
        private static int yearOfDot(int dot) => dot / 100;

        private static int monthOfDot(int dot) => dot % 100;

        /// <summary>
        /// 獲取當前實例的年月標記值,如2018年1月記為 : 201801
        /// </summary>
        /// <returns></returns>
        public int ToDot() => this.Dot;

        /// <summary>
        /// 以當前實例與years的和值為依據創建一個新實例
        /// </summary>
        public Monthly AddYears(int years) => Monthly.FromTickes(this.Tickes + years * 12);

        /// <summary>
        /// 以當前實例與months的和值為依據創建一個新實例
        /// </summary>
        public Monthly AddMonths(int months) => Monthly.FromTickes(this.Tickes + months);

        /// <summary>
        /// 判斷當前實例的值與給定實例的值是否相等
        /// </summary>
        public bool Equals(Monthly other) => this.Tickes == other.Tickes;

        /// <summary>
        /// 獲取當前實例與給定實例的月份差值
        /// </summary>
        public int SpanMonths(Monthly other) => this - other;

        /// <summary>
        /// 獲取當前實例與DateTime實例的月份差值
        /// </summary>
        public int SpanMonths(DateTime date) => this.Tickes - date.Year * 12 - date.Month;

        /// <summary>
        /// 獲取當前實例與給定實例的大小比較的結果標識
        /// </summary>
        /// <param name="other"></param>
        /// <returns>-1:小於other實例值 ; 0 等於other實例值 ; 1:大於other實例值</returns>
        public int CompareTo(Monthly other)
        {
            if (this.Tickes < other.Tickes) return -1;
            if (this.Tickes > other.Tickes) return 1;
            else return 0;
        }

        /// <summary>
        /// 以年月標記值創建一個Monthly新實例
        /// </summary>
        /// <param name="dot">格式:201801</param>
        /// <returns></returns>
        public static Monthly FromDot(int dot)
        {
            var year = yearOfDot(dot);
            var month = monthOfDot(dot);
            if (year < 0 || year > 9999 || month < 1 || month > 12)
                throw new ArgumentOutOfRangeException("dot", dot, "Please enter correct dot format such as \'201801\'.");

            return new Monthly
            {
                _year = yearOfDot(dot),
                _month = monthOfDot(dot)
            };
        }

        private static Monthly fromTickes(int tickes)
        {
            return new Monthly
            {
                _year = (tickes - 1) / 12,
                _month = tickes % 12 == 0 ? 12 : tickes % 12
            };
        }

        /// <summary>
        /// 以年月累計值創建一個Monthly新實例
        /// </summary>
        /// <param name="tickes">以公元零年一月為起點的月份計數值(1-120000)</param>
        public static Monthly FromTickes(int tickes)
        {
            if (tickes < 1 || tickes > 120000)
                throw new ArgumentOutOfRangeException("tickes", tickes, "The tickes must beteen 1 and 120000 .");
            return fromTickes(tickes);
        }

        /// <summary>
        /// 以DateTime實例創建一個Monthly新實例
        /// </summary>
        public static Monthly FromDate(DateTime time) => new Monthly() { _year = time.Year, _month = time.Month };

        /// <summary>
        /// 以諸如"2018/01"格式的字元串創建一個Monthly新實例
        /// </summary>
        /// <param name="s">"2018/01"格式的字元串</param>
        /// <param name="spliter">分隔符</param>
        public static Monthly FromString(string s)
        {
            if (string.IsNullOrEmpty(s))
                throw new Exception("The parameter cannot be null or empty.");

            var nums = Regex.Matches(s, "[0-9]+");
            if (nums.Count == 0)
                throw new Exception("Please give the correct parameters, such as '2018/01' .");

            if (nums.Count == 1)
                return new Monthly(0, Convert.ToInt32(nums[0].ToString().TrimStart('0')));
            else
                return new Monthly(Convert.ToInt32(nums[0].ToString().TrimStart('0')), Convert.ToInt32(nums[1].ToString().TrimStart('0')));
        }

        /// <summary>
        /// 獲取一段時間內的Monthly數軸(包含開始與結束月份)
        /// </summary>
        /// <param name="from">開始月份</param>
        /// <param name="to">結束月份</param>
        /// <returns></returns>
        public static List<Monthly> Axis(Monthly from, Monthly to)
        {
            var result = new List<Monthly>();
            var span = from - to;
            var len = (span ^ (span >> 31)) - (span >> 31) + 1;
            for (int i = 0; i < len; i++)
            {
                if (span > 0) result.Add(from - i);
                else result.Add(from + i);
            }
            return result;
        }

        /// <summary>
        /// 獲取給定時間段內的Monthly集合(包含開始與結束月份)
        /// </summary>
        /// <param name="from">開始月份</param>
        /// <param name="to">結束月份</param>
        /// <returns></returns>
        public static List<Monthly> Axis(int from, int to)
        {
            return Axis(Monthly.FromDot(from), Monthly.FromDot(to));
        }

        /// <summary>
        /// 檢查year的合法性
        /// </summary>
        private static void CheckYear(int year)
        {
            if (year < 0 || year > 9999)
                throw new ArgumentOutOfRangeException("year", year, "The year must beteen 0 and 9999 .");
        }

        /// <summary>
        /// 檢查month的合法性
        /// </summary>
        private static void CheckMonth(int month)
        {
            if (month < 1 || month > 12)
                throw new ArgumentOutOfRangeException("month", month, "The month must beteen 1 and 12 .");
        }
        #endregion

        #region Operator
        /// <summary>
        /// 以給定實例與months的和值創建一個新實例
        /// </summary>
        /// <param name="months">月分數</param>
        public static Monthly operator +(Monthly m, int months) => FromTickes(m.Tickes + months);

        /// <summary>
        /// 以給定實例與months的差值創建一個新實例
        /// </summary>
        /// <param name="months">月分數</param>
        public static Monthly operator -(Monthly m, int months) => FromTickes(m.Tickes - months);

        /// <summary>
        /// 獲取當前實例與給定實例的月份差值
        /// </summary>
        public static int operator -(Monthly m1, Monthly m2) => m1.Tickes - m2.Tickes;

        /// <summary>
        ///獲取當前實例與DateTime實例的月份差值
        /// </summary>
        public static int operator -(Monthly m, DateTime d) => m.SpanMonths(d);

        public static Monthly operator ++(Monthly m) => m + 1;

        public static Monthly operator --(Monthly m) => m - 1;

        /// <summary>
        ///判斷m1是否等於m2
        /// </summary>
        public static bool operator ==(Monthly m1, Monthly m2) => m1.Tickes == m2.Tickes;

        /// <summary>
        /// 判斷m1是否不等於m2
        /// </summary>
        public static bool operator !=(Monthly m1, Monthly m2) => m1.Tickes != m2.Tickes;

        /// <summary>
        /// 判斷m1是否小於m2
        /// </summary>
        public static bool operator <(Monthly m1, Monthly m2) => m1.Tickes < m2.Tickes;

        /// <summary>
        /// 判斷m1是否大於m2
        /// </summary>
        public static bool operator >(Monthly m1, Monthly m2)
        {
            return m1.Tickes > m2.Tickes; ;
        }

        /// <summary>
        /// 判斷m1是否小於等於m2
        /// </summary>
        public static bool operator <=(Monthly m1, Monthly m2)
        {
            return m1.Tickes <= m2.Tickes; ;
        }

        /// <summary>
        /// 判斷m1是否大於等於m2
        /// </summary>
        public static bool operator >=(Monthly m1, Monthly m2)
        {
            return m1.Tickes >= m2.Tickes; ;
        }

        /// <summary>
        /// 以年月標識的Monthly實例
        /// </summary>
        /// <param name="dot">格式:201801</param>
        public static implicit operator Monthly(int dot)
        {
            return Monthly.FromDot(dot);
        }
        #endregion

        #region Override
        /// <summary>
        /// 獲取包含"Y、y、M、m"字元格式的自定義Monthly字元串
        /// </summary>
        /// <param name="format">
        /// 如:yyyy/mm ; yy/mm ; yyyy年mm月 ;YYYY-Mm...
        /// 不區分大小寫
        /// </param>
        /// <returns></returns>
        public string ToString(string format = "yyyy/mm")
        {
            return Format(this, format);
        }

        /// <summary>
        /// 判斷當前實例的值與給定實例的轉換值是否相等
        /// </summary>
        public override bool Equals(object obj)
        {
            if (obj is null) throw new ArgumentNullException("obj", "The parameter cannot be null.");
            if (obj is Monthly) return this == (Monthly)obj;
            if (obj is DateTime) return this == Monthly.FromDate((DateTime)obj);
            throw new ArgumentException("The parameter must be System.DateTime type or System.Monthly type .", "obj");
        }

        public override int GetHashCode()
        {
            Int64 ticks = Tickes;
            return unchecked((int)ticks) ^ (int)(ticks >> 32);
        }

        private static string Format(Monthly m, string format)
        {
            string _y = m.Year.ToString();
            string _m = m.Month.ToString();
            format = format.ToLower();
            if (!(format.Contains("yyyy") || format.Contains("yyyy")) && !(format.Contains("mm") || format.Contains("m")))
                throw new ArgumentException("The format expression error. ", nameof(format));
            if (format.Contains("yyyy"))
                format = format.Replace("yyyy", m.Year < 10 ? $"0{_y}" : _y);
            else if (format.Contains("yy"))
                format = format.Replace("yy", m.Year < 10 ? $"0{_y}" : _y.PadLeft(4, '0').Substring(2));
            if (format.Contains("mm"))
                format = format.Replace("mm", m.Month < 10 ? $"0{_m}" : _m);
            else if (format.Contains("m"))
                format = format.Replace("m", _m.TrimStart('0'));
            return format;
        }
        #endregion
    }
}


測試

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Pilipa.Utility.Test
{
    [TestClass]
    public class MonthlyTest
    {
        [TestMethod]
        public void TestProps()
        {
            var tar = DateTime.Now;
            Monthly plan = Monthly.FromDate(tar);

            Assert.AreEqual(Monthly.Current, new Monthly(tar.Year, tar.Month));

            Assert.AreEqual(plan.Year, tar.Year);
            Assert.AreEqual(plan.Month, tar.Month);
            Assert.AreEqual(plan.Dot, tar.Year * 100 + tar.Month);
            Assert.AreEqual(plan.Tickes, tar.Year * 12 + tar.Month);

            Assert.AreEqual(plan.First.ToDot(), tar.Year * 100 + 1);
            Assert.AreEqual(plan.Last.ToDot(), tar.Year * 100 + 12);


            Assert.AreEqual(plan.First.Previous.ToDot(), tar.AddYears(-1).Year * 100 + 12);
            Assert.AreEqual(plan.Last.Next.ToDot(), tar.AddYears(1).Year * 100 + 1);

            Assert.AreEqual(plan.Quarter, GetQuarter(tar.Month));

            Assert.AreEqual(Monthly.MinValue, new Monthly(0, 1));
            Assert.AreEqual(Monthly.MaxValue, new Monthly(9999, 12));
        }

        private int GetQuarter(int q)
        {
            if (new System.Collections.Generic.List<int>() { 1, 2, 3 }.Contains(q)) return 1;
            if (new System.Collections.Generic.List<int>() { 4, 5, 6 }.Contains(q)) return 2;
            if (new System.Collections.Generic.List<int>() { 7, 8, 9 }.Contains(q)) return 3;
            if (new System.Collections.Generic.List<int>() { 10, 11, 12 }.Contains(q)) return 4;
            return 0;
        }


        [TestMethod]
        public void TestMethods()
        {
            Monthly plan = 201801;
            var tar = new DateTime(2018, 1, 1);
            var tip = false;

            //Dot
            Assert.AreEqual(new Monthly(0, 11), 11);
            Assert.AreEqual(new Monthly(1, 1), 101);
            Assert.AreEqual(new Monthly(100, 12), 10012);
            Assert.AreEqual(new Monthly(2018, 12), 201812);

            //Tickes
            Assert.AreEqual(((Monthly)101).Tickes, 13);
            Assert.AreEqual(((Monthly)201811).Tickes, 2018 * 12 + 11);

            //加月
            Assert.AreEqual(plan.AddMonths(-1), 201712);
            Assert.AreEqual(plan.AddMonths(-23), 201602);
            Assert.AreEqual(plan.AddMonths(22), 201911);

            //加月(隨機)
            for (int i = 0; i < 100; i++)
            {
                var rd = new Random(Guid.NewGuid().GetHashCode()).Next(100);
                Assert.AreEqual(plan.AddMonths(rd), Monthly.FromDate(tar.AddMonths(rd)));
                Assert.AreEqual(plan.AddMonths(rd).Dot, tar.AddMonths(rd).Year * 100 + tar.AddMonths(rd).Month);
            }

            //加年
            Assert.IsTrue(plan.AddYears(6) == 202401);
            Assert.IsTrue(plan.AddYears(-18) == 200001);

            //加年(異常)
            try { var m = Monthly.Current.AddYears(-3000); }
            catch (Exception e) { if (e.Message.Contains("beteen 1 and 120000")) { tip = true; } }
            Assert.IsTrue(tip);

            //月份差
            Assert.AreEqual(plan.SpanMonths(201711), 2);
            Assert.AreEqual(plan.SpanMonths(201902), -13);

            //比較大小
            Assert.AreEqual(plan.CompareTo(201801), 0);
            Assert.AreEqual(plan.CompareTo(201701), 1);
            Assert.AreEqual(plan.CompareTo(202001), -1);

            //構造
            Assert.AreEqual(Monthly.FromDot(3), 3);
            Assert.AreEqual(Monthly.FromTickes(13), 101);
            Assert.AreEqual(Monthly.FromDate(new DateTime(2018, 12, 12)), 201812);
            Assert.AreEqual(Monthly.FromString("2018/01"), 201801);
            Assert.AreEqual(Monthly.FromString("2018年01月"), 201801);
            Assert.AreEqual(Monthly.FromString("2018@01/01"), 201801);
            Assert.AreEqual(Monthly.FromString((new DateTime(2018, 1, 1)).ToString()), 201801);
            Assert.AreEqual(Monthly.FromString("3"), 3);

            //月份軸
            var axis = Monthly.Axis(201711, 201901);
            Assert.IsTrue(axis.Count == 15);
            Assert.AreEqual(axis[0], 201711);
            Assert.AreEqual(axis[3], 201802);
            Assert.AreEqual(axis[14], 201901);

            axis = Monthly.Axis(201812, 201712);
            Assert.IsTrue(axis.Count == 13);
            Assert.AreEqual(axis[0], 201812);
            Assert.AreEqual(axis[12], 201712);
            
            //異常
            tip = false;
            try { Monthly m = 201800; }
            catch (Exception e) { if (e.Message.Contains("correct dot format")) { tip = true; } }  //dot format
            Assert.IsTrue(tip);

            tip = false;
            try { Monthly m = Monthly.FromDot(13); }
            catch (Exception e) { if (e.Message.Contains("correct dot format")) { tip = true; } }  //13月
            Assert.IsTrue(tip);

            tip = false;
            try { Monthly m = Monthly.FromTickes(999999); }
            catch (Exception e) { if (e.Message.Contains("must beteen 1 and 120000")) { tip = true; } }  //越界
            Assert.IsTrue(tip);

            tip = false;
            try { Monthly m = Monthly.FromString(null); }
            catch (Exception e) { if (e.Message.Contains("null or empty")) { tip = true; } }  //IsNullOrEmpty
            Assert.IsTrue(tip);

            tip = false;
            try { Monthly m = Monthly.FromString("abc"); }
            catch (Exception e) { if (e.Message.Contains("parameters")) { tip = true; } }  //格式錯誤
            Assert.IsTrue(tip);

            tip = false;
            try { Monthly m = Monthly.FromString("88"); }
            catch (Exception e) { if (e.Message.Contains("must beteen")) { tip = true; } }  //越界
            Assert.IsTrue(tip);
        }

        [TestMethod]
        public void TestOps()
        {
            Monthly plan = 201801;
            var tar = Monthly.FromString("2018.01");

            Assert.AreEqual(plan + 12, 201901);
            Assert.AreEqual(plan - 13, 201612);

            Assert.AreEqual(plan - (Monthly)201701, 12);
            Assert.AreEqual(plan - (new DateTime(2017, 12, 12)), 1);

            Assert.AreEqual(--plan, 201712);
            Assert.AreEqual(++plan, 201801);

            Assert.IsTrue(plan == Monthly.FromDot(201801));
            Assert.IsTrue(plan != Monthly.FromDot(201802));

            Assert.IsTrue(plan >= Monthly.FromDot(201801));
            Assert.IsTrue(plan < Monthly.FromDot(201803));
        }

        [TestMethod]
        public void TestOvr()
        {
            Monthly plan = 201801;
            var tar = Monthly.FromString("2018.01");

            //哈希碼(相同dot具有相同的哈希碼)
            Assert.AreEqual(plan.GetHashCode(), tar.GetHashCode());
            tar++;
            Assert.AreNotEqual(plan.GetHashCode(), tar.GetHashCode());

            //格式化
            Assert.AreEqual(plan.ToString(), "2018/01");
            Assert.AreEqual(plan.ToString("yy/mm"), "18/01");
            Assert.AreEqual(Monthly.FromDot(501).ToString("yy/mm"), "05/01");
            Assert.AreEqual(plan.ToString("YYYY年m月"), "2018年1月");
            Assert.AreEqual(plan.ToString("公元YyYy年mM月,哈哈..."), "公元2018年01月,哈哈...");

            //比較相等
            Assert.IsTrue(plan.Equals(Monthly.FromDot(201801)));
            Assert.IsTrue(plan.Equals(new DateTime(2018, 1, 1)));
            Assert.IsTrue(plan.Equals((object)Monthly.FromDot(201801)));
            Assert.IsFalse(plan.Equals(Monthly.FromDot(201901)));
        }
    }
}


Monthly使用介紹

1.Monthly構造

  //創建一個“2018年1月”的賬期
   Monthly m1 = 201801;
   Monthly m2 = new Monthly(2018, 1);
   Monthly m3 = Monthly.FromDate(new DateTime(2018, 1, 1));
   Monthly m4 = Monthly.FromDot(201801);
   Monthly m5 = Monthly.FromTickes(2018 * 12 + 1);
   Monthly m6 = Monthly.FromString("2018年01月");
   
   Monthly cur = Monthly.Current;   //當前時間實例
   Monthly min = Monthly.MinValue;  //Monthly最小實例
   Monthly max = Monthly.MaxValue;  //Monthly最大實例

2. Monthly屬性

屬性 說明
Year 獲取當前實例的年
Month 獲取當前實例的月
Dot 獲取當前實例的年月標記值,如2018年1月記為 : 201801
Tickes 獲取當前實例從公元零年一月開始的月份累計值
First 獲取當前年份的一月為依據的新實例
Last 獲取當前年份的十二月為依據的新實例
Previous 獲取當前時間點的上月為依據的新實例
Next 獲取當前時間點的下月為依據的新實例
Quarter 獲取當前實例所在的季度

3.Monthly方法

  • ToDot();
    說明:獲取當前實例的年月標記值,如2018年1月記為 : 201801
  • AddYears(int years)
    說明:以當前實例與years的和值為依據創建一個新實例

  • AddMonths(int months)
    說明:以當前實例與months的和值為依據創建一個新實例
  • Equals(Monthly other)
    說明:判斷當前實例的值與給定實例的值是否相等
  • Equals(object obj)
    說明:判斷當前實例的值與給定實例的轉換值是否相等,obj可以是DateTime類型
  • SpanMonths(Monthly other)
    說明:獲取當前實例與給定實例的月份差值
  • SpanMonths(DateTime date)
    說明:獲取當前實例與DateTime實例的月份差值
  • CompareTo(Monthly other)
    說明:獲取當前實例與給定實例的大小比較的結果標識, -1:小於other實例值 ; 0 等於other實例值 ; 1:大於other實例值
  • List<Monthly> Axis(int from, int to)
    說明:獲取一段時間內的Monthly數軸(包含開始與結束月份)
  • List<Monthly> Axis(Monthly from, Monthly to)
    說明:同 List Axis(int from, int to)
  • ToString(string format = "yyyy/mm")
    說明:獲取包含"Y、y、M、m"字元格式的自定義Monthly字元串,format 格式如:yyyy/mm ; yy/mm ; yyyy年mm月 ;YYYY-Mm...,不區分大小寫

示例:

   Monthly m = 201801;
   m.CompareTo(201701);            
   m.Equals(DateTime.Now);
   m.Equals(201701);
   m.SpanMonths(new DateTime(2017, 1, 1));
   m.SpanMonths(201701);

   m.ToString();
   m.ToString("yy/mm");
   Monthly.FromDot(501).ToString("yy/mm");
   m.ToString("YYYY年m月");
   m.ToString("公元YyYy年mM月,哈哈...");

4.Monthly操作符

Monthly支持+、- 、* 、/ 、> 、>= 、< 、<= 、++ 、-- 、== 、!= 運算符操作。

特別註意:-操作,他有operator -(Monthly m, int months)operator -(Monthly m1, Monthly m2)兩個重載版本,且方法功能不同,如果是第二個版本,則必須顯式標註被減對象的數據類型,如m-(Monthly)201701


參考:
https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,df6b1eba7461813b 微軟Datetime數據類型


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、 樣式一 我們要實現上圖中的效果,需要如下的操作: 從工具欄上的“Smobiler Components”拖動一個VoiceRecorder控制項和一個ImageButton控制項到窗體界面上 修改ImageButton的屬性 1.BackColor屬性 設置控制項的背景色,將該屬性設置為“Gray” ...
  • select a,sys_guid() as b from mytable sys_guid() 是生成帶分隔符(-)的GUID的自定義函數 查詢B表的內容插入A表,MY_ID是A表的主鍵不可為空,因此需要B查詢出來的數據增加一列並且賦值插入A insert into A (plan_id,ship ...
  • 新建了一個類繼承EF Model類,運行報錯 EF Code First列名 'Discriminator' 無效 EF會把項目中在DbContext中引用的所有的Model類及這些Model類對應的子類都生成對應映射視圖。如果資料庫沒有對應表或欄位就會報錯。 在繼承的Model 類加上NotMap ...
  • 索引 NET Core應用框架之BitAdminCore框架應用篇系列 框架演示:http://bit.bitdao.cn 框架源碼:https://github.com/chenyinxin/cookiecutter-bitadmin-core 20180605更新內容 一、本次更新內容如下: 集 ...
  • 一、結論: 1.實例構造函數與靜態構造函數執行順序 一、初始化順序(依次是靜態變數、靜態構造函數、實例變數、實例構造函數) 二、初始化次數(靜態的都只會初始化一次) 三、初始化時機(使用類引用任何靜態成員之前對或者首次實例化類時會調用靜態構造函數,通過靜態構造函數初始化類級別的項,即靜態欄位。非靜態 ...
  • LRUCache是Least Recently Used 近期最少使用演算法的緩存,是android提供的一個緩存工具類。可以以兩種排序方式來輸出緩存,一種是按插入順序輸出,一種是按最近最少方式輸出,最近使用的放在隊首,使用頻率低的,間隔時間最長的放在隊尾。 下麵是實現 構造函數中傳入緩存大小和輸出緩 ...
  • 基本上HTTP是沒有記錄狀態的協定,但可以通過Cookies將Request來源區分出來,並將部分數據暫存於Cookies及Session,是寫網站常用的用戶數據暫存方式。本篇將介紹如何在ASP.NET Core使用Cookie及Session。 Cookies Cookies是將用戶數據存在Cli ...
  • 在窗體或用戶控制項中重寫CreateParams MSDN上對CreateParams的解釋: image.png image.png ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...