Calculating daylight saving time from only date
我正在使用Arduino和实时时钟芯片。 芯片可以补偿闰年等,所以它总是有正确的日期,但它不能处理夏令时,我认为由于区域复杂性。 时钟可以给我一天,一个月和一年(基于1)和星期几(星期日= 0到星期六= 6)。
因为我需要与用户输入的日期和时间进行比较,我需要知道为夏令时调整的日期和时间。 如果当前日期是夏令时我可以简单地从时钟添加一个小时,我有我需要的东西。
困难的部分是确定我是否在夏令时,因为它每年都在变化。 我只关心它在我的位置(山区时间)有效。 我的平台似乎没有任何全面的日期库,我觉得无论如何都会有点过头了。 是否有一个简单的公式来确定我是否在使用DST?
这实际上看似简单。有一些事实可以帮助我们:
这些事实导致以下代码(C#,但可以轻松地移植到您的平台):
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
事实证明,你甚至不需要知道这一年的时间,只要你可以相信你的一周中的价值。
我写了一个快速的单元测试并验证了这段代码与1800到2200的所有日期的
中欧代码(2014-3000年范围内每天测试)
1 2 3 4 5 6 7 8 9 10 11 12 | 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 } |
测试功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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(); |
}
虽然根据现行规则很容易计算特定日期是否在特定地点的DST中,但请注意DST是政治家的心血来潮,并且可能随时改变。我有一个2007年之前制造的时钟,它可以自动调整夏令时,现在我必须每年更换四次:实际发生变化时发生两次,现在发生两次 - 在旧日期不正确地改变自己。
在这种情况下,您可以通过让用户输入时区以及日期和时间的简单权宜来完全忽略DST。或者您可以像大多数消费者设备一样,让用户每年两次调整到当地时区的时间。
但是如果你真的需要处理DST并且真的想要做正确的事情,请使用zoneinfo数据库并确保它可以以某种方式更新。如果由于某种原因无法做到这一点,至少允许用户覆盖规则。即使这太难了,至少可以让用户选择关闭自动调整(不像我的愚蠢闹钟)。
我发现这一切都很有帮助。巴西有一些特殊问题,南半球,有时狂欢节与秋季更改日期重叠。
在这些情况下,立法机关将DST延迟一周。美国海军天文台计算可以找到复活节,(http://aa.usno.navy.mil/faq/docs/easter.php,检索1/3/2017),嘉年华是一个确切的天数,星期三之前的周末(狂欢节意味着"胖子星期二")。
所以,在C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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; } } |
此代码使用mktime来获取一周中的某一天。它使用一周中的某一天来计算夏令时。如果您不想使用mktime,可以使用程序second_sunday。从2007年3月14日开始,即星期三。一周中的每一天将每年提前1天,并且在2004年之后的每一次飞跃将提前2天。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | #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(" syntax: %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 ", 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(" syntax: %s year, with year >= 2007. ", argv[0]); return -1; } year = atoi(argv[1]); if( year < 2007 ) { printf(" syntax: %s year, with year >= 2007. ", argv[0]); return -1; } second_sunday = secondSunday(year); printf("second_sunday=%d ", 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 ", //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 ***************/ |
这是我的答案,我欢迎任何更正。假设这些年份在2000年至2099年之间。通过参考链接提供更多详细信息。
1 2 3 4 5 6 7 8 9 10 11 | 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++; } |
星期几计算参考:如何确定星期几,给定月,日和年
DST测试参考是通过本文作为回答,由captncraig和我自己的推理和解释他的答案。
我正在尝试这种方法,我认为它简单而准确:
//对于3月的第一个星期日,好像DoW = 1表示周日
if(月= = 3&amp;&amp; day> = 8&amp;&amp; day <= 14&amp;&amp; DoW = 1)返回True
//对于11月的第二个星期日,好像DoW = 1表示周日
if(月== 11&amp;&amp; day> = 1&amp;&amp; day <= 7&amp;&amp; DoW = 1)返回True
3月14日和11月7日总是美国一天节约光线的一周的一部分......它们可能是星期日或星期六或星期几,但它们总是那一周的一部分。下面的代码将找到星期日,即这两个日期是相关日期发生的年份的一部分。然后它会在此日期增加2小时以获得实际发生夏令时的时间。然后,您将有问题的日期与夏令时开始日期和结束日期进行比较,并通过gmt偏移量调整时间。此过程适用于其他国家/地区的开始和结束日期。您可以设置一个包含每个国家/地区代码和邮政编码的表格,其中包括夏令时结束和开始日期以及两个期间的gmt偏移量。日期是一个月的第一个到第四个星期日的第7天,第14天,第21天和第28天。你会在9月30日或10月31日之前的最后一个星期日放入最长的一天。
3月的第2个星期天:
1 | cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24 |
11月1日星期日:
1 | cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24 |
防爆。
1 | 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示例
情况[date2check]