Unix Time

Introduction

Timekeeping on Unix systems is a frequently misunderstood functionality that many Unix administrators, users, and developers take for granted. The goal of this paper is to provide a quick but sufficiently deep overview of Unix timekeeping to insure that the reader is properly versed in the concepts to talk intelligently on the subject.

To begin, some explanation of basic time concepts in the physical and the logical (computational) world are in order.

As we move from one point to another across lines of longitude (meridian lines) on the planet we are now oriented differently with respect to the sun from where we once were. While this fact was apparent from the earliest ages, the idea of standardization1 of different times did not take effect until the 19th century with the widespread implementation of standard time zones. The full adoption by the rest of the civilized world did not take place until well into the 20th century.

Later the concept of DST (Daylight Savings Time) was added in many locations to save energy costs during the summer months by advancing the clock by one hour. This had the effect of giving the evening an extra hour of daylight at the expense of the morning. DST was briefly enacted in a number of countries during WWI, and again during WWII, but was not permanently enacted to its current extent until the late 60's.

Like time zone boundaries, DST is a choice made by local governments of that area based upon location and politics. Because any local government can choose at any time to modify when or if DST is used there is a resulting degree of complexity to implementing a global database of how time is calculated worldwide.2 One example of this changing complexity is the Energy Policy Act of 2005 that changed (in 2007) the times at which DST begins and ends in the United States. These variations and any changes to them in location, time, and time changes must be accounted for in keeping accurate time on a Unix system. The result is that rules for determining local time are encoded in various forms on Unix systems and are applied as algorithms to convert from a common / world time to a more local time.

To add to this mess is the increasing accuracy of the atomic age that has defined a second to an astonishing degree. This accuracy in defining time has surpassed the inaccuracies of calculating time by the movements of our planet. Variations in the speed of the earth's rotation have caused us to move away from trying to map a number of rigorously defined seconds to a variable day to an approximation of time based upon the earths position3 that is really based upon atomic time keeping and occasionally synchronized with the planet. This allows a second to be rigorously defined and then we can simply add or subtract seconds rather than making one second today different in length than one tomorrow.

The resulting fix for this was to officially drop the concept of GMT and accept UTC (Universal Coordinated Time) as a standard. UTC is independent of the movement of the planet but closely approximates it. On the exceptionally rare event the movement of the planet shifts noticeably from UTC (> .9 seconds), additional time is added or removed (typically in the form of a second) from the UTC clock to re-adjust it to physical (planetary) time. Because the planetary rotation of Earth is actually slowing down seconds have always been added rather than subtracted from days.

The most popular method of keeping time in computers is to set a point in time (called an "epoch") and then use a primitive data type to keep track of instants (typically in seconds or less) from that point in time. This makes updating the time quite simple as you only need to increment the counter for each time period passed. For the relatively few times you actually need to know what time it is, you can request this epoch offset and then convert this basic data type into a more human-friendly form using time zone rules previously mentioned. This method is quite simple, effective, and consequentially widely used by most operating systems in use today. Unix is no exception from this group.

Unix keeps time internally using a UTC synchronized clock that is converted to the appropriate local time based upon user preferences. Logically, this places the kernel of every Unix system in the world on the prime meridian. As far as the kernel is concerned, every system exists on that line, while its users may exist somewhere else. Unix makes it (effectively) the users responsibility to keep track of what time zone they are in, the current DST value, and any leap seconds.

Physically, of course, Unix systems are spread world wide. But because of the true multi-user and remotely accessible nature of the operating system it cannot be assumed that the user community is local to the same time zone as the physical system. Resorting to UTC for all transactions that happen below the user (such as in the kernel and through protocols relating time based information) keeps a sensible standard on how these systems interoperate.

Points of Reference

The following items are defined here to serve as a reference when they are used throughout the document.

