Linux daylight savings notification
我试图找到一种方法,在应用夏令时从系统(Linux)接收通知,但我似乎找不到类似的东西。
假设一个程序位于一个
由于用户在本地时间提供这些时间,并且Linux在UTC上运行,因此需要在每次夏时制发生时重新调整时区调整计时器
正如我想象的那样,实现这一点的智能方法是在应用夏令时向系统/kernel/init/注册任何需要通知的内容,避免陷入自己试图确定这些日期和时间的混乱事务中,并希望系统同意您的结果(即,您的重新同步操作和实际夏令时保存NGS同时发生)。
是否有任何方法通知DST变更?或者对当地时间的任何更改(假设DST更改会修改这一点)?
Unix/Linux系统只处理UTC,它们使用
从
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 | $ zdump -v Europe/Madrid Europe/Madrid Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 20:45:52 1901 WET isdst=0 gmtoff=0 Europe/Madrid Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 20:45:52 1901 WET isdst=0 gmtoff=0 Europe/Madrid Sat May 5 22:59:59 1917 UTC = Sat May 5 22:59:59 1917 WET isdst=0 gmtoff=0 Europe/Madrid Sat May 5 23:00:00 1917 UTC = Sun May 6 00:00:00 1917 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 6 22:59:59 1917 UTC = Sat Oct 6 23:59:59 1917 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 6 23:00:00 1917 UTC = Sat Oct 6 23:00:00 1917 WET isdst=0 gmtoff=0 Europe/Madrid Mon Apr 15 22:59:59 1918 UTC = Mon Apr 15 22:59:59 1918 WET isdst=0 gmtoff=0 Europe/Madrid Mon Apr 15 23:00:00 1918 UTC = Tue Apr 16 00:00:00 1918 WEST isdst=1 gmtoff=3600 Europe/Madrid Sun Oct 6 22:59:59 1918 UTC = Sun Oct 6 23:59:59 1918 WEST isdst=1 gmtoff=3600 Europe/Madrid Sun Oct 6 23:00:00 1918 UTC = Sun Oct 6 23:00:00 1918 WET isdst=0 gmtoff=0 Europe/Madrid Sat Apr 5 22:59:59 1919 UTC = Sat Apr 5 22:59:59 1919 WET isdst=0 gmtoff=0 Europe/Madrid Sat Apr 5 23:00:00 1919 UTC = Sun Apr 6 00:00:00 1919 WEST isdst=1 gmtoff=3600 Europe/Madrid Mon Oct 6 22:59:59 1919 UTC = Mon Oct 6 23:59:59 1919 WEST isdst=1 gmtoff=3600 Europe/Madrid Mon Oct 6 23:00:00 1919 UTC = Mon Oct 6 23:00:00 1919 WET isdst=0 gmtoff=0 Europe/Madrid Wed Apr 16 22:59:59 1924 UTC = Wed Apr 16 22:59:59 1924 WET isdst=0 gmtoff=0 Europe/Madrid Wed Apr 16 23:00:00 1924 UTC = Thu Apr 17 00:00:00 1924 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 4 22:59:59 1924 UTC = Sat Oct 4 23:59:59 1924 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 4 23:00:00 1924 UTC = Sat Oct 4 23:00:00 1924 WET isdst=0 gmtoff=0 Europe/Madrid Sat Apr 17 22:59:59 1926 UTC = Sat Apr 17 22:59:59 1926 WET isdst=0 gmtoff=0 Europe/Madrid Sat Apr 17 23:00:00 1926 UTC = Sun Apr 18 00:00:00 1926 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 2 22:59:59 1926 UTC = Sat Oct 2 23:59:59 1926 WEST isdst=1 gmtoff=3600 Europe/Madrid Sat Oct 2 23:00:00 1926 UTC = Sat Oct 2 23:00:00 1926 WET isdst=0 gmtoff=0 Europe/Madrid Sat Apr 9 22:59:59 1927 UTC = Sat Apr 9 22:59:59 1927 WET isdst=0 gmtoff=0 ... |
顺便说一句,如果您希望得到即将发生的本地时间更改的通知,您可以使用前面的信息来构造一个crontab文件,包括所有信息,或者简单地构造一个crontab文件,其中包括在本地应用的规则。例如,如果我想在西班牙交换机更改前一天得到通知(它在3月/10月的最后一个星期日,02/03H发生更改),您可以在crontab文件中添加一些规则:
1 | 0 0 24-30 3,10 5 echo Time daylight savings change scheduled for tomorrow | mail $USER@your.domain.com |
每年3月24日至30日(3月30日至10月30日)每星期六(5日)00:00(当地时间)向您发送一封邮件。我相信您能够将这个例子适应您的本地性或提前时间(所以,在时间变化发生的前一天)。
Consider a program sits on a pselect() waiting for a number of timer fd's, all which have exactly 24-hour intervals, but differing start times
这就是你的根本问题。所有的日子都不完全是24小时长——有时是一小时(夏令时)或几秒(闰秒),就像不是每个二月都有28天一样。
一种更简单、更轻(消耗的资源更少)的方法是在UTC中使用一个最小的未来事件堆,类似于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | struct trigger { /* Details on how the event is defined; for example,"each day at 07:00 local time". */ }; struct utc_event { struct trigger *trigger; time_t when; }; struct event_min_heap { size_t max_events; size_t num_events; struct utc_event event[]; }; |
当当前时间至少为
(如果这是一个多用户服务,那么您可以支持多个并发时区,每个触发器一个,方法是将
当删除或过滤(筛选)根(
当下一个
这就是一切。您不需要多个计时器——这是一个系统范围的有限资源——并且您不需要担心某个本地时间是否为夏令时;C库
现在,如果您不喜欢这种方法(同样,这种方法使用的资源比您在问题中概述的方法少),请与SystemD开发人员联系。如果你对他们有足够的恭维,我相信他们会为你提供一个DBUS信号。它不像在它的当前设计中有任何健全,而且一个疣肯定不会使它更糟。切换到C可能被认为是一个优势。
重要的是要理解,
此外,UTC时间是协调世界时,不受时区或夏令时的限制。
考虑以下程序,
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 | #define _POSIX_C_SOURCE 200809L #include <stdlib.h> #include <string.h> #include <stdio.h> #include <time.h> static time_t epoch(struct tm *const tm, const int year, const int month, const int day, const int hour, const int minute, const int second, const int isdst) { struct tm temp; time_t result; memset(&temp, 0, sizeof temp); temp.tm_year = year - 1900; temp.tm_mon = month - 1; temp.tm_mday = day; temp.tm_hour = hour; temp.tm_min = minute; temp.tm_sec = second; temp.tm_isdst = isdst; result = mktime(&temp); if (isdst >= 0 && isdst != temp.tm_isdst) { /* The caller is mistaken about DST, and mktime() * adjusted the time. We readjust it. */ temp.tm_year = year - 1900; temp.tm_mon = month - 1; temp.tm_mday = day; temp.tm_hour = hour; temp.tm_min = minute; temp.tm_sec = second; /* Note: tmp.tm_isdst is kept unchanged. */ result = mktime(&temp); } if (tm) memcpy(tm, &temp, sizeof temp); return result; } static void show(const time_t t, const struct tm *const tm) { printf("(time_t)%lld = %04d-%02d-%02d %02d:%02d:%02d", (long long)t, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if (tm->tm_isdst == 1) printf(", DST in effect"); else if (tm->tm_isdst == 0) printf(", DST not in effect"); else if (tm->tm_isdst == -1) printf(", Unknown if DST in effect"); if (tzname[0] && tzname[0][0]) printf(", %s timezone", tzname[0]); printf(" "); fflush(stdout); } int main(int argc, char *argv[]) { struct tm tm; time_t t; long long secs; int arg, year, month, day, hour, min, sec, isdst, n; char ch; if (argc < 2 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) { fprintf(stderr,"Usage: %s [ -h | --help ] ", argv[0]); fprintf(stderr," %s [ :REGION/CITY | =TIMEZONE ] @EPOCH | YYYYMMDD-HHMMSS[+-] ... ", argv[0]); fprintf(stderr,"Where: "); fprintf(stderr," EPOCH is in UTC seconds since 19700101T000000, "); fprintf(stderr," + after time indicates you prefer daylight savings time, "); fprintf(stderr," - after time indicates you prefer standard time. "); fprintf(stderr," "); return EXIT_FAILURE; } for (arg = 1; arg < argc; arg++) { if (argv[arg][0] == ':') { if (argv[arg][1]) setenv("TZ", argv[arg], 1); else unsetenv("TZ"); tzset(); continue; } if (argv[arg][0] == '=') { if (argv[arg][1]) setenv("TZ", argv[arg] + 1, 1); else unsetenv("TZ"); tzset(); continue; } if (argv[arg][0] == '@') { if (sscanf(argv[arg] + 1," %lld %c", &secs, &ch) == 1) { t = (time_t)secs; if (localtime_r(&t, &tm)) { show(t, &tm); continue; } } } n = sscanf(argv[arg]," %04d %02d %02d %*[-Tt] %02d %02d %02d %c", &year, &month, &day, &hour, &min, &sec, &ch); if (n >= 6) { if (n == 6) isdst = -1; else if (ch == '+') isdst = +1; /* DST */ else if (ch == '-') isdst = 0; /* Not DST */ else isdst = -1; t = epoch(&tm, year, month, day, hour, min, sec, isdst); if (t != (time_t)-1) { show(t, &tm); continue; } } fflush(stdout); fprintf(stderr,"%s: Cannot parse parameter. ", argv[arg]); return EXIT_FAILURE; } return EXIT_SUCCESS; } |
使用例如
1 | gcc -Wall -O2 mktime-example.c -o mktime-example |
运行它时不带参数,以查看命令行的用法。跑
1 | ./mktime-example :Europe/Helsinki 20161030-035959+ 20161030-030000- 20161030-030000+ 20161030-035959- 20161030-040000- |
在芬兰赫尔辛基研究2016年夏令时结束前后的Unix时间戳。命令将输出
1 2 3 4 5 | (time_t)1477789199 = 2016-10-30 03:59:59, DST in effect, EET timezone (time_t)1477789200 = 2016-10-30 03:00:00, DST not in effect, EET timezone (time_t)1477785600 = 2016-10-30 03:00:00, DST in effect, EET timezone (time_t)1477792799 = 2016-10-30 03:59:59, DST not in effect, EET timezone (time_t)1477792800 = 2016-10-30 04:00:00, DST not in effect, EET timezone |
不管运行此DST时是否在某些时区生效,输出都将相同!
当用