在运行Quartz作业时补偿TimeZone偏移 [英] Compensating for TimeZone offsets while running Quartz jobs

查看:91
本文介绍了在运行Quartz作业时补偿TimeZone偏移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个独特的问题,就是我的石英作业调度程序实现是使用quartz.net代码库版本2.0.1构建的,最近发现在运行和执行作业时,时区和utc偏移会被忽略。这是此版本的quartz.net中的一个继承错误,目前更新至版本2.1.1已超出范围,因此我编写了一种使用此算法来计算偏移量的快速而肮脏的方法:

I have a bit of a unique problem in that my quartz job scheduler implementation which build using the quartz.net code base ver 2.0.1, recently discovered that the time zone and utc offsets are being ignored while running and executing jobs. This is an inherit bug in this version of quartz.net and updating to version 2.1.1 is out of scope right now, so I wrote a quick and dirty way of calculating the offset using this algorithm:


(ServerTime-ClientTime)-TargetTime = New_TargetTime_With_Offset

(ServerTime - ClientTime) - TargetTime = New_TargetTime_With_Offset

这里的想法是例如,在纽约市,该客户在下午5:00做一份工作,希望它在下午2:00运行。服务器(此应用程序和作业服务器在其中运行)的当前时间是下午2:00,因此我们将客户端时间和服务器时间用于获取偏移并将该偏移应用于目标时间,即作业应运行的时间。

The idea here is the client, who is say in NYC, makes a job at 5:00pm and wants it to run at 2:00pm. The Server (where this app and the job server runs) current time is 2:00pm, so we take the client time and server time to get the offset and apply that offset to the target time, which is the time the job should run.

我的问题是,这似乎是关于日期计算方式的一轮回合,但似乎可以完成这项工作。有没有更好/更可靠的方法来进行日期数学计算?在某些情况下,这似乎还是个问题,我想念什么?

My question is that this feels like a round about way of calculating the dates, but seems like it would do the job. Is there a better / more reliable way of doing this date math? Also this seems to be buggy in edge cases, what am I missing?

这里是实现:

    /// <summary>
    /// Takes three dates and returns the adjusted hour value.
    /// All date data is ignored except for the hour. 
    /// </summary>
    /// <param name="serverTime"></param>
    /// <param name="clientTime"></param>
    /// <param name="targetTime"></param>
    /// <returns></returns>
    private static DateTime OutputDate(DateTime serverTime, DateTime clientTime, DateTime targetTime)
    {
        DateTime? output = null;
        TimeSpan? dateDiff;

        if (serverTime < clientTime)
        {
            dateDiff = (clientTime - serverTime);
        }
        else
        {
            dateDiff = (serverTime - clientTime);
        }

        output = (targetTime - dateDiff);

        return output.Value;
    }

这是两个利用它的示例:

and here are two examples of leveraging it:

    /// <summary>
    /// -5 Offset (NYC)
    /// </summary>
    /// <returns></returns>
    private static Int32 ZoneTest001()
    {
        var targetTime = DateTime.Parse("6/12/2013 5:00PM");  // NYC (est) [The time the report should be received in NYC]
        var clientTime = DateTime.Parse("6/12/2013 5:00PM");   // NYC (est) [The time of the client when the report is created (now) ]
        var serverTime = DateTime.Parse("6/12/2013 2:00PM");  // SEA (pst) [The time of the app server when the report is created (now) ]

        //
        // NYC Wants to send a report at 5:00pm EST
        // The server time will be 2:00pm PST
        // The client time will be 5:00pm EST

        double outputHour = 0;   // should end up as 2:00pm PST

        //
        // 1) Get offset (diff between client & server time)
        // 2) Subtract offset from "targetTime"
        // 3) Set the report to be sent at the new hour value.

        outputHour = OutputDate(serverTime, clientTime, targetTime).Hour;

        return (int)outputHour;

    }

    /// <summary>
    /// +5 Offset (India)
    /// </summary>
    /// <returns></returns>
    private static Int32 ZoneTest002()
    {
        var targetTime = DateTime.Parse("6/12/2013 5:00PM"); // IND (ist)
        var clientTime = DateTime.Parse("6/12/2013 9:00AM");  // IND (ist)
        var serverTime = DateTime.Parse("6/12/2013 2:00PM"); // SEA (pst)

        //
        // INDIA Wants to send a report at 5:00pm IST
        // The server time will be 2:00pm PST
        // The client time will be 9:00am PST

        double outputHour = 0;   // should end up as 2:00pm PST
        outputHour = OutputDate(serverTime, clientTime, targetTime).Hour;

        return (int)outputHour;

    }

谢谢。

推荐答案

您实际上确实缺少很多。

You're missing quite a bit actually.


  1. 时区偏移量不是恒定的。许多时区会切换夏令时的偏移量(也称为夏令时)。因此,当您根据每个位置(服务器,客户端,目标)的现在来计算偏移量时,这仅反映了 current 的偏移量。

在任何具有DST的时区中,时钟向前滚动会丢失一个小时,而时钟向后滚动会出现一个 duplicate 小时。如果您正在处理当地时间,并且计划的事件陷入了一个模糊的时间段,则无法确定运行该事件的实际时间。要消除歧义,您需要告诉