UTC (Coordinated Universal Time4) This atomic time that is within .9 seconds of UT1
UT Universal Time is a series of time observations and calculations based upon the relationship of the earth to celestial bodies. Some forms such as UT0 are based on observations while others such as UT1, UT1R, and others are calculated versions of UT0. By convention, UT is often synonymous with UT1.
UT0 Time based upon observations of the earth in relationship to stars and other celestial sources.
UT1 UT1 is calculated from UT0 and accounts for rotational variations of the earth and he effect of polar motion on the longitude from where UT0 was observed.
GMT This is commonly considered the time zone at Greenwich, England (in London). With the advent of atomic time as a synchronization standard of computational resources that time zone is currently UTC (when not in DST). While technically not the same, GMT and UTC are frequently considered synonymous. GMT is based upon astronomical observations but has been replaced with UT for that purpose.
DST The concept of advancing time by one hour during the summer months so that the evening gets an extra hour of daylight at the expense of the morning
1 ms 1 millisecond. 1 second = 1,000 ms
1 μs 1 microsecond. 1 second = 1,000,000 μs
1 ns 1 nanosecond. 1 second = 1,000,000,000 ns
Hz Hertz - Cycles per second. 1 Hz = 1 cycle per second.
KHz Kilohertz - 1,000 cycles per second.
MHz Megahertz - 1,000,000 cycles per second.
GHz Gigahertz - 1,000,000,000 cycles per second.
RTC Real Time Clock - Also called the CMOS clock. This is a battery backed, 1 second precision, clock that can keep time even when the system is off. It is not used to provide timing to the system, but is used instead to give the system a reference point for wall clock time when the system / OS starts. After the RTC is read, time handling is maintained by more precise timers and time based protocols.

The Administrator

All Unices listed here keep time both on the hardware with a RTC (Real Time Clock) and within the software / kernel using some sort of hardware dependent timer. Time kept in the kernel and the RTC are typically both stored as UTC. The exception to this tends to be Intel based systems where there is no universal standard how the hardware (RTC) time is set in relation to UTC. (It can be local or UTC). A a result, a Unix on Intel system needs to keep track of how it should interpret (during boot) and/or set (the RTC) upon shutdown.

Like most other operating systems, Unix systems keep time as an offset in seconds from an epoch (the Unix epoch is Midnight January 1, 1970 UTC5). When a process requests time from the kernel this number in the form of an 32 bit signed integer is what is returned. Rule based conversions can then be made to this number to convert it to local time. These conversions are calculated based upon the time zone, DST, and potential leap second rules as defined in the TZ variable or the system wide time zone database. Most unices also support a more granular timer that is used to calculate sub-second intervals for things such as how much time a process spent waiting for disk or consuming CPU.

The size of a 32 bit signed integer is relevant to when time will reset as the data type fills up. Based upon the Unix epoch and the size of a signed 32 bit value, this event will happen on January 19, 2038 UTC. At that point the date will swing to Friday the 13th 1901 UTC. Here is a sample time utility that demonstrates the rollover of Unix wall clock time.

Using the concept that the kernel sits on the prime meridian and that the user community sits in a separate time zone, then it is the administrators task that the data that controls this functionality be properly set. The result of this requirement is that the administrator must insure that the system time is correct (in that it is synchronized with what the world recognizes as the accurate time), and that the system default time zone is correct. Time synchronization helps to keep network applications working correctly, properly set file access times, and insures that logs across all systems are in-sync so that events (such as security and performance issues) can be correlated across the entire computing environment. Setting the proper time zone insures that all human readable time is consistent with the physical location that users expect for this system.

Network Protocols

Keeping systems in sync is typically achieved using a network based protocol that continually adjusts time on a system to keep in step with other sources.

Unless you have a local radio / GPS / atomic clock or equivalent time device on each Unix server, your systems will need to reach out across the network to find and synchronize the time. And if by some odd set of circumstances you did have a clock with that kind of accuracy on each system, you would still need the logic provided by a time daemon to keep the kernel in sync with it. For this reason, Unix has methods to synchronize itself with local high resolution clocks or other systems across the network.

Since the inception of Unix and the Internet, a number of network based protocols have been created to deal with time synchronization and responding to time requests in general. Over time as these protocols have been replaced and upgraded, they have become more robust and accurate in each implementation.

The ToD (Time of Day) text port (TCP/UDP 13 - RFC 867) and binary port (TCP/UDP 37 - RFC 868) are used to make the internal time available to remote systems. Neither of these protocols has sufficient logic or capability to properly synchronize systems across a network. The practical use of these ToD protocols is for diagnosis and testing, and on most modern Unix deployments they are disabled.

Like the ToD service, the utime / unixtime service listens on port 519 and responds with a unsigned integer, presumably the time_t results of a time() system call. No meaningful protocol was built upon this service and also like the ToD service, this is typically disabled on most modern systems.

The BSD timed daemon is an actual time synchronization service. This daemon listens and responds on port 525/UDP and will elect a master server to synchronize from. If a radio clock (or similar device) is used, one machine can be made to trust only itself and essentially promote its value in the election process. timed and the timedc control utility is typically only found on BSD systems (FreeBSD, OS-X). While this process provides much of the functionality required, it is the more structured NTP protocol that has become the standard time synchronization tool.

The most popular and most robust network time synchronization tool in use on Unix systems is NTP (Network Time Protocol) and the lighter weight variant called SNTP (Simple Network Time Protocol). NTP is specifically designed to deal with problems inherent in asking remote servers for highly accurate time over network links of varying latency.

NTP uses a rating system of accuracy called strata. The most accurate devices, such as radio or GPS clocks, are considered stratum 0. Systems that are downstream from a high accuracy clock have higher stratum as they increase in "hops" and latency/error from a stratum 0 device. Not every NTP consumer requires access to a stratum 0 or 1 device, so the design allows one or more systems in a data center to get highly accurate time, and then make the remaining systems in the data center (as well as desktops and workstations) as higher stratum from the designated systems.

A server synchronizing directly off a stratum 0 device can do so without much fear of transmission problems that cause inaccurate results. For higher (number) strata consumers it is important to counteract the inaccuracies and error introduced by remote network based access. This is done by using a pool of time sources as your NTP source and allowing the NTP algorithms to determine which to use. A larger set of time sources allows the NTP implementation to discard those servers that are less accurate due to network conditions or not consistent with the others. For this reason, a number of NTP source servers should be chosen to achieve quorum if one is significantly out of sync. (Basically this means use three or more source NTP servers.) ntpdc -p or ntpdc -s can be used to view the status of the nptd and what server it is using.

SNTP and remote / high stratum systems can use a single NTP source but will be unable to accurately determine if the source is valid or not. (SNTP only uses one source NTP server at a time as part of its design.)

The most popular NTP implementation for Unix is ntpd. (Older versions are often called xntpd. The version can be checked by running ntpd --version.) A copy of ntpd ships with every major Unix distribution. The ntpd daemon starts early in the boot process and will begin to slew time in an effort to keep it in sync with upstream (lower stratum) servers.

ntpd can make one-time adjustments of up to 1000 seconds (nearly 17 minutes) to the system time if the initial time read from the RTC was off or it can slew the Unix clock to eventually get to the appropriate time. Both methods are quite problematic as a server should never be off to that degree upon startup. The one time jump can be disruptive to time sensitive processes such as databases, and so can a prolonged clock slew effort to fix a significant deviation as it will require that the server be out of sync for a longer period.

When time is considerably off, either ntpdate or ntpd -qg commands can be used for one time jumps to synchronize a local system with another. The ntpdate command is not as accurate as ntpd as the ntpd daemon spends a significant amount of effort to determine propagation delays and jitter in the sources it reads from and may take some time to achieve a proper consensus on which time source to use before it begins to adjust time.

Intel (x86) Architecture Issues

One key side effect of the loose coupled nature of hardware and OS on Intel based platforms is that the OS creators do not get to define how the hardware works. In the case of Linux, BSD, and Solaris Intel, the OS creators are working on hardware that is primarily designed for an entirely different operating system with its own legacy concerns.

The big Unix manufacturers that control the software and the hardware that they run on can define exactly how time is stored in the RTC and exactly what precision they can expect from the timing circuits that they use. The Intel based Unix solution to this is to cover each possible configuration of the underlying hardware capabilities. As a result, the Intel based Unix administrator must account for the offset of the RTC from UTC, where the big Unix administrator does not have this problem.

The personal recommendation of the author is for data center users to use UTC time in the RTC clock to be consistent with other (big) Unix systems. It may be beneficial to use local time on systems where you may be inclined to change it by hand, such as on a desktop or a laptop. Either method that is chosen, it is recommended that this be a consistent standard across all systems in your environment.

Virtualization Issues

Like basic time(ing) operations, big Unix virtualization is the result of a close relationship between OS and hardware designers, and it is here we see minimal timekeeping issues when virtualizing these systems. When one vendor controls both development efforts we tend to see a virtualization-aware OS and virtualization-ready hardware. For these implementations timekeeping tends to be of relatively little concern. The OS is well aware of the hardware that it runs on and time related design issues from performance measurement, keeping time, and CPU scheduling are handled by a hypervisor in conjunction with the OS. Instead, it is solutions that run on Intel architecture that present problems to virtualized hosts that are not seen in the big Unix environment.

Because most virtualized OS's on Intel systems are not completely aware that they or virtualized or what product is used to do the virtualization we see a software hypervisor that is trying to completely mimic a hardware resource and a OS that is assuming that it is on an actual hardware resource. The end result is a soft implementation of an assumed hard resource. When it comes to clock circuits, this can be devastating for accurate timekeeping. This is further complicated by even more drastic events such as suspension of the host environment and the resulting time jumps upon awakening.

