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 s
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
***************/
This is actually deceptively simple. There are a few facts that will help us:
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).
I found this all very helpful. Brazil has some special issues, southern hemisphere, and sometimes Carnival overlaps the fall change date.
In these cases, the legislature delays DST by one week. The US Naval observatory calculation can find easter, (at http://aa.usno.navy.mil/faq/docs/easter.php, retrieved 1/3/2017), and carnival is an exact number of days earlier, the week-end before ash wednesday (Mardi Gras means "fat tuesday").
So, in C:
static const uint8_t carnival[] = {
0x04, 0x24, 0x24, 0x21, // 2000... 2031
0x01, 0x09, 0x48, 0x09, // 2032... 2063
0x4a, 0x40, 0x4a, 0x52, // 2064... 2095
0x02, 0x90, 0x12, 0x94 // 2096... 2127
}
/* Returns the current time offset. */
int dst(struct tm *tm_ptr)
{
int st = 0;
int dst = 60;
int mon = tm_ptr->tm_mon;
int mday, previous_sunday;
int gmt_offset = tm_ptr->gmt_offset;
// If not Brasilia or Amazon time, no DST.
if(gmt_offset != -240 && gmt_offset != -300)
return st;
if(NOV < mon || FEB > mon) // Summer?
return dst;
else if(NOV > mon && FEB < mon) // Winter?
return st;
mday = tm_ptr->tm_mday;
previous_sunday = mday - tm_ptr->tm_wday;
// Begin DST on first Sunday of November.
if(NOV == mon) // If it's November... i.e. spring, so forward
{
if(previous_sunday < 1) // Before Sunday, week 1?
{
return st;
} else { // After or during Sunday, week 1
return dst;
}
// End DST in February, accounting for Carnival.
} else { // It has to be February, i.e. fall, so backward.
int year, week_start;
year = tm_ptr->tm_year;
if(0 == (carnival[year/8] & (1 << (year%8))))
week_start = 15; // 3rd Sunday is not in Carnival.
else
week_start = 22; // Use 4th Sunday, 1 week after Carnival.
if(previous_sunday < (week_start-1))
return dst;
if(previous_sunday < week_start) {
if(tm_ptr->tm_isdst == st) // if it already fell backward, stay.
return st;
return dst;
}
// On or after the correct Sunday?
return st;
}
}