• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
netcore_vue
博客园    首页    新随笔    联系   管理    订阅  订阅

基于HangFire任务调试中心-Cron表达式日期

基于HangFire开发一个后台任务调度中心,完成实现与业务系统分隔执行。

定时任务

 新建定时任务

 在开发过程中,由于没有很好的理解Cron表达式的应用,在获取定时任务时,创建时间和任务最后的执行时间,有很严重的偏差,不得其原因为何!如下图所示。

创建时间是上午8点,执行时间是当天的凌晨1点?

 

分析原因之前,了解一下Cron表达式:

Cron 表达式是用于定义固定时间、日期和间隔的掩码。掩码由第二个(可选)、分钟、小时、月份中的某一天、月份和星期几字段组成。所有字段都允许您指定多个值,如果所有字段都包含匹配的值,则任何给定的日期/时间都将满足指定的 Cron 表达式。

                                       Allowed values    Allowed special characters   Comment

┌───────────── second (optional)       0-59              * , - /                      
│ ┌───────────── minute                0-59              * , - /                      
│ │ ┌───────────── hour                0-23              * , - /                      
│ │ │ ┌───────────── day of month      1-31              * , - / L W ?                
│ │ │ │ ┌───────────── month           1-12 or JAN-DEC   * , - /                      
│ │ │ │ │ ┌───────────── day of week   0-6  or SUN-SAT   * , - / # L ?                Both 0 and 7 means SUN
│ │ │ │ │ │
* * * * * *
或理解如下

* * * * * *
- - - - - -
| | | | | |
| | | | | +----- 星期中星期几 (0 - 7) [星期天 = 0或7]
| | | | +------- 月份 (1 - 12)
| | | +--------- 一个月中的第几天 (1 - 31)
| | +----------- 小时 (0 - 23)
| +------------- 分钟 (0 - 59)
+--------------- 秒 (0 - 59)

推荐一个Cron表达式的Nuget包,

PM> Install-Package Cronos

github上地址:GitHub - HangfireIO/Cronos:一个功能齐全的 .NET 库,用于处理 Cron 表达式。在构建时时考虑了时区,并直观地处理夏令时转换

using Cronos;

CronExpression expression = CronExpression.Parse("* * * * *");

DateTime? nextUtc = expression.GetNextOccurrence(DateTime.UtcNow);

说明:将包含给定时间之后的 UTC 中的下一个匹配项,或无法访问时的值(例如,2 月 30 日)。如果给出无效的 Cron 表达式,则会引发异常。nextUtcnullCronFormatException

从上述描述中可以知道 Cron使用的时间是UTC,接着了解一下UTC:

在.NET中,`DateTime.UtcNow` 是 `System.DateTime` 类的一个属性,它返回当前的协调世界时(UTC,Universal Time Coordinated)时间。UTC 是一个全球统一的时间标准,它与格林尼治标准时间(GMT)相同,不考虑夏令时(DST)。

`DateTime.UtcNow` 属性的特点是:

- 它总是返回 UTC 时间,不考虑调用它的计算机的本地时区设置。
- 它返回的时间是不带时区信息的,也就是说,它不包含关于时区的任何信息,只是简单地返回当前的 UTC 时间。

如果你看到 `DateTime.Now` 或者 `DateTime.UtcNow`,它们的区别在于:

- `DateTime.Now` 返回的是调用它的计算机的本地时间,它考虑了本地时区和夏令时设置。
- `DateTime.UtcNow` 返回的是 UTC 时间,不考虑任何时区转换。

在进行日期和时间的编程时,使用 `DateTime.UtcNow` 可以避免时区引起的混淆,尤其是在涉及跨时区通信或存储时间戳的应用程序中。

如何将UTC转成本地时间?

CronExpression expression = CronExpression.Parse("* * * * *");
DateTimeOffset? next = expression.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);

var nextLocalTime = next?.DateTime;

或

使用`UtcNow` 是 `DateTime` 结构的一个静态属性,它返回当前的协调世界时(UTC)时间。UTC 是一种标准的时间参考,不受时区的影响,被广泛用于跨时区的时间表示和计算。

`DateTime.UtcNow` 返回的是一个 `DateTime` 对象,表示当前的 UTC 时间。这个对象包含了年、月、日、时、分、秒和毫秒等时间信息。

使用 `DateTime.UtcNow` 可以获取当前的 UTC 时间,而不受本地时区的影响。如果需要将 UTC 时间转换为其他时区的本地时间,可以使用 `TimeZoneInfo` 类的方法进行转换。

示例代码如下:

DateTime utcTime = DateTime.UtcNow;
Console.WriteLine("Current UTC time: " + utcTime);

TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, timeZone);
Console.WriteLine("Current local time in Shanghai: " + localTime);

在上述代码中,我们首先使用 `DateTime.UtcNow` 获取当前的 UTC 时间,并将其存储在 `utcTime` 变量中。然后,我们使用 `TimeZoneInfo.FindSystemTimeZoneById` 方法获取 "Asia/Shanghai" 时区的信息,并使用 `TimeZoneInfo.ConvertTimeFromUtc` 方法将 UTC 时间转换为上海的本地时间。

示例代码,UTC转成本地时间:

     public async Task<List<HttpJobDescriptorDto>> GetAllRecurringJobsList(string? jobName)
     {
         var getRecurringJobs = (await _repository.GetQueryableAsync()).Where(p => p.JobType == ((int)HangFireJobTypeEnum.RecurringJobs).ToString()).ToList();//过滤定时任务列表
         var dtos = ObjectMapper.Map<List<HttpJobDescriptor>, List<HttpJobDescriptorDto>>(getRecurringJobs);
         if (dtos.Count > 0)
         {
             var jobList = GetAllRecurringJobs();
             var _query = from job in dtos
                          join hangfirejob in jobList
                          on job.JobName equals hangfirejob.Id into tempJob
                          from hangfire in tempJob.DefaultIfEmpty()
                          where hangfire != null
                          select new HttpJobDescriptorDto()
                          {
                              Id = job.Id,//主键ID
                              HttpUrl = job.HttpUrl,
                              JobId =string.IsNullOrEmpty(hangfire.LastJobId) ? 0 : int.Parse(hangfire.LastJobId),//最近执行的任务的ID
                              JobName = job.JobName,
                              JobType = ((int)HangFireJobTypeEnum.RecurringJobs).ToString(),//标识定时任务
                              StateName = string.IsNullOrEmpty(hangfire.LastJobId) ? "任务新建,启动中" : hangfire.LastJobState,
                              HttpMethod = job.HttpMethod,//参数类型
                              Cron = job.Cron,
                              LastJobState = hangfire.LastJobState,//最近一次运行状态
                              LastExecution = hangfire.LastExecution.HasValue ? TimeZoneInfo.ConvertTimeFromUtc(hangfire.LastExecution.Value, TimeZoneInfo.Local):null,
                              //LastExecution = CronExpression.Parse(job.Cron).GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local)?.DateTime,
                              //LastExecution = hangfire.LastExecution,//最近一次运行时间,用的是协调世界时(UTC)时间,不受时区的影响
                              CreationTime = job.CreationTime,//创建时间
                              Remark = job.Remark,
                              TimeSpanFromSeconds=job.TimeSpanFromSeconds,
                          };
             dtos = _query.WhereIf(!string.IsNullOrEmpty(jobName), p => p.JobName.Contains(jobName)).ToList();
         }
         return dtos;
     }

 

posted @ 2024-05-31 08:52  梦想代码-0431  阅读(382)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3