4

We have DateTimeOffsets in Database/Model. To display these values in Web, we convert the DateTimeOffsets into the current user's timezone.

According MSDN, DateTimeOffset can be ambiguous in a specific TimeZone:

TimeZoneInfo.IsAmbiguousTime Method (DateTimeOffset)

This doesn't make sense to me at all. Can someone please give me an example DateTimeOffset which is ambiguous?
We're in TimeZone "W. Europe Standard Time".

Thomas Zweifel
  • 627
  • 1
  • 6
  • 19

3 Answers3

3

Does what the documentation says not make it clear?

Typically, ambiguous times result when the clock is set to return to standard time from daylight saving time

I.e. if at 2am you come off of DST and reset the clock to 1am, then if someone starts talking about 1.30am, you don't know if that's 30 minutes from now or happened 30 minutes in the past.

There are a set of values (typically an hour long) which map to two different set of moments in UTC time.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • 1
    Except that DateTimeOffset refers to an absolute point in time (time + offset). There is no ambiguity in that case. There can only be one offset for a time zone at a particular DateTimeOffset. – Sellorio Nov 03 '22 at 22:09
3

I think the confusion comes from the way that "ambiguous" is defined here.

To be clear, a DateTimeOffset is never ambiguous unto itself. It always represents a specific moment in absolute, instantaneous time. Given a date, time, and offset, I can tell you both the local wall-time, and the precise UTC time (by applying the offset).

However, the wall-time portion of the value can be ambiguous within a specific time zone. That is, the date and time only when you ignore the offset. That's what TimeZoneInfo.IsAmbiguousTime is telling you. That if it weren't for the offset, the value would be ambiguous. The wall-time may be one that a person in that time zone might find confusing.

Consider that there are two overloads of this method, one that takes a DateTime and one that takes a DateTimeOffset.

  • The DateTime one makes perfect sense when .Kind is DateTimeKind.Unspecified.

    DateTime dt = new DateTime(2016, 10, 30, 2, 0, 0);
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    bool ambiguous = tz.IsAmbiguousTime(dt);  // true
    
  • It makes a little less sense with the other kinds, because it does conversions to the given time zone first - but still it does the same thing:

    DateTime dt = new DateTime(2016, 10, 30, 1, 0, 0, DateTimeKind.Utc);
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    bool ambiguous = tz.IsAmbiguousTime(dt);  // true
    
  • The DateTimeOffset overload is essentially doing the same thing as the previous example. Whatever the offset is, it gets applied to the date and time, then ambiguity is checked on the resulting date and time alone - just like in the first example.

    DateTimeOffset dto = new DateTimeOffset(2016, 10, 30, 2, 0, 0, TimeSpan.FromHours(1));
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    bool ambiguous = tz.IsAmbiguousTime(dto);  // true
    
  • Even with an offset that is meaningless to that time zone, it still gets applied before comparing.

    DateTimeOffset dto = new DateTimeOffset(2016, 10, 29, 19, 0, 0, TimeSpan.FromHours(-5));
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
    bool ambiguous = tz.IsAmbiguousTime(dto);  // true
    

It boils down to the implementation of the overload, which is essentially:

// Make sure the dto is adjusted to the tz.  This could be a no-op if it already is.
DateTimeOffset adjusted = TimeZoneInfo.ConvertTime(dto, tz);

// Then just get the wall time, stripping away the offset.
// The resulting datetime has unspecified kind.
DateTime dt = adjusted.DateTime;

// Finally, call the datetime version of the function
bool ambiguous = tz.IsAmbiguousTime(dt);

You can see this in the .net reference source here. They condense it to two lines, and preface it with a shortcut for better perf when DST is not applicable, but that's what it does.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Great answer! Your explanation of wall-time portion and ignorance of timezone value helps a lot! I wasn't aware the the Framework only uses the DateTime part of the DateTimeOffset. – Thomas Zweifel Jan 15 '16 at 08:21
  • 1
    That's just with regards to *this* function. There are plenty of other places where offset is indeed considered. – Matt Johnson-Pint Jan 16 '16 at 01:35
2

Well the sample is (the last October's Sunday 2:00-3:00)

DateTimeOffset example = new DateTimeOffset(2015, 10, 25, 2, 30, 0, 
  new TimeSpan(0, 2, 0, 0));

TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");

if (tst.IsAmbiguousTime(example))
  Console.Write("Ambiguous time");

Opposite to Ambiguous time is Invalid time (the last March's Sunday 2:00-3:00):

TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");

if (tst.IsInvalidTime(new DateTime(2016, 03, 27, 2, 30, 0)))
  Console.Write("Invalid time");
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 1
    Even your own example highlights that invalid time is only valid for DateTime (no provided offst). The OP and I want to know how an absolute time (DateTimeOffset - date, time and offset) can be ambiguous when it refers to an exact point in time. – Sellorio Nov 03 '22 at 22:10