0

Yesterday Uruguay changed their clocks, and now I keep seeing exceptions when converting specific times for their timezone:

ERROR   Exception: - DateTime ConvertTimeToUtc(DateTime, String)    (05/10/2014 02:31:00, America/Montevideo)
NodaTime.SkippedTimeException: Specified argument was out of the range of valid values.
Parameter name: Local time 05/10/2014 02:31:00 is invalid in time zone America/Montevideo

I understand how a local time can be invalid:

"For example, suppose the time zone goes forward at 2am, so the second after 01:59:59 becomes 03:00:00. In that case, local times such as 02:30:00 never occur."

However, what I don't understand (and I probably need more coffee), is why NodaTime is not accounting for this? Should it not be aware that 02:31 is now an invalid local time - or should I be doing additional handling to account for this?

Functions I'm calling:

var timeZone = DateTimeZoneProviders.Tzdb[timezoneName];
var localTime = LocalDateTime.FromDateTime(timeToConvert).InZoneStrictly(timeZone);;
return DateTime.SpecifyKind(localTime.ToDateTimeUtc(), DateTimeKind.Utc);  
FBryant87
  • 4,273
  • 2
  • 44
  • 72

1 Answers1

1

Yes, it is aware that it's an invalid local time - which is why when you specifically ask it to convert that local time into UTC, it throws an exception. It's roughly equivalent to calling Math.sqrt(-1).

The InZoneStrictly call you're making specifically throws an exception on either ambiguous or skipped times. If you use InZoneLeniently you won't get an exception, but it may not give you the result you want. Or, you could use LocalDateTime.InZone(DateTimeZone, ZoneLocalMappingResolver) which will allow you to map invalid local date/time values however you want.

As side-notes:

  • Your localTime variable is a ZonedDateTime, so the name is a bit misleading
  • You don't need to call SpecifyKind - ToDateTimeUtc will always return a DateTime with a kind of Utc, hence the name.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for your answer John. I've added the functions I'm calling to the question. I now understand I get the error when converting the local time to UTC, should I be doing something different to get the local time in the first place? – FBryant87 Oct 06 '14 at 12:45
  • @FBryant87: It's not really clear what you're trying to achieve, to be honest - where did you get `timeToConvert` from? In some cases it may be reasonable data, e.g. if you have a recurrent event at 2:30am every day... you then need to work out what that means on a day where 2:30am happens 0 or 2 times. – Jon Skeet Oct 06 '14 at 12:46
  • Thanks I think your updated answer explains it a bit more, I'll read up on InZoneLeniently. – FBryant87 Oct 06 '14 at 13:11
  • 1
    @FBryant87: Right - I've added a couple more links, too. – Jon Skeet Oct 06 '14 at 13:18
  • One last quick question about InZoneLeniently Jon: In this case I can see it returns a UTC date of 5:00am (which is a good "guess"), I'm just wondering if there's a reason it loses the minutes when it 'jumps' forwards? For example 02:31 doesn't jump to 03:21, it jumps to 03:00 – FBryant87 Oct 06 '14 at 14:20
  • 1
    @FBryant87: It gives you the closest local time after the one you asked for. We've had a feature request to add a resolver which will add an hour instead - but you can already build it with the 1.3 API. Look at the `Resolvers` class. – Jon Skeet Oct 06 '14 at 14:54