c - Linux 夏令时通知

标签 c linux notifications dst

我试图找到一种在应用夏令时时从系统(Linux)接收通知的方法,但我似乎无法找到类似的东西。

考虑一个程序位于 pselect()等待多个定时器fd 's,所有这些都具有精确的 24 小时间隔,但开始时间不同,由用户定义; “07:00 ON , 07:25 OFF ”(例如,如果它是咖啡机)。

因为用户在本地时间给出这些时间并且 Linux 在 UTC 上运行,所以时区调整计时器 fd每次夏令时都需要重新调整。 (当他的夏令时兼容的闹钟把他叫醒时,用户希望喝咖啡......)

正如我想象的那样,解决这个问题的智能方法是注册到系统/内核/初始化/应用夏令时时要通知的任何内容,并避免陷入尝试自己确定此类日期和时间的困惑事务并希望系统同意您的结果(即您的重新同步操作和实际的夏令时同时发生)。

有什么方法可以通知 DST 更改?或者也许对本地时间的任何更改(假设 DST 更改会修改)?

最佳答案

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 中使用 future 事件的最小堆,例如
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[];
};
event struct event_min_heap 中的 C99 灵活数组成员是一个数组,num_events min heap 中的事件(为 max_events 分配的内存;如果需要更多事件,可以重新分配)由 when 键入每个 event 中的字段入口。也就是说,最早的事件总是在根源上。

只要当前时间至少为 event[0].when ,它是“触发的”——意味着要采取的任何行动,都会被采取——并且基于 struct trigger它指的是,该事件下一次发生的时间更新为event[0] ,然后它在堆中向下渗透到适当的位置。请注意,您只需使用 mktime() 从分解的本地时间字段中获取 UTC 时间。

(如果这是一个多用户服务,那么您可以支持多个并发时区,每个触发器一个,方法是将 TZ 环境变量设置为相应的时区定义,并在调用 tzset() 之前调用 mktime()。因为环境由进程中的所有线程共享,所以如果您有一个多线程进程,您需要确保一次只有一个线程执行此操作。通常,使用单线程进程可以完美地实现这样的东西。)

当根 (event[0]) 中的事件被删除或渗透(筛选)时,具有下一个最小 when 的事件将在根。如果 when等于或小于 UTC 中的当前时间,它也会被触发。

当下一个when是将来,进程可以休眠的剩余时间间隔。

这就是它的全部。您不需要多个计时器(这是系统范围的有限资源),并且您无需担心某个本地时间是否为夏令时; C 库 mktime()将为您处理此类细节。

现在,如果您不喜欢这种方法(同样,它使用的资源比您在问题中概述的方法少),请联系 SystemD 开发人员。如果你足够恭顺地亲吻他们,我相信他们会为你提供 dbus 信号。它目前的设计并没有任何理智,多一个疣肯定不会让它变得更糟。切换到 C# 可能被认为是一个加分项。

了解 mktime() 至关重要。计算指定时刻的 Unix 纪元时间 ( time_t ),如果适用于该特定时刻,则应用夏令时。调用函数时夏令时是否生效并不重要!

此外,UTC 时间是协调世界时,不受时区或夏令时的影响。

考虑以下程序,mktime-example.c :
#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("\n");
    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 ]\n", argv[0]);
        fprintf(stderr, "       %s [ :REGION/CITY | =TIMEZONE ] @EPOCH | YYYYMMDD-HHMMSS[+-] ...\n", argv[0]);
        fprintf(stderr, "Where:\n");
        fprintf(stderr, "       EPOCH is in UTC seconds since 19700101T000000,\n");
        fprintf(stderr, "       + after time indicates you prefer daylight savings time,\n");
        fprintf(stderr, "       - after time indicates you prefer standard time.\n");
        fprintf(stderr, "\n");
        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.\n", argv[arg]);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

使用例如编译它
gcc -Wall -O2 mktime-example.c -o mktime-example

不带参数运行它以查看命令行用法。跑
./mktime-example :Europe/Helsinki 20161030-035959+ 20161030-030000- 20161030-030000+ 20161030-035959- 20161030-040000-

检查 2016 年 DST 在芬兰赫尔辛基结束时的 Unix 时间戳。该命令将输出
(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 时是否在某个时区生效,输出都将是相同的!

调用mktime()时与 .tm_isdst = 0.tm_isdst = 1 , 和 mktime()改变它,它也会改变指定的时间(由夏令时)。当.tm_isdst = -1 ,这意味着调用者不知道是否应用了夏令时,库会发现;但如果同时存在有效的标准时间和 DST 时间,C 库将选择一个(您应该假设它是随机选择的)。 epoch()上面的功能会在必要时对此进行更正,如果用户对 DST 不正确,则取消调整时间。

关于c - Linux 夏令时通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39675739/

相关文章:

linux - 保留备份

linux - Git 如何知道要推送到哪个存储库?

android - 应用程序未运行时 Phonegap 中的通知

ios - 使用计时器安排通知触发

c - 使用 rabin karp 算法对文件进行切片

c - 如何更改嵌套 for 循环中的行索引和列索引?

linux - 如果没有错误使用linux shell脚本编译并运行程序

events - 当我 fork 的存储库的上游有更新时通知我

C:得到可能与空终止符有关的错误输出?

c - 我的 "exit(0)"函数将 ": Success"打印到标准输出。我怎样才能阻止这个?