-1

I'm not in front of my code right now, but don't really think I need it to ask this question. So I have a countdown timer that goes off every 18 hours and then resets. The timer checks the current DateTime.Now and adjusts the countdown timer as needed. I am having an issue trying to account for when daylight savings when it goes back an hour because; for example this past event on November 5th 2017 at 2am it goes back to 1am but when I do DateTime.IsDaylightSavingTime() it tells me that it's false even though Daylight Saving Time just went off. This makes my timer go back an extra hour because it thinks that Daylight Saving Time still hasn't happen for that one hour period. How would I get around this?

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Nolemonpledge
  • 139
  • 1
  • 2
  • 11
  • Instead of checking the current time, make a countdown. Use a timer with a predefined interval to decrease a counter and when it reaches zero you're done. For example, setup a variable with the number of seconds in 18 hours and a timer with a 1-second interval. This completely avoids the need to know the system time, and may change at will without disturbing your system. – Alejandro Nov 09 '17 at 14:24
  • 1
    `DateTime.UtcNow` doesn't have this problem. It will still break if, say, someone decides the clock is off by 18 hours and sets the time to something else. – Jeroen Mostert Nov 09 '17 at 14:27
  • @Alejandro, I need to use the system time because my timer needs to be based off of it. So if my application is closed down and reopened it can get the current time and adjust to what the countdown timer needs to be. If that makes sense. – Nolemonpledge Nov 09 '17 at 14:31
  • @JeroenMostert I adjust for timezone changes by checking the time difference between my local time and UtcNow. If the difference changes I adjust the timer because I think a timezone occurred. When DST happens and it goes back and hour, the offset between my local time and UtcNow changes by -1. This makes it think a timezone change occurred and my timer loses an hour when I don't want it to. If I was able to detect if DST is false for that 1 hour period I could just ignore the timezone change "event". Thing is I don't have a way to know if that hour once its set back is DST or not. – Nolemonpledge Nov 09 '17 at 14:38
  • 1
    You don't *need* to adjust for anything if you use `UtcNow` consistently in all calculations. If by "every 18 hours" you mean something other than "every 64800 seconds", that's not clear from your question. (The whole reason UTC is a good idea, incidentally, is exactly because it is impossible, in general, to say whether you're in a repeated hour if you have no context. This is a problem for database systems that store local time as well.) – Jeroen Mostert Nov 09 '17 at 14:43
  • As Jeroen says, you need to use `UtcNow`. The localized `Now` property should be used _only_ for when you need to display the value to the user and you want to display in local time. For any algorithmic use, including timing, you should _always_ use `UtcNow`, specifically because it's immune to DST changes. – Peter Duniho Nov 10 '17 at 03:24

2 Answers2

1

If you realy need to use local time for some reason, than you should count for DST changes in advance (prior scheduling next event).

TimeZoneInfo.GetAdjustmentRules() should help you to get the time and delta of next DST adjustment.

Following is the code for getting upcomming adjustment:

    public static DateTime? GetNextAdjustmentDate(TimeZoneInfo timeZoneInfo)
    {
        var adjustments = timeZoneInfo.GetAdjustmentRules();

        if (adjustments.Length == 0)
        {
            return null;
        }

        int year = DateTime.UtcNow.Year;
        TimeZoneInfo.AdjustmentRule adjustment = null;
        foreach (TimeZoneInfo.AdjustmentRule adjustment1 in adjustments)
        {
            // Determine if this adjustment rule covers year desired 
            if (adjustment1.DateStart.Year <= year && adjustment1.DateEnd.Year >= year)
                adjustment = adjustment1;
        }
        if (adjustment == null)
            return null;

        //TimeZoneInfo.TransitionTime startTransition, endTransition;

        DateTime dstStart = GetCurrentYearAdjustmentDate(adjustment.DaylightTransitionStart);
        DateTime dstEnd = GetCurrentYearAdjustmentDate(adjustment.DaylightTransitionEnd);


        if (dstStart >= DateTime.UtcNow.Date)
            return dstStart;
        if (dstEnd >= DateTime.UtcNow.Date)
            return dstEnd;
        return null;
    }

    private static DateTime GetCurrentYearAdjustmentDate(TimeZoneInfo.TransitionTime transitionTime)
    {
        int year = DateTime.UtcNow.Year;

        if (transitionTime.IsFixedDateRule)
            return new DateTime(year, transitionTime.Month, transitionTime.Day);
        else
        {
            // For non-fixed date rules, get local calendar
            System.Globalization.Calendar cal = CultureInfo.CurrentCulture.Calendar;
            // Get first day of week for transition 
            // For example, the 3rd week starts no earlier than the 15th of the month 
            int startOfWeek = transitionTime.Week * 7 - 6;
            // What day of the week does the month start on? 
            int firstDayOfWeek = (int)cal.GetDayOfWeek(new DateTime(year, transitionTime.Month, 1));
            // Determine how much start date has to be adjusted 
            int transitionDay;
            int changeDayOfWeek = (int)transitionTime.DayOfWeek;

            if (firstDayOfWeek <= changeDayOfWeek)
                transitionDay = startOfWeek + (changeDayOfWeek - firstDayOfWeek);
            else
                transitionDay = startOfWeek + (7 - firstDayOfWeek + changeDayOfWeek);

            // Adjust for months with no fifth week 
            if (transitionDay > cal.GetDaysInMonth(year, transitionTime.Month))
                transitionDay -= 7;

            return new DateTime(year, transitionTime.Month, transitionDay);
        }
    }

You'd need to add some more code to retrieve and apply the adjustment delta.

Well - now, when you see all the hard work that would need to be done (and than maintained and made sure to be bug free), you might want to rething your problem to be able to use UTC.

Jan
  • 1,905
  • 17
  • 41
0

I would use DateTime.UtcNow you won't have the issue with daylight savings

Dave
  • 2,829
  • 3
  • 17
  • 44