节点时间—从DateTime和TimeZoneId中创建一个ZonedDateTime

本文关键字:ZonedDateTime 一个 创建 时间 DateTime TimeZoneId 节点 | 更新日期: 2023-09-27 18:14:21

假设我有以下日期、时间和时区:2016-10-15, 1:00:00, America/Toronto .

如何在指定的区域中创建一个表示确切日期和时间的ZonedDateTime ?

基本上我需要一个ZonedDateTime对象,它代表准确时区的确切日期和时间。

如果时间被跳过,我想在新时间上加上小时的刻度。例子:

如果00:00被跳过到1:00,并且我试图获得时区中的时间00:30,我希望结果是1:30,而不仅仅是1:00,这是间隔的第一次。

如果00:00被跳过到1:45,而我试图获得该区域的时间00:20,我希望结果是2:05。

如果时间有歧义,即出现两次,则需要较早的映射

节点时间—从DateTime和TimeZoneId中创建一个ZonedDateTime

你所描述的正是LocalDateTime.InZoneLeniently在Noda Time 2.0中的行为。(感谢马特约翰逊的改变:)然而,因为这仍然是在alpha,这里是1.3.2的解决方案。基本上,您只需要一个合适的ZoneLocalMappingResolver,您可以使用Resolvers构建它。下面是一个完整的例子。

using NodaTime.TimeZones;
using NodaTime.Text;
class Program
{
    static void Main(string[] args)
    {
        // Paris went forward from UTC+1 to UTC+2
        // at 2am local time on March 29th 2015, and back
        // from UTC+2 to UTC+1 at 3am local time on October 25th 2015.
        var zone = DateTimeZoneProviders.Tzdb["Europe/Paris"];
        ResolveLocal(new LocalDateTime(2015, 3, 29, 2, 30, 0), zone);
        ResolveLocal(new LocalDateTime(2015, 6, 19, 2, 30, 0), zone);
        ResolveLocal(new LocalDateTime(2015, 10, 25, 2, 30, 0), zone);
    }
    static void ResolveLocal(LocalDateTime input, DateTimeZone zone)
    {
        // This can be cached in a static field; it's thread-safe.
        var resolver = Resolvers.CreateMappingResolver(
            Resolvers.ReturnEarlier, ShiftForward);
        var result = input.InZone(zone, resolver);
        Console.WriteLine("{0} => {1}", input, result);
    }
    static ZonedDateTime ShiftForward(
        LocalDateTime local,
        DateTimeZone zone,
        ZoneInterval intervalBefore,
        ZoneInterval intervalAfter)
    {
        var instant = new OffsetDateTime(local, intervalBefore.WallOffset)
            .WithOffset(intervalAfter.WallOffset)
            .ToInstant();
        return new ZonedDateTime(instant, zone);
    }            
}
输出:

29/03/2015 02:30:00 => 2015-03-29T03:30:00 Europe/Paris (+02)
19/06/2015 02:30:00 => 2015-06-19T02:30:00 Europe/Paris (+02)
25/10/2015 02:30:00 => 2015-10-25T02:30:00 Europe/Paris (+02)

编辑
之前的解决方案存在一些问题,例如DST期间无效的日期时间等。

这是一个解释一切的新解决方案。感谢@Veeram。

// Transform the "time" in a localized time.                
var tzLocalTime = LocalDateTime.FromDateTime(time);
try
{
    // To get the exact same time in the specified zone.
    zoned = tzLocalTime.InZoneStrictly(zone);
}
catch(SkippedTimeException)
{
    // This happens if the time is skipped
    // because of daylight saving time.
    //
    // Example:
    // If DST starts at Oct 16 00:00:00,
    // then the clock is advanced by 1 hour
    // which means Oct 16 00:00:00 is *skipped*
    // to Oct 16 01:00:00.
    // In this case, it is not possible to convert
    // to exact same date, and SkippedTImeException
    // is thrown.
    // InZoneLeniently will convert the time
    // to the start of the zone interval after
    // the skipped date.
    // For the example above, this would return Oct 16 01:00:00.
     // If someone schedules an appointment at a time that
     // will not occur, than it is ok to adjust it to what
     // will really happen in the real world.
     var originalTime = ste.LocalDateTime;
     // Correct for the minutes, seconds, and milliseconds.
     // This is needed because if someone schedueld an appointment
     // as 00:30:00 when 00:00:00 is skipped, we expect the minute information
     // to be as expected: 01:30:00, instead of 01:00:00.
     var minuteSecondMillisecond = Duration.FromMinutes(originalTime.Minute) + Duration.FromSeconds(originalTime.Second) + Duration.FromMilliseconds(originalTime.Millisecond);
     zoned = zLocalTime.InZoneLeniently(zone).Plus(minuteSecondMillisecond);
}
catch(AmbiguousTimeException ate)
{
    // This happens when the time is ambiguous.
    // During daylight saving time, for example,
    // an hour might happen twice.
    //
    // Example:
    // If DST ends on Feb 19 00:00:00, then
    // Feb 18 23:00:00 will happen twice:
    // once during DST, and once when DST ends
    // and the clock is set back.
    // In such case, we assume the earlier mapping.
    // We could work with the second time that time
    // occur with ate.LaterMapping.
    zoned = ate.EarlierMapping;
}
相关文章: