28

I am storing schedules in the database as a day of the week, hour and minute. When the data is read we create a DateTime object for the next occurrence of that day, hour and minute, but I need to modify this to be DST-aware. I am able to modify the database if necessary.

I know that DateTimeOffset stores a UTC date/time and an offset. I also know from this MSDN blog entry that DateTimeOffset should be used to "Work with daylight saving times".

What I'm struggling to understand is exactly how DateTimeOffset "work(s) with daylight saving times". My understanding, little that there is, is that daylight saving times are a political decision and cannot be inferred from purely an offset. How can it be that this structure is DST friendly if it only stores an offset and not a named timezone or country?

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
  • 3
    What if the "next" day and time fall into the gap during a DST transition? I.e. that time never occurs on that day, since the time jumps ahead by a whole hour. I don't think there's a magic solution that can just be plugged in. – Damien_The_Unbeliever Sep 26 '11 at 10:59
  • 1
    @Damien_The_Unbeliever makes a good point, and the reverse should be considered too - a local time can occur twice on the same day. Noda Time forces you to consider these options when you convert from LocalDateTime to ZonedDateTime; .NET doesn't :( – Jon Skeet Sep 26 '11 at 11:00
  • 2
    Job is mostly done and I'm testing it now - Noda-Time was the magic solution as it tells me all about skipped times, ambiguities in mapping etc :) – Stephen Kennedy Nov 10 '11 at 18:45
  • In the database I store the day of week, hour, minute and timezone. The scheduler calculates the local time when the event should next fire using this data and Noda-Time. If there are unresolved ambiguities coming up it emails me. – Stephen Kennedy Nov 02 '14 at 15:36

1 Answers1

41

DateTimeOffset itself isn't really DST-aware, but TimeZoneInfo is. A DateTimeOffset represents a fixed instant in time - so you get to a DateTimeOffset via something that is time zone aware. In other words, if I asked for a DateTimeOffset now in the UK, I'd end up with something with an offset of +1 hour from UTC. If I asked for a DateTimeOffset for some time in December in the UK, I'd end up with something with an offset of 0 hours from UTC.

If you change your database to include the offset and you create the DateTimeOffset from the user's chosen DateTime (which should be of kind "unspecified") and their time zone, then that should give you the correct offset taking DST into account.

One thing to be aware of though: if I schedule something now for "2 years time" and you determine the offset now, that offset may not be correct in the future - for example, the government could change when DST applies, and obviously that's not going to change what's stored in your database.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    @ Jon Is Noda Time ready for production use in a scenario like this? – Stephen Kennedy Sep 26 '11 at 11:05
  • @StephenKennedy: I don't know of any problems around time zone calculations etc - the bulk of the remaining pre-1.0 work is around text formatting/parsing. Given that we *haven't* hit 1.0, I would be *cautious* about adopting it - if I were in your shoes I'd want a lot of tests for your specific use case - but if you want any support on using Noda Time, I'd be very happy to help, and I'm sure other members of the mailing list would be too. – Jon Skeet Sep 26 '11 at 11:34
  • Thanks Jon. 99% of the battle here is storing something suitable in the db and constructing a DateTimeOffset instance from that data for the start time of the Job object. The Job object with a payload, does the work, and the underlying scheduler already works. I'll download Noda Time and RTFM to try and get a handle on whether this might be suitable. That's if I'm unsuccessful in my pitch that we should do everything in UTC and let the end users worry about DST not least because of the issues Damien raised :) – Stephen Kennedy Sep 26 '11 at 18:27
  • 1
    @StephenKennedy: Doing everything in UTC should be fine - basically you should be storing *something* which corresponds to a single UTC time, whether that's a DateTimeOffset or whatever. I'd be really interested to hear your thoughts about Noda Time though - please mail me about it. – Jon Skeet Sep 26 '11 at 18:29
  • 1
    @Damien_The_Unbeliever Jon's answer was spot on, but we moved on a lot from there. I implemented the calculations using Noda-Time, along with ambiguity handling to address the issues raised by Damien, and then mapped the result to a DateTimeOffset and fed that into the scheduler. Am very impressed with the ease with which this was done and happy with the preliminary results. – Stephen Kennedy Nov 10 '11 at 18:52
  • I built a scheduling system with recurring events of different frequency (weekly, monthly, yearly) and regularity (every X weeks/months/years) depending on the frequency type, and the weekly frequency also allows specifying particular days of the week (e.g. weekly on Tuesday and Thursday, every 2 weeks). It directly calculates the most recent due date prior to "now", but this cannot be done in UTC, because the shift in hours could alter the day. If something is to run every Tuesday at 11pm local time, I must work with local dates. DST causes the hour to shift for an entire part of the year. – Triynko Feb 18 '16 at 20:27
  • The confusing part is that if I construct a DateTimeOffset with a time like 3am (on March 13th, 2016 which happens to be the start of DST), and I assign an offset of -5 hours, then the TimeOfDay property remain correct at 3am; however, the LocalDateTime.TimeOfDay property says it's 4am. The DateTimeOffset's time of day seems to be coming from a private variable called ClockDateTime, which I can see in the debugger. The local date time and the clock date time have different tick values. The relationship between all of these is not obvious. – Triynko Feb 18 '16 at 20:49
  • 1
    @Triynko: It sounds like this should all be in a new question, to be honest. – Jon Skeet Feb 18 '16 at 21:04