C#开发系统服务时用的定时器组件

[csharp] view plaincopy
 
  1. // 相较上一版本改进  
  2. // 1. 修改Bug  
  3. //  当设置每月一次频率时,设置的Day日期如果为31,30,29,在有些年份的有些月份会抛出异常,因为有些月份是没有31天的,改正之后,  
  4. //  如果设置了31天,则只有有31天的月份会执行。  
  5. // 2. 修正一年中某天的日期较验功能。  
  6. // 3. 新增加循环模式  
  7. //  每个月最后一天执行一次。  
  8. // 4. 支持到秒的定时  
[csharp] view plaincopy
 
  1. using System;  
  2. using System.Text;  
  3. using System.Windows.Forms;  
  4. using UpSoft.Framework.CommonFunction.WinService;  
  5.   
  6. namespace TestProject  
  7. {  
  8.     /// <summary>  
  9.     /// 测试服务  
  10.     /// </summary>  
  11.     public class TestServices : ServiceTimerControl  
  12.     {  
  13.         /// <summary>  
  14.         /// 服务代码  
  15.         /// </summary>  
  16.         protected override void StartService()  
  17.         {  
  18.             // 需要处理的服务代码  
  19.         }  
  20.   
  21.         /// <summary>  
  22.         /// 时间配置策略名(可不重写。默认读配置文件中的default)  
  23.         /// </summary>  
  24.         public override string ConfigName { get { return "A"; } }  
  25.     }  
  26. }  
  27.   
  28.   
  29. 要调用时,只需输入以下代码  
  30. new TestServices().Start();  
[csharp] view plaincopy
 
  1. //时间策略配置,可选择以下两种之一,配置文件,或是重写实现基类的获取时间策略配置  
  2. //1.代码重写  
  3.         /// <summary>  
  4.         /// 时间策略配置  
  5.         /// </summary>  
  6.         /// <returns></returns>  
  7.         protected override TimerConfig GetTimerConfig()  
  8.         {  
  9.             return new TimerConfig{ TimerMode=..., ...};  
  10.         }  
  11. //2.配置文件实现  
[html] view plaincopy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <configuration>  
  3.     <configSections>  
  4.         <section name="ServiceTimerConfig" type="UpSoft.Framework.CommonFunction.WinService.ServiceTimerConfigManager,CommonFunction"></section>  
  5.     </configSections>  
  6.     <ServiceTimerConfig>  
  7.         <!--默认采用策略-->  
  8.         <Default>A</Default>  
  9.         <!--A配置项(全节点)-->  
  10.         <Config>  
  11.             <!--A策略-->  
  12.             <RefName>A</RefName>  
  13.             <TimerMode>Interval</TimerMode>  
  14.             <!--延迟开始处理(单位毫秒)可为空-->  
  15.             <Delay>10000</Delay>  
  16.             <!--文件生成时间间隔(单位毫秒,1800000=30分钟)-->  
  17.             <Interval>600000</Interval>  
  18.             <!--月份-->  
  19.             <MonthSeq></MonthSeq>  
  20.             <!--指定第几天的序号-->  
  21.             <DaySeq></DaySeq>  
  22.             <!--定时配置-->  
  23.             <Times>  
  24.                 <!--一天之中需要执行任务的时间点-->  
  25.                 <TimeValue>11:20:19</TimeValue>  
  26.                 <TimeValue>10:10:43</TimeValue>  
  27.                 <TimeValue>19:10:28</TimeValue>  
  28.             </Times>  
  29.         </Config>  
  30.         <!--B配置项(轮询策略)-->  
  31.         <Config>  
  32.             <!--B策略,每隔设置的时间执行一次-->  
  33.             <RefName>B</RefName>  
  34.             <TimerMode>Interval</TimerMode>  
  35.             <!--延迟开始处理(单位毫秒)-->  
  36.             <Delay>10000</Delay>  
  37.             <!--文件生成时间间隔(单位毫秒,1800000=30分钟)-->  
  38.             <Interval>600000</Interval>  
  39.         </Config>  
  40.         <!--C配置项(天设置)-->  
  41.         <Config>  
  42.             <!--C策略,每周4在配置的时间点上执行-->  
  43.             <RefName>C</RefName>  
  44.             <TimerMode>Week</TimerMode>  
  45.             <!--延迟开始处理(单位毫秒)-->  
  46.             <Delay>10000</Delay>  
  47.             <!--每周的星期四的以下时间执行-->  
  48.             <DaySeq>4</DaySeq>  
  49.             <!--定时配置-->  
  50.             <Times>  
  51.                 <!--一天之中需要执行任务的时间点-->  
  52.                 <TimeValue>11:20:19</TimeValue>  
  53.                 <TimeValue>10:10:43</TimeValue>  
  54.                 <TimeValue>19:10:28</TimeValue>  
  55.             </Times>  
  56.         </Config>  
  57.         <!--D配置项(月、天设置)-->  
  58.         <Config>  
  59.             <!--D策略,每年12月8号在配置的时间点上执行-->  
  60.             <RefName>D</RefName>  
  61.             <TimerMode>Month</TimerMode>  
  62.             <!--延迟开始处理(单位毫秒)-->  
  63.             <Delay>10000</Delay>  
  64.             <!--月份-->  
  65.             <MonthSeq>12</MonthSeq>  
  66.             <!--天数-->  
  67.             <DaySeq>8</DaySeq>  
  68.             <!--定时配置-->  
  69.             <Times>  
  70.                 <!--一天之中需要执行任务的时间点-->  
  71.                 <TimeValue>11:20:19</TimeValue>  
  72.                 <TimeValue>10:10:43</TimeValue>  
  73.                 <TimeValue>19:10:28</TimeValue>  
  74.             </Times>  
  75.         </Config>  
  76.     </ServiceTimerConfig>  
  77. </configuration>  
  78.   
  79. // TimerMode的定义  
  80.     public enum TimerMode  
  81.     {  
  82.         ///<summary>  
  83.         /// 轮询方式  
  84.         /// </summary>  
  85.         Interval = 0,  
  86.         /// <summary>  
  87.         /// 一个月中某个天数的指定时间  
  88.         /// </summary>  
  89.         Month = 1,  
  90.         /// <summary>  
  91.         /// 一周中的周几的指定时间  
  92.         /// </summary>  
  93.         Week = 2,  
  94.         /// <summary>  
  95.         /// 一天中的指定时间  
  96.         /// </summary>  
  97.         Day = 3,  
  98.         /// <summary>  
  99.         /// 一年中第几天的指定时间  
  100.         /// </summary>  
  101.         Year = 4,  
  102.         /// <summary>  
  103.         /// 一年中的指定日期的指定时间  
  104.         /// </summary>  
  105.         Date = 5,  
  106.         /// <summary>  
  107.         /// 每个月倒数第N天  
  108.         /// </summary>  
  109.         LastDayOfMonth  
  110.         /// <summary>  
  111.         /// 未设置  
  112.         /// </summary>  
  113.         NoSet  
  114.     }  

