可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
This is actually deceptively simple. There are a few facts that will help us:
- 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.
- The second Sunday in March will always be between the 8th and the 14th inclusive.
- The first Sunday in November will always be between the 1st and 7th inclusive.
- 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).
回答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();
}
回答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).
回答4:
March 14th and November 7 are always part of the week that the day light savings occur in the united states.. they may be the Sunday or the Saturday or and day of the week in-between but they are always part of that week. the code below will find the Sunday of that those two dates are part of in the year that the date in question occurs in. it then adds 2 hours to this date to get the time in which the daylight savings actually occur. you then compare the date in question against the daylight savings beginning and end dates and adjust the time by the gmt offset. This process can work for other countries begin and end dates. you could setup a table that has every country code and postal code in it with daylight savings end and begin dates and the gmt offset for both periods. the dates would be the 7th, 14th, 21th, and 28th, for the first through forth Sundays of a month. you would put in the max day in for the last Sunday of the month ex Sep 30th or Oct 31st.
2nd Sunday in March:
cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24
1st Sunday November:
cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24
Ex.
If(now() < cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-5,now()), if(now() < cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-6,now()), dateadd("H",-5,now())))
t_SQL example
CASE WHEN [date2check] < DATEADD(hh, 2, CAST('3/14/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime)
+ 1 - DATEPART(w, CAST('3/14/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime))) THEN dateadd(hh, - DST_GMT_TM_ZN_DIFF,
[date2check]) ELSE CASE WHEN [date2check] < DATEADD(hh, 2, CAST('11/7/' + CAST(DATEPART(yyyy,
[date2check]) AS nvarchar(4)) AS datetime) + 1 - DATEPART(w, CAST('11/7/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4))
AS datetime))) THEN dateadd(hh, - STD_GMT_TM_ZN_DIFF, [date2check]) ELSE dateadd(hh, - DST_GMT_TM_ZN_DIFF,
[date2check]) END END
回答5:
I am trying with this approach and I think it simple and accurate:
// for first Sunday of March as if DoW = 1 for Sunday
if (month==3 && day>=8 && day <=14 && DoW=1) return True
// for second Sunday of Nov as if DoW = 1 for Sunday
if (month==11 && day>=1 && day <=7 && DoW=1) return True
回答6:
Here is my answer, and I welcome any corrections. It is assumed that the years are between 2000 and 2099 inclusive. Further details available via the reference link.
int timezone = 0; // Set to correct initial value depending on where you are (or via GPS if you like).
// Calculate day of week for Daylight savings time.
int day_of_week = (day_of_month + int(2.6 * (((month + 12 - 3) % 12) + 1) - 0.2) - 40 +
(month < 3 ? year-1 : year) + int((month < 3 ? year-1 : year)/4) + 5) % 7;
// Adjust timezone based on Daylight savings time for northern hemisphere, USA
if ((month > 3 && month < 11 ) ||
(month == 3 && day_of_month >= 8 && day_of_week == 0 && hour >= 2) || // DST starts 2nd Sunday of March; 2am
(month == 11 && day_of_month < 8 && day_of_week > 0) ||
(month == 11 && day_of_month < 8 && day_of_week == 0 && hour < 2)) { // DST ends 1st Sunday of November; 2am
timezone++;
}
Day of Week calculation reference: How to determine the day of the week, given the month, day and year
DST test reference is via this article as answered by captncraig and my own reasoning and interpretation of his answer.
回答7:
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
***************/