Bug 15685

Summary: at-3.1.7: Mishandles Time Zones
Product: [Retired] Red Hat Linux Reporter: John Kono <jkono>
Component: atAssignee: Crutcher Dunnavant <crutcher>
Status: CLOSED RAWHIDE QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 6.1   
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2000-08-23 15:18:56 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---
Description Flags
at-3.1.8-UTC.patch none

Description John Kono 2000-08-07 22:08:38 UTC
If a job is scheduled for a UTC time, "at" seems to assume that the
specified time is local and converts it to UTC, rather than converting it
from UTC to local.  This is the opposite of expected behavior.


	(For TZ=US/Pacific during Daylight Savings Time)

	$ date; date -u ; at -f foo 23:45 UTC; atq
	Mon Aug  7 15:04:02 PDT 2000
	Mon Aug  7 22:04:02 UTC 2000
	warning: commands will be executed using /bin/sh
	job 15 at 2000-08-08 06:45
	15      2000-08-08 06:45 a

(N.B. The job *should* have been scheduled for 16:45 PDT.)

Comment 1 Crutcher Dunnavant 2000-08-08 15:07:37 UTC
I agree, this is borken behavior on at's part.
I've looked about in the at code, haven't found right
way to fix it yet. It uses a bison parser :(

Comment 2 John Kono 2000-08-23 06:37:29 UTC
After spending about an hour looking at the source code for at-3.1.7, I'm
convinced that at is doing the right thing and that the fault is in the
time-related functions in libc.  The at command uses timezone (see CTIME(3)
manpage and parsetime() in at-3.1.7/y.tab.c) to calculate the correct local time
from UTC.  Unfortunately, timezone returns a positive number for PST/PDT instead
of a negative value.

Comment 3 John Kono 2000-08-23 08:16:18 UTC
Okay, after taking some caffine and spending another hour playing with the time
functions, I'm back to my original position.  It looks like the timezone
external returns the value of UTC - LOCAL_TIME, rather than LOCAL_TIME - UTC
(which would be a more intuitive value, IMHO).  So, to fix the bug, it looks
like the following change needs to be made in line 306 of parsetime.y:

	exectime += timezone;


	exectime -= timezone;

The actual modification and testing of fix is left as an exercise for the
reader. :^)

Comment 4 Crutcher Dunnavant 2000-08-23 14:38:07 UTC
Yep, it's on crack.

from time.h:
extern long int __timezone;     /* Seconds west of UTC.  */

Guaranteeing that UTC TIME + timezone != Local Time. This is stupid.
Putting in the fix.

Comment 5 Crutcher Dunnavant 2000-08-23 14:51:26 UTC
Okay, so there is something else screwing with the timezones.
THis doesn't fix it. Still looking

Comment 6 Crutcher Dunnavant 2000-08-23 15:16:59 UTC
Created attachment 2878 [details]

Comment 7 Crutcher Dunnavant 2000-08-23 15:18:54 UTC
Okay, so THAT is the fix.

In all the little files that use this, you need to:
        if (isgmt) {
-           exectime += timezone;
+           exectime -= timezone;
            if (daylight) {
-               exectime -= 3600;
+               exectime += 3600;

Comment 8 John Kono 2000-08-23 19:50:21 UTC
After some additional consideration (and a good night's sleep), I think that a
slightly more elegant fix would be:

	if ( daylight = 0 )
		exectime -= altzone;
		exectime -= timezone;

That should take care of DST conversions correctly for all values of TZ that
have the correct DST info, as well as those that don't observe DST (e.g.

Comment 9 John Kono 2000-08-23 19:53:14 UTC
Er, that should be:

	if ( daylight > 0 )
		exectime -= altzone;
		exectime -= timezone;

Ooops. :(

Comment 10 John Kono 2000-08-23 20:59:22 UTC

It looks like the altzone isn't defined in time.h with the current Linux
distribution.  This means that using the 3600 second offset if daylight is 1 is
the quickest solution to the problem.  Unfortunately, since some values of TZ
have DST offsets other than 3600, the 3.1.8-UTC.patch may produce unexpected
results.  Also, if DST info is not available for a TZ, then daylight (and
tm->tm_isdst) is supposed to be set to -1, which will also result in incorrect

So, what's the solution?  Well, it looks like a combination of our two fixes is
the best we can do short-term, though people in places that use DST values other
than 3600 will still have problems.

	exectime -= timezone;
	if (daylight > 0)
		exectime += 3600;

I'll keep looking (as time permits) and, if a more elegant solution presents
itself, I'll report it here.

Comment 11 John Kono 2000-08-23 22:48:24 UTC
Okay, how about this?

	if (isgmt) {
-	    exectime += timezone;
-	    if (daylight) {
+	    if (daylight > 0) {
-	        exectime -= 3600;
+	        exectime -= mktime( gmtime(&currtime) ) - 
+	                       mktime( localtime(&currtime) );
+	    } else {
+	        exectime -= timezone;

The main assumption made by this fix is that the system is handling non-standard
DST conversions (as defined in /usr/share/zoneinfo files) correctly for those
TZs that use them.  If not, well, that's a different bug...
(sombody_elses_problem = TRUE;  :^)

Also, this may cause some problems if at is run before a DST switch date with a
scheduled time after the switch, but then so does the current code and I can't
think of a simple way to fix that short of major modifications.

Comment 12 John Kono 2000-08-23 23:46:11 UTC
Ignore my last comment:  localtime() does not take DST into account.  Grr.

So, in Linux, just how does one go about determining the correct DST offset for
an arbitrary timezone?  In SVR4, it's stored in altzone.

Comment 13 Crutcher Dunnavant 2000-08-24 01:19:57 UTC
I think the 'right' answer to this one is "use UTC". Anyone who is serious about
time scheduling won't use a system that use daylight savings time (an idea whose
time is waay over.)

Short term, looks like we do the stupid thing, and use daylight > 0, and forget
about it.

(The alternative being, um, non-trivial.)