C
C#2y ago
Kiel

❔ Accounting for Daylight Savings Time skips at runtime

My Discord bot I am developing has a reminder system which utilizes https://github.com/robbell/nChronic.Core for natural date/time parsing - allowing a user to specify a reminder for tomorrow at noon. As not everyone lives in the same timezone, users can configure their timezone via a command, where the user inputs their approximate TZDB equivalent (see image). The bot also supports repeating reminders, IE a reminder where a user can be reminded hourly, daily, or weekly at the same interval of time - IE, a reminder to do something at 10PM daily. As I just learned this past weekend with the time changing, this system has a flaw, where anyone whose region observes DST will have reminders become off by an hour once it begins or ends. My current system involves storing the user's offset from UTC as a TimeSpan for parsing purposes, and then storing reminders as DateTimeOffset relative to UTC regardless of their timezone (because Npgsql's EFCore thing seems to only support UTC offset?). Is there any way to account for this behavior? Especially since it's not something observed worldwide, and even in America, it's apparently on the way out and some regions don't even follow it
10 Replies
Kiel
KielOP2y ago
I noticed because I have a daily reminder for 10PM, which went off at 11PM today, and I realized it was because of the time change.
jcotton42
jcotton422y ago
for your own sanity, I suggest switching to NodaTime, which has support for actual time zones https://nodatime.org/3.1.x/userguide/type-choices npgsql has support for it https://www.npgsql.org/efcore/mapping/nodatime.html
Kiel
KielOP2y ago
I'm actually using NodaTime for the timezone parsing, that's where I'm getting the TZDB info from Unless I'm wrong, this still doesn't seem to account for DST switchovers, unless you're telling me nodatime is somehow aware of these at runtime and can account for it (if it matters, the way I "expire" things like reminders is by sorting them by which one's expiry date is lowest)
jcotton42
jcotton422y ago
oh wait hm you might be right
Kiel
KielOP2y ago
I'm really not familiar with NodaTime, I actually tried using it instead of the BCL stuff for this exact stuff originally since I was actually storing their timezone as a TZDB string (IE America/Chicago instead of a TimeSpan, but I was either a) getting tripped up on some of the conversion methods to/from BCL types, or b) came across an issue I couldn't solve using NodaTime. This was...a month or so ago so I honestly don't remember
jcotton42
jcotton422y ago
you've now nerdsniped me kek think it would work if you stored a LocalTime (which is a time of day with no timezone) plus a time zone ID in a separate column...
Kiel
KielOP2y ago
i'll have to probably do some testing before I mess around with my db entities...not even sure how to test it. maybe just have a LocalDateTime of just before the changeover, convert it to a ZonedDateTime, then repeat the process after the changeover, and see if the difference is correct?
Kiel
KielOP2y ago
You might be onto something. Here's my findings:
var currentTimeZone = DateTimeZoneProviders.Bcl.GetSystemDefault();

// DST changeover: Sun, Mar 12, 2023 2:00 AM
var beforeChangeOver = new LocalDateTime(2023, 3, 12, 01, 55);
var beforeZdt = beforeChangeOver.InZoneLeniently(currentTimeZone);
var afterChangeOver = new LocalDateTime(2023, 3, 12, 02, 05);
var afterZdt = afterChangeOver.InZoneLeniently(currentTimeZone);

return Response($"Before: {beforeZdt}\nAfter: {afterZdt}");
var currentTimeZone = DateTimeZoneProviders.Bcl.GetSystemDefault();

// DST changeover: Sun, Mar 12, 2023 2:00 AM
var beforeChangeOver = new LocalDateTime(2023, 3, 12, 01, 55);
var beforeZdt = beforeChangeOver.InZoneLeniently(currentTimeZone);
var afterChangeOver = new LocalDateTime(2023, 3, 12, 02, 05);
var afterZdt = afterChangeOver.InZoneLeniently(currentTimeZone);

return Response($"Before: {beforeZdt}\nAfter: {afterZdt}");
Kiel
KielOP2y ago
it jumps from 1:55 to 3:05 AM, indicating it was "aware" of the changeover at 2AM @jcotton42 sorry, meant to ping reply, but discord broke the message sending thonk2 it would probably make the most sense to store the user's TimeZoneInfo in the db and then store reminder expiry as a LocalDateTime (UTC), then convert to a ZonedDateTime on the fly assuming the npgsql thingy supports those two, i'll look into it tomorrow
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server