In any time zone that has DST, there is a missing hour when the clocks roll forward, and a duplicated hour when the clocks roll backward. If you are dealing with local time, and a scheduled event falls into an ambiguous time period, you cannot be certain about what actual moment to run it in. In order to disambiguate, you either need to be told what the corresponding offset is, or you need to deal in UTC.

如果要从一个时区转换为另一个时区,则需要处理在时区中,而不仅仅是它们的偏移量。在.Net中,您可以使用内置的Windows时区数据库和相应的 TimeZoneInfo 类。另外,您可以使用更标准的IANA时区数据库,并使用诸如 Noda Time 之类的库。

If you are going to be converting from one time zone to another, you need to deal in time zones, not just their offsets. In .Net, you can use the built in Windows time zone database and the corresponding TimeZoneInfo class. Alternatively, you could use the more standard IANA time zone database, with a library such as Noda Time.

在使用 DateTime 类型时,请特别注意 .Kind 属性设置为。当使用不同的种类时,许多功能具有不同的行为。改用 DateTimeOffset 类型会更安全和有用。

When working with DateTime types, be very careful about what the .Kind property is set to. Many functions have different behaviors when working with different kinds. It would be much safer and more useful to use the DateTimeOffset type instead.

您绝对不应该取决于运行代码的服务器所在的时区。服务器代码应与时区无关。 唯一涉及到 DateTime.Now TimeZoneInfo.Local 或任何类似功能的地方是桌面移动应用程序。服务器代码应仅取决于UTC。

You really should never be dependent on the time zone of the server that your code is running on. Server code should be time zone neutral. The only place where you should ever involve DateTime.Now or TimeZoneInfo.Local or any similar functionality is in desktop or mobile applications. Server code should only depend on UTC.

我真的不明白为什么在$code> OutputDate 方法。没有任何理由。而且,您实际上是在获取差的绝对值-这就是降低方向性。时区偏移量确实是有方向性的,因此您在当前的实现中可能会得到无效的结果。

I don't really see why you have nullable values inside your OutputDate method. There's no reason for that. Also, you are effectively taking the absolute value of the difference - which is dropping directionality. Time zone offsets are indeed directional, so you are probably going to get invalid results with your current implementation.

我查看了Quartz.net API,它似乎他们希望您使用UTC安排活动时间。这是一件非常好的事情,因为与UTC并没有歧义。从 Quartz.Net教程中, trigger.StartTimeUtc 显然是UTC DateTime 。既然您说您不能使用最新版本,我还检查了他们较早的1.0 API文档,但该文档仍然是UTC。

I looked at the Quartz.net API, and it would seem that they prefer you schedule event times in UTC. This is a very good thing, since there is no ambiguity concern with UTC. From the Quartz.Net Tutorial, the trigger.StartTimeUtc is clearly a UTC DateTime. Since you said you couldn't use the latest version, I also checked their older 1.0 API documentation and it is still UTC there.

更新: 石英.Net 2.5和更高版本可以更好地处理时区。有关详细信息,请参见#317

让我们将所有这些放在一起作为示例用例。纽约市的一位客户希望在当地时区的2:00 PM进行工作。服务器的时区无关紧要,他创建作业的时间也无关紧要。

Let's put this all together for your example use case. A customer in NYC wants to run a job at 2:00PM in his local time zone. The time zone of the server is irrelevant, and so is the time that he created the job.

// June 6, 2013 2:00 PM  Kind = Unspecified
DateTime dt = new DateTime(2013, 6, 13, 14, 0, 0);

// This is the correct Windows time zone for New York
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

// Get the time in UTC - The kind matters here.
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);

// Feed it to a quartz event trigger
trigger.StartTimeUtc = utc;

在第三步中将时间转换为UTC时,如果时间不确定,.Net将假设您想要的是标准时间,而不是白天时间。如果您想更具体一点,则必须检查一下模棱两可,然后询问您的用户他们想要在两个本地时间中的哪个时间。然后,您必须使用 DateTimeOffset 进行区分。如果您认为可能需要这样做,请告诉我,我可以提供一个样本,但这有点复杂。

When I converted the time to UTC in the third step, if the time was ambiguous, .Net will assume that you wanted the standard time instead of the daylight time. If you want to be more specific, you'd have to check for ambiguity and then ask your user which of the two local times they wanted. Then you would have to use a DateTimeOffset to distinguish between them. If you think you might need this, let me know and I can produce a sample, but it is a bit more complicated.

如果您想将IANA时区与 Noda时间,它看起来像这样:

And just for good measure, if you wanted to use IANA time zones with Noda Time, it would look like this:

LocalDateTime ldt = new LocalDateTime(2013, 6, 13, 14, 0);
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
ZonedDateTime zdt = ldt.InZoneLeniently(tz);
trigger.StartTimeUtc = zdt.ToDateTimeUtc();

InZoneLeniently 方法将产生相同的行为如上面的代码。但是如果需要,您还可以指定其他选项。

The InZoneLeniently method will give the same behavior as the above code. But there are other options you can specify if desired.

哦,这没关系,但是印度是 +5:30 ,而不是 +5

Oh, and not that it matters, but India is +5:30, not +5

这篇关于在运行Quartz作业时补偿TimeZone偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