仅通过日期计算夏令时

25

我正在使用Arduino和实时时钟芯片。该芯片可以补偿闰年等因素,因此它始终具有正确的日期,但它不处理夏令时,我想是由于地区复杂性。时钟可以提供给我日、月和年(从1开始计数)以及星期几(星期天=0到星期六=6)。

因为我需要与用户输入的日期和时间进行比较,所以我需要知道根据夏令时调整后的日期和时间。如果当前日期处于夏令时,我只需在时钟时间上加一小时即可得到所需结果。

难点在于确定我是否处于夏令时,因为夏令时会因年份而异。我只关心它在我的位置(山区时间)有效。似乎没有适用于我的平台的全面日期库,而且我觉得那也太过复杂了。是否有一个简单的公式来确定我是否处于夏令时?

9个回答

57

实际上,这很简单。有一些事实可以帮助我们:

  1. 在美国大部分地区,夏令时开始于三月第二个星期日,结束于十一月第一个星期日,都是在早上2点。
  2. 三月的第二个星期日将始终在8日至14日之间(包括这两天)。
  3. 十一月的第一个星期日将始终在1日至7日之间(包括这两天)。
  4. 星期几的编号非常方便,因为日期与星期几相减可得到前一个星期日。

这些事实导致以下代码(使用C#编写,但可以轻松移植到您的平台):

    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年所有日期的代码都与TimeZone.IsDayLightSavingsTime()一致。虽然我没有考虑2点钟的规则,但如果星期日并且日期在三月的8到14之间或十一月的1到7之间,你可以轻松完成此检查。


2
如果该代码确实对1800年至2006年的所有日期与TimeZone.IsDayLightSavingsTime()一致,那么TimeZone.IsDayLightSavingsTime()是有问题的。在美国,全国范围内的夏令时直到1966年才开始实行(除了两次世界大战期间的某些时期)。直到2007年,夏令时始于四月的第一个星期日,结束于十月的最后一个星期日,但在1974-1975年期间,夏令时全年运行。 - Anomie
文档说明它不是历史性的,它只适用于当前文化规则。还有另一种方法可以获取准确的历史数据。 - captncraig
所以至少损坏情况有记录。 - Anomie
1
我认为你在其中一个检查中可能有一个偏移量错误。如果三月的第二个星期日是8号,那么我们在3月8日星期日处于DST状态。8-1应该是7。所以,检查应该是previousSunday >= 7,假设星期日是DOW=1?如果星期日DOW是0,那么11月的检查就应该是previousSunday <= 1;,对吗? - dannysauer
1
@dannysauer 发现得好,但我认为 dow 的值应该是从 0-6 而不是从 1-7 才能使此函数正常工作。 - ObjSal
1
请不要犯我曾经犯过的傻错误,即将C风格的struct tm结构体的tm_mon成员作为月份传递;struct tm结构体的所有成员都是基于零的,除了tm_mday(月份中的天数)。如果你这样做,当夏令时实际生效时,你会出现一个月的偏差。 - SmokinGrunts

14

中欧地区代码(经过2014-3000年间每天的测试)

    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
    }

测试函数

    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();

4
虽然根据当前规则计算特定位置的特定日期是否处于DST很容易,但请注意DST取决于政治家们的心情,随时可能发生变化。我有一台2007年前制造的时钟,可以自动调整夏令时,现在我不得不每年更改四次:两次是当实际变化发生时,另外两次是当它错误地在旧日期自行更改时。
在这种情况下,您可以通过让用户输入时区来完全忽略DST。或者您可以像大多数消费设备一样,让用户每年两次将时间调整为当地时区。
但如果您真的需要处理DST并且真的想做到正确,使用zoneinfo数据库并确保它可以以某种方式进行更新。如果由于某种原因无法执行此操作,请至少允许用户覆盖规则。如果即使那也太困难了,请至少给用户关闭自动调整的选项(与我的愚蠢闹钟不同)。

请注意,我正在使用嵌入式平台,因此内存非常宝贵。那个数据库比我的可用内存大几个数量级,除非进行硬件扩展,否则无法容纳。这个项目只是为了单个安装,所以我只关心它在一个时区内保持准确。如果夏令时规则发生变化,我可以重新编程它,但我宁愿不要每年两次手动调整时间而重新编程它。 - Muggz

3

这段代码使用mktime获取一周中的某一天。它使用一周中的某一天来计算夏令时。如果您不想使用mktime,可以使用程序second_sunday。从3/14/2007开始,也就是星期三。每年增加1天,自2004年以来每个闰年增加2天。

#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://dev59.com/Em035IYBdhLWcg3wKsqa
***************/



/*****
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
***************/

