如何解决使用 Noda Time 从本地日期和时区中查找最接近的有效全球日期
给定一个时区,我需要检查某个特定时刻是否包含在一天范围和本地时间范围内。因此,假设时区为 Europe/Paris
,日期范围为 2020-03-01 to 2020-03-31
,本地时间范围为 02:30 to 03:30
。因此,在此示例中,目标是找出给定时刻是否发生在 2020 年 3 月的任何一天的 02:30 到 03:30 之间。
问题是,在这个时区的 2020-03-29
日,在 02:00
应用夏令时,时钟跳到 03:00
,因此本地时间范围 {{1} } 实际上并不存在。
我想通过将特定日期的时间范围移动到 02:00 to 03:00
来处理这种情况,03:00 to 03:30
是给定时区的最接近 03:00
之前的有效时间。我知道 02:30
,但它没有按预期工作:
DateTimeZone.AtLeniently(LocalDateTime)
如您所见,var localDate = new LocalDateTime(2020,03,29,02,30);
DateTimeZone timeZone = DateTimeZoneProviders.Tzdb["Europe/Paris"];
zoneddatetime globalDate = timeZone.AtLeniently(localDate);
Console.WriteLine(globalDate); // 2020-03-29T03:30:00 Europe/Paris (+02)
^
将时间移动了整整 1 小时(向前移动了“间隙”的持续时间,如 the documentation 中所述),因此解析的日期为 {{1 }} 而不是 AtLeniently
。我不想将无效时间移动整整 1 小时,我宁愿向前移动到最近的有效时间(例如 03:30
)。
解决方法
解决这个问题的方法是换个角度看问题。与其将 [天 + 本地时间范围 + 时区] 转换为全局时间范围并将其与给定的时刻进行比较,我们可以将即时转换为该时区的本地日期时间,并将其与本地值进行比较。
DateTimeZone dtz = DateTimeZoneProviders.Tzdb[ianaTimeZone];
var globalInstant = Instant.FromUtc(2020,03,29,01,15); // 2020-03-29T03:15:00 Europe/Paris (+02)
LocalDateTime localDate = globalInstant.InZone(dtz).LocalDateTime;
var localStartDate = new LocalDateTime(2020,02,30);
var localEndDate = new LocalDateTime(2020,30);
Console.WriteLine(localDate > localStartDate && localDate < localEndDate); // true
,
根据 Jon Skeet 的评论,我决定使用自定义 ZoneLocalMappingResolver
。这是我们当前的实现:
private ZonedDateTime ResolveLocal(
LocalDateTime localDate,DateTimeZone timeZone
)
{
return timeZone.ResolveLocal(
localDate,resolver: mapping => mapping.Count == 0 ?
// Handle the case where the specified local date is skipped in
// the time zone by using the next valid date.
mapping.LateInterval.Start.InZone(mapping.Zone) :
// Otherwise,use the last available result (single if
// unambiguous).
mapping.Last()
);
}
private bool IsInInterval(
Instant instant,LocalDateTime start,LocalDateTime end,DateTimeZone timeZone
)
{
ZonedDateTime globalStart = this.ResolveLocal(start,timeZone);
ZonedDateTime globalEnd = this.ResolveLocal(end,timeZone);
return instant > globalStart.ToInstant() &&
instant < globalEnd.ToInstant();
}
那么:
DateTimeZone timeZone = DateTimeZoneProviders.Tzdb["Europe/Paris"];
// instant matches 2020-03-29T03:15:00 Europe/Paris (+02)
var instant = Instant.FromUtc(2020,15);
// Interval bounds
// start will be translated to 2020-03-29T03:00:00 Europe/Paris (+02)
// end will be translated to 2020-03-29T03:30:00 Europe/Paris (+02)
var start = new LocalDateTime(2020,30);
var end = new LocalDateTime(2020,30);
bool isInInterval = this.IsInInterval(instant,start,end,timeZone);
Console.WriteLine(isInInterval); // true
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。