0

I need to import measured data (one datapoint each hour) and want to represent the dates with noda time. The series is affected by daylight saving transitions - and with those I have some issues.

I created some test dates with the daylight saving time changing days of 2016 (Europe/Berlin): (year, month, day, hour)

2016,3,27,0
2016,3,27,1
2016,3,27,3
2016,3,27,4
2016,10,20,0
2016,10,20,1
2016,10,20,2
2016,10,20,2
2016,10,20,3

Each line is incremented by one hour. The gap and the double data is dur to daylight saving time transition

I use the following code to test the import:

private void TestImport()
{
    List<ZonedDateTime> resultSet = new List<ZonedDateTime>();

    IDateTimeZoneProvider provider = DateTimeZoneProviders.Tzdb;
    DateTimeZone dtz = provider.GetZoneOrNull("Europe/Berlin");
    bool first = true;

    foreach (string line in File.ReadAllLines(@"C:\tmp\problemdates.txt"))
    {
        string[] split = line.Split(",".ToCharArray());
        LocalDateTime ldt = new LocalDateTime(Convert.ToInt32(split[0]), Convert.ToInt32(split[1]), Convert.ToInt32(split[2]), Convert.ToInt32(split[3]), 0);

        resultSet.Add(Convert2Zoned(ldt, dtz, ref first));
    }

    DataGrid_Results.DataContext =  return resultSet;
}


private ZonedDateTime Convert2Zoned(LocalDateTime ldt, DateTimeZone dtz, ref bool isFirstAmbigue)
{
    try
    {
        return ldt.InZoneStrictly(dtz);
    }
    catch (SkippedTimeException ex)
    {
        // rethrow
    }
    catch (AmbiguousTimeException ex)
    {
        if (isFirstAmbigue)
        {
            isFirstAmbigue = false;
            return ldt.InZone(dtz, Resolvers.CreateMappingResolver(Resolvers.ReturnEarlier, Resolvers.ThrowWhenSkipped));
        }
        else
        {
            isFirstAmbigue = true;
            return ldt.InZoneLeniently(dtz);
        }
    }
}

Edith: Here is a simple GUI application that executes the import

What I'd expected -

27.03. Hour 3: TickOfDay 7200... instead of 10800... because this is the second hour of the day

20.10. Hour 2: The second appearing should be TickOfDay 10800... instead of 7200... again, as it is one hour later than the first appearing. Also, Offset should be +01 instead of +02 from the second appearing on, as we are in wintertime again.

I'd also would have expected LocalDateTime to be "normalized" (e.g. 1:00, 2:00, 3:00 ...) but this migfht relate to the Offset ...

Thanks for your help,
Frank

Aaginor
  • 4,516
  • 11
  • 51
  • 75
  • Currently this is incomplete, seems to need a UI, and expects a file. Could you edit it to be a complete console app (so we can copy, paste, compile, run) using hard-coded data, and with the expected and actual results listed? – Jon Skeet Dec 19 '16 at 16:08
  • sure, give me a scecond :) – Aaginor Dec 19 '16 at 16:09
  • This is still a GUI app, and isn't a complete example. It's really worth getting comfortable with writing short *console* apps to demonstrate problems. They help focus on just the problem at hand, without any extraneous complexities. – Jon Skeet Dec 20 '16 at 08:24

2 Answers2

1

The .TickOfDay property on a ZonedDateTime or an OffsetDateTime is related to its LocalDateTime, which is timezoneless. It's not related to its instantaneous point in time, but rather the wall-clock time as displayed.

Therefore, the expectations you described are invalid. A local time of 03:00 is always going to have TickOfDay == 10800.

If you wanted to normalize these values such that 3:00 was identified as the second hour of the day, you'd have to first adjust them all to use the same offset - the one that was in effect at the start of the day.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Hey Matt, thank for your reply! The offset was automatically created by the LocalDateTime.InZone[Leniently] operation. Unfortunately, there seems to be no way to create a ZonedDateTime out of the zoned value and the timezone – Aaginor Dec 21 '16 at 10:58
  • Huh? I'm not sure what you're getting at. I didn't say anything about an offset, other than you'd have to normalize them to all be the same if you wanted equivalent wall time values. `TickOfDay` is wall time. – Matt Johnson-Pint Dec 21 '16 at 20:04
1

My clock shows the local time in Berlin.

Something (A) happens when my clock shows 2016-03-27T00:00:00.

Something (B) happens when my clock shows 2016-03-27T03:00:00.

The elapsed time between (A) and (B) is 2 hours. It's the only "valid" expectation, because it's the truth.

The question is if we can represent this valid expectation with any software artifact. If not, the software is invalid, not the expectation.

  • Hey Jorge! I think we *can* represent the expectation - we need the UTC time, the timezone and the logic to transform it to "ZonedDateTime" (and back). What I am lacking is a way to directly read the date/time into a ZonedDateTime struct – Aaginor Dec 21 '16 at 10:52
  • The problem is that, finally, `ZonedDateTime.TickOfDay` does not represent the actual time elapsed since midnight, when there is a clock transition in the middle. And you expect that "27.03. Hour 3: TickOfDay 7200... instead of 10800... because this is the second hour of the day", that is, you expect the actual time elapsed since midnight. Note that 27.03 has only 23 wall-clock hours and 10.20 has 25 wall-clock hours. – Jorge Gomez Dec 21 '16 at 16:15
  • (Please, change 10.20 to 20.10). – Jorge Gomez Dec 21 '16 at 16:25