我在“蓝色药丸”或简单的STM32F103C8系统上使用了isDST方法,该系统具有嵌入式RTC和电池备份。它正在运行Bruce Hall的WWVB程序,并以UTC格式存储日期和时间,因此所有闰年问题都会自动处理。这是一种确定夏令时的好方法,在开机时使用日历规则,例如三月第二个星期日,十一月第一个星期日,早上2点。 美国国会可能很快通过永久性夏令时法案,因此可能不再需要这个方法。其他数据可能需要存储在有限的NVRAM中,以便存储规则和其他标志,使其更加灵活。 - pwrgreg007
只要所查询的月份和年份中的日期是正确的,它就适用于任何年份(年份不是计算的一部分)。蓝色药丸RTC从2000年开始,但这是使用C#进行测试的,其中DateTime生成了星期几。 - pwrgreg007
更多细节:首先通过本地时区将UTC偏移量设置为EST=-5,PST=-8等。将小时中的分钟数乘以并加到time_t值中的UTC,然后应用算法。 - pwrgreg007

0
3月14日和11月7日总是在美国夏令时生效的那周的一部分。它们可能是星期日或星期六,也可能是这两天之间的任何一天,但它们总是属于那个周。下面的代码将找到这两个日期所在年份中的那个周的星期日。然后它会添加2个小时到这个日期,以得到夏令时实际发生的时间。然后,您可以将问题日期与夏令时开始和结束日期进行比较,并通过GMT偏移调整时间。此过程适用于其他国家的夏令时开始和结束日期。您可以设置一个表,其中包含每个国家/地区的代码和邮政编码,以及夏令时结束和开始日期和两个时期的GMT偏移量。日期将为每月的第1个星期日、第2个星期日、第3个星期日和第4个星期日的第7、14、21和28日。最后一个星期日的最大日期(例如9月30日或10月31日)应该加入其中。
三月的第二个星期日:
cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24

十一月的第一个星期日:

cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24

例子。

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] < 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))) 时,返回 dateadd(hh, - DST_GMT_TM_ZN_DIFF, [date2check]); 否则,如果 [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))),返回 dateadd(hh, - STD_GMT_TM_ZN_DIFF, [date2check]); 否则,返回 dateadd(hh, - DST_GMT_TM_ZN_DIFF, [date2check])。


2
请添加一些描述这里正在发生什么的文本。谢谢! - Max Malysh
你应该将解释放在答案本身中。此外,您可能希望格式化最后的代码片段,以使其稍微可读一些。 - Nathaniel Ford

0

我发现这些都非常有帮助。巴西有一些特殊问题,位于南半球,有时候狂欢节会与秋季时间变更日期重叠。

在这些情况下,立法机构会将夏令时延迟一周。美国海军天文台的计算可以找到复活节(在http://aa.usno.navy.mil/faq/docs/easter.php,检索日期为2017年1月3日),而狂欢节比复活节早几天,是在灰色星期三前一个周末(Mardi Gras意为“肥胖星期二”)。

因此,在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;
    }
}

0

我正在尝试这种方法,我认为它简单而准确:

// 对于三月的第一个星期日,如果DoW = 1表示星期日 if (month==3 && day>=8 && day <=14 && DoW=1) return True

// 对于十一月的第二个星期日,如果DoW = 1表示星期日 if (month==11 && day>=1 && day <=7 && DoW=1) return True


0

这是我的答案,欢迎任何纠正。假设年份在2000年至2099年之间(包括两个端点)。更多细节请参考链接。

  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++;
  }

星期计算参考:如何确定给定月份、日期和年份的星期几
夏令时测试参考是通过这篇文章回答的,由captncraig和我自己的推理和解释。


0

如果有人在寻找Python相关的内容,这里有一篇很棒的文章: http://code.activestate.com/recipes/425607-findng-the-xth-day-in-a-month/

它包含以下代码:

from calendar import monthrange

def dow_date_finder(which_weekday_in_month=FIRST,day=MONDAY,month=JANUARY,year=2000):
    bom, days = monthrange(year, month)
    firstmatch = (day - bom) % 7 + 1
    return xrange(firstmatch, days+1, 7)[which_weekday_in_month]

请记住,为了使页面更加用户友好,您需要在上面链接的所有变量都存在。

以下内容适用于任何2007年或之后的年份:

def find_dt_of_daylight_savings_time(yr=date.today().year):
    dst = {}
    spring = dow_date_finder(SECOND, SUNDAY, MARCH, yr)
    spring_dt = datetime(int(yr), 3, spring, 3, 0)
    dst['spring'] = spring_dt
    fall = dow_date_finder(FIRST, SUNDAY, NOVEMBER, yr)
    fall_dt = datetime(int(yr), 11, fall, 3, 0)
    dst['fall'] = fall_dt
    return dst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接