How do I update the time in lua to reflect the system timezone change at runtime?
Problem
I want to change the widget awful.widget.textclock
in awesome-wm to reflect the change in the system timezone right away. This widget and all awesome-wm config are written in lua.
Currently, if the system time zone is changed, the widget continues to display the time according to the time zone set at runtime. The widget uses a function os.time
to get the time, but this does not match the system time.
Solution given below
lua script:
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()))
Output:
Before Changes (America/Los_Angeles)
15:33
America/Chicago
17:33
America/New_York
18:33
Bypass
This can be solved by restarting the awesome window manager which makes the widget get the correct timezone again. Naturally, if changing the time zone requires restarting the window manager.
The desired effect is to update the timezone from the system when it changes, either periodically or every time the function is called os.time
.
Use register
If you're interested, there is a precedent for this on a laptop. I often travel and run tzupdate
on systemd timer
. I would like to automate changing my timezone. This works great, except that the widget that actually displays the time doesn't notice the system timezone change.
Tried so far
- Cancel the $ TZ environment variable. But Arch Linux never sets this variable in the first place, so I'm not sure how lua even determines the correct timezone.
- Use a library
luatz
and in particular a functiontzcache.clear_tz_cache()
. It doesn't seem to have any effect. - Get the system time using functions other than
os.time()
:luatz.time()
andluatz.gettime.gettime()
. They retrieve the same time as other functions. - Use a function
luatz.time_in()
, but this returns the time when the timeline offset is applied twice to UTC time.luatz.time()
returns the correct local time, but must return UTC time.
UPDATED luatz
I tried messing around with the library luatz
as recommended, but doesn't seem to recheck the system timezone even after the function call tzcache.clear_tz_cache()
.
I cloned the relay luatz
and copied luatz
to the system modules directory. The script seems to load correctly, but did not change the effect of ignoring the system timezone change. As far as I can tell, this does nothing but a function os.time()
.
luatz test script:
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()))
Output:
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()
This way the function is luatz.time_in()
updated as the system timezone changes, and I love it! However, it time_in()
does not display the correct local time. It adds the time zone offset to the correct local time, resulting in a few hours behind. I experimented with setting the environment variable TZ
, but it had no effect. luatz.time()
Returns local time for some reason , but luatz.time_in()
returns the result of double timezone offset.
lua script:
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()))
output:
Before Changes (America/Los_Angeles)
08:59
America/Chicago
10:59
America/New_York
11:59
Current local time systems: 15:59
.
source to share
The low-level function behind os.date
, localtime(3)
"acts like it's called tzset(3)
", tzset
uses an environment variable TZ
to determine the timezone, and if that doesn't exist, read on from /etc/localtime
.
Environment variables are mostly defined before you start your program, so to change your timezone, you can find a way to set your variable TZ
. A os.setenv
is available through several lua libraries eg. lua-ex
If that doesn't seem like a sane way, you can simply ensure that your script runs without TZ
; which will force tzset
reading from /etc/localtime
. Unfortunately, most of the time this file is cached and you will not receive updates; it depends on your system.
Alternatively, you can use a different library to get the time, not a library os
. In luatz
you can clear the timezone cache with require "luatz.tzcache".clear_tz_cache()
, you can call this function before getting the time.
source to share