微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Noda Time 从本地日期和时区中查找最接近的有效全球日期

如何解决使用 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 举报,一经查实,本站将立刻删除。