The solution for virtualized systems is largely dependent upon the OS that is virtualized and the virtualization environment that is used. Time drift is inevitable in systems like this, and some mechanism must be used to occasionally bring the system to the current time. This is primarily done via tools provided by the virtualization vendor. The key point here is that the RTC clock (that is now virtualized) tends to be more accurate as it is typically synchronized with a NTP server running on the virtualization engine and is not actually a RTC but a virtualized representation of one. At the same time, the clock / tick devices that the host OS may rely upon are now virtualized and tend to be less reliable when virtualized.

The proper solution for each OS and virtualization method (on Intel hardware) is well beyond the scope of this document. One should be aware that timekeeping in a virtualized environment can be problematic for Intel hosts and it is important to refer to vendor recommendations as your configuration on virtual systems may be quite different than that on actual hardware.

Local Time Conversion

After time synchronization, the second key task of the administrator is defining the time zone (UTC offset) to allow for the proper conversion of the kernel UTC time to the local time of the user. This is accomplished differently depending upon what Unix you are administering. Ultimately all unices are the same in that they use the TZ environmental variable to determine the time zone (and how to calculate local time) and if TZ is not set then resort to a system-wide value.

The TZ variable (or system default time zone setting) is responsible for encoding:

  1. the abbreviation and offset from UTC (how much time to add or subtract from the system clock to get a local time)
  2. if, and how, to calculate DST and its abbreviation
  3. and potentially the addition of any leap-seconds

Setting the time zone tends to take two flavors on Unix; those that use a "compiled" binary file called an Olsen or TZif database file or those that use a string representation of how to adjust time. Because of the overwhelming popularity of the Olsen based systems I will refer to these two types of systems as "Olsen" and "Non-Olsen".

Non-Olsen systems

Systems that do not use a time zone database tend to rely upon a well formatted POSIX TZ string that describes two of the requirements of the TZ variable listed in the previous section. In the most simple form, only the abbreviation and the UTC offset is expressed. If the locale of the system utilizes DST then the abbreviation and rules for the summer shift are included. The POSIX TZ string is shown in this TZ environmental variable string.

The key downfall of the non-Olsen method is that the TZ string can easily be mistyped into uselessness. The formatting of the TZ variable string can be difficult to generate on the fly. The rules portion has several different formats including the use of Julian dates6 that may be confusing to many. It is no coincidence that the two primary unices that do not use the Olsen system also have very robust management tools to validate and set these values for you.

It should be noted that the POSIX TZ formatted string is of value even on systems that use Olsen database (TZif) files as they will (tend to) accept the POSIX format as well as the Olsen filename. So this environmental variable string format is of interest to even those who rely upon the Olsen db files for fixes or changes to the Olsen db when updates are not available.

AIX (5.3 and earlier)

AIX uses the standard POSIX TZ string. The system default is set in /etc/environment and the user TZ can be set in /etc/security/environ or the user's profile.

NOTE: AIX differs from other unices in that the TZ environmental variable is always used. In other unices one can unset the TZ variable to get the system default time zone. If the TZ variable is unreadable, mistyped, or unset then all local time conversions will not be performed and local time will be returned as UTC. For this reason, you should modify your AIX TZ variable with extreme care, and once you have modified it you should log back into the system and verify that the time representation (local time conversion) is correct.

Versions of AIX prior to 6.1 include an Olsen-like database in /usr/share/lib/zoneinfo but they do not use it for system time conversions. These files are not technically TZif files as they are not of the same format but do use zic and zdump utilities to create and display the contents of the compiled files. (Note that AIX 6.1 and later systems actually use Olsen TZif files.)

Like most configuration options in AIX, the time zone settings are configured with a command (chtz or smitty chtz) rather than touching files (even though the result is stored in ASCII text that can be managed via tools like vi). This method is preferable to most AIX administrators considering the sometimes complex stanza nature of AIX configuration files and specifically the error-prone nature of the POSIX TZ string.

HP-UX

Local TZ variables work like other POSIX TZ systems but the system default time zone is kept in /usr/lib/tztab. The format of the file is best described as a text version of the Olsen file in that it can contain multiple years of DST change dates.

The Olsen DB based systems