以下是组件的源代码

[csharp] view plaincopy
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Configuration;  
  4. using System.Text.RegularExpressions;  
  5. using System.Threading;  
  6. using System.Xml;  
  7.   
  8. namespace UpSoft.Framework.CommonFunction.WinService  
  9. {  
  10.     /// <summary>  
  11.     /// 服务定时器管理  
  12.     /// </summary>  
  13.     public abstract class ServiceTimerControl  
  14.     {  
  15.         #region 私有成员  
  16.         /// <summary>  
  17.         /// 定时器  
  18.         /// </summary>  
  19.         private Timer SysTimer { get; set; }  
  20.         /// <summary>  
  21.         /// 是否启用定时器  
  22.         /// </summary>  
  23.         private bool _EnabledTimer = true;  
  24.         /// <summary>  
  25.         /// 服务执行状态, 0-休眠, 1-运行  
  26.         /// </summary>  
  27.         private int _serviceStatus = 0;  
  28.         #endregion  
  29.  
  30.         #region 公共属性  
  31.         /// <summary>  
  32.         /// 获取服务状态  
  33.         /// </summary>  
  34.         public int ServiceStatus { get { return _serviceStatus; } }  
  35.   
  36.         /// <summary>  
  37.         /// 定时器配置  
  38.         /// </summary>  
  39.         public TimerConfig Config { get; set; }  
  40.   
  41.         /// <summary>  
  42.         /// 时间计算类  
  43.         /// </summary>  
  44.         public TimerControl TimerControl { get; set; }  
  45.   
  46.         /// <summary>  
  47.         /// 配置名称  
  48.         /// </summary>  
  49.         public virtual string ConfigName { get { return ( ServiceTimerConfigManager.ServiceConfig == null ? "" : ServiceTimerConfigManager.ServiceConfig.Default ); } }  
  50.         #endregion  
  51.   
  52.         /// <summary>  
  53.         /// 停止  
  54.         /// </summary>  
  55.         public void Stop()  
  56.         {  
  57.             _EnabledTimer = false;  
  58.   
  59.             if ( SysTimer != null ) SysTimer.Change( Timeout.Infinite, Timeout.Infinite );  
  60.         }  
  61.   
  62.         /// <summary>  
  63.         /// 开始服务  
  64.         /// </summary>  
  65.         public void Start()  
  66.         {  
  67.             try  
  68.             {  
  69.                 _EnabledTimer = true;  
  70.                 Config = this.GetTimerConfig();  
  71.                 if ( Config.Delay == null )  
  72.                     Config.Delay = new TimeSpan( 0 );  
  73.   
  74.                 SysTimer = new Timer( new TimerCallback( this.TimerProcess ), AppDomain.CurrentDomain, Config.Delay, this.Config.Interval );  
  75.   
  76.                 this.Logger( LogLevel.INFO, "服务启动成功!" );  
  77.             }  
  78.             catch ( Exception ex )  
  79.             {  
  80.                 this.ServiceException( ex );  
  81.             }  
  82.         }  
  83.   
  84.         /// <summary>  
  85.         /// 单次执行服务程序  
  86.         /// </summary>  
  87.         public void Process()  
  88.         {  
  89.             try  
  90.             {  
  91.                 //开始处理服务  
  92.                 this.StartService();  
  93.             }  
  94.             catch ( Exception ex ) { this.ServiceException( ex ); } // 处理服务执行过程中出现的异常  
  95.         }  
  96.   
  97.         /// <summary>  
  98.         /// 处理间隔服务  
  99.         /// </summary>  
  100.         /// <param name="sender"></param>  
  101.         private void TimerProcess( object sender )  
  102.         {  
  103.             if ( !_EnabledTimer ) return;  
  104.   
  105.             bool TimeIsUp = true;  
  106.             if ( this.Config.TimerMode != TimerMode.Interval )  
  107.             {  
  108.                 // 如果定时方式不是定时轮询的话,就构造TimerControl类,该类用来计算每次执行完程序后  
  109.                 // 到下次执行服务时需要休眠的时间  
  110.                 try  
  111.                 {  
  112.                     this.TimerControl = new TimerControl( this.Config );  
  113.                     TimeIsUp = this.TimerControl.TimeIsUp;  // 获取是否到了执行服务程序的时间了  
  114.                 }  
  115.                 catch ( Exception ex )  
  116.                 {  
  117.                     // 读取配置出错且TimerControl对象已不存在,则再抛出异常  
  118.                     // 如果上一次读取配置成功,那就就算这次的配置有问题,则也不会停止程序的运行,仍用上一次的数据做为参数  
  119.                     if ( this.TimerControl == null ) throw ex;  
  120.                 }  
  121.             }  
  122.   
  123.             try  
  124.             {  
  125.                 if ( TimeIsUp )// 时间到了可以执行程序了  
  126.                 {  
  127.                     // 服务运行了  
  128.                     _serviceStatus = 1;  
  129.   
  130.                     // 设置计时器,在无穷时间后再启用(实际上就是永远不启动计时器了--停止计时器计时)  
  131.                     SysTimer.Change( Timeout.Infinite, Timeout.Infinite );  
  132.   
  133.                     //开始处理服务  
  134.                     this.StartService();  
  135.                 }  
  136.             }  
  137.             catch ( Exception ex ) { this.ServiceException( ex ); } // 处理服务执行过程中出现的异常  
  138.             finally  
  139.             {  
  140.                 // 如果计时器不为空,则重新设置休眠的时间  
  141.                 if ( SysTimer != null )  
  142.                 {  
  143.                     if ( this.Config.TimerMode == TimerMode.Interval )// 定时轮询设置  
  144.                     {  
  145.                         // 重新启用计时器  
  146.                         SysTimer.Change( this.Config.Interval, this.Config.Interval );  
  147.                     }  
  148.                     else// 定时设置  
  149.                     {  
  150.                         // 用cft类计算下一次到期的时间  
  151.                         TimeSpan Interval = this.TimerControl.GetNextTimeUp();  
  152.                         // 重新启用计时器  
  153.                         SysTimer.Change( Interval, Interval );  
  154.                     }  
  155.                 }  
  156.                 _serviceStatus = 0;  
  157.             }  
  158.         }  
  159.   
  160.         /// <summary>  
  161.         /// 开始服务  
  162.         /// </summary>  
  163.         protected abstract void StartService();  
  164.   
  165.         /// <summary>  
  166.         /// 记录日志  
  167.         /// </summary>  
  168.         /// <param name="level">错误级别</param>  
  169.         /// <param name="msg"></param>  
  170.         protected virtual void Logger( LogLevel level, string msg ) { return; }  
  171.   
  172.         /// <summary>  
  173.         /// 定时器初始化  
  174.         /// </summary>  
  175.         protected virtual TimerConfig GetTimerConfig()  
  176.         {  
  177.             var config = ServiceTimerConfigManager.ServiceConfig;  
  178.             if ( config != null && config.Config.Length > 0 )  
  179.             {  
  180.                 // 如果没有配置则默认为第1个  
  181.                 if ( String.IsNullOrEmpty( ConfigName ) )  
  182.                     return config.Config[0];  
  183.                 else// 返回配置项  
  184.                     foreach ( var c in config.Config ) if ( String.Compare( c.RefName, ConfigName, true ) == 0 ) return c;  
  185.             }  
  186.   
  187.             throw new Exception( "时间策略配置不正确!" );  
  188.         }  
  189.   
  190.         /// <summary>  
  191.         /// 系统服务错误  
  192.         /// </summary>  
  193.         /// <param name="ex"></param>  
  194.         protected virtual void ServiceException( Exception ex ) { this.Logger( LogLevel.ERROR, "服务异常:" + ex.Message + "  堆栈:" + ex.StackTrace ); }  
  195.     }  
  196.  
  197.     #region 定时服务休眠计算类  
  198.     /// <summary>  
  199.     /// 文件生成时间配置  
  200.     /// </summary>  
  201.     public class TimerControl  
  202.     {  
  203.         #region 私有成员  
  204.         private TimerConfig Config { get; set; }  
  205.         #endregion  
  206.  
  207.         #region 公共成员方法  
  208.         /// <summary>  
  209.         /// 构造函数  
  210.         /// </summary>  
  211.         /// <param name="config">配置参数</param>  
  212.         /// </param>  
  213.         public TimerControl( TimerConfig config )  
  214.         {  
  215.             Config = config;  
  216.             if ( Config == null ) throw new Exception( "定时器时间配置异常!" );  
  217.   
  218.             switch ( Config.TimerMode )  
  219.             {  
  220.                 case TimerMode.Date:  
  221.                     if ( Config.MonthSeq < 1 || Config.MonthSeq > 12 )  
  222.                         throw new Exception( "定时器时间配置异常(月份取值只能是1~12)!" );  
  223.                     var dt = new DateTime( 2012, Config.MonthSeq, 1 );  // 之所以选2012,是因为他是闰年,因此2月有29天。  
  224.                     var lastDay = GetLastDayByMonth( dt );  
  225.                     if ( Config.DaySeq < 1 || Config.DaySeq > lastDay )  
  226.                         throw new Exception( "定时器时间配置异常(" + Config.MonthSeq + "月份的天数取值只能是1~" + lastDay + ")!" );  
  227.                     break;  
  228.                 case TimerMode.Day: break;  
  229.                 case TimerMode.Month:  
  230.                     if ( Config.DaySeq < 1 || Config.DaySeq > 31 )  
  231.                         throw new Exception( "定时器时间配置异常(天数取值只能是1~31)!" );  
  232.                     break;  
  233.                 case TimerMode.Week:  
  234.                     if ( Config.DaySeq < 0 || Config.DaySeq > 6 )  
  235.                         throw new Exception( "定时器时间配置异常(星期取值只能是0~6)!" );  
  236.                     break;  
  237.                 case TimerMode.LastDayOfMonth:  
  238.                     if ( Config.DaySeq != 0 )  
  239.                     {// 如果等于0的话,表示是每个月的最后一天。  
  240.                         if ( Config.DaySeq < 1 || Config.DaySeq > 28 )  
  241.                             throw new Exception( "定时器时间配置异常(倒数的天数只能是1~28,即倒数的第1天,第2天。。。有些月份并没有29.30.31天,因此最大只允许倒数第28天)!" );  
  242.                         Config.DaySeq -= 1;  
  243.                     }  
  244.                     break;  
  245.                 case TimerMode.Year:  
  246.                     if ( Config.DaySeq < 1 || Config.DaySeq > 366 )  
  247.                         throw new Exception( "定时器时间配置异常(天数取值只能是1~366)!" );  
  248.                     break;  
  249.             }  
  250.         }  
  251.   
  252.         /// <summary>  
  253.         /// 判断时间是否到了  
  254.         /// </summary>  
  255.         /// <returns>true时间已经到了,false时间还未到</returns>  
  256.         public bool TimeIsUp  
  257.         {  
  258.             get  
  259.             {  
  260.                 DateTime dt = DateTime.Now;  
  261.                 if ( CheckTimeIsUp( dt.TimeOfDay ) )  
  262.                 {  
  263.                     switch ( Config.TimerMode )  
  264.                     {  
  265.                         case TimerMode.Day: return true;  
  266.                         case TimerMode.Date: return dt.Month == Config.MonthSeq && dt.Day == Config.DaySeq;  
  267.                         case TimerMode.Week: return ( ( int )dt.DayOfWeek ) == Config.DaySeq;  
  268.                         case TimerMode.Month: return dt.Day == Config.DaySeq;  
  269.                         case TimerMode.Year: return dt.DayOfYear == Config.DaySeq;  
  270.                         case TimerMode.LastDayOfMonth: return dt.Day == ( GetLastDayByMonth( dt ) - Config.DaySeq );  
  271.                         default: return false;  
  272.                     }  
  273.                 }  
  274.                 else  
  275.                     return false;  
  276.             }  
  277.         }  
  278.   
  279.         /// <summary>  
  280.         /// 时间是否到了  
  281.         /// </summary>  
  282.         /// <returns></returns>  
  283.         private bool CheckTimeIsUp( TimeSpan time )  
  284.         {  
  285.             var tmp = new TimeSpan( time.Hours, time.Minutes, time.Seconds );  
  286.             if ( Config.Times == null )  
  287.                 return ( tmp.Ticks == 0 );  
  288.             else  
  289.             {  
  290.                 foreach ( var t in Config.Times )  
  291.                 {  
  292.                     if ( t == tmp ) return true;  
  293.                 }  
  294.                 return false;  
  295.             }  
  296.         }  
  297.   
  298.         /// <summary>  
  299.         /// 从现在起到下次时间到还有多少时间  
  300.         /// </summary>  
  301.         /// <returns>时间间隔</returns>  
  302.         public TimeSpan GetNextTimeUp()  
  303.         {  
  304.             ///目标时间  
  305.             DateTime _NextDateTime = this.GetNextDateTime();    // 保存下一次要执行的时间  
  306.             return _NextDateTime - DateTime.Now;  
  307.         }  
  308.   
  309.         /// <summary>  
  310.         /// 获取下一次指定配置的时间是多少  
  311.         /// </summary>  
  312.         /// <returns></returns>  
  313.         public DateTime GetNextDateTime()  
  314.         {  
  315.             var time = GetNextTimeConfig();  
  316.             DateTime dt = DateTime.Now;  
  317.             DateTime now, target;  
  318.             switch ( Config.TimerMode )  
  319.             {  
  320.                 case TimerMode.Day:  
  321.                     #region 每天指定某时执行一次  
  322.                     now = new DateTime( 1, 1, 1, dt.Hour, dt.Minute, dt.Second );  
  323.                     target = new DateTime( 1, 1, 1, time.Hours, time.Minutes, time.Seconds );  
  324.                     if ( now.Ticks >= target.Ticks ) dt = dt.AddDays( 1.0 ); //如果当前时间小于指定时刻,则不需要加天  
  325.   
  326.                     dt = new DateTime( dt.Year, dt.Month, dt.Day, time.Hours, time.Minutes, time.Seconds );  
  327.                     #endregion  
  328.                     break;  
  329.                 case TimerMode.Month:  
  330.                     #region 每月指定某天某时执行一次  
  331.                     now = new DateTime( 1, 1, dt.Day, dt.Hour, dt.Minute, dt.Second );  
  332.                     target = new DateTime( 1, 1, Config.DaySeq, time.Hours, time.Minutes, time.Seconds );   // 1月有31天,所以可以接受任何合法的Day值(因为在赋值时已判断1~31)  
  333.                     if ( now.Ticks >= target.Ticks ) dt = dt.AddMonths( 1 );  
  334.   
  335.   
  336.                     // 当前月份的指定天数执行过了,因此月份加上一个月,当月份加了一个月之后,很可能当前实现的Day值可能会变小(例:3月31号,加上一个月,则日期会变成,4月30号,而不会变成5月1号),  
  337.                     // 因此需要判断指定的this.Day是不是比Day大(月份的Day变小的唯一原因是因为月份加了一个月之后,那个月并没有this.Day的天数),如果没有该this.Day的天数。则需要为该月份再加一个月。  
  338.                     // 加一个月份,则那下个月一定可以大于等于this.Day, 看看每个月的天数就可以断定了,  
  339.                     // 因为没有连着两个月的日期小于等于30的,只有连续两个月是31天。其它就是间隔的出现(this.Day最大只可能为31)  
  340.                     // 如此之后,接下来的dt=new DateTime时不为因为dt.Month的月份,因没有this.Day天数而抛异常  
  341.                     if ( Config.DaySeq > GetLastDayByMonth( dt ) ) dt = dt.AddMonths( 1 );   // 如此是为了确保dt.Month的月份一定有this.Day天(因此如果设置为每个月的31号执行的程序,就只会在1,3,5,7,8,10,12几个月份会执行)  
  342.   
  343.                     dt = new DateTime( dt.Year, dt.Month, Config.DaySeq, time.Hours, time.Minutes, time.Seconds );  
  344.                     #endregion  
  345.                     break;  
  346.                 case TimerMode.LastDayOfMonth:  
  347.                     #region 每个月倒数第N天的某时某刻执行一次  
  348.                     var lastDaybymonth = GetLastDayByMonth( dt ) - Config.DaySeq;  
  349.                     now = new DateTime( 1, 1, dt.Day, dt.Hour, dt.Minute, dt.Second );  
  350.                     target = new DateTime( 1, 1, lastDaybymonth, time.Hours, time.Minutes, time.Seconds );  // 1月有31天,所以可以接受任何合法的Day值(因为在赋值时已判断1~31)  
  351.                     if ( now.Ticks >= target.Ticks )  
  352.                     {  
  353.                         dt = dt.AddMonths( 1 );  
  354.                         dt = new DateTime( dt.Year, dt.Month, GetLastDayByMonth( dt ) - Config.DaySeq, time.Hours, time.Minutes, time.Seconds );// 根据新月份求新月份的最后一天。  
  355.                     }  
  356.                     else  
  357.                         dt = new DateTime( dt.Year, dt.Month, lastDaybymonth, time.Hours, time.Minutes, time.Seconds );  
  358.                     #endregion  
  359.                     break;  
  360.                 case TimerMode.Week:  
  361.                     #region 每星期指定星期某时执行一次  
  362.                     int dow = ( int )dt.DayOfWeek;  
  363.                     now = new DateTime( 1, 1, dow + 1, dt.Hour, dt.Minute, dt.Second );  
  364.                     target = new DateTime( 1, 1, Config.DaySeq + 1, time.Hours, time.Minutes, time.Seconds );  
  365.   
  366.                     if ( now.Ticks >= target.Ticks )  
  367.                         dt = dt.AddDays( Config.DaySeq - dow + 7 );  
  368.                     else  
  369.                         dt = dt.AddDays( Config.DaySeq - dow );  
  370.   
  371.                     dt = new DateTime( dt.Year, dt.Month, dt.Day, time.Hours, time.Minutes, time.Seconds );  
  372.                     #endregion  
  373.                     break;  
  374.                 case TimerMode.Date:  
  375.                     #region 每年指定某月某日某时执行一次  
  376.                     now = new DateTime( 4, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second );  
  377.   
  378.                     // 0004年闰年,可以支持2月29.因此选了0004, 这样就不会在构造Target时异常,  
  379.                     // 因为比较的关键不在年。所以,只要Now和Target的年份一样就可以,设置成什么年份无所谓  
  380.                     target = new DateTime( 4, Config.MonthSeq, Config.DaySeq, time.Hours, time.Minutes, time.Seconds );  
  381.   
  382.                     if ( now.Ticks >= target.Ticks ) dt = dt.AddYears( 1 );  
  383.                     if ( Config.MonthSeq == 2 && Config.DaySeq == 29 )  
  384.                     {  
  385.                         // 因为闰年的最大间隔是8年,平时是4年一闰,可是0096年闰完之后,下一个闰年就是0104年,因此。。。  
  386.                         for ( int i = 0; i < 8; i++ )  
  387.                             if ( DateTime.IsLeapYear( dt.Year + i ) )  
  388.                             {  
  389.                                 dt = dt.AddYears( i );  
  390.                                 break;  
  391.                             }  
  392.                     }  
  393.   
  394.                     dt = new DateTime( dt.Year, Config.MonthSeq, Config.DaySeq, time.Hours, time.Minutes, time.Seconds );  
  395.                     #endregion  
  396.                     break;  
  397.                 case TimerMode.Year:  
  398.                     #region 每年指定第N天某时执行一次  
  399.                     now = new DateTime( 1, 1, 1, dt.Hour, dt.Minute, dt.Second );  
  400.                     target = new DateTime( 1, 1, 1, time.Hours, time.Minutes, time.Seconds );  
  401.                     if ( dt.DayOfYear > Config.DaySeq || dt.DayOfYear == Config.DaySeq && now.Ticks >= target.Ticks ) dt = dt.AddYears( 1 );  
  402.                     dt = dt.AddDays( Config.DaySeq - dt.DayOfYear );  
  403.   
  404.                     dt = new DateTime( dt.Year, dt.Month, dt.Day, time.Hours, time.Minutes, time.Seconds );  
  405.                     #endregion  
  406.                     break;  
  407.                 default:  
  408.                     throw new Exception( "定时器时间配置异常!" );  
  409.             }  
  410.   
  411.             return dt;  
  412.         }  
  413.   
  414.         /// <summary>  
  415.         /// 获取指定日期所在月份的最后一天  
  416.         /// </summary>  
  417.         /// <param name="dt"></param>  
  418.         /// <returns></returns>  
  419.         private int GetLastDayByMonth( DateTime dt )  
  420.         {  
  421.             switch ( dt.Month )  
  422.             {  
  423.                 case 4:  
  424.                 case 6:  
  425.                 case 9:  
  426.                 case 11:  
  427.                     return 30;  
  428.                 case 2:  
  429.                     return DateTime.IsLeapYear( dt.Year ) ? 29 : 28;  
  430.                 default:  
  431.                     return 31;  
  432.             }  
  433.         }  
  434.   
  435.         /// <summary>  
  436.         /// 获取下一个时间点  
  437.         /// </summary>  
  438.         /// <returns></returns>  
  439.         private TimeSpan GetNextTimeConfig()  
  440.         {  
  441.             if ( Config.Times == null || Config.Times.Length == 0 )  
  442.                 return new TimeSpan( 0 );  
  443.             else  
  444.             {  
  445.                 var minData = TimeSpan.MaxValue;        // 最小时间  
  446.                 var minExecData = TimeSpan.MaxValue;    // 大于当前时间的最小时间  
  447.                 foreach ( var t in Config.Times )  
  448.                 {  
  449.                     if ( DateTime.Now.TimeOfDay < t && minExecData >= t ) // 找出比当前时间大的最小时间  
  450.                         minExecData = t;  
  451.                     if ( minData > t )   // 找出最小的一个时间,当前时间不参与运算  
  452.                         minData = t;  
  453.                 }  
  454.   
  455.                 if ( minExecData == TimeSpan.MaxValue ) // 如果找不到比当前时间大的最小时间,则选择最小时间返回  
  456.                     return minData;  
  457.                 else  
  458.                     return minExecData;  
  459.             }  
  460.         }  
  461.         #endregion  
  462.     }  
  463.     #endregion  
  464.  
  465.     #region 系统配置实体类&配置读取类  
  466.     /// <summary>  
  467.     /// 时间配置类  
  468.     /// </summary>  
  469.     public class ServiceTimerConfig  
  470.     {  
  471.         /// <summary>  
  472.         /// 默认配置  
  473.         /// </summary>  
  474.         public string Default { get; set; }  
  475.         /// <summary>  
  476.         /// 配置项  
  477.         /// </summary>  
  478.         public TimerConfig[] Config { get; set; }  
  479.     }  
  480.     /// <summary>  
  481.     /// 时间配置  
  482.     /// </summary>  
  483.     public class TimerConfig  
  484.     {  
  485.         /// <summary>  
  486.         /// 配置引用名  
  487.         /// </summary>  
  488.         public string RefName { get; set; }  
  489.   
  490.         /// <summary>  
  491.         /// 时间模式  
  492.         /// timeMode取值如下:TimerMode.Month、TimerMode.Week、TimerMode.Week、TimerMode.Day、TimerMode.Date、TimerMode.Year  
  493.         /// </summary>  
  494.         public TimerMode TimerMode { get; set; }  
  495.   
  496.         /// <summary>  
  497.         /// 指定某个时间算法的第几天,第1天就为1  
  498.         /// TimerMode=TimerMode.Month           时, 该DaySeq表示每个月中的第几天  
  499.         /// TimerMode=TimerMode.Week            时, 该DaySeq表示每个星期中的星期几(0-星期天,其它用1-6表示)  
  500.         /// TimerMode=TimerMode.Day             时, 该值不需要设置  
  501.         /// TimerMode=TimerMode.Date            时, 该DaySeq表示每个日期中的天数,如:8月12号,则DaySeq为12,MonthSeq为8  
  502.         /// TimerMode=TimerMode.LastDayOfMonth  时, 该DaySeq表示每个月倒数第几天  
  503.         /// TimerMode=TimerMode.Year            时, 该DaySeq表示每年中的第几天  
  504.         /// </summary>  
  505.         public int DaySeq { get; set; }  
  506.   
  507.         /// <summary>  
  508.         /// 当指定一年中某个月的某个日期时有用到如:指定一年中8月12号,则这里的MonthSeq就应该为8  
  509.         /// </summary>  
  510.         public int MonthSeq { get; set; }  
  511.   
  512.         /// <summary>  
  513.         /// 循环处理时间间隔(单位毫秒)  
  514.         /// </summary>  
  515.         public TimeSpan Interval { get; set; }  
  516.   
  517.         /// <summary>  
  518.         /// 启动延迟时间(单位毫秒)  
  519.         /// </summary>  
  520.         public TimeSpan Delay { get; set; }  
  521.   
  522.         /// <summary>  
  523.         /// 时间设置  
  524.         /// </summary>  
  525.         public TimeSpan[] Times { get; set; }  
  526.     }  
  527.     /// <summary>  
  528.     /// 服务处理方法  
  529.     /// </summary>  
  530.     public enum TimerMode  
  531.     {  
  532.         /// <summary>  
  533.         /// 轮询方式  
  534.         /// </summary>  
  535.         Interval = 0,  
  536.         /// <summary>  
  537.         /// 一个月中某个天数的指定时间  
  538.         /// </summary>  
  539.         Month = 1,  
  540.         /// <summary>  
  541.         /// 一周中的周几的指定时间  
  542.         /// </summary>  
  543.         Week = 2,  
  544.         /// <summary>  
  545.         /// 一天中的指定时间  
  546.         /// </summary>  
  547.         Day = 3,  
  548.         /// <summary>  
  549.         /// 一年中第几天的指定时间  
  550.         /// </summary>  
  551.         Year = 4,  
  552.         /// <summary>  
  553.         /// 一年中的指定日期的指定时间  
  554.         /// </summary>  
  555.         Date = 5,  
  556.         /// <summary>  
  557.         /// 每个月倒数第N天  
  558.         /// </summary>  
  559.         LastDayOfMonth,  
  560.         /// <summary>  
  561.         /// 未设置  
  562.         /// </summary>  
  563.         NoSet  
  564.     }  
  565.     /// <summary>  
  566.     /// 读取配置数据  
  567.     /// </summary>  
  568.     public class ServiceTimerConfigManager : IConfigurationSectionHandler  
  569.     {  
  570.         private static Regex regEx = new Regex( @"^(?<h>[01]?d|2[0-3])(?:[::](?<m>[0-5]d?))?(?:[::](?<s>[0-5]d?))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase );  
  571.   
  572.         /// <summary>  
  573.         /// 请求服务配置  
  574.         /// </summary>  
  575.         public static ServiceTimerConfig ServiceConfig { get; set; }  
  576.         /// <summary>  
  577.         /// 静态构造函数  
  578.         /// </summary>  
  579.         static ServiceTimerConfigManager()  
  580.         {  
  581.             ConfigurationManager.GetSection( "ServiceTimerConfig" );  
  582.         }  
  583.         /// <summary>  
  584.         /// 读取自定义配置节  
  585.         /// </summary>  
  586.         /// <param name="parent">父结点</param>  
  587.         /// <param name="configContext">配置上下文</param>  
  588.         /// <param name="section">配置区</param>  
  589.         /// <returns></returns>  
  590.         object IConfigurationSectionHandler.Create( object parent, object configContext, XmlNode section )  
  591.         {  
  592.             ServiceConfig = new ServiceTimerConfig();  
  593.             var config = new List<TimerConfig>();  
  594.   
  595.             foreach ( XmlNode node in section.ChildNodes )  
  596.             {  
  597.                 if ( node.NodeType == XmlNodeType.Element )  
  598.                 {  
  599.                     switch ( node.Name.ToLower() )  
  600.                     {  
  601.                         case "default":  
  602.                             ServiceConfig.Default = node.InnerText;  
  603.                             break; ;  
  604.                         case "config":  
  605.                             var tmp = new TimerConfig();  
  606.                             SetTimerConfigValue( tmp, node );  
  607.                             config.Add( tmp );  
  608.                             break; ;  
  609.                     }  
  610.                 }  
  611.             }  
  612.             ServiceConfig.Config = config.ToArray();  
  613.   
  614.             return ServiceConfig;  
  615.         }  
  616.         /// <summary>  
  617.         /// 设置定时器值  
  618.         /// </summary>  
  619.         /// <param name="Config"></param>  
  620.         /// <param name="node"></param>  
  621.         private void SetTimerConfigValue( TimerConfig Config, XmlNode node )  
  622.         {  
  623.             int tmp, h, m, s;  
  624.             long longTmp;  
  625.             var times = new List<TimeSpan>();  
  626.   
  627.             foreach ( XmlNode xn in node.ChildNodes )  
  628.             {  
  629.                 if ( xn.NodeType == XmlNodeType.Element )  
  630.                 {  
  631.                     switch ( xn.Name.ToLower() )  
  632.                     {  
  633.                         case "refname":  
  634.                             Config.RefName = xn.InnerText;  
  635.                             break;  
  636.                         case "timermode":  
  637.                             if ( xn.InnerText != null )  
  638.                                 Config.TimerMode = ( TimerMode )Enum.Parse( typeof( TimerMode ), xn.InnerText );  
  639.                             break;  
  640.                         case "delay":  
  641.                             Int64.TryParse( xn.InnerText, out longTmp );  
  642.                             Config.Delay = new TimeSpan( longTmp * 10 * 1000L );    // Delay配置值为毫秒  
  643.                             break;  
  644.                         case "interval":  
  645.                             Int64.TryParse( xn.InnerText, out longTmp );        // Interval配置值为毫秒  
  646.                             Config.Interval = new TimeSpan( longTmp * 10 * 1000L );  
  647.                             break;  
  648.                         case "monthseq":    // 月份  
  649.                             Int32.TryParse( xn.InnerText, out tmp );  
  650.                             Config.MonthSeq = tmp;  
  651.                             break;  
  652.                         case "dayseq":      // 指定第几天的序号  
  653.                             Int32.TryParse( xn.InnerText, out tmp );  
  654.                             Config.DaySeq = tmp;  
  655.                             break;  
  656.                         case "times":  
  657.                             //还是用这个函数处理下一级的配置  
  658.                             SetTimerConfigValue( Config, xn );  // 设置时间策略  
  659.                             break;  
  660.                         case "timevalue":  
  661.                             var mc = regEx.Match( xn.InnerText );  
  662.                             if ( !mc.Success ) throw new Exception( "时间配置不正确!" );  
  663.                             Int32.TryParse( mc.Groups["h"].Value, out h );  
  664.                             Int32.TryParse( mc.Groups["m"].Value, out m );  
  665.                             Int32.TryParse( mc.Groups["s"].Value, out s );  
  666.                             times.Add( new TimeSpan( h, m, s ) );  
  667.                             break;  
  668.                     }  
  669.                 }  
  670.             }  
  671.             if ( times.Count != 0 )  
  672.                 Config.Times = times.ToArray();  
  673.         }  
  674.     }  
  675.     #endregion  
  676. }  
原文地址:https://www.cnblogs.com/gc2013/p/3940774.html