Calculating daylight saving time from only date

49,058

Solution 1

This is actually deceptively simple. There are a few facts that will help us:

  1. In most of the US, DST starts on the second Sunday of March and ends on the first Sunday of November, at 2:AM both times.
  2. The second Sunday in March will always be between the 8th and the 14th inclusive.
  3. The first Sunday in November will always be between the 1st and 7th inclusive.
  4. The day of week numbering is quite convenient because the day - day of week will give you the previous Sunday.

These facts lead to the following code (C#, but trivially portable to your platform):

    public bool IsDST(int day, int month, int dow)
    {
        //January, february, and december are out.
        if (month < 3 || month > 11) { return false; }
        //April to October are in
        if (month > 3 && month < 11) { return true; }
        int previousSunday = day - dow;
        //In march, we are DST if our previous sunday was on or after the 8th.
        if (month == 3) { return previousSunday >= 8; }
        //In november we must be before the first sunday to be dst.
        //That means the previous sunday must be before the 1st.
        return previousSunday <= 0;
    }

It turns out you don't even need to know the year to do this, as long as you can trust your day of the week value.

I wrote a quick unit test and verified that this code agrees with TimeZone.IsDayLightSavingsTime() for all dates from 1800 to 2200. I did not account for the 2 am rule, but you could easily do that check if the day of week is Sunday and the date is between 8 and 14 (in March) or 1 and 7 (in November).

Solution 2

Code for Central Europe (tested for every day in range 2014-3000 year)

    public static bool IsDst(int day, int month, int dow)
    {
        if (month < 3 || month > 10)  return false; 
        if (month > 3 && month < 10)  return true; 

        int previousSunday = day - dow;

        if (month == 3) return previousSunday >= 25;
        if (month == 10) return previousSunday < 25;

        return false; // this line never gonna happend
    }

Test function

    static void Main(string[] args)
    {
        TimeZoneInfo tzf2 = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");

        var date = new DateTime(2014, 01, 1, 5, 0,0);
        bool wasSummer = false;

        while (date <= new DateTime(3000,1,1))
        {                                         
            var dow = (int) date.DayOfWeek;

            var isDst = IsDst(date.Day, date.Month, dow);               

            DateTime f2 = TimeZoneInfo.ConvertTime(date, tzf2);
            var isSummer = f2.IsDaylightSavingTime();

            if (isSummer != isDst)
            {
                Console.WriteLine("ERROR");
                Console.WriteLine(date);
            }

            if (isSummer != wasSummer)
            {
                Console.WriteLine(date.AddDays(-1).ToShortDateString());
            }

            date = date.AddDays(1);
            wasSummer = isSummer;
        }

        Console.ReadKey();

}

Solution 3

While it is easy to calculate whether a particular date is in DST for a particular location under the current rules, do note that DST is at the whim of politicians and could change at any point. I have a clock manufactured pre-2007 that automatically adjusts for daylight savings time, and now I have to change it four times a year: twice when the actual change occurs, and twice when it now-incorrectly changes itself at the old dates.

In this case, you might be able to ignore DST completely by the simple expedient of having the user enter the timezone along with the date and time. Or you could do like most consumer devices and let the user adjust the time to the local time zone twice a year.

But if you really need to handle DST and really want to do things right, use the zoneinfo database and make sure it can be updated somehow. If you can't do that for some reason, at least allow the user to override the rules. And if even that is too difficult, at least give the user the option to turn off automatic adjustments (unlike my stupid alarm clock).

Solution 4

this code uses mktime to get a day of the week. It used a day of the week to compute daylight savings time. If you don't want to use mktime, you can use program second_sunday. Start with 3/14/2007, which is Wednesday. The day of the week will advance 1 day for every year and 2 days for every leap after 2004.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>


int isDst(int month, int dayOfMonth, int hour, int dayOfWeek);

int main(int argc, char *argv[])
{
   int isdst, dayOfWeek;
   char buf[80];
   struct tm tmData;

   if( argc == 1 )
   {
      printf("\nsyntax: %s mm/dd/yyyy_hh:mm:00", argv[0]);
      return -1;
   }

   // 0123456789A12
   // 03/12/2018_12
   strcpy(buf, argv[1]);
   tmData.tm_mon   = atoi(&buf[0]) - 1;  //month -1
   tmData.tm_mday  = atoi(&buf[3]); //day of month
   tmData.tm_year  = atoi(&buf[6]) - 1900; // year - 1900
   tmData.tm_hour  = atoi(&buf[11]); // hour
   tmData.tm_min   = 0; //minutes (not used)
   tmData.tm_sec   = 0; //seconds (not used)
   //tmData.tm_min   = atoi(&buf[14]);
   //tmData.tm_sec   = atoi(&buf[27]);

   //day light saving time variable.
   //NOT used in this calculation.
   //Tells mktime the input date is in day light saving time
   tmData.tm_isdst = 0; //

   mktime(&tmData);
   dayOfWeek = tmData.tm_wday;

   printf("%02d/%02d/%2d_%02d dayWk=%d ",
      tmData.tm_mon+1, tmData.tm_mday, tmData.tm_year, tmData.tm_hour, dayOfWeek);
   isdst = isDst(tmData.tm_mon+1, tmData.tm_mday, tmData.tm_hour, dayOfWeek);
   printf("isdst=%d\n", isdst);

   return 0;
}


int isDst(int month, int dayOfMonth, int hour, int dayOfWeek)
{
   int second_sunday, first_sunday;

   if( month  > 3 && month < 11  ) return 1; //4,5,6,7,8,9,10
   if( month  < 3 || month == 12 ) return 0; //1, 2 or 12
   if( month == 3 )
   {
      //The 2nd Sunday in March is 8,9,10,11,12,13,14
      if( dayOfMonth < 8  ) return 0;
      if( dayOfMonth > 14 ) return 1;

      //To get here dayOfMonth >= 8 && dayOfMonth <= 14
      second_sunday = dayOfMonth - dayOfWeek;
      if( second_sunday < 8 ) second_sunday += 7;
printf("2nd_Sunday=%2d ", second_sunday);
      if( dayOfMonth > second_sunday ) return 1;
      if( dayOfMonth < second_sunday ) return 0;

      //To get here dayOfMonth = second_sunday
      if( hour >= 2 ) return 1;
      else return 0;
   }

   if( month == 11 )
   {
      //The 1st Sunday in Nov is 1,2,3,4,5,6,7
      if( dayOfMonth > 7 ) return 0;

      //To get here dayOfMonth >= 1 && dayOfMonth <= 7
      first_sunday = dayOfMonth - dayOfWeek;
      if( first_sunday < 1 ) first_sunday += 7;
printf("1st_Sunday=%2d ", first_sunday);
      if( dayOfMonth > first_sunday ) return 0;
      if( dayOfMonth < first_sunday ) return 1;

      //To get here dayOfMonth = first_sunday
      if( hour >= 2 ) return 0;
      else return 1;
   }
   return -1;
}
/**************
   Compile via   cl.exe  isDst.c

   Begin and End dates for day light saving time
   03/11/2007_01:00:00    11/04/2007_01:00:00
   03/09/2008_01:00:00    11/02/2008_01:00:00
   03/08/2009_01:00:00    11/01/2009_01:00:00
   03/14/2010_01:00:00    11/07/2010_01:00:00
   03/13/2011_01:00:00    11/06/2011_01:00:00
   03/11/2012_01:00:00    11/04/2012_01:00:00
   03/10/2013_01:00:00    11/03/2013_01:00:00
   03/09/2014_01:00:00    11/02/2014_01:00:00
   03/08/2015_01:00:00    11/01/2015_01:00:00
   03/13/2016_01:00:00    11/06/2016_01:00:00
   03/12/2017_01:00:00    11/05/2017_01:00:00
   03/11/2018_01:00:00    11/04/2018_01:00:00
   03/10/2019_01:00:00    11/03/2019_01:00:00
   03/08/2020_01:00:00    11/01/2020_01:00:00
   03/14/2021_01:00:00    11/07/2021_01:00:00
   03/13/2022_01:00:00    11/06/2022_01:00:00
   03/12/2023_01:00:00    11/05/2023_01:00:00
   03/10/2024_01:00:00    11/03/2024_01:00:00
   03/09/2025_01:00:00    11/02/2025_01:00:00
   03/08/2026_01:00:00    11/01/2026_01:00:00
   03/14/2027_01:00:00    11/07/2027_01:00:00
   03/12/2028_01:00:00    11/05/2028_01:00:00
   03/11/2029_01:00:00    11/04/2029_01:00:00
   03/10/2030_01:00:00    11/03/2030_01:00:00
   03/09/2031_01:00:00    11/02/2031_01:00:00
   03/14/2032_01:00:00    11/07/2032_01:00:00

   isDst.exe 03/11/2007_02:00:00  >> dst.txt
   isDst.exe 03/09/2008_02:00:00  >> dst.txt
   isDst.exe 03/08/2009_02:00:00  >> dst.txt
   isDst.exe 03/14/2010_02:00:00  >> dst.txt
   isDst.exe 03/13/2011_02:00:00  >> dst.txt
   isDst.exe 03/11/2012_02:00:00  >> dst.txt
   isDst.exe 03/10/2013_02:00:00  >> dst.txt
   isDst.exe 03/09/2014_02:00:00  >> dst.txt
   isDst.exe 03/08/2015_02:00:00  >> dst.txt
   isDst.exe 03/13/2016_02:00:00  >> dst.txt
   isDst.exe 03/12/2017_02:00:00  >> dst.txt
   isDst.exe 03/11/2018_02:00:00  >> dst.txt
   isDst.exe 03/10/2019_02:00:00  >> dst.txt
   isDst.exe 03/08/2020_02:00:00  >> dst.txt
   isDst.exe 03/14/2021_02:00:00  >> dst.txt
   isDst.exe 03/13/2022_02:00:00  >> dst.txt
   isDst.exe 03/12/2023_02:00:00  >> dst.txt
   isDst.exe 03/10/2024_02:00:00  >> dst.txt
   isDst.exe 03/09/2025_02:00:00  >> dst.txt
   isDst.exe 03/08/2026_02:00:00  >> dst.txt
   isDst.exe 03/14/2027_02:00:00  >> dst.txt
   isDst.exe 03/12/2028_02:00:00  >> dst.txt
   isDst.exe 03/11/2029_02:00:00  >> dst.txt
   isDst.exe 03/10/2030_02:00:00  >> dst.txt
   isDst.exe 03/09/2031_02:00:00  >> dst.txt
   isDst.exe 03/14/2032_02:00:00  >> dst.txt
   isDst.exe 11/04/2007_02:00:00  >> dst.txt
   isDst.exe 11/02/2008_02:00:00  >> dst.txt
   isDst.exe 11/01/2009_02:00:00  >> dst.txt
   isDst.exe 11/07/2010_02:00:00  >> dst.txt
   isDst.exe 11/06/2011_02:00:00  >> dst.txt
   isDst.exe 11/04/2012_02:00:00  >> dst.txt
   isDst.exe 11/03/2013_02:00:00  >> dst.txt
   isDst.exe 11/02/2014_02:00:00  >> dst.txt
   isDst.exe 11/01/2015_02:00:00  >> dst.txt
   isDst.exe 11/06/2016_02:00:00  >> dst.txt
   isDst.exe 11/05/2017_02:00:00  >> dst.txt
   isDst.exe 11/04/2018_02:00:00  >> dst.txt
   isDst.exe 11/03/2019_02:00:00  >> dst.txt
   isDst.exe 11/01/2020_02:00:00  >> dst.txt
   isDst.exe 11/07/2021_02:00:00  >> dst.txt
   isDst.exe 11/06/2022_02:00:00  >> dst.txt
   isDst.exe 11/05/2023_02:00:00  >> dst.txt
   isDst.exe 11/03/2024_02:00:00  >> dst.txt
   isDst.exe 11/02/2025_02:00:00  >> dst.txt
   isDst.exe 11/01/2026_02:00:00  >> dst.txt
   isDst.exe 11/07/2027_02:00:00  >> dst.txt
   isDst.exe 11/05/2028_02:00:00  >> dst.txt
   isDst.exe 11/04/2029_02:00:00  >> dst.txt
   isDst.exe 11/03/2030_02:00:00  >> dst.txt
   isDst.exe 11/02/2031_02:00:00  >> dst.txt
   isDst.exe 11/07/2032_02:00:00  >> dst.txt
   https://stackoverflow.com/questions/5590429/calculating-daylight-saving-time-from-only-date
***************/



/*****
The previous programs used mktime to compute day_of_week.
It used day_of_week to compute 2nd_sunday in march and
1st_sunday in Nov.
If you don't want to use mktime, you can use this program to
compute 2nd_sunday.  The same technique will compute 1st_sunday.

On 03/14/2007, the day of the week is Wed, or 3.
Every year after 2007, the day of the week advances 1 day.
on leap years, the day of the week advances 2 days.
Must include the no. of leap years sinc 2004.
******/

#include <stdio.h>
#include <string.h>
#include <time.h>

int secondSunday(year);

int main(int argc, char *argv[])
{
   int year, second_sunday;

   if( argc == 1 )
   {
      printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
      return -1;
   }

   year = atoi(argv[1]);
   if( year < 2007 )
   {
      printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
      return -1;
   }

   second_sunday = secondSunday(year);
   printf("second_sunday=%d\n", second_sunday);

   return 0;
}


int secondSunday(year)
{
   //On 03/14/2007, the day of the week is Wed, or 3.
   int no_years, no_leaps, day_of_week, second_sunday;

   no_years = year - 2007;
   no_leaps = (year - 2004)/4;
   day_of_week = 3 + (no_years + no_leaps) % 7;
   second_sunday = 14 - day_of_week;
   if( second_sunday < 8 ) second_sunday += 7;

   //printf("no_years=%d,no_leaps=%d,day_of_week=%d, second_sunday=%d\n",
   //no_years, no_leaps, day_of_week, second_sunday);

   return second_sunday;
}
/**************
   Compile via   cl.exe  second_sunday.c

   second_sunday.exe 2007
   second_sunday.exe 2008
   second_sunday.exe 2009
   second_sunday.exe 2010
   second_sunday.exe 2011
   second_sunday.exe 2012
   second_sunday.exe 2013
   second_sunday.exe 2014
   second_sunday.exe 2015
   second_sunday.exe 2016
   second_sunday.exe 2017
   second_sunday.exe 2018
   second_sunday.exe 2019
   second_sunday.exe 2020
   second_sunday.exe 2021
   second_sunday.exe 2022
   second_sunday.exe 2023
   second_sunday.exe 2024
   second_sunday.exe 2025
   second_sunday.exe 2026
   second_sunday.exe 2027
   second_sunday.exe 2028
   second_sunday.exe 2029
   second_sunday.exe 2030
   second_sunday.exe 2031
   second_sunday.exe 2032
***************/
Share:
49,058

Related videos on Youtube

Muggz
Author by

Muggz

I try hard

Updated on March 25, 2020

Comments

  • Muggz
    Muggz over 3 years

    I am working with an Arduino and a real time clock chip. The chip compensates for leap years and such, so it will always have the correct date, but it does not handle daylight saving time, I assume due to regional complications. The clock can give me the day, month, and year (1 based) and the day of the week (sunday = 0 to saturday = 6).

    Because I need to compare with user entered dates and times, I need to know the date and time adjusted for daylight saving time. If the current date is in daylight saving time I can simply add an hour to the time from the clock and I have what I need.

    The hard part is determining whether I am in daylight saving time or not, because it changes from year to year. I only care that it works in my location (Mountain Time). There doesn't appear to be any comprehensive date libraries for my platform, and I feel like that would be overkill anyway. Is there a simple formula to determine if I am in DST or not?

  • Anomie
    Anomie over 12 years
    If that code really does agree with TimeZone.IsDayLightSavingsTime() for all dates from 1800 to 2006, then TimeZone.IsDayLightSavingsTime() is broken. Daylight savings time didn't even exist at a national level in the US until 1966 (except for certain periods during the two World Wars). And until 2007, it started the first Sunday in April and ended the last Sunday in October, except for a period in 1974–1975 when DST ran year-round.
  • Rahul
    Rahul over 12 years
    The documentation says that it is not historic. It only applies the rule for the current culture. There is another facility to get accurate historic data.
  • Anomie
    Anomie over 12 years
    So at least the brokenness is documented.
  • Muggz
    Muggz over 12 years
    Note that I am working with an embedded platform, so memory is at a premium. That database is several orders of magnitude larger than my available memory without hardware expansion. This project is for a single installation, so I really only care that it stays accurate within a single time zone. I can reprogram it if the DST rules change, but I would rather not reprogram it twice a year to manually adjust the time if I can avoid it.
  • Max Malysh
    Max Malysh about 8 years
    Please add some text describing what is going on here. Thanks!
  • Nathaniel Ford
    Nathaniel Ford about 8 years
    You should put the explanation in the answer itself. Also, you might want to format your last code snippet so it's vaguely readable.
  • dannysauer
    dannysauer over 7 years
    I think you may have an off-by-one in one of your checks. If the second Sunday in March is the 8th, then we're in DST on Sunday the 8th. 8-1 would be 7. So, shouldn't the check be previousSunday >= 7 assuming Sunday is DOW=1? If Sunday DOW is 0, then the November check would be previousSunday <= 1;, right?
  • ObjSal
    ObjSal almost 7 years
    @dannysauer good catch, but I think that the values of dow are from 0-6 and not from 1-7 for this to function to work.
  • SmokinGrunts
    SmokinGrunts over 6 years
    Please don't make the same silly mistake I did and pass a C-style struct tm tm_mon member in as the month; struct tm members are all zero-based, except for tm_mday (day of the month). If you do, you'll be a month off when Daylight Savings Time actually hits.
  • pwrgreg007
    pwrgreg007 over 1 year
    I used the isDST method above on a "Blue Pill" or simple STM32F103C8 system, which has embedded RTC, with battery backup. It is running Bruce Hall's WWVB program, and stores the date & time in UTC format, so all the leap-year stuff is handled automatically. This is a good method for determining DST at power-on, using calendar rules, e.g. second Sunday in March, first Sunday in November, 2:00 AM. The US Congress may pass permanent DST soon, so this may not be needed. Other data may have to be stored in limited NVRAM for rules and other flags to make it flexible.
  • pwrgreg007
    pwrgreg007 over 1 year
    It works for any year (year not part of calculation) as long as the day-of-week is correct for the month & day of year in question. Blue Pill RTC starts at 2000 but this was tested with C#, where the DateTime generates the day-of-week.
  • pwrgreg007
    pwrgreg007 over 1 year
    More details: first offset UTC by local timezone: EST=-5, PST = -8, etc. Multiply by minutes in hour and add to UTC in time_t value, then apply the algorithm.

Related