Cover V03, I03
Article
Listing 1
Sidebar 1
Table 1
Table 2

may94.tar


Time for UNIX

Larry Reznick

Midnight, Thu Jan 1, 1970 is the traditional beginning of the UNIX epoch. UNIX keeps track of time as the number of seconds elapsed since that date, Greenwich Mean Time (GMT). UNIX stores the seconds as a 32-bit offset from that date, where 0 is that date. Using 32 bits, UNIX can represent a range of 4,294,967,296 seconds, which amounts to 136 years, 36 days, 6 hours, 28 minutes, and 16 seconds. The program in Listing 1 shows that the minimum date representable (-2,147,483,648) is Fri Dec 13, 1901, at 8:45:52 pm and the maximum (2,147,483,647) is Tue Jan 19, 2038, at 3:14:07 am. Only about 44 more years remain before UNIX has to set a new epoch.

This 32-bit number (the seconds offset from the beginning of the epoch) is used throughout UNIX operations. It figures in the time stamping of files, in the process and login time stamps, and in all other time calculations. The ANSI C Standard borrowed this idea for the time_t data type, so any C program written with the standard library's time functions is using the UNIX epoch format. Other systems may use a different epoch -- for instance, MSDOS and derivatives use midnight, Jan 1, 1980 instead -- but the idea is the same.

Time is an important aspect of UNIX operations because UNIX systems are often left on around the clock to take advantage of background processing and deferred processing. cron doesn't work without the real time clock operating properly, and for the clock to run properly the system administrator must understand how UNIX treats local time zones, daylight savings time, and holidays.

Local Time Zones

Each user has an individual time zone setting stored in the TZ environment variable. This variable's initial setting derives from the system-wide setting in the /etc/TIMEZONE file. TZ can hold a simple value composed of two parts, but typically the value is composed of at least three parts: the three-letter abbreviation for the standard time zone, the number of hours offset from GMT to that time zone, and the three-letter abbreviation for the zone's daylight savings time. For example, PST8PDT means that Pacific Standard Time is eight hours away from GMT and Pacific Daylight Time applies. A system in New York is only five hours away from GMT, so it would use EST5EDT.

Hour offsets can be either positive or negative. One adds eight hours to PST to get GMT. At a site east of GMT, one would subtract hours to get to GMT. For instance, Middle European Time, also known as Central European Time, is MET-1 DST, Hong Kong time is HKT-8, and Japan time is JST-9. (Add -1, -8, and -9 hours, respectively, to local time at these locations to get GMT.) The offset can include minutes, and even seconds, if necessary. Hours, minutes, and seconds are separated with colons. For instance, Newfoundland and parts of Australia use half-hour offsets from GMT. Newfoundland's is NST3:30NDT.

UNIX assumes that daylight savings time is one hour different from the usual local offset. If that isn't true for your location, you can overide the default with an additional offset value following the daylight savings time abbreviation.

Each user can have an individual time zone setting. (This is handy for people who commonly login to the system from remote sites.) You can set the TZ variable from within the user's own .profile files under Bourne Shell (sh) or Korn Shell (ksh), or within the .login files if you're using C-Shell (csh). Once TZ is reset, all time stamps are adjusted to the new time zone, yet other people with different time zone settings would see their own zone-adjusted times. Try it! Create a file, then look at a long listing showing the time stamp of that file. Change your TZ variable's setting. Be sure to export TZ if you're using sh or ksh. Then look at a long listing of that file. The time stamp has adjusted to the new time zone. You can reset your TZ to the system's standard by executing the /etc/TIMEZONE script. Use the command . /etc/TIMEZONE in sh or ksh. For csh, you'll need to manually execute the TZ setting shown in /etc/TIMEZONE by using the setenv command.

Daylight Savings Time

One great problem in time-keeping today is daylight savings time (DST). Different regions have different rules and these rules are often inconsistent. The general rule now in the United States is to add one hour to the clock at 2 am on the first Sunday in April, and take that hour back at 2 am on the last Sunday in October. Of course, it wasn't always this way. Before 1974, DST started on the last Sunday in April. In 1974, Presidential decree set the start date back to January. In 1975, DST started in February. From 1976 to 1986 it started on the last Sunday in April. In 1987 it was pushed back to the first Sunday in April, where it is today.

If you think that sounds complicated be warned that it gets worse. Some countries change in different months, on different days of the week, and at different times of the day. Some countries don't use DST. According to the rules I've found, the People's Republic of China turns back to standard time in September, not October, while the Republic of Korea changes to DST in May, not April. European countries appear to change in March and September, but some change at 2 am, and some at 3 am. Southern hemisphere countries must reverse their change months. Some parts of Australia had variations during the 1970s but now seem to revert to standard time in March. Other parts of Australia don't use DST. New South Wales had several variations in the 1980s but has settled down to starting DST in October and ending it in March. Brazil changes on Saturdays instead of Sundays.

Given these examples, a system administrator clearly can't set the DST date once and never look at it again. New UNIX site installations may require someone to look at the local conventions and adjust the system's clock to seasonal changes. Existing UNIX installations must occasionally update their settings when the government declares a new time change.

The simplest example of a TZ setting is one without daylight savings time, such as JST-9. Japan Standard Time (JST-9) is nine hours east of GMT. (Japan doesn't observe Daylight Savings Time.) In the United States, PST8PDT, Pacific Standard Time is eight hours west of GMT and has a daylight time abbreviation without a number. Without a number following the daylight time abbreviation (PDT), the system assumes that the daylight time is offset one hour from the standard time. You may specify a DST absolute hour offset if you need one. For example, PST8PDT7 explicitly requires that daylight savings time in the Pacific zone be seven hours away from GMT.

I have yet to determine how the system identifies when in the year to change to daylight savings time. The TZ variable definition includes a mechanism for explicitly citing only one year. After the DST abbreviation and its own optional hour offset, you may include a comma followed by a starting date and time, and another comma followed by an ending date and time. On the starting date, at the specified time, the system applies the DST offset. When the ending date and time come, the standard offset is restored. For example,

PST8PDT,92/2:00:00,302/2:00:00

A start time comes after the first comma. The number 92 refers to April 3, the first Sunday in April in 1994. (This 92 is determined from Table 1.) Following the 92 is a slash, which separates the day number from the time to start the PDT change: 2 am. Another comma signals the end time. The 302, again from Table 1, refers to October 30, the last Sunday in October in 1994. On that date at 2 am, PDT is turned off and the system returns to PST. Changing at 2 am is the default, so the line could have been:

PST8PDT,92,302

The problem with this Table 1 method is that in 1995 the Sundays will fall on different dates. Daylight savings time will begin on day 91 (instead of 92), and end on day 301 (instead of 302). So a system administrator using this method must change the date numbers annually.

(There are 14 possible calendars -- seven plain years with January 1 on each day of the week and seven leap years with January 1 on each day of the week. A system administrator could create the 14 possible variations as separate files, but would still have to enable the appropriate one each year. I can imagine a cron job using my day-of-the-week script ("Which Day of the Week Is This?," Jul/Aug 1993 Sys Admin, vol. 2, no. 4, p. 79) that runs every January 1 and enables the appropriate variation in /etc/TIMEZONE. Or maybe one could change the /etc/TIMEZONE script itself, making it far more sophisticated so that it can figure the local time directly at every login. I don't find this an elegant solution, though. There still exists the problem of modifying all 14 variations to accommodate a national or local daylight savings time change.)

Somebody came up with a better way. I've found that if I don't explicitly name the DST start and end dates my system knows to start on the first Sunday in April and end on the last Sunday in October. I didn't tell it to do that, so the method must exist somewhere. My Esix SVR4 system has a /usr/lib/locale/TZ directory containing a set of files that identify many time zones and their DST variations. I found some documentation for this in zic(1M), which stands for zone information compiler. There are seven files in zic containing data for major regions of the world. Table 2 reviews the contents of those files.

These files contain two kinds of tables, known as rules and zones, and may contain links that identify variations in zone names. The tables make it easy to track and modify time zone variations. The zic program reads these files and produces very small compiled files and occasionally some directories that represent the rules in some system-usable form.

There's only one problem. I can't find where the zic's files get installed so the system can use them. The zic man page doesn't say. The man page refers to a standard directory, /usr/share/lib/zoneinfo, where the compiled files will reside, but that directory doesn't exist on a newly installed system and running zic doesn't create it. If the directory doesn't exist, where are the files that drive newly installed systems? I searched through my system using the names that zic created but I couldn't find any originals with those file names. So, I'm stumped.

I know my system knows the appropriate time to change to DST. I know the system's time change corresponds with the information contained within the appropriate /usr/lib/locale/TZ table file. I don't know how it knows that. I don't know where to install the new files created by zic if I need to change or add to the rules, or how to tell the system to use the newly created files. Because zic has an option to put the files into a directory other than /usr/share/lib/zoneinfo, I assume there is some way to tell the system which directory to use. (This method would be similar to debugging terminfo files, where you can identify a specific directory for testing new terminfo definitions without fouling up the rest of the system.) No man pages I've found yet answer any of these questions about zic.

If you know the secrets of the time zone tables used by zic, or if you have found or will write more thorough documentation for using these files, please contact Sys Admin.

Accounting Holidays

The first time I ran accounting on UNIX I discovered an error message about an undocumented /etc/acct/holidays file. This file defines the current year, the hour and minute when prime time system usage begins, the hour and minute when non-prime time usage begins, and the holidays recognized by the system site.

At least five accounting programs use the holidays file: acctcms(1M), acctcon(1M), acctcon1(1M), acctprc(1M), and acctprc1(1M). The runacct(1M) script tests to see if acctcon creates an error log file. That log file is created if some process named pnpsplit fails. This pnpsplit, most likely a compiled function named for Prime time/Non-Prime time parsing, probably reads, parses, and validates the holidays file, preparing the holidays data for further use by other parts of those programs. I've deduced this from error messages within the five compiled accounting programs. There is no documentation for pnpsplit and my system contains no source code associated with it. (I refuse to disassemble acctcon.)

There is a maximum number of holiday entries because the error messages ask the user to recompile pnpsplit and increase a constant named NHOLIDAYS. Unfortunately, this isn't easy to do when the source code is unavailable and I have no idea what the maximum NHOLIDAYS is set to. However, I know that at least eight holidays are supported because that's how many are listed in my copy of /etc/acct/holidays.

Because this holidays file isn't documented except by the comments in the file, I'll try to document the file format here:

1. Comments begin with an asterisk (*) and extend to the end of the line.

2. The first uncommented line contains three tab-separated numbers. Leading white space is ignored.

a. The first number is the current year expressed as a four-digit number, such as 1994.

b. The second number is the time when prime time starts expressed as a four-digit number representing a 24-hour clock. For instance, use 0800 to mean 8:00 am.

c. The third number is the time when non-prime time starts expressed as a four-digit number representing a 24-hour clock. For instance, use 1800 to mean 6:00 pm.

The line containing all three examples would look like this:

1994 0800 1800

3. The remaining uncommented lines contain the month, a slash (/), and the day of the holiday followed optionally by at least one tab and any comments about the date, such as the name of the holiday. Each holiday requires a new line. New Year's Day would look like this:

1/1 New Year's Day

Every date listed in the /etc/acct/holidays file is presumably treated as non-prime time, and so are times on all other dates between the non-prime start time and the prime start time.

Here's the problem for the system administrator: this holidays file has to be changed every year. Certain holidays fall on specific dates despite the day of the week, such as New Year's Day, Christmas Eve, and Christmas Day. These holidays never need changing. But other holidays fall on certain weekdays, such as Memorial Day, Labor Day, and Thanksgiving Day. The dates for these holidays will be different every year.

I propose a script that cron can run once a year. This script reads a configuration file containing a table that identifies facts about the holiday, which could be the specific month and day or could be some designation such as the fourth Thursday of November. The advantage of this configuration file is in uniformly identifying different holidays for different locales. The script analyzes the current year's calendar, possibly using my day-of-the-week script, and updates the /etc/acct/holidays file indentifying the current year and the holiday dates for that current year. If you have the script, or wish to write the script, to handle this problem, contact Sys Admin.

About the Author

Larry Reznick has been programming professionally since 1978. He is currently working on systems programming in UNIX, MS-DOS, and OS/2. He teaches C language courses at American River College and at National University in Sacramento. He can be reached via email at: rezbook!reznick@csusac.ecs.csus.edu.