Hangfire: TimeZone conversion

Created on 24 Sep 2018  Â·  5Comments  Â·  Source: HangfireIO/Hangfire

Non windows timezones are not converted on windows when Hangfire server is hosted in windows environment. The RecurringJobs dashboard crashes while trying to find the timezone.

Here is my use case....

I am hosting Hangfire in an ASP.NET MVC core project and also MongoDB in linux docker containers. When I receive a request to add a recurringJob, I create a recurringJob with the timezone in IANA format(America/New_York). If I have another instance of my process running in a windows machine trying to access the same database, the dashboard page crashes as it can't find the IANA timezoneId on the windows machine. Can the dashboard use some sort of converter like the TimeZoneConverter to convert the timezone to windows format if it is stored as IANA format?

Here is the screenshot.

image

I am using Hangfire 1.7.0-beta1.

core problem

Most helpful comment

Starting from 1.7.0 there's the ITimeZoneResolver interface you can use to specify the resolver. So together with TimeZoneConverter package it's possible to implement a platform-independent resolver that understand both IANA and Windows time zone identifiers.

public sealed class TimeZoneConverterResolver : ITimeZoneResolver
{
    public TimeZoneInfo GetTimeZoneById(string timeZoneId)
    {
        return TZConvert.GetTimeZoneInfo(timeZoneId);
    }
}

You can pass it using the BackgroundJobServerOptions.TimeZoneResolver property or register implementation for ITimeZoneResolver interface in .NET Core applications:

services.TryAddSingleton<ITimeZoneResolver>(new TimeZoneConverterResolver());

All 5 comments

I'd been speculating that i would run into this problem since I started to pass to timeZone parameter to the RecurringJobManager.

I'd run in to this issue #11897 on the CoreFx repository when I was researching how to map Linux TimeZoneIds to Windows TimeZoneIds. I needed to fetch a TimeZoneInfo by the method TimeZoneInfo.FindSystemTimeZoneById (string), with TimeZoneId being a string passed by the client application. Since both Client and Server could be running on any one of this OSs, I had to find something that could convert on both ways. And I found it. On this issue @mj1856 posted a link to his library TimeZoneConverter, that is able to make the conversion i needed.

Now, as you can read on the discussion, this conversion is not something easy to maintain, and require a constant update of the library.

I don't think @odinserj want to put this kind of dependency on this library.

But, I think it is possible to make the TimeZoneInfo resolution extensible. We could find all the places where TimeZoneInfo.FindSystemTimeZoneById (string) is called, and repleace it with a call to some ITimeZoneInfoResolver.FindSystemTimeZoneById(string). Then we would have a DefaultTimeZoneInfoResolver, that would the same as today, just call TimeZoneInfo.FindSystemTimeZoneById (string), and provide a way for the library user to inject the a different implementation for the ITimeZoneInfoResolver.

Do you think it's doable, @odinserj?

Also ran into this issue. We have a client app running on Mac which is able to create recurring jobs and then the servers running on Azure (Windows). Needless to say, this does not play well.

In my opinion, the easiest solution would be to change RecurringJobOptions object so it contains a time zone id instead of a TimeZoneInfo object. Then, when a TimeZoneInfo object needs to be instantiated, a ITimeZoneInfoResolver suggested above by @jamir-araujo should kick in.

In any case, we need some kind of solution here. Nowdays, when .NET Core allows us to run stuff on the multitude of OSes, we cannot ignore the problem anymore. @odinserj, do you agree?

Starting from 1.7.0 there's the ITimeZoneResolver interface you can use to specify the resolver. So together with TimeZoneConverter package it's possible to implement a platform-independent resolver that understand both IANA and Windows time zone identifiers.

public sealed class TimeZoneConverterResolver : ITimeZoneResolver
{
    public TimeZoneInfo GetTimeZoneById(string timeZoneId)
    {
        return TZConvert.GetTimeZoneInfo(timeZoneId);
    }
}

You can pass it using the BackgroundJobServerOptions.TimeZoneResolver property or register implementation for ITimeZoneResolver interface in .NET Core applications:

services.TryAddSingleton<ITimeZoneResolver>(new TimeZoneConverterResolver());

@PaitoAnderson, thanks! I've just created an issue for this – #1407

Was this page helpful?
0 / 5 - 0 ratings