v9.10.2019.0101 全面巩固批量Insert/Update/Upsert,支持数据备份、恢复和同步,支持实体列表保存到文件以及加载
|
# Cron±í´ïʽ
## ¸ÅÊö
`NewLife.Threading.Cron` ÊÇÒ»¸öÇáÁ¿¼¶µÄ Cron ±í´ïʽ½âÎöºÍÆ¥ÅäÆ÷£¬ÓÃÓÚÅжÏij¸öʱ¼äµãÊÇ·ñÂú×ã¹æÔò£¬²¢ÄܼÆËãÏÂÒ»´Î/ÉÏÒ»´ÎµÄÖ´ÐÐʱ¼ä¡£
Óë±ê×¼ Cron Ïà±È£¬NewLife.Cron ¸ü¼ÓÇáÁ¿¼ò½à£¬ÊʺÏÔÚ¶¨Ê±ÈÎÎñ¡¢µ÷¶ÈϵͳÖÐʹÓá£
**ÃüÃû¿Õ¼ä**: `NewLife.Threading`
**Ô´Âë**: [NewLife.Core/Threading/Cron.cs](https://github.com/NewLifeX/X/blob/master/NewLife.Core/Threading/Cron.cs)
---
## ¿ìËÙÈëÃÅ
### ´´½¨ºÍ½âÎö
```csharp
using NewLife.Threading;
// ·½Ê½1£º¹¹Ôìʱ´«Èë±í´ïʽ
var cron = new Cron("0 0 2 * * 1-5");
// ·½Ê½2£ºÏÈ´´½¨ºó½âÎö
var cron2 = new Cron();
cron2.Parse("*/5 * * * * *");
```
### ÅжÏʱ¼äÊÇ·ñÆ¥Åä
```csharp
var cron = new Cron("0 0 2 * * 1-5"); // ÿ¸ö¹¤×÷ÈÕÁ賿2µã
// Åжϵ±Ç°Ê±¼äÊÇ·ñ·ûºÏ±í´ïʽ
if (cron.IsTime(DateTime.Now))
{
Console.WriteLine("ÏÖÔÚÊÇÖ´ÐÐʱ¼ä");
}
// ÅжÏÌØ¶¨Ê±¼ä
var time = new DateTime(2025, 1, 6, 2, 0, 0); // 2025Äê1ÔÂ6ÈÕ(ÖÜÒ») 2µã
if (cron.IsTime(time))
{
Console.WriteLine("¸Ãʱ¼äµã·ûºÏ¹æÔò");
}
```
### ¼ÆËãÏÂÒ»´ÎÖ´ÐÐʱ¼ä
```csharp
var cron = new Cron("0 0 2 * * *"); // ÿÌìÁ賿2µã
var next = cron.GetNext(DateTime.Now);
Console.WriteLine($"ÏÂÒ»´ÎÖ´ÐÐʱ¼ä£º{next}");
// ¼ÆËãÉÏÒ»´ÎÖ´ÐÐʱ¼ä
var prev = cron.GetPrevious(DateTime.Now);
Console.WriteLine($"ÉÏÒ»´ÎÖ´ÐÐʱ¼ä£º{prev}");
```
---
## ±í´ïʽÓï·¨
### ±í´ïʽ½á¹¹
Cron ±í´ïʽÓɿոñ·Ö¸ôµÄ **6 ¸ö×Ö¶Î**×é³É£¨µÚ7¸ö×Ö¶Î"Äê"Ôݲ»Ö§³Ö£©£º
```
Ãë ·Ö Ê± ÈÕ Ô ÐÇÆÚ
```
| ×Ö¶Î | ·¶Î§ | ˵Ã÷ |
|-----|------|------|
| Ãë | 0-59 | ÃëÊý |
| ·Ö | 0-59 | ·ÖÖÓÊý |
| ʱ | 0-23 | СʱÊý£¨24Ð¡Ê±ÖÆ£© |
| ÈÕ | 1-31 | ÿÔµĵڼ¸Ìì |
| Ô | 1-12 | ÔÂ·Ý |
| ÐÇÆÚ | 0-6 | ÐÇÆÚ¼¸£¨0=ÖÜÈÕ£¬1=ÖÜÒ»£¬...£¬6=ÖÜÁù£© |
**ʾÀý**£º
```
0 30 8 * * 1-5 // ÿ¸ö¹¤×÷ÈÕ 8:30
0 0 */2 * * * // ÿ2СʱÕûµã
0 0 0 1 * * // ÿÔÂ1ºÅÁ賿
*/10 * * * * * // ÿ10Ãë
```
### Ö§³ÖµÄÓï·¨
#### 1. ͨÅä·û `*`
±íʾËùÓпÉÄܵÄÖµ¡£
```
* * * * * * // ÿÃë
0 * * * * * // ÿ·ÖÖÓµÄ0Ãë
0 0 * * * * // ÿСʱµÄ0·Ö0Ãë
```
#### 2. ռλ·û `?`
²»Ö¸¶¨Öµ£¬Êµ¼ÊÉϵȼÛÓÚ `*`£¬Ö÷ÒªÓÃÓÚ¼æÈÝ Quartz µÈÆäËû Cron ʵÏÖ¡£
```
0 0 0 ? * 1 // ÿÖÜÒ»Á賿£¨ÈÕÆÚ²»Ö¸¶¨£©
```
#### 3. ö¾Ù `a,b,c`
Áгö¶à¸öÖ¸¶¨Öµ¡£
```
0 0 0 1,15 * * // ÿÔÂ1ºÅºÍ15ºÅÁ賿
0 0 8,12,18 * * * // ÿÌì8µã¡¢12µã¡¢18µã
```
#### 4. ·¶Î§ `a-b`
±íʾһ¸öÁ¬Ðø·¶Î§£¨±ÕÇø¼ä£©¡£
```
0 0 2 * * 1-5 // ÖÜÒ»µ½ÖÜÎåÁ賿2µã
0 0 9-17 * * * // ÿÌì9µãµ½17µã£¨Ã¿Ð¡Ê±£©
```
#### 5. ²½½ø `*/n`¡¢`a/n`¡¢`a-b/n`
±íʾ°´ÕÕÒ»¶¨µÄÔöÁ¿Ñ¡ÔñÖµ¡£
- `*/n`£º´Ó0¿ªÊ¼£¬Ã¿¸ônѡһ¸ö
- `a/n`£º´Óa¿ªÊ¼£¬Ã¿¸ônѡһ¸ö
- `a-b/n`£ºÔÚaµ½b·¶Î§ÄÚ£¬Ã¿¸ônѡһ¸ö
```
*/2 * * * * * // ÿ2Ã루0,2,4,6...Ã룩
5/20 * * * * * // ÿ·ÖÖÓµÄ5Ãë¡¢25Ãë¡¢45Ãë
0 */30 * * * * // ÿ30·ÖÖÓ
0 0 0 */5 * * // ÿ5Ìì
```
#### 6. µÚ¼¸¸öÐÇÆÚ¼¸ `d#k` ºÍ `d#Lk`
½öÐÇÆÚ×Ö¶ÎÖ§³Ö£¬ÓÃÓÚ±í´ï"ÿÔµڼ¸¸öÐÇÆÚ¼¸"¡£
- `d#k`£ºÃ¿ÔµÚk¸öÐÇÆÚd
- `d#Lk`£ºÃ¿Ôµ¹ÊýµÚk¸öÐÇÆÚd
```
0 0 0 ? ? 1#1 // ÿÔµÚ1¸öÖÜÒ»Á賿
0 0 0 ? ? 5#2 // ÿÔµÚ2¸öÖÜÎåÁ賿
0 0 0 ? ? 1#L1 // ÿÔÂ×îºó1¸öÖÜÒ»Á賿
0 0 0 ? ? 3-5#L2 // ÿÔµ¹ÊýµÚ2¸öÖÜÈýµ½ÖÜÎåÁ賿
```
**×¢Òâ**£º`#` Óï·¨»á½«ÐÇÆÚÊýÄ£ 7£¬Òò´Ë¿ÉÒÔÓà 7 ±íʾÖÜÈÕ¡£
---
## ÐÇÆÚÆ«ÒÆ
### ĬÈÏÐÐΪ
Cron ĬÈϲÉÓà Linux/.NET ·ç¸ñ£º
- `0` ±íʾÖÜÈÕ (Sunday)
- `1` ±íʾÖÜÒ» (Monday)
- `2` ±íʾÖܶþ (Tuesday)
- ...
- `6` ±íʾÖÜÁù (Saturday)
### Sunday ÊôÐÔ
`Sunday` ÊôÐÔÓÃÓÚµ÷Õû"ÖÜÈÕ"¶ÔÓ¦µÄÊý×ÖÆ«ÒÆ£º
```csharp
var cron = new Cron();
cron.Sunday = 0; // ĬÈÏÖµ£¬0±íʾÖÜÈÕ
// 0=ÖÜÈÕ£¬1=ÖÜÒ»£¬2=Öܶþ...
cron.Sunday = 1; // ÐÞ¸ÄΪ1±íʾÖÜÈÕ
// 1=ÖÜÈÕ£¬2=ÖÜÒ»£¬3=Öܶþ...
```
**ʹÓý¨Òé**£º
- Ò»°ãÇé¿öϱ£³ÖĬÈÏ `Sunday = 0` ¼´¿É
- Èç¹ûÐèÒª¼æÈÝÆäËûϵͳ£¨ÈçijЩÊý¾Ý¿â£©£¬¿Éµ÷ÕûΪ `Sunday = 1`
- `Parse` ·½·¨²»»á×Ô¶¯ÍÆ¶Ï `Sunday`£¬ÐèÒªÊÖ¶¯ÉèÖÃ
---
## ºËÐÄ API
### IsTime - ÅжÏʱ¼äÊÇ·ñÆ¥Åä
```csharp
/// <summary>Ö¸¶¨Ê±¼äÊÇ·ñλÓÚ±í´ïʽ֮ÄÚ</summary>
/// <param name="time">ÒªÅжϵÄʱ¼ä</param>
/// <returns>ÊÇ·ñÆ¥Åä</returns>
public Boolean IsTime(DateTime time)
```
**ʾÀý**£º
```csharp
var cron = new Cron("0 0 2 * * 1-5");
var time = new DateTime(2025, 1, 6, 2, 0, 0);
if (cron.IsTime(time))
{
Console.WriteLine("Æ¥Åä");
}
```
**×¢ÒâÊÂÏî**£º
- ÅжÏʱ»á¿¼ÂÇÃë¡¢·Ö¡¢Ê±¡¢ÈÕ¡¢Ô¡¢ÐÇÆÚËùÓÐά¶È
- ÐÇÆÚ×Ö¶ÎÖ§³Ö"µÚ¼¸¸öÐÇÆÚ¼¸"µÄ¸´ÔÓÅжÏ
- ʱ¼ä»á°´ÕÕ `Sunday` ÊôÐÔ½øÐÐÐÇÆÚ¼ÆËã
### GetNext - »ñÈ¡ÏÂÒ»´ÎÖ´ÐÐʱ¼ä
```csharp
/// <summary>»ñµÃÖ¸¶¨Ê±¼äÖ®ºóµÄÏÂÒ»´ÎÖ´ÐÐʱ¼ä£¬²»º¬Ö¸¶¨Ê±¼ä</summary>
/// <param name="time">´Ó¸Ãʱ¼äÃëµÄÏÂÒ»ÃëËãÆðµÄÏÂÒ»¸öÖ´ÐÐʱ¼ä</param>
/// <returns>ÏÂÒ»´ÎÖ´ÐÐʱ¼ä£¨Ãë¼¶£©£¬Èç¹ûûÓÐÆ¥ÅäÔò·µ»Ø×îСʱ¼ä</returns>
public DateTime GetNext(DateTime time)
```
**ʾÀý**£º
```csharp
var cron = new Cron("0 0 2 * * *"); // ÿÌìÁ賿2µã
var now = DateTime.Now;
var next = cron.GetNext(now);
Console.WriteLine($"ÏÂÒ»´ÎÖ´ÐУº{next:yyyy-MM-dd HH:mm:ss}");
```
**×¢ÒâÊÂÏî**£º
- Èç¹û´«Èëʱ¼ä´øÓкÁÃ루Èç 09:14:23.456£©£¬»áÏòǰ¶ÔÆëµ½ÏÂÒ»Ã루09:14:24£©ºóÔÙ¼ÆËã
- ·µ»ØµÄʱ¼ä²»°üº¬´«ÈëµÄʱ¼ä±¾Éí
- Èç¹û1ÄêÄÚÕÒ²»µ½Æ¥Åäʱ¼ä£¬·µ»Ø `DateTime.MinValue`
- **ÐÔÄܾ¯¸æ**£º¸Ã·½·¨Í¨¹ýÖðÃë±éÀú²éÕÒ£¬×î¶à±éÀú1Ä꣬²»ÊÊºÏÆµ·±µ÷ÓÃ
### GetPrevious - »ñÈ¡ÉÏÒ»´ÎÖ´ÐÐʱ¼ä
```csharp
/// <summary>»ñµÃÓëÖ¸¶¨Ê±¼ä·ûºÏ±í´ïʽµÄ×î½ü¹ýȥʱ¼ä£¨Ãë¼¶£©</summary>
/// <param name="time">»ù׼ʱ¼ä</param>
/// <returns>ÉÏÒ»´ÎÖ´ÐÐʱ¼ä£¬Èç¹ûûÓÐÆ¥ÅäÔò·µ»Ø×îСʱ¼ä</returns>
public DateTime GetPrevious(DateTime time)
```
**ʾÀý**£º
```csharp
var cron = new Cron("0 0 2 * * *");
var prev = cron.GetPrevious(DateTime.Now);
Console.WriteLine($"ÉÏÒ»´ÎÖ´ÐУº{prev:yyyy-MM-dd HH:mm:ss}");
```
### ÅúÁ¿ Cron ¼ÆËã
```csharp
/// <summary>¶ÔÒ»ÅúCron±í´ïʽ£¬»ñÈ¡ÏÂÒ»´ÎÖ´ÐÐʱ¼ä</summary>
public static DateTime GetNext(String[] crons, DateTime time)
/// <summary>¶ÔÒ»ÅúCron±í´ïʽ£¬»ñȡǰһ´ÎÖ´ÐÐʱ¼ä</summary>
public static DateTime GetPrevious(String[] crons, DateTime time)
```
**ʾÀý**£º
```csharp
var crons = new[] { "0 0 2 * * *", "0 0 14 * * *" }; // ÿÌì2µãºÍ14µã
var next = Cron.GetNext(crons, DateTime.Now);
Console.WriteLine($"ÏÂÒ»´ÎÖ´ÐУº{next}");
```
---
## ÅäºÏ TimerX ʹÓÃ
Cron ×î³£¼ûµÄʹÓó¡¾°ÊÇÅäºÏ `TimerX` ʵÏÖ¶¨Ê±ÈÎÎñ£º
```csharp
using NewLife.Threading;
// ´´½¨ Cron ¶¨Ê±Æ÷£ºÃ¿¸ö¹¤×÷ÈÕÔçÉÏ8µãÖ´ÐÐ
var timer = new TimerX(state =>
{
Console.WriteLine($"Ö´ÐÐÈÎÎñ£º{DateTime.Now}");
}, null, "0 0 8 * * 1-5");
// Ö§³Ö¶à¸ö Cron ±í´ïʽ£¬·ÖºÅ·Ö¸ô
var timer2 = new TimerX(state =>
{
Console.WriteLine("Ö´ÐÐÈÎÎñ");
}, null, "0 0 2 * * 1-5;0 0 3 * * 6"); // ¹¤×÷ÈÕ2µã£¬ÖÜÁù3µã
// ʹÓÃÍê±Ï¼ÇµÃÊÍ·Å
timer.Dispose();
timer2.Dispose();
```
Ïê¼û£º[TimerX ʹÓÃÊÖ²á](/NewLife/X/Blob/dev/Doc/timerx-¸ß¼¶¶¨Ê±Æ÷TimerX.md)
---
## ³£Óñí´ïʽʾÀý
### ÿÃë/ÿ·Ö/ÿʱ
```
* * * * * * // ÿÃë
*/2 * * * * * // ÿ2Ãë
*/5 * * * * * // ÿ5Ãë
0 * * * * * // ÿ·ÖÖÓ
0 */5 * * * * // ÿ5·ÖÖÓ
0 */15 * * * * // ÿ15·ÖÖÓ
0 */30 * * * * // ÿ30·ÖÖÓ
0 0 * * * * // ÿСʱ
0 0 */2 * * * // ÿ2Сʱ
```
### ÿÌì¹Ì¶¨Ê±¼ä
```
0 0 0 * * * // ÿÌìÁ賿0µã
0 0 1 * * * // ÿÌìÁ賿1µã
0 30 8 * * * // ÿÌì8µã30·Ö
0 0 12 * * * // ÿÌìÖÐÎç12µã
0 0 23 * * * // ÿÌìÍíÉÏ23µã
0 0 0,12 * * * // ÿÌì0µãºÍ12µã
0 0 8,12,18 * * * // ÿÌì8µã¡¢12µã¡¢18µã
```
### ¹¤×÷ÈÕ/ÖÜÄ©
```
0 0 9 * * 1-5 // ÿ¸ö¹¤×÷ÈÕÔçÉÏ9µã
0 0 2 * * 1-5 // ÿ¸ö¹¤×÷ÈÕÁ賿2µã
0 0 10 * * 6,0 // ÿ¸öÖÜÄ©£¨ÖÜÁù¡¢ÖÜÈÕ£©10µã
0 0 0 * * 1 // ÿÖÜÒ»Á賿
0 0 0 * * 5 // ÿÖÜÎåÁ賿
```
### ÿÔ¹̶¨ÈÕÆÚ
```
0 0 0 1 * * // ÿÔÂ1ºÅÁ賿
0 0 0 15 * * // ÿÔÂ15ºÅÁ賿
0 0 0 1,15 * * // ÿÔÂ1ºÅºÍ15ºÅÁ賿
0 0 0 L * * // ÿÔÂ×îºóÒ»ÌìÁ賿£¨ÐèÅäºÏÌØÊâ´¦Àí£©
```
### ¸´ÔÓ³¡¾°
```
0 0 2 * * 1-5 // ÿ¸ö¹¤×÷ÈÕÁ賿2µã
5/20 * * * * * // ÿ·ÖÖÓµÄ5Ãë¡¢25Ãë¡¢45Ãë
0 0 0 ? ? 1#1 // ÿÔµÚ1¸öÖÜÒ»Á賿
0 0 0 ? ? 5#L1 // ÿÔÂ×îºó1¸öÖÜÎåÁ賿
0 0 0 1-7 * 1 // ÿÔµÚÒ»¸öÖÜÒ»Á賿£¨ÁíÒ»ÖÖд·¨£©
```
---
## ×¢ÒâÊÂÏîÓëÏÞÖÆ
### ÐÔÄÜ¿¼Á¿
`GetNext` ºÍ `GetPrevious` ·½·¨Í¨¹ý**ÖðÃë±éÀú**ʵÏÖ£¬ÐÔÄÜÌØµã£º
- **Êʺϳ¡¾°**£ºµÍƵÂʵ÷Óã¬È綨ʱÈÎÎñ³õʼ»¯Ê±¼ÆËãÏÂÒ»´ÎÖ´ÐÐʱ¼ä
- **²»Êʺϳ¡¾°**£º¸ßƵµ÷Óá¢ÊµÊ±Â·¾¶¡¢Èȵã´úÂë
- **ÐÔÄÜÉÏÏÞ**£º×î¶à±éÀú1Ä꣨Լ3ǧÍò´ÎÑ»·£©
**½¨Òé**£º
- ÔÚ `TimerX` ³õʼ»¯Ê±¼ÆËãÒ»´ÎÏÂÒ»´ÎÖ´ÐÐʱ¼ä¼´¿É
- ±ÜÃâÔÚÑ»·ÖÐÆµ·±µ÷ÓÃ
- ÈçÐè¸ßÐÔÄܳ¡¾°£¬¿¼ÂÇ×ÔÐÐʵÏÖËã·¨
### ²»Ö§³ÖÄê·Ý×Ö¶Î
µ±Ç°ÊµÏÖ½öÖ§³Ö6¸ö×ֶΣ¨Ãë¡¢·Ö¡¢Ê±¡¢ÈÕ¡¢Ô¡¢ÐÇÆÚ£©£¬**²»Ö§³ÖµÚ7¸ö×Ö¶Î"Äê"**¡£
### ×Ö¶Îȱʡ¹æÔò
- Èç¹û±í´ïʽÉÙÓÚ6¸ö×ֶΣ¬È±Ê¡×Ö¶ÎĬÈÏΪ `*`
- ÀýÈ磺`0 0 2 * *` »á±»½âÎöΪ `0 0 2 * * *`
### ÐÇÆÚ×Ö¶ÎÌØÊâÐÔ
- ÐÇÆÚ¼ÆËãÊÜ `Sunday` ÊôÐÔÓ°Ïì
- `#` Óï·¨»á½«ÐÇÆÚÊýÄ£7£¬Òò´Ë 7 Ò²¿ÉÒÔ±íʾÖÜÈÕ
- ÐÇÆÚ·¶Î§ÊÇ 0-6£¨»ò¸ù¾Ý `Sunday` µ÷ÕûΪ 1-7£©
---
## Ô´Âë½âÎö
### ½âÎöÁ÷³Ì
```csharp
public Boolean Parse(String expression)
{
var ss = expression.Split([' '], StringSplitOptions.RemoveEmptyEntries);
// ½âÎöÃë
if (!TryParse(ss[0], 0, 60, out var vs)) return false;
Seconds = vs;
// ½âÎö·Ö
if (!TryParse(ss.Length > 1 ? ss[1] : "*", 0, 60, out vs)) return false;
Minutes = vs;
// ... ÒÀ´Î½âÎöʱ¡¢ÈÕ¡¢ÔÂ
// ½âÎöÐÇÆÚ£¨ÌØÊâ´¦Àí£©
var weeks = new Dictionary<Int32, Int32>();
if (!TryParseWeek(ss.Length > 5 ? ss[5] : "*", 0, 7, weeks)) return false;
DaysOfWeek = weeks;
return true;
}
```
### Æ¥ÅäÅжÏ
```csharp
public Boolean IsTime(DateTime time)
{
// »ù´¡Ê±¼äÅжÏ
if (!Seconds.Contains(time.Second) ||
!Minutes.Contains(time.Minute) ||
!Hours.Contains(time.Hour) ||
!DaysOfMonth.Contains(time.Day) ||
!Months.Contains(time.Month))
return false;
// ÐÇÆÚÅжϣ¨¿¼ÂÇ Sunday Æ«ÒÆ£©
var w = (Int32)time.DayOfWeek + Sunday;
if (!DaysOfWeek.TryGetValue(w, out var index)) return false;
// µÚ¼¸¸öÐÇÆÚ¼¸Åжϣ¨index > 0 ±íʾÕýÊýµÚ¼¸¸ö£¬< 0 ±íʾµ¹Êý£©
// ... ¸´ÔÓÂß¼
return true;
}
```
---
## ³£¼ûÎÊÌâ
### 1. ±í´ïʽ½âÎöʧ°Ü£¿
¼ì²éÓï·¨ÊÇ·ñÕýÈ·£º
- ×Ö¶ÎÊýÁ¿ÊÇ·ñΪ6¸ö£¨»ò¸üÉÙ£¬È±Ê¡Ä¬ÈÏ`*`£©
- ·¶Î§ÖµÊÇ·ñÔÚÓÐЧ·¶Î§ÄÚ
- ²½½øÖµÓï·¨ÊÇ·ñÕýÈ·
```csharp
var cron = new Cron();
if (!cron.Parse("0 0 2 * * 1-5"))
{
Console.WriteLine("½âÎöʧ°Ü");
}
```
### 2. GetNext ·µ»Ø MinValue£¿
˵Ã÷1ÄêÄÚÕÒ²»µ½Æ¥ÅäµÄʱ¼ä£¬¿ÉÄÜÔÒò£º
- ±í´ïʽ±¾Éíì¶Ü£¨Èç `0 0 0 31 2 *` 2ÔÂûÓÐ31ºÅ£©
- ÈÕÆÚºÍÐÇÆÚ³åÍ»
### 3. ÐÇÆÚ¼ÆËã²»¶Ô£¿
¼ì²é `Sunday` ÊôÐÔÉèÖãº
```csharp
var cron = new Cron("0 0 0 * * 1");
cron.Sunday = 0; // È·ÈÏÆ«ÒÆÁ¿
```
### 4. ºÁÃëÓ°Ïì¼ÆË㣿
`GetNext` »á×Ô¶¯´¦ÀíºÁÃ룬ÏòÉÏ¶ÔÆëµ½ÏÂÒ»Ã룺
```csharp
var time = new DateTime(2025, 1, 1, 9, 14, 23, 456); // ´øºÁÃë
var next = cron.GetNext(time); // ´Ó 09:14:24 ¿ªÊ¼¼ÆËã
```
---
## ²Î¿¼×ÊÁÏ
- **TimerX Îĵµ**: [timerx-¸ß¼¶¶¨Ê±Æ÷TimerX.md](/NewLife/X/Blob/dev/Doc/timerx-¸ß¼¶¶¨Ê±Æ÷TimerX.md)
- **°¢ÀïÔÆCron²Î¿¼**: https://help.aliyun.com/document_detail/64769.html
- **ÔÚÏßÎĵµ**: https://newlifex.com/core/cron
- **Ô´Âë**: https://github.com/NewLifeX/X/blob/master/NewLife.Core/Threading/Cron.cs
---
## ¸üÐÂÈÕÖ¾
- **2025-01**: ÍêÉÆÎĵµ£¬²¹³äÏêϸʾÀýºÍÔ´Âë½âÎö
- **2024**: Ö§³Ö .NET 9.0
- **2023**: ÓÅ»¯½âÎöÐÔÄÜ
- **2022**: Ôö¼ÓÅúÁ¿Cron¼ÆËã·½·¨
- **2020**: ³õʼ°æ±¾£¬Ö§³Ö»ù±¾CronÓï·¨
|