struct tm tm_isdst disagrees with BST

孤街浪徒 提交于 2020-07-22 06:12:58

问题


I'm in the UK. I'm using C++ builder 10.2 with the clang compiler. The following code

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

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif

int _tmain()
{

    printf("TZ set = %s\r\n",putenv("TZ=Europe/London")==0 ? "true" : "false");
    printf("TZ=%s\r\n",getenv("TZ"));

    for (int dst = 0, year = 2017; year <= 2023; year++)
        for (int mon = 1; mon <= 12; mon++)
            for (int mday = 1; mday <= 31; mday++)
            {
                struct tm st = { 0, 0, 12, mday, mon - 1, year - 1900 };
                st.tm_isdst=-1;
                time_t tt = mktime(&st); // this sets the tm_isdst to 1 or 0
                if (st.tm_isdst != dst)
                {
                    dst = st.tm_isdst;
                    printf("%02d/%02d/%d (%ld) ", mday - !dst, mon, year, tt-(!dst)*24*60*60);
                    if (!dst) printf("\r\n");
                }
            }
    getch();
}

produces the following output

12/03/2017 (1489316400)   04/11/2017 (1509796800)
11/03/2018 (1520766000)   03/11/2018 (1541246400)
10/03/2019 (1552215600)   02/11/2019 (1572696000)
08/03/2020 (1583665200)   00/11/2020 (1604145600)
14/03/2021 (1615719600)   06/11/2021 (1636200000)
13/03/2022 (1647169200)   05/11/2022 (1667649600)
12/03/2023 (1678618800)   04/11/2023 (1699099200)

(The 00/11/2020 should be 30/10/2020 but I don't see the point of complicating the code to correct it).

The problem is the above dates are totally at odds with British Summer Time as listed by wiki -

2017 26 March 29 October 
2018 25 March 28 October 
2019 31 March 27 October 
2020 29 March 25 October 
2021 28 March 31 October 
2022 27 March 30 October 
2023 26 March 29 October 

The BST starting dates provided by my code (left hand side) return unix timestamps that are 3600 secs (1 hour) out. From comments below it seems the output would be all correct if my TZ was set to Canadian-American but it's set to London.

EDIT: I'm rephrasing the question. HITF do you get the code above to use the time zone as set in the Windows 10 settings? No matter what I set the time zone to it still comes up with similar dates. The only time I get a correct answer is if I specifically make the time zone (UTC-8.00) Pacific Time (US & Canada). It seems to use that time zone regardless of the one selected in settings. It's been bad enough waking up during this lockdown and not knowing what day it is. Now I don't even know what time zone it is.

EDIT2: I added the lines

printf("TZ set = %s\r\n",putenv("TZ=Europe/London")==0 ? "true" : "false");
printf("TZ=%s\r\n",getenv("TZ"));

to the code and while they printed

TZ set = true TZ=Europe/London

nothing changed.


回答1:


Those dates are the first and last date of Canadian-American DST. Check what time zone is specified by the TZ environment variable.

Other issues:

  • You assume the order of fields in struct tm, but the order isn't specified by the language.
  • You don't initialize the tm_isdst field correctly. -1 should be used for if it's unknown whether DST is being used or not. The value is presumably used to handle the overlapped ("fall back") hours in a change out of DST.
  • Your code assumes the switch to DST happens earlier in the year than the switch from DST, but it would be the opposite in the southern hemisphere.

Program with these issues fixed:

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

int main(void) {
   int dst = -1, dst_syear, dst_smon, dst_smday;
   for (int year=2017; year<=2023; ++year) {
      for (int mon=1; mon<=12; ++mon) {
         for (int mday=1; mday<=31; ++mday) {
            // Note that using .tm_isdst = -1 instead of the proper value 
            // will cause mktime to fail during one of the overlapped hours
            // of a "fall back" change from DST.
            struct tm st = {
               .tm_year  = year-1900,
               .tm_mon   = mon-1,
               .tm_mday  = mday,
               .tm_hour  = 12,
               .tm_isdst = -1,
            };

            mktime(&st);  // This sets the tm_isdst to 1 or 0

            if (dst == -1) {
               if (st.tm_isdst == 0) {
                  dst = 0;
               }
            } else {
               if (st.tm_isdst != dst) {
                  dst = st.tm_isdst;
                  if (st.tm_isdst) {
                     dst_syear = year;
                     dst_smon  = mon;
                     dst_smday = mday;
                  } else {
                     printf("%d-%02d-%02d %d-%02d-%02d\n",
                        dst_syear, dst_smon, dst_smday,
                        year, mon, mday);
                  }
               }
            }
         }
      }
   }

   return 0;
}

Output:

$ TZ=Europe/London ./a
2017-03-26 2017-10-29
2018-03-25 2018-10-28
2019-03-31 2019-10-27
2020-03-29 2020-10-25
2021-03-28 2021-10-31
2022-03-27 2022-10-30
2023-03-26 2023-10-29

$ TZ=America/Toronto ./a
2017-03-12 2017-11-05
2018-03-11 2018-11-04
2019-03-10 2019-11-03
2020-03-08 2020-11-01
2021-03-14 2021-11-07
2022-03-13 2022-11-06
2023-03-12 2023-11-05

$ TZ=Australia/Sydney ./a
2017-09-31 2018-04-01
2018-10-07 2019-04-07
2019-10-06 2020-04-05
2020-10-04 2021-04-04
2021-10-03 2022-04-03
2022-10-02 2023-04-02


来源:https://stackoverflow.com/questions/61989232/struct-tm-tm-isdst-disagrees-with-bst

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!