2016-09-24 2 views
1

Я пытался найти способ получения уведомления от системы (Linux), когда применяется летнее время, но я, похоже, не могу найти ничего подобного.Уведомление о летнем свете Linux

Рассмотрите программу, которая находится на pselect() в ожидании количества таймеров fd, которые имеют ровно 24-часовые интервалы, но отличаются временем начала, которые определяются пользователем; «07:00 ON, 07:25 OFF» (например, если это была кофеварка).

Поскольку пользователь дает эти моменты по местному времени, а Linux работает по UTC, таймер с настраиваемым часовым поясом fd необходимо перенастроить каждый раз, когда происходит переход на летнее время. (пользователь ожидает кофе, когда его дневной будильник совместит с будильником, пробудивший его ...)

Интеллектуальный способ обойти это, как я мог бы себе представить, это зарегистрироваться в систему/kernel/init/уведомляются, когда применяются летние сбережения, и избегайте попадания в беспорядочный бизнес, пытаясь определить такие даты и время самостоятельно, и надеемся, что система согласуется с вашими результатами (то есть ваши повторные действия и фактические летние сбережения произойдут в одно и то же время).

Есть ли способ получить уведомление о внесении изменений в DST? Или, возможно, при любых изменениях местного времени (при условии, что изменение DST изменяет это)?

ответ

0

Рассмотрим программу сидит на pselect() ждет ряд ФД таймера, все которые имеют ровно каждые 24 часа, но разное время начала

В этом и состоит ваша фундаментальная проблема. Все дни не ровно 24 часа - иногда они отключены на час (летнее время) или секунды (прыжки секунд); точно так же, как не каждый февраль имеет 28 дней.

Намного проще и легкий (потребляется меньше ресурсов) способ заключается в использовании мин кучу будущих событий в формате UTC, что-то вроде

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 C99 в struct event_min_heap массив с num_events события (память, выделенная для max_events; может быть перераспределена, если требуется больше событий) в min heap, введенном в поле 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 Epoch (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- 

изучить Unix метки вокруг того времени, когда DST заканчивается в 2016 году в Хельсинки, Финляндия. Команда вывод

будет
(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 

выход будет одинаковым, независимо от того, в момент запуска этого перехода на летнее время действует в каком-то часовом поясе или нет!

При вызове mktime() с .tm_isdst = 0 или .tm_isdst = 1 и mktime() изменяет его, он также изменяет заданное время (а в летнее время). Когда .tm_isdst = -1, это означает, что вызывающий абонент не знает, применяется ли DST или нет, и библиотека узнает; но если есть и действующее стандартное время и время DST, библиотека C выберет один (вы должны предположить, что он делает это случайным образом). Функция epoch() выше, если необходимо, исправляет это, отменяя время, если пользователь не подходит к DST.

+0

Если я полностью понял, основной смысл заключается в перепланировании после истечения каждого события, но только один (следующий за триггером), чтобы сохранить таймеры. Перепланирование будет поддерживать события в эффективном локальном времени, и в худшем случае я бы смотрел 23-25-часовые периоды два раза в год, когда событие не срабатывало в правильное время. (случай, когда запускается только одно событие, запускающее 23:59 и получение перенесенного, что за одну минуту до того, как местное время перескочит с DST). И если я точно знаю, что DST прыгает всегда, независимо от TZ, в полночь, я мог бы ежедневно проверять и корректировать, если я нахожу '.tm_gmtoff' изменено. – Tammi

+0

@Tammi: ** Нет! ** Всякий раз, когда событие срабатывает, вы используете 'mktime()', чтобы узнать время UTC в следующий раз, когда должно произойти событие. Обработка часового пояса библиотеки C достаточно интеллектуальна, чтобы определить это время в UTC, в том числе применительно к летнему времени. * Все события будут срабатывать в правильное время. * Я попытаюсь добавить практический пример в свой ответ. –

+0

@Tammi: В системах POSIXy, включая Linux и Mac OS, переход на летнее время - это не какой-то глобальный флаг, который включен и выключен некоторыми службами операционной системы. Это свойство часового пояса. Библиотека 'mktime()' библиотеки C выполняет разбитые локальные поля времени и преобразует ее в UTC (Epoch), применяя текущие правила часового пояса, включая DST * для целевого времени *. Не имеет значения, применяется ли DST в момент вызова. Когда вы используете временные метки UTC, «прыжки с DST» не имеют значения: * мы готовимся к ним заранее *, поэтому на самом деле нет «прыжков». –

0

Системы Unix/linux работают только с UTC, и они используют данные time_t (количество секунд с 00:00 до 01:00 по UTC до настоящего времени) в качестве внутреннего времени. Конверсии по местному времени (со сложностями из-за исключений, вариаций для летне-зимних периодов и т. Д.) выполняется только при отображении информации пользователю, поэтому только при преобразовании в локальное время это делается. Как сказано, в системе unix не предусмотрено никаких условий для планирования или подготовки к ней.

От zdump(1) вы можете получить всю необходимую информацию по часовой стрелке и использовать ее для создания crontab, чтобы уведомить вас, когда должен быть сделан переключатель. Он консультирует местную базу данных часовых поясов и извлекает всю информацию о переключении (включая историческую) с зимы на лето или наоборот.

$ 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 
... 

Кстати, если вы хотите быть уведомлены о предстоящем изменении МестноеВремя, вы можете использовать предыдущую информацию, чтобы построить файл кронтаб, включая всю информацию, или просто построить файл кронтаб, которые включают правила которые применяются в вашей местности. Например, если я хочу иметь в виду один день до переключаемые в Испании (она меняется на последнее воскресенье марта/октября, на 02/03h) вы можете добавить некоторые правила в файле кронтаб:

0 0 24-30 3,10 5 echo Time daylight savings change scheduled for tomorrow | mail [email protected] 

и письмо будет отправлено вам в каждую субботу (5), которая, случается, будет проходить с 24-30 марта и октября (3,10 часть) каждого года в 00:00 (местное время). Я уверен, что вы сможете адаптировать этот пример к своей локальности или времени продвижения (так, за день до изменения времени).

+0

Хорошая информация по этому вопросу - другие, ищущие подобную информацию, могут по-прежнему получать фактическое уведомление. – Tammi

+0

Как узнать, кого интересует эта информация? Хороший способ узнать, это увеличить его :) –