Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all IANA Timezones from an offset in NodaTime?

In our ASP.NET MVC 5 application we have user profiles with a Timezone ID (IANA).

public class User
{
    public int Id { get; set; }
    public string TimeZoneId { get; set; }
}

The task is to send all users an email at 6 AM local time. Our plan is to send out emails every hour to all timezones where the local time is 6 AM at that point in time (calculated based on the UTC time of the server).

Since we only have the Timezone ID to go by, I was hoping to get the corresponding Timezone IDs for, let's say, offset -4:00:00 -- taking into account DST.

string[] timezoneIds;

I could then query my database like so:

db.Users.Where(x => timezoneIds.Contains(x.TimeZoneId));

My question is, obviously, is this a decent idea or are there best practices for this problem that I am not aware of?

Thanks.

like image 543
Fred Fickleberry III Avatar asked Dec 24 '22 12:12

Fred Fickleberry III


2 Answers

Simpler code for the "building a dictionary" that Serge shows: use LINQ's lookup, DateTimeZone.GetUtcOffset, and Offset keys instead of TimeSpan values:

var now = SystemClock.Instance.GetCurrentInstant();
var provider = DateTimeZoneProviders.Tzdb;
var idsByCurrentOffset = provider.Ids.ToLookup(id => provider[id].GetUtcOffset(now));

Then you can use:

var offset = Offset.FromHours(5);
foreach (var id in idsByCurrentOffset[offset])
{
    ...
}

(You don't even need to check for existence first, as the lookup indexer returns an empty sequence when presented with a key that doesn't exist in the lookup.)

like image 163
Jon Skeet Avatar answered Jan 13 '23 15:01

Jon Skeet


You could try the following:

First create a dictionary with all the offsets and the timezones belonging to that offset

Instant now = SystemClock.Instance.Now;
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
Dictionary<TimeSpan, List<string>> timezonesForOffset = new Dictionary<TimeSpan, List<string>>();
foreach(var id in timeZoneProvider.Ids){
    ZonedDateTime zdt = now.InZone(timeZoneProvider[id]);
    var timespan = zdt.Offset.ToTimeSpan();
    if(timezonesForOffset.ContainsKey(timespan)){
        timezonesForOffset[timespan].Add(id);
    } else {
        timezonesForOffset[timespan] = new List<string> {id, };
    }
}

After you have done that you could use the following snippet to get all users within a certain timezone

var timespan2 = new TimeSpan(1,0,0);
var timezonesWithOffset = timezonesForOffset[timespan2];

var usersinTimezone = db.Users.Where(x=> timezonesWithOffset.Contains(x.TimezoneId)).ToList();

That would get all users in the timezone utc+1

like image 42
Serge Bekenkamp Avatar answered Jan 13 '23 15:01

Serge Bekenkamp