Как обновить время в lua, чтобы отразить изменение часового пояса системы во время выполнения?

Проблема

Я хочу изменить виджет awful.widget.textclock в awesome-wm, чтобы он немедленно отражал изменение системного часового пояса. Этот виджет и весь конфиг awesome-wm написаны на lua.

В настоящее время, если системный часовой пояс изменен, виджет продолжает отображать время в соответствии с часовым поясом, установленным во время выполнения. Виджет использует функцию os.time для получения времени, но это не соответствует системному времени.


Решение, как показано ниже

луа скрипт:

local tz=require"luatz";

require "luatz.tzcache".clear_tz_cache()
print("Before Changes (America/Los_Angeles)")
print(os.date("!%H:%M",tz.time_in()))

os.execute("timedatectl set-timezone America/Chicago")

require "luatz.tzcache".clear_tz_cache()
print("America/Chicago")
print(os.date("!%H:%M",tz.time_in()))

os.execute("timedatectl set-timezone America/New_York")
require "luatz.tzcache".clear_tz_cache()

print("America/New_York")
print(os.date("!%H:%M",tz.time_in()))

Выход:

Before Changes (America/Los_Angeles)
15:33
America/Chicago
17:33
America/New_York
18:33

Обходной путь

Это можно решить, перезапустив удивительный оконный менеджер, из-за чего виджет снова получает правильный часовой пояс. Естественно, при изменении часового пояса требуется еще один перезапуск оконного менеджера.

Желаемый эффект заключается в обновлении часового пояса из системы при его изменении либо периодически, либо при каждом вызове функции os.time.


Пример использования

Если вам интересно, вариант использования для этого на ноутбуке. Я часто путешествую и езжу tzupdate на systemd timer. Я хотел бы автоматизировать смену часового пояса. Это отлично работает, за исключением того, что виджет, который фактически отображает время, не замечает изменения системного часового пояса.


Пытался до сих пор

  1. Сбросьте переменную окружения $TZ. Но Arch Linux никогда не устанавливает эту переменную в первую очередь, поэтому я не уверен, как lua ​​вообще определяет правильный часовой пояс.
  2. Используйте библиотеку luatz и, в частности, функцию tzcache.clear_tz_cache(). Кажется, это не имеет никакого эффекта.
  3. Получить системное время с помощью функций, отличных от os.time(): luatz.time() и luatz.gettime.gettime(). Они извлекают то же время, что и другие функции.
  4. Используйте функцию luatz.time_in(), но она возвращает время со смещением часового пояса, примененным дважды ко времени UTC. luatz.time() возвращает правильное местное время, но должно возвращать время UTC.

ОБНОВЛЕНО luatz

Я попытался возиться с библиотекой luatz в соответствии с рекомендациями, но, похоже, она не перепроверяет системный часовой пояс даже после вызова функции tzcache.clear_tz_cache().

Я клонировал репозиторий luatz и скопировал luatz в каталог системных модулей. Скрипт вроде загружается корректно, но не изменился эффект игнорирования изменения системного часового пояса. Насколько я могу судить, это ничем не отличается от функции os.time().

тестовый сценарий луатца:

local luatz = require "luatz"
local tzcache = require "luatz.tzcache"
local gettime = require "luatz.gettime"

print ("\nBefore Change - System TZ is ")
os.execute("timedatectl | grep 'Time zone' | awk '{ print $3 }'")
print("\nos.time(): "..os.date("%H:%M", os.time()))
print("luatz.time(): "..os.date("%H:%M", luatz.time()))
print("gettime..gettime(): "..os.date("%H:%M", gettime.gettime()))

print("\nTime zone changed to America/New_York")
os.execute("timedatectl set-timezone America/New_York")

tzcache.clear_tz_cache()

print ("\nAfter  Change - System TZ is ")
os.execute("timedatectl | grep 'Time zone' | awk '{ print $3 }'")
print ("\nos.time(): "..os.date("%H:%M", os.time()))
print ("luatz.time(): "..os.date("%H:%M", luatz.time()))
print("gettime.gettime(): "..os.date("%H:%M", gettime.gettime()))

