Skip to content

Instantly share code, notes, and snippets.

@stephanGarland
Last active March 7, 2026 18:53
Show Gist options
  • Select an option

  • Save stephanGarland/b7cdd963e0ac53ea42f8ed15e35b193d to your computer and use it in GitHub Desktop.

Select an option

Save stephanGarland/b7cdd963e0ac53ea42f8ed15e35b193d to your computer and use it in GitHub Desktop.
cron bug during timezone changes

Debian 11 cron bug

This is for historical interest only, as the patch was merged as of 3.0pl1-152.

See Bug Report #1019716 for more information.

Steps to reproduce:

  1. Create cron in /etc/cron.d/$name running at a specific hour.
  2. Change system time with dpkg-reconfigure tzdata.
  3. Without issuing a systemctl restart cron, the change will not be seen by crond, and the job will run at the old timezone.

Notes

Compiled with DDEBUGGING=1 and -g, using cron_3.0pl1-137 in Debian 11. File to follow along with is here.

Observations

GMToff only responds to cron restarts - this is expected since the loop passes FALSE into set_time(). As such, clockTime (which is startTime + GMToff), will not change. timeRunning is set equal to clockTime, and timeDiff is calculated as timeRunning - virtualTime.

The result is that jobs will execute according to the timezone present when cron.service was started.

Debugger

Once system time is shifted forward, gdb is immediately launched (you may need to time this to launch at the start of a minute). At this time, wakeupKind has not been assigned yet, and has a random value since it isn't initialized.

(gdb) f 4
#4  0x0000562f6e861b3f in main (argc=2, argv=0x7ffcfd281c88) at cron.c:169
169				cron_sleep(timeRunning + 1);
(gdb) p GMToff
$1 = -18000
(gdb) info locals
timeDiff = 1
wakeupKind = 21917
database = {head = 0x559da27b7a50, tail = 0x559da27c9310, user_mtime = 1662925987, sys_mtime = 1614033804, sysd_mtime = 1662945190}
cs = 0x7f7376399c0c "UTF-8"

After some time, if gdb is launched again or otherwise reloaded, the following data can be observed. Notably, GMToff has not changed despite system time having been shifted forward by an hour.

(gdb) f 4
#4  0x0000562f6e861b3f in main (argc=2, argv=0x7ffcfd281c88) at cron.c:169
169				cron_sleep(timeRunning + 1);
(gdb) p GMToff
$1 = -18000
(gdb) info locals
timeDiff = 2
wakeupKind = 1
database = {head = 0x559da27b7a50, tail = 0x559da27c9310, user_mtime = 1662925987, sys_mtime = 1614033804, sysd_mtime = 1662945190}
cs = 0x7f7376399c0c "UTF-8"

If cron is restarted, the change to GMToff is immediate, and after some time, timeDiff will be recalculated and will set wakeupKind accordingly, and the job will fire at its next scheduled time.

(gdb) f 4
#4  0x0000562f6e861b3f in main (argc=2, argv=0x7ffcfd281c88) at cron.c:169
169				cron_sleep(timeRunning + 1);
(gdb) p GMToff
$1 = -14400
(gdb) info locals
timeDiff = 2
wakeupKind = 1
database = {head = 0x562f6ff83c50, tail = 0x562f6ff97300, user_mtime = 1662925987, sys_mtime = 1614033804, sysd_mtime = 1662945190}
cs = 0x7f6ff8198c0c "UTF-8"

If instead, the calculation for GMToff is moved out of the loop in set_time() such that it's calculated at every call, then after one minute the offset will be noted.

(gdb) f 4
#4  0x000056220f044b3f in main (argc=2, argv=0x7ffc42e27598) at cron.c:169
169				cron_sleep(timeRunning + 1);
(gdb) p GMToff
$1 = -14400
(gdb) info locals
timeDiff = 62
wakeupKind = 2
database = {head = 0x562210237a50, tail = 0x562210249310, user_mtime = 1662925987, sys_mtime = 1614033804, sysd_mtime = 1662942515}
cs = 0x7f2ff76e3c0c "UTF-8"

Then, the internal time tracking will be updated, and the job will fire at its next scheduled time.

(gdb) f 4
#4  0x000055662b61cb3f in main (argc=2, argv=0x7ffcb38aa688) at cron.c:169
169				cron_sleep(timeRunning + 1);
(gdb) p GMToff
$1 = -14400
(gdb) info locals
timeDiff = 1
wakeupKind = 2
database = {head = 0x55662b961a50, tail = 0x55662b973310, user_mtime = 1662925987, sys_mtime = 1614033804, sysd_mtime = 1662945190}
cs = 0x7ff3ea3a4c0c "UTF-8"

Patch

diff --git a/cron.c b/cron.c
index 613e7bf..7b0b69c 100644
--- a/cron.c
+++ b/cron.c
@@ -372,9 +372,9 @@ set_time(int initialize)

     /* We adjust the time to GMT so we can catch DST changes. */
     tm = *localtime(&StartTime);
+    GMToff = get_gmtoff(&StartTime, &tm);
     if (initialize || tm.tm_isdst != isdst) {
        isdst = tm.tm_isdst;
-       GMToff = get_gmtoff(&StartTime, &tm);
        Debug(DSCH, ("[%d] GMToff=%ld\n",
            getpid(), (long)GMToff))
     }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment