如何从NodaTime中的偏移量获取所有IANA时区?

4
在我们的ASP.NET MVC 5应用程序中,我们有用户配置文件并带有时区ID(IANA)。
public class User
{
    public int Id { get; set; }
    public string TimeZoneId { get; set; }
}

任务是在本地时间早上6点向所有用户发送电子邮件。我们的计划是每小时向所有本地时间为当时早上6点(根据服务器的UTC时间计算)的时区发送电子邮件。
由于我们只有时区ID可供使用,我希望能够获取相应的时区ID,比如偏移量为-4:00:00 - 考虑到夏令时。
string[] timezoneIds;

我可以像这样查询我的数据库:

然后,我可以像这样查询我的数据库:

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

我的问题是,这是一个好主意还是我不知道的最佳实践方法来解决这个问题?谢谢。
2个回答

7

简化Serge展示的“构建字典”的代码:使用LINQ的lookup,DateTimeZone.GetUtcOffsetOffset键代替TimeSpan值:

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

然后您可以使用:

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

(当查找索引器收到一个在查找表中不存在的键时,它会返回一个空序列,因此您甚至不需要先检查存在性。)

3
你可以尝试以下方式:
首先创建一个字典,其中包含所有偏移量和属于该偏移量的时区。
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, };
    }
}

在完成这些工作后,您可以使用以下代码片段获取某个时区内的所有用户。

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

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

那将获取时区为UTC+1的所有用户。

这种方法是可以的,但要考虑生成的SQL可能会相当大,这取决于匹配的时区数量。此外,如果代码提前运行,则确保传递正确的“Instant”以获取实际执行时间,而不是“now”。 - Matt Johnson-Pint
哦,我相信在字典中使用“Offset”键也同样有效,虽然这并不重要。;) - Matt Johnson-Pint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接