Выход:

Before Change - System TZ is 
America/Los_Angeles

os.time(): 11:54
luatz.time(): 11:54
gettime..gettime(): 11:54

Time zone changed to America/New_York

After  Change - System TZ is 
America/New_York

os.time(): 11:54
luatz.time(): 11:54
gettime.gettime(): 11:54

luatz.time_in()

Таким образом, функция luatz.time_in() обновляется при изменении системного часового пояса, и я в восторге от этого! Однако time_in() не отображает правильное местное время. Он добавляет смещение часового пояса к правильному местному времени, в результате чего время отстает на несколько часов. Я экспериментировал с установкой переменной окружения TZ, но это не дало никакого эффекта. По какой-то причине luatz.time() возвращает местное время, а luatz.time_in() возвращает результат двойного применения смещения часового пояса.

луа скрипт:

local tz=require"luatz";

require "luatz.tzcache".clear_tz_cache()
print("Before Changes (America/Los_Angeles)")
print(os.date("%H:%M",tz.time_in()))

os.execute("timedatectl set-timezone America/Chicago")

require "luatz.tzcache".clear_tz_cache()
print("America/Chicago")
print(os.date("%H:%M",tz.time_in()))

os.execute("timedatectl set-timezone America/New_York")
require "luatz.tzcache".clear_tz_cache()

print("America/New_York")
print(os.date("%H:%M",tz.time_in()))

выход:

Before Changes (America/Los_Angeles)
08:59
America/Chicago
10:59
America/New_York
11:59

Фактическое системное местное время: 15:59.


person JKav77    schedule 08.08.2014    source источник


Ответы (1)


Низкоуровневая функция позади os.date, localtime(3) "действует так, как если бы он вызывал tzset(3)", tzset использует переменную среды TZ для определения часового пояса, а если она не существует, считывает из /etc/localtime.

Переменные среды в основном определяются до запуска вашей программы, поэтому, чтобы изменить часовой пояс, вы можете найти способ установить переменную TZ. os.setenv доступен через несколько библиотек lua, например. lua-ex

Если это не кажется разумным, вы можете просто убедиться, что ваш сценарий запущен без установки TZ; что заставит tzset читать из /etc/localtime. К сожалению, большую часть времени этот файл кэшируется, и вы не будете получать обновления; это зависит от вашей системы.

В качестве альтернативы вы можете использовать другую библиотеку для получения времени вместо библиотеки os. В luatz вы можете очистить кеш часового пояса с помощью require "luatz.tzcache".clear_tz_cache(), вы можете вызвать эту функцию перед получением времени.

person daurnimator    schedule 09.08.2014
comment
Я использую Arch Linux, который еще не устанавливает переменную среды TZ. echo $TZ ничего не возвращает в командной строке, поэтому я не уверен, что это вариант. Я посмотрю на luatz. - person JKav77; 10.08.2014
comment
функции luatz также продолжают возвращать неверное системное время даже после очистки файла tzcache. - person JKav77; 13.08.2014
comment
luatz.time() — время UTC. luatz.time_in() — местное время. попробуйте этот код: codepad.org/3nt9p4Ub - person daurnimator; 14.08.2014
comment
Я попробовал luatz.time_in(), и хотя он меняется при изменении системного часового пояса, он не сообщает правильное местное время. Есть идеи? - person JKav77; 14.08.2014
comment
os.date добавляет текущее системное смещение часового пояса. time_in уже настроил его для вас. добавьте к строке формата даты префикс !, чтобы предотвратить это. print(os.date("!%H:%M",tz.time_in())) - person daurnimator; 14.08.2014
comment
Спасибо! Это решение. Я добавил свой тестовый сценарий к вопросу для ясности. - person JKav77; 15.08.2014
comment
Кстати, вместо опроса, чтобы узнать, изменился ли часовой пояс, вы можете подписаться на уведомления через DBus: freedesktop.org/wiki/Software/systemd/timedated - person daurnimator; 01.09.2014