Subject: kernel timezone struct deprecated Index: src/bin/date.c src/lib/libc/gen/ctime.c src/man/man1/date.1 Description: This started many months ago with a discussion with bqt@softjar.se that began: The date command, when setting the date, does not make use of /etc/localtime. So if someone have a kernel built for a completely different timezone, installs the correct /etc/localtime, everything seems fine in displaying time, but if they try to set the time, they are getting completely unexpected results... There was much more and over several months of on/off email the 'bug' was identified as date(1) using both kernel timezone info and the timezone info (either /etc/localtime or the environment variable TZ) resulting in inconsistent and unexpected behaviour. The decision has been made to offically deprecate the kernel timezone structure and modify date(1) to not use the kernel timezone structure and only use ctime(3) information. The kernel has never used the timezone structure. The structure will now be present only for historical reference and compatibility with user programs that use gettimeofday() and settimeofday() to get and set the timezone structure. date(1) has the -d and -t options removed. date(1) will no longer get or set the DST and TZ info in the kernel structure. ctime(3)'s order of timezone determination has been changed from: 1) environment variable TZ 2) /etc/localtime 3) kernel timezone (from gettimeofday()) 4) GMT (fallthrough default) to 1) environment variable TZ 2) /etc/localtime 3) GMT (fallthrough default) The kernel config file for the GENERIC kernel is updated to set explicitly the kernel timezone to 0 (GMT). The 'config' script is modified to force the TZ and DST to 0. Note: while the kernel config file and script are changed it is NOT necessary to reconfigure or rebuild any kernels. You may of course edit your own/local conf files to follow the GENERIC pattern ;) Repeat-By: 1) Use date(1) as described in the Description. 2) Do an exhaustive search of the system sources. The only files outside of the kernel that reference 'struct timezone' (tz_minuteswest and tz_dsttime) are date.c and ctime.c (in libc). Fix: Cut where indicated and save to a file (/tmp/454.patch) and then: cd / patch -p0 < /tmp/454.patch cd /usr/src/lib/libc/gen make ctime.o ar rv /lib/libc.a ctime.o ranlib /lib/libc.a cd profiled ar rv /usr/lib/libc_p.a ctime.o ranlib /usr/lib/libc_p.a cd .. make clean cd /usr/src/man/man1 make date.0 /usr/man/manroff date.1 > date.0 install -c -o bin -g bin -m 444 date.0 /usr/man/cat1 make clean cd /usr/src/bin make date install -s -m 751 -g staff date /bin/date make clean This and previous updates to 2.11BSD are available at the following locations: ftp://ftp.update.uu.se/pub/pdp11/2.11BSD https://www.tuhs.org/Archive/Distributions/UCB/2.11BSD/Patches/ ftp://ftp.2bsd.com/2.11BSD ---------------------------cut here-------------------- *** usr/src/bin/date.c.old Wed Jul 10 22:00:34 1996 --- usr/src/bin/date.c Mon Oct 21 13:03:48 2019 *************** *** 2,7 **** --- 2,10 ---- * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. + * + * 2019-07-11 Fixed inconsistent timezone/dst management + * 2019-10-20 Remove use of deprecated kernel timezone structure */ #if !defined(LINT) && defined(DOSCCS) *************** *** 9,15 **** "@(#) Copyright (c) 1985 Regents of the University of California.\n\ All rights reserved.\n"; ! static char sccsid[] = "@(#)date.c 4.20.1 (2.11BSD) 96/7/10"; #endif /* --- 12,18 ---- "@(#) Copyright (c) 1985 Regents of the University of California.\n\ All rights reserved.\n"; ! static char sccsid[] = "@(#)date.c 5.0 (2.11BSD) 2019/10/20"; #endif /* *************** *** 47,77 **** int argc; char **argv; { ! static char usage[] = "usage: date [-nu] [-d dst] [-t timezone] [yymmddhhmm[.ss]]\n"; ! struct timezone tz; char *ap, /* time string */ *tzn; /* time zone */ int ch, /* getopts char */ - uflag, /* do it in GMT */ nflag, /* only set time locally */ wf; /* wtmp file descriptor */ char *username; ! nflag = uflag = 0; ! tz.tz_dsttime = tz.tz_minuteswest = 0; ! while ((ch = getopt(argc,argv,"d:nut:")) != EOF) switch((char)ch) { - case 'd': - tz.tz_dsttime = atoi(optarg) ? 1 : 0; - break; case 'n': nflag = 1; break; - case 't': - tz.tz_minuteswest = atoi(optarg); - break; case 'u': ! uflag = 1; break; default: fputs(usage,stderr); --- 50,72 ---- int argc; char **argv; { ! static char usage[] = "usage: date [-nu] [[[[yy]mm]dd]hhmm[.ss]]\n"; ! struct tm *tp; char *ap, /* time string */ *tzn; /* time zone */ int ch, /* getopts char */ nflag, /* only set time locally */ wf; /* wtmp file descriptor */ char *username; ! nflag = 0; ! while ((ch = getopt(argc,argv,"nu")) != EOF) switch((char)ch) { case 'n': nflag = 1; break; case 'u': ! (void)setenv("TZ", "GMT", 1); break; default: fputs(usage,stderr); *************** *** 85,106 **** exit(1); } - if ((tz.tz_minuteswest || tz.tz_dsttime) && - settimeofday((struct timeval *)NULL,&tz)) { - perror("settimeofday"); - retval = 1; - goto display; - } - if (gettimeofday(&tv,&tz)) { - perror("gettimeofday"); - exit(1); - } - if (!argc) goto display; wtmp[0].ut_time = tv.tv_sec; if (gtime(*argv)) { fputs(usage,stderr); retval = 1; --- 80,93 ---- exit(1); } if (!argc) goto display; + time(&tv.tv_sec); + wtmp[0].ut_time = tv.tv_sec; + if (gtime(*argv)) { fputs(usage,stderr); retval = 1; *************** *** 107,120 **** goto display; } ! if (!uflag) { /* convert to GMT assuming local time */ ! tv.tv_sec += (long)tz.tz_minuteswest * SECS_PER_MIN; ! /* now fix up local daylight time */ ! if (localtime((time_t *)&tv.tv_sec)->tm_isdst) ! tv.tv_sec -= SECS_PER_HOUR; ! } ! if (nflag || !netsettime(tv)) { ! if (settimeofday(&tv,(struct timezone *)0)) { perror("settimeofday"); retval = 1; goto display; --- 94,108 ---- goto display; } ! /* ! * To avoid using the (obsolete) kernel timezone structure call localtime() ! * to initialize the timezone info and set tm_gmtoff ! */ ! tp = localtime((time_t *)&tv.tv_sec); ! tv.tv_sec -= tp->tm_gmtoff; ! ! if (nflag || !netsettime(tv.tv_sec)) { ! if (settimeofday(&tv, NULL)) { perror("settimeofday"); retval = 1; goto display; *************** *** 139,155 **** perror("gettimeofday"); exit(1); } ! if (uflag) { ! ap = asctime(gmtime((time_t *)&tv.tv_sec)); ! tzn = "GMT"; ! } ! else { ! struct tm *tp; ! ! tp = localtime((time_t *)&tv.tv_sec); ! ap = asctime(tp); ! tzn = tp->tm_zone; ! } printf("%.20s%s%s",ap,tzn,ap + 19); exit(retval); } --- 127,138 ---- perror("gettimeofday"); exit(1); } ! /* ! * Now display the time ! */ ! tp = localtime((time_t *)&tv.tv_sec); ! ap = asctime(tp); ! tzn = tp->tm_zone; printf("%.20s%s%s",ap,tzn,ap + 19); exit(retval); } *************** *** 246,253 **** * Returns 1 on success, 0 on failure. */ static ! netsettime(ntv) ! struct timeval ntv; { int s, length, port, timed_ack, found, err; long waittime; --- 229,236 ---- * Returns 1 on success, 0 on failure. */ static ! netsettime(tval) ! time_t tval; { int s, length, port, timed_ack, found, err; long waittime; *************** *** 298,305 **** } (void) strncpy(msg.tsp_name, hostname, sizeof (hostname)); msg.tsp_seq = htons((u_short)0); ! msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec); ! msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec); length = sizeof (struct sockaddr_in); if (connect(s, &dest, length) < 0) { perror("date: connect"); --- 281,288 ---- } (void) strncpy(msg.tsp_name, hostname, sizeof (hostname)); msg.tsp_seq = htons((u_short)0); ! msg.tsp_time.tv_sec = htonl((u_long)tval); ! msg.tsp_time.tv_usec = 0; length = sizeof (struct sockaddr_in); if (connect(s, &dest, length) < 0) { perror("date: connect"); *** usr/src/lib/libc/gen/ctime.c.old Wed Apr 1 03:42:55 1987 --- usr/src/lib/libc/gen/ctime.c Fri Oct 18 17:11:02 2019 *************** *** 5,24 **** */ #if defined(LIBC_SCCS) && !defined(lint) ! static char sccsid[] = "@(#)ctime.c 1.1 (Berkeley) 3/25/87"; #endif LIBC_SCCS and not lint ! #include "sys/param.h" ! #include "sys/time.h" ! #include "tzfile.h" char * ctime(t) time_t *t; { - struct tm *localtime(); - char *asctime(); - return(asctime(localtime(t))); } --- 5,23 ---- */ #if defined(LIBC_SCCS) && !defined(lint) ! static char sccsid[] = "@(#)ctime.c 2.0 (2.11BSD) 2019/10/18"; #endif LIBC_SCCS and not lint ! #include ! #include ! #include ! #include ! #include char * ctime(t) time_t *t; { return(asctime(localtime(t))); } *************** *** 48,61 **** return result; } - #ifndef TRUE - #define TRUE 1 - #define FALSE 0 - #endif /* !TRUE */ - - extern char * getenv(); - extern char * strcpy(); - extern char * strcat(); struct tm * offtime(); struct ttinfo { /* time type information */ --- 47,52 ---- *************** *** 90,98 **** static long detzcode(codep) ! char * codep; { ! register long result; register int i; result = 0; --- 81,89 ---- static long detzcode(codep) ! register char *codep; { ! long result; register int i; result = 0; *************** *** 106,118 **** register char * name; { register int i; ! register int fid; if (name == 0 && (name = TZDEFAULT) == 0) return -1; { ! register char * p; ! register int doaccess; char fullname[MAXPATHLEN]; doaccess = name[0] == '/'; --- 97,109 ---- register char * name; { register int i; ! register char *p; ! int fid; if (name == 0 && (name = TZDEFAULT) == 0) return -1; { ! int doaccess; char fullname[MAXPATHLEN]; doaccess = name[0] == '/'; *************** *** 129,135 **** */ while (*name != '\0') if (*name++ == '.') ! doaccess = TRUE; name = fullname; } if (doaccess && access(name, 4) != 0) --- 120,126 ---- */ while (*name != '\0') if (*name++ == '.') ! doaccess = 1; name = fullname; } if (doaccess && access(name, 4) != 0) *************** *** 138,144 **** return -1; } { - register char * p; register struct tzhead * tzhp; char buf[sizeof s]; --- 129,134 ---- *************** *** 219,245 **** } static - tzsetkernel() - { - struct timeval tv; - struct timezone tz; - char *tztab(); - - if (gettimeofday(&tv, &tz)) - return -1; - s.timecnt = 0; /* UNIX counts *west* of Greenwich */ - s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN; - s.ttis[0].tt_abbrind = 0; - (void)strcpy(s.chars, tztab(tz.tz_minuteswest, 0)); - tzname[0] = tzname[1] = s.chars; - #ifdef USG_COMPAT - timezone = tz.tz_minuteswest * 60; - daylight = tz.tz_dsttime; - #endif /* USG_COMPAT */ - return 0; - } - - static tzsetgmt() { s.timecnt = 0; --- 209,214 ---- *************** *** 258,264 **** { register char * name; ! tz_is_set = TRUE; name = getenv("TZ"); if (!name || *name) { /* did not request GMT */ if (name && !tzload(name)) /* requested name worked */ --- 227,233 ---- { register char * name; ! tz_is_set = 1; name = getenv("TZ"); if (!name || *name) { /* did not request GMT */ if (name && !tzload(name)) /* requested name worked */ *************** *** 265,272 **** return; if (!tzload((char *)0)) /* default name worked */ return; - if (!tzsetkernel()) /* kernel guess worked */ - return; } tzsetgmt(); /* GMT is default */ } --- 234,239 ---- *************** *** 336,345 **** long offset; { register struct tm * tmp; ! register long days; ! register long rem; ! register int y; ! register int yleap; register int * ip; static struct tm tm; --- 303,310 ---- long offset; { register struct tm * tmp; ! long days, rem; ! register int y, yleap; register int * ip; static struct tm tm; *** usr/src/man/man1/date.1.old Mon Mar 30 18:52:13 1987 --- usr/src/man/man1/date.1 Mon Oct 21 13:23:14 2019 *************** *** 2,24 **** .\" All rights reserved. The Berkeley software License Agreement .\" specifies the terms and conditions for redistribution. .\" ! .\" @(#)date.1 6.5 (Berkeley) 3/24/87 .\" ! .TH DATE 1 "March 24, 1987" .UC 4 .SH NAME date \- print and set the date .SH SYNOPSIS .B date ! .RB "[-nu] [-d dst] [-t timezone] [yymmddhhmm [" . "ss] ]" .SH DESCRIPTION If no arguments are given, the current date and time are printed. Providing an argument will set the desired date; only the superuser ! can set the date. The \fI-d\fP and \fI-t\fP flags set the kernel's ! values for daylight savings time and minutes west of GMT. If \fIdst\fP ! is non-zero, future calls to \fIgettimeofday\fP(2) will return a non-zero ! \fItz_dsttime\fP. \fITimezone\fP provides the number of minutes returned ! by future calls to \fIgettimeofday\fP(2) in \fItz_minuteswest\fP. The \fI-u\fP flag is used to display or set the date in GMT (universal) time. .I yy represents the last two digits of the year; --- 2,26 ---- .\" All rights reserved. The Berkeley software License Agreement .\" specifies the terms and conditions for redistribution. .\" ! .\" @(#)date.1 7.0 (2.11BSD) 2019/10/20 .\" ! .TH DATE 1 "October 20, 2019" .UC 4 .SH NAME date \- print and set the date .SH SYNOPSIS .B date ! .RB "[-nu] [[[[yy]mm]dd]hhmm [" . "ss] ]" .SH DESCRIPTION If no arguments are given, the current date and time are printed. Providing an argument will set the desired date; only the superuser ! can set the date. ! .PP ! The kernel timezone structure is obsolete and is a historic relic. \fPdate(1)\fP ! \fBno longer\fP updates the kernel timezone structure (daylight savings time ! and minutes west of GMT). ! .PP ! The \fI-u\fP flag is used to display or set the date in GMT (universal) time. .I yy represents the last two digits of the year; *** usr/src/sys/conf/GENERIC.old Sat Dec 27 19:17:24 2008 --- usr/src/sys/conf/GENERIC Fri Oct 18 13:45:18 2019 *************** *** 1,3 **** --- 1,4 ---- + # 2019/10/18 - TIMEZONE set to GMT # 1997/1/21 - RK added to GENERIC kernel (for use with Bob Supnik's emulator) # 1995/12/14 - RX added to GENERIC kernel. # 1995/07/21 - XP_PROBE removed. *************** *** 60,71 **** #BOOTDEV sc21 # Emulex SC21 boot device #BOOTDEV si # si 9500 boot device ! # Timezone, in minutes west of GMT ! #TIMEZONE 300 # EST ! #TIMEZONE 360 # CST ! #TIMEZONE 420 # WST ! TIMEZONE 480 # PST ! DST 1 # Daylight Savings Time (1 or 0) # Filesystem configuration # Rootdev, swapdev etc. should be in terms of makedev. For example, --- 61,68 ---- #BOOTDEV sc21 # Emulex SC21 boot device #BOOTDEV si # si 9500 boot device ! # Timezone ! TIMEZONE 0 # kernel uses GMT by default # Filesystem configuration # Rootdev, swapdev etc. should be in terms of makedev. For example, *** usr/src/sys/conf/config.old Sun Feb 16 00:37:51 1997 --- usr/src/sys/conf/config Fri Oct 18 13:43:15 2019 *************** *** 1,6 **** --- 1,9 ---- #! /bin/sh # 2.11BSD script to set up a new kernel configuration directory. # + # 2019/10/18 - the kernel runs on GMT. The kernel timezone structure + # is obsolete, will be initialized to 0 now and only + # present for historical reference. # 1997/2/14 - LINEHZ removed from localopts.h. The clock rate is # now known in _exactly 1_ place in the kernel (param.c) # rather than having references scattered all over the *************** *** 45,55 **** fi if [ ! "$TIMEZONE" ]; then ! TIMEZONE=480 fi if [ ! "$DST" ]; then ! DST=1 fi cat << EOF > $LO --- 48,58 ---- fi if [ ! "$TIMEZONE" ]; then ! TIMEZONE=0 fi if [ ! "$DST" ]; then ! DST=0 fi cat << EOF > $LO *** VERSION.old Fri Oct 11 12:34:04 2019 --- VERSION Mon Oct 21 13:11:02 2019 *************** *** 1,5 **** ! Current Patch Level: 453 ! Date: October 11, 2019 2.11 BSD ============ --- 1,5 ---- ! Current Patch Level: 454 ! Date: October 20, 2019 2.11 BSD ============