The Olsen DB (TZif7) file is a binary database file that contains all local time conversion rules for a time zone such as the standard (three letter) time zone name, the use, or nonuse, of DST and if / when leap seconds were introduced. The TZif files are collected in a directory tree where they are referenced as path/file rather than the previous POSIX strings.

So instead of specifying a cryptic rule for a single DST cycle in your (POSIX) TZ variable, you instead specify a filename relative to a zoneinfo directory that contains significantly more time change information. When a local time conversion is done on an Olsen based system the time is calculated using the rules found in this binary file. Because the binary file can have years of time zone data it is possible to have the proper time values and even DST flags as these times are converted8.

These binary files are compiled from a source file using the zic compiler. The binary file can be dumped back to a human readable format using the zdump command. The compiled (binary TZif) files are typically kept in /usr/*/zoneinfo and on some systems this directory can be specified using the TZDIR environmental variable.

As an alternative to the TZDIR variable additional pathing can be included in the TZ variable. The common convention for TZ is that if it is simply the filename it is relative to the root of the zoneinfo directory. If the filename is preceded to with a ":" character then it is a relative path from the current (CWD) location and if the TZ variable begins with a "/" then it is an absolute path. As a form of POSIX backwards compatibility, files have been created in the root of the zoneinfo directory that, when used, are exactly the same as the equivalent POSIX string.

Some OS's tend to use city name named TZif files, and others tend to use those named for older time zones. A comparison using diff will show that the only difference is the filename for many of these files. For example, America/New_York and US/Eastern are internally the same.9

AIX (6.1 and later)

With the introduction of AIX 6.1, IBM has begun the use of the Olsen system. New smitty screens allow the administrator to browse locations and choose the appropriate time zone from a list. This is different than the previous versions where the administrator was effectively expected to build the TZ string in smitty or with chtz on the command line.

FreeBSD

FreeBSD uses the Olsen TZif file database. The system default time zone value is set by copying the appropriate TZif file to /etc/localtime (rather than linking). The root of the zoneinfo directory is /usr/share/zoneinfo.

When FreeBSD starts the adjkerntz binary (started by /etc/rc.d/adjkerntz) looks for /etc/wall_cmos_clock as a flag to see if the hardware RTC keeps local / wall-clock time. If /etc/wall_cmos_clock does not exist then the hardware clock uses UTC time. adjkerntz is called at startup with the -i option to read and convert the hardware clock into UTC epoch offset time the kernel keeps. adjkerntz is used with the -a option to write time changes back to the hardware clock.

The tzsetup utility can be used to "browse" the Olsen database and properly set the default time zone in /etc/localtime and the localtime or UTC status in /etc/wall_cmos_clock.

Linux

Note: This section assumes x86 Linux and more specifically Red Hat Linux unless otherwise specified.

The default system time zone is kept as a copy of the appropriate Olsen TZif file in /etc/localtime. The man pages for this file erroneously list it as a link to the appropriate TZif file in /usr/share/zoneinfo. As a result it is necessary to either diff this file against an expected value (file), to zdump it, or possibly run strings against it to determine if it is the time zone file you expected.

The Red Hat time zone setup (GUI) utilities use the "<Area>/<Location>" naming convention. For example, Mountain time in the US is America/Denver instead of US/Mountain, or MST7MDT.

The Intel (x86) specific tool to maintain the RTC in Linux is hwclock. This utility keeps runtime (maintained by the tool) track of drift, last (--)systohc set, and UTC/LOCAL offset of the RTC in /etc/adjtime. The /etc/sysconfig/clock file contains startup information (maintained by admin) on the offset of the RTC clock. If the value for UTC in the sysconfig file is true then hwclock will be called with the --utc option, otherwise it will be called with the --localtime option. Upon shutdown hwclock --systohc is called to push the time back down to the hardware clock. Upon shutdown, hwclock finds its offset (from UTC) by comparing the localtime to the kernel time (through the mktime() system call).

The RTC can be queried on Linux using hwclock --show or by cat-ing /proc/driver/rtc. The RTC can be set to an explicit time using hwclock --set.

/etc/timezone is set on Linux systems but does not appear to be relevant to core functionality. It is not accessed or set by any tool that I can see and is not set by system-config-date. This appears to be a standard red-herring Linux file10 that is not maintained by the Red Hat tools.

Solaris

Solaris sets the default time zone in /etc/TIMEZONE. (This file is a symlink to /etc/default/init.) The TZ value in this file is a reference to the appropriate TZif file in the /usr/share/lib/zoneinfo directory.

Intel Solaris keeps track of the RTC offset in /etc/rtc_config using the zone_info and zone_lag variables. This file is used and maintained by the rtc command to properly read and set the RTC. One can check this setting by running rtc (with no options) to find the settings in this file. If the RTC does not use UTC then the resulting value should match the system default time zone.

The tzselect11 command can be used to determine the appropriate value for the TZ variable. It will ask the user several questions about time zones and print a result to stdout. (Note: You can capture stdout with this script to capture the result. All user interaction is done through stderr and stdin.)

OS-X

Because of its BSD foundation, OS-X uses the Olsen time zone database and many of the same conventions as BSD. The key difference is that many of the Unix-sh command line utilities and conventions have been replaced with the OS-X Gui-centric interface. The Date & Time settings are managed through a System Preferences pane of that name.

Underneath it all, OS-X keeps its Olsen TZif files in /usr/share/zoneinfo and its default system time in /etc/localtime as a link to the appropriate TZif zoneinfo file. User applications depend upon the TZ variable (just like in Unix) so a TZ set in the profile will override the system setting as long as that application is loaded from the shell. The key difference with OS X is that your process tree begins below the shell and is not loaded from your profile and therefore most apps will use the system default setting. Applications that are started from the shell using the open command will use the TZ variable from the profile. To be consistent with the design of the OS-X platform as a desktop, I highly recommended against setting the TZ variable in your profile.

The developer

C

C is the reference language for Unix as it is fundamentally tied to the operating system. It is no coincidence that every Unix internals book uses C as the language to express algorithms and data structures as C is the language Unix is written in. Many of the following languages listed here follow the C API fairly closely.

Wall clock time is retrieved from the kernel using the time() function. time() returns a time_t (32 bit signed integer) value that contains the number of seconds since the Unix epoch UTC. The call to time() does not use the system time zone information as it does not convert or calculate the time to a local representation. So while the value returned from time() is the basis for the wall clock time, it is not what one would consider "the time".

Here is an example of the time() function.

To create the local representation of time the seconds since epoch (time_t) value can be converted into a tm struct using either gmtime() or localtime(). This structure breaks out the time_t value into more meaningful struct values such as seconds, minutes, hours, day (of week and/or month), month, year, etc. Here is a tm struct from (OS X) time.h. gmtime() returns a tm struct not modified for a time zone. localtime() converts the time to the current local offset from UTC using the TZ variable or the default system time zone. The results of gmtime() and localtime() are the same only in the locales that are in the same time zone as GMT and do not use DST.

Here is an example of time() and localtime().

The tm struct can be further simplified into an ASCII string representation using the strftime() or asctime() functions. strftime() creates a user defined string based upon a number of format specifiers. asctime() creates a "standard" time string. The time_t value can be converted directly into a "standard" string representing local time using ctime().

The "broken down time" of the tm struct can be returned to a time_t value using either mktime() or timegm(). mktime() will convert back to UTC using the local time conversion rules for the user, while timegm() will assume the input to be UTC time and will not convert the time to UTC as it converts to time_t.

The tzset() function is called to initialize the items required for calculating local time. It is typically not called directly but is called implicitly by any function that converts the time. tzset() initializes the tzname[2] arrays, the timezone, and daylight values. Uninitialized, the tzname[] strings may return "WILDABBR", "UTC", or "GMT" depending upon the implementation.

The elapsed time between two moments (expressed in time_t values) can be determined using the difftime() function. The time is returned in seconds, and is held in a double rather than a time_t or other integer type.

For one-time jumps, time can be pushed back to the kernel using the stime() function. Provided the user has sufficient permissions, this function will change the kernel time to the time_t value passed as the single parameter. For slewing time, the adjtime() function will tell the kernel to adjust the clock gradually over a period. adjtime() can also be used to find the remaining time from a previous request remains to be adjusted.

More granular versions of the time() and stime() are provided with the gettimeofday(), settimeofday(), and ftime() functions. These functions are not consistent across Unices and may cause porting problems when used on other Unices.

A slightly misleading but still related API is the clock() function. This function returns the time (in sub-second CLOCKS_PER_SEC value) spent running on the CPU along with the time of reaped (wait()ed) child processes. This is not the wall clock time spent running but how much time it (and children) was actually running rather than sleeping / waiting. If the process was 100% CPU bound on a relatively idle multiprocessor system, then this value (once converted to seconds) should be close to the wall clock time elapsed since the application started. Otherwise, in most cases, this number once converted to seconds will be substantially less than the total time elapsed (wall clock time) since the application started. This function is closely related to the (frequently poorly implemented) getrusage() call and the user-land time command.

The getrusage() and times() standard C functions return the time spent by a process and its children. These are slightly more complex versions of the clock() function that can also include the times of active child processes in the output. The times() function is widely supported and is also available in the shell built-in command by the same name. getrusage() provides significantly more points of data but has mixed support from one Unix to the other. The user-land equivalent of getrusage() is the timex command.

Java

Even when running on the Unix platform, the Java environment provides its own Olsen database that it uses for local time calculations. By using its own zoneinfo directory, Java can present a consistent environment and API across many different underlying system types. Particularly those that do not natively support multiple time zones.

The problems of tying the Olsen database version to the JRE version means that a developer may be forced to modify code (because of a JRE upgrade) just because they needed a new Olsen DB. This has been fixed when Sun introduced a TZUpdater tool to update only the Olsen database in JRE versions 1.4 and later.

The primary Java class for finding the current date and time zone dependent calculations is java.util.Calendar. The TimeZone object is used as a parameter to this class to explicitly set the time zone. The TimeZone object is defined by the java.util.TimeZone class.

Earlier versions of the Java environment used the java.util.Date class to determine time. The majority of methods in this class have been depreciated in favor of the java.util.Calendar class.

The java.util.Calendar and the depreciated methods in java.util.Date calculate the current time based on milliseconds since midnight January 1, 1970. This is the same epoch as Unix time but the default measurement is much more granular in Java than is commonly used in most Unix APIs.

As of this writing, there is a new Date/Time API is proposed for Java 7 called JSR 310. This is intended to simplify use and increase performance of the time functionality of Java.

Perl

Perl offers the following functions: gmtime(), localtime(), time(), and times(). These are much like the C counterparts (but with Perl-like usage). time() works much like the C version in that it returns seconds since the Unix epoch. localtime() returns an array rather than the tm struct returned by the C version by the same name. The Perl array members map closely to the C struct members.

Additionally one can use the POSIX module to call many of the same functions available in the standard C library. The functionality is nearly the same, some of the parameters and calling methods are Perl-centric.

Finally there are the Date and Time modules to provide some additional functionality on top of the base functions.

The user

Of primary importance to the Unix user is the setting of the TZ variable and knowledge of time related commands that are available. The first has been largely covered but will be explicitly stated in this section. The second is a listing of user-level commands that can be run to access time and timer functionality in Unix.

As stated earlier, the system time zone settings can be overridden by setting an appropriate value for the TZ variable in your environment. TZ can be set and exported either in the users profile, a shell wrapper script (prior to running an application), or on the command line. This value will be used for all time based functions called in your environment under your personal TZ variable setting.

The following is an example of setting the TZ variable on a TZif based OS-X system then calling the date command to display the current date and time. Each iteration is described as it is run. (These are actual, scripted, results from an OS-X 10.5 system.)

Set TZ to an empty string. This local time conversion defaults to UTC because of the invalid entry in TZ.
$ export TZ=
$ date
Mon Sep 22 20:55:39 UTC 2008

Set TZ to a POSIX compatible string. This is an Olsen system, but it accepts POSIX strings as well.
$ export TZ=CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00
$ date
Mon Sep 22 15:55:39 CDT 2008

Set TZ to a Olsen US/<timezone> file (The other option being the <area>/<location> file.)
$ export TZ=US/Mountain
$ date
Mon Sep 22 14:55:39 MDT 2008

Set TZ to the absolute path of a specific Olsen file. Note that Puerto Rico does not have a DST value as they do not shift for summer time.
$ export TZ=:/usr/share/zoneinfo/America/Puerto_Rico
$ date
Mon Sep 22 16:55:39 AST 2008

Clear the value of TZ so that the local time conversion will use the system default. (This was run on a system in New Hampshire, USA - EST/EDT.)
$ unset TZ
$ date
Mon Sep 22 16:55:39 EDT 2008

date is used in the previous examples to display the default time and date string. The string can be customized using format specifiers to print only specific parts of the time/date or in a different format. The following are a few examples of the date with various formats specified.

Hour:Minute (3:44 PM)
$ date +%H:%M
15:44

The date (month day year) that could be used as a file extension (for saving a copy of a file).
$ date +%m%d%y
101008

The system time can be set with the date command (provided the user has sufficient permissions). The recommendation is to use the ntpdate command instead as the syntax is much easier and far more accurate.

The time spent in the current shell can be obtained with the $SECONDS variable. To get the number of seconds between two events (in the same shell) calculate the time between two saved $SECONDS values. To get the time between two system events not in the same shell use the following Perl one liner:
perl -e 'print time, "\n";'
Seconds can be broken down using this sample (ksh) script segment to factor time in the shell.

The other time related commands are for determining how long a process has been running and how much CPU time it has consumed. The three commands related to this purpose are time, times, and timex. time is used to tell wall clock time and CPU time consumed by a process that it watches. times is a shell builtin that reports the time consumed by the shell and child processes. timex is a more information rich version of time.

Here is an example of time as used to report on the wall-clock-time and CPU-time consumed by the sleep command. The sleep command, by its very definition and command line arguments, is designed to run for 5 seconds. Note here how little CPU (user or system) time is consumed by the process even though it runs for a full 5 seconds according to the wall clock. Options to the time command vary by system and in my experience on more than one system, vary from the stated options in the man page.

$ time sleep 5

real   0m5.005s
user   0m0.001s
sys    0m0.003s

The following is an example of the times shell builtin command run from bash on OS-X. The first column is user time, the second is system time. The first row is times for the shell and the second row is all reaped children.

$ times
0m0.069s 0m0.140s
0m1.037s 0m1.135s

The timex command provides significantly more information on the systems that support it. timex -s on AIX returns 57 lines of output (some of which are empty). The timex command is not available on every Unix and its options and results vary widely by versions on each OS that do include it.

Footnotes

1. The concept of the prime meridian (line of longitude) placed in Royal Observatory in Greenwich, London England is the result of the early efforts to begin this standardization by the British. While GMT (Greenwich Mean Time) is no longer officially used, it is still commonly referred to as the base time zone that all others are offset from.
2. One clear testament to this complexity is the commentary in the Olsen source files (before they are compiled with zic). The sources of time information vary from diverse sources such as official government documentation, historical sources, and even a guy named "Bubba".
3. There are over five different official / standards based calculations for approximating the earths position in relation to various celestial bodies. These calculations account for concepts such as the dampening effect of the tides on earths rotation, and the general slowing of our rotational speed. Additionally, when referring to items that are "fixed" outside our solar system sidereal time is used. Sidereal time is the concept that as the earth rotates during the day it has moved in relation to the sun in its annual orbit that makes a year. While these concepts are bread and butter for astronomers, they make my Unix Admin brain hurt. Google "Sidereal Time", "UT", or "Universal Time" to see how deep the rabbit hole goes.
4. UTC is a compromise acronym between the French and English. The English wanted CUT (Coordinated Universal Time) the French wanted TUC (Temps Universel Coordonne), so they settled on UTC that equally ambiguous in both languages. Rack up another win for standards bodies.
5. Unix and Java uses an epoch of Jan 1, 1970. Windows (Win32) uses Jan 1, 1601 but has functions to return the value since the Unix epoch. .Net uses Jan 1, 1 (1/1/1). Mainframes and NTP use Jan 1, 1900. VMS uses Nov. 17, 1858. Pre-OS-X (non-Unix) Macintoshes use Jan 1, 1904.
6. In the context of the POSIX TZ string the Julian date is the ordinal date, meaning the day of the year (independent of the month, week, etc...). So the 32nd day of the year (J32) is Feb 1st. (The Julian date is available from date +%j.) This is different from the Julian date that is a number of days from the Julian date epoch (noon Jan. 1, 4713 BC GMT (A modified version now uses midnight)). This is also different than the Julian calendar that is the calendar generally used prior to the introduction of the Gregorian calendar in 1582. The Julian calendar used an average year of 365.25 where the actual year is slightly shorter. That is why not every year divisible by four is a leap year in our current calendar.
7. A TZif file is named here for its "magic" identifier. The first four bytes of an Olsen database file are the ASCII characters "TZif".
8. Because DST was first implemented during war time in the US, many of the DST time zone identifiers were named as "War Time", such as Eastern War Time. Later these same time zones were renamed to "Peace Time" after the war. Today, of course, they are referred to as "Savings Time".
9. US/Eastern is actually a link to America/New_York created by the zic compiler. The old zone names are created from the "backward" source file found in the tzdata source package.
10. Red Herring = Used by something but documented nowhere.
11. tzselect is actually part of the Olsen (tzcode) distribution. I have found it on Red Hat Linux, but the Red Hat tools appear to be the preferred methods on that platform.

Author: William Favorite <wfavorite@tablespace.net>
Version: 1.0.0
Date: 3/28/9