Noda Time Create a ZonedDateTime from DateTime and TimeZoneId

Let's say I have the following date, time, and time zone: 2016-10-15, 1:00:00, America/Toronto.

How do I create a ZonedDateTime that represents that exact date and time in the specified zone?

Basically what I need a ZonedDateTime object that represents the exact date and time in the exact time zone.

In case the time is skipped, I would like to add the tick of hour to the new time. Example:

If 00:00 is skipped to 1:00, and I attempt to get the time 00:30 in the zone, I want the result to be 1:30, not only 1:00, which is the first time of the interval.

If 00:00 is skipped to 1:45, and I attempt to get the time 00:20 in the zone, I want the result ot be 2:05.

If a time is ambiguous, i. e., occurs twice, I want the earlir mapping.

Jon Skeet
people
quotationmark

What you've described is precisely the behaviour of LocalDateTime.InZoneLeniently in Noda Time 2.0. (Thanks to Matt Johnson's change :) However, as that's still in alpha, here's a solution for 1.3.2. Basically, you just want an appropriate ZoneLocalMappingResolver, which you can build using Resolvers. Here's a complete example.

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);
    }            
}

Output:

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)

people

See more on this question at Stackoverflow