Chapter 18. Time

Telling Time and Dates

Representing Time

Unix and Linux keep track of time in seconds before or after the epoch, which is defined as midnight, January 1, 1970 UTC.[1] Positive time values are after the epoch; negative time values are before the epoch. In order to provide processes with the current time, Linux, like all versions of Unix, provides a system call called time():

#include <time.h>
time_t time(time_t *t);

time() returns the number of seconds since the epoch, and if t is non-null, it also fills in t with the number of seconds since the epoch.

Some problems require higher resolution. Linux provides another system call, gettimeofday(), which provides more information:

#include <sys/time.h>
#include <unistd.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);

struct timeval {
    int tv_sec;         /* seconds */
    int tv_usec;        /* microseconds */
};

struct timezone {
    int tz_minuteswest; /* minutes west of Greenwich */
    int tz_dsttime;     /* type of dst correction */
};

On most platforms, including the i386 platform, Linux is able to provide very accurate time measurements. Industry-standard PCs have a hardware clock that provides microsecond-accurate time information. Alpha and SPARC hardware also provides a high-resolution timer. On some other platforms, however, Linux can keep track of time only within the resolution of the system timer, which is generally set to 100Hz, so the tv_usec member of the timeval structure may be less accurate on those systems.

Five macros are provided in sys/time.h for operating on timeval structures:

timerclear(struct timeval *)

This clears a timeval structure.

timerisset(struct timeval *)

This checks to see if a timeval structure has been filled in (that is, if either element is nonzero).

timercmp(struct timeval *t0, struct timeval *t1, operator)

This allows you to compare two timeval structures in the time domain. It evaluates the logical equivalent of t0 operator t1, if t0 and t1 were arithmetic types. Note that timercmp() does not work for the <= and >= operators. Use !timercmp(t1, t2, >) and !timercmp(t1, t2, <) instead.

timeradd(struct timeval *t0, struct timeval *t1, struct timeval
*result)

Adds t0 to t1 and places the sum in result.

timersub(struct timeval *t0, struct timeval *t1, struct timeval
*result)

Subtracts t1 from t0 and places the difference in result.

A third representation of time, struct tm, puts the time in terms that are more human-oriented:

struct tm {
{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
    long int tm_gmtoff;
    const char *tm_zone;
};

The first nine elements are standard; the final two are nonstandard but useful when they exist, as they do on Linux systems.

tm_sec

The number of elapsed seconds in the minute. Will be between 0 and 61 (two extra seconds are allocated to deal with leap seconds).

tm_min

The number of elapsed minutes in the hour. Will be between 0 and 59.

tm_hour

The number of elapsed hours in the day. Will be between 0 and 23.

tm_mday

The number of the day of the month. Will be between 1 and 31. This is the only member that will never be 0.

tm_mon

The number of elapsed months in the year. Will be between 0 and 11.

tm_year

The number of elapsed years since 1900.

tm_wday

The number of elapsed days in the week (since Sunday). Will be between 0 and 6.

tm_yday

The number of elapsed days in the year. Will be between 0 and 365.

tm_isdst

Whether some sort of daylight savings time is in effect in the expressed time value in the current time zone, if applicable. tm_isdst will be positive if daylight savings time is in effect,0 if it is not in effect, and -1 if the system does not know.

tm_gmtoff

This is not portable as it does not exist on all systems. If it exists, it may also be named _ _tm_gmtoff. It specifies seconds east of UTC or negative seconds west of UTC for time zones east of the date line.

tm_zone

This is not portable as it does not exist on all systems. If it exists, it may also be named _ _tm_zone. It holds a name for the current time zone (some time zones may have several names).

Finally, the POSIX.1b real-time processing standard allows even higher resolution than the microsecond resolution available in struct timeval. struct timespec uses nanoseconds instead and provides larger places to put the numbers:

struct timespec {
    long int tv_sec;   /* seconds     */
    long int tv_nsec;  /* nanoseconds */
};

Converting, Formatting, and Parsing Times

Four functions are available for converting among times expressed in terms of time_t and times expressed in terms of struct tm. Three are standard and are available on all Linux and Unix systems. The fourth, although useful, is not universally available, although it is available on all current Linux systems. A fifth function, which is standard, calculates the difference in seconds between time_t times. (Notice that even the time_t arguments are passed as pointers, not just the struct tm arguments.)

struct tm * gmtime(const time_t *t)

Short for Greenwich Mean Time, gmtime() converts a time_t value into a struct tm that expresses that time in UTC.

struct tm * localtime(const time_t *t)

localtime() acts like gmtime() except that it creates the struct tm expressed in terms of local time. Local time is defined for the whole system by the settings of the zoneinfo files, and that can be overridden by a TZ environment variable by users who are working from a different time zone than the one the computer is in.

time_t mktime(struct tm *tp);

mktime() converts a struct tm to a time_t, assuming that the struct tm is expressed in terms of local time.

time_t timegm(struct tm *tp);

timegm() acts like mktime() except that it assumes that the struct tm is expressed in terms of UTC. It is not a standard function.

double difftime(time_t time1, time_t time0);

difftime() returns a floating-point number representing the difference in time in seconds between two time_t values. Although time_t is guaranteed to be an arithmetic type, the unit is not specified by ANSI/ISO C; difftime() returns the difference in seconds, regardless of the units of time_t.

Four more functions are available to convert time between computer-friendly numbers and human-friendly textual representations. Again, the final one is not standard, despite its obvious general usefulness.

char *asctime(struct tm *tp);
char *ctime(time_t *t);

asctime() and ctime() both convert a time value into a standard Unix date string that looks something like this:

Tue Jun 17 23:17:29 1997

In both cases, the string is 26 characters long and includes a final newline character and the terminating ''.

The length of the string may not be guaranteed to be 26 characters in all locales, as it is in the default C locale.

ctime() expresses this date in local time. asctime() expresses this date in whatever time zone the struct tm specifies; if it was created with gmtime(), it is in UTC, but if it was created with localtime(), it is in local time.

size_t strftime(char *s, size_t max, char *fmt, struct tm *tp);

strftime() is like sprintf() for time. It formats a struct tm according to the format fmt and places the result in no more than max bytes (including the terminating '') of the string s.

Like sprintf(), strftime() uses the % character to introduce escape sequences into which data is substituted. All of the substituted strings are expressed in terms of the current locale. However, the escape sequences are completely different. In several cases, lowercase letters are used for abbreviations and uppercase letters are used for full names. Unlike sprintf(), you do not have the option of using numbers in the middle of an escape sequence to limit the length of the substituted string; %. 6 A is invalid. Like sprintf(), strftime() returns the number of characters printed into the s buffer. If it is equal to max, the buffer is not large enough for the current locale; allocate a larger buffer and try again.

strftime() uses the same substitutions used by the date program. These definitions of the substitutions are for the default locale and are here to help you identify the type of information they provide; they may be somewhat different in other locales.

%a

The three-character abbreviation for the name of the weekday.

%A

The full name of the weekday.

%b

The three-character abbreviation for the name of the month.

%B

The full name of the month.

%c

The preferred local expression of the date and time, as returned by ctime() and asctime().

%d

The numeric day of the month, counting from zero.

%H

The hour of the day, in 24-hour time, counting from zero.

%I

The hour of the day, in 12-hour time, counting from zero.

%j

The day of the year, counting from one.

%m

The month of the year, counting from one.

%M

The minute of the hour, counting from zero.

%p

The correct string for the local equivalent of AM or PM.

%S

The second of the minute, counting from zero.

%U

The numeric week of the year, where week one starts on the first Sunday of the year.

%W

The numeric week of the year, where week one starts on the first Monday of the year.

%w

The numeric day of the week, counting from zero.

%x

The preferred local expression of the date only, without the time.

%X

The preferred local expression of the time only, without the date.

%y

The two-digit representation of the year, without the century. (Do not use this—it is a potent source of year-2000 problems.)

%Y

The full four-digit numeric representation of the year.

%Z

The name or standard abbreviation of the time zone.

%%

The literal character %.

char *strptime(char *s, char *fmt, struct tm *tp);

Like scanf(), strptime() converts a string to a parsed format. It tries to be liberal in interpreting the s input string according to the fmt format string. It takes the same escape sequences that strftime() takes, but for each type of input, it accepts both abbreviations and full names. It does not distinguish between upper and lower case, and it does not recognize %U and %W.

strptime() also provides a few extra escape sequences and interprets a few sequences slightly differently than does strftime(). Only the significantly different escape sequences (that is, beyond the changes already mentioned) are documented in this list. Numbers may have leading zeros, but they are not required.

%h

Equivalent to %b and %B.

%c

Reads the date and time as printed by strftime() with the format string %x %X.

%C

Reads the date and time as printed by strftime() with the format string %c.

%e

Equivalent to %d.

%D

Reads the date as printed by strftime() with the format string %m/%d/%y.

%k

Equivalent to %H.

%l

Equivalent to %I.

%r

Reads the time as printed by strftime() with the format string %I:%M:%S %p.

%R

Reads the time as printed by strftime() with the format string %H:%M.

%T

Reads the time as printed by strftime() with the format string %H:%M:%S.

%y

Reads the year within the twentieth century. 1900 is added to the value, and only values of 0-99 are allowed.

%Y

Reads the full year. Use this instead of %y if you possibly can, to avoid year-2000 problems.

strptime() returns a pointer to the character in s one character beyond the final character that it reads while parsing.

The strptime() function is, unfortunately, specified neither by ANSI/ISO nor POSIX, which limits its portability.

The Limits of Time

On 32-bit Linux systems, like most Unix systems, time_t is a signed integer 32 bits long. This means that it will overflow Monday, January 18, 2038, at 10:14:07 PM. So Monday, January 18, 2038, 10:14:08 PM will be represented as Friday, December 13th, 3:45:52 PM, 1901. As you can see, Linux did not exhibit a year-2000 problem (as far as the native time libraries are concerned), but it does have a year-2038 problem.

On 64-bit platforms, time_t is instead a 64-bit signed long. This is effectively forever; signed 64-bit time is truly astronomical, as it will not overflow until well after the sun is predicted to envelop the Earth as it becomes a red giant.

To find the beginning of time, the current time, and the end of time for the system you are using, you can build and run this program, daytime.c:

 1: /* daytime.c */
 2:
 3: #include <stdio.h>
 4: #include <sys/time.h>
 5: #include <unistd.h>
 6:
 7: int main() {
 8:     struct timeval tv;
 9:     struct timezone tz;
10:     time_t now;
11:     /* beginning_of_time is smallest time_t-sized value */
12:     time_t beginning_of_time = 1L<<(sizeof(time_t)*8 - 1);
13:     /* end_of_time is largest time_t-sized value */
14:     time_t end_of_time = ~beginning_of_time;
15:
16:     printf("time_t is %d bits long

", sizeof(time_t)*8);
17:
18:     gettimeofday(&tv, &tz);
19:     now = tv.tv_sec;
20:     printf("Current time of day represented as a struct timeval:
"
21:            "tv.tv_sec = 0x%08x, tv.tv_usec = 0x%08x
"
22:           "tz.tz_minuteswest = 0x%08x, tz.tz_dsttime = 0x%08x

",
23:           tv.tv_sec, tv.tv_usec, tz.tz_minuteswest, tz.tz_dsttime);
24:
25:     printf("Demonstrating ctime()%s:
",
26:            sizeof(time_t)*8 <= 32 ? "":
27:            " (may hang after printing first line; press "
28:            "control-C)");
29:     printf("time is now %s", ctime(&now));
30:     printf("time begins %s", ctime(&beginning_of_time));
31:     printf("time ends %s", ctime(&end_of_time));
32:
33:     exit (0);
34: }

Unfortunately, the ctime() function is iterative by nature, which means that it (for all practical purposes) never terminates on 64-bit systems for astronomical dates like the 64-bit beginning and end of time. Use Control-C to terminate the program when you become tired of waiting for it to complete.

Using Timers

A timer is simply a way of scheduling an event to happen at some point in the future. Instead of looping around, looking at the current time, and wasting CPU cycles, a program can ask the kernel to notify it when at least a certain amount of time has passed.

There are two ways to use timers: synchronously and asynchronously. The only way to use a timer synchronously is to wait for the timer to expire—sleeping. Using a timer asynchronously, like every other asynchronous facility, involves signals. What may be surprising is that using a timer synchronously may also involve signals.

Sleeping

A process requesting that it not be scheduled for at least a certain amount of time is called sleeping. Four functions are available for sleeping; each measures time in different units. They also have slightly different behavior and interact with other parts of the system differently.

unsigned int sleep(unsigned int seconds);

sleep() causes the current process to sleep at least seconds seconds or until a signal that the process does not ignore is received by the process. On most platforms, sleep() is implemented in terms of the SIGALRM signal, and therefore it does not mix well with using the alarm() system call, creating a SIGALRM handler, ignoring the SIGALRM signal, or using ITIMER_REAL interval timers (described later), which share the same timer and signal.

If sleep() does not sleep for the full time allotted, it returns the number of seconds left to sleep. If it has slept at least as long as requested, it returns zero.

void usleep(unsigned long usec);

usleep() causes the current process to sleep at least usec microseconds. No signals are used. On most platforms, usleep() is implemented via select().

int select(0, NULL, NULL, NULL, struct timeval tv);

select(), documented in Chapter 13, provides a portable way to sleep for a precise amount of time. Simply fill in the struct timeval with the minimum amount of time you wish to wait, and do not wait for any events to occur.

int nanosleep(struct timespec *req, struct timespec *rem);

nanosleep() causes the current process to sleep at least the amount of time specified in req (see page 484 for timespec), unless a signal is received by the process. If nanosleep() terminates early due to a received signal, it returns -1 and sets errno to EINTR and, if rem is not NULL, sets rem to represent the amount of time remaining in the sleep period.

nanosleep() is currently the least portable of these functions, because it was specified as part of the POSIX.1b (previously called POSIX.4) real-time specification, which is not implemented on all versions of Unix. However, all new Unix implementations implement it, because the POSIX.1b functions are now a standard part of the Single Unix Specification.

Not all platforms that provide the nanosleep() function provide high accuracy, but Linux, like other real-time operating systems, attempts to honor short waits with extreme accuracy for real-time processes. See Programming for the Real World [Gallmeister, 1995] for more information on real-time programming.

Interval Timers

Interval timers, once enabled, continually deliver signals to a process on a regular basis. Exactly what regular means depends on which interval timer you use. Each process has three interval timers associated with it.

ITIMER_REAL

Tracks time in terms of the clock on the wall—real time, regardless of whether the process is executing—and delivers a SIGALRM signal. It conflicts with the alarm() system call, which is used by the sleep() function. Use neither alarm() nor sleep() if you have a real itimer active.

ITIMER_VIRTUAL

Counts time only when the process is executing—excluding any system calls the process makes—and delivers a SIGVTALRM signal.

ITIMER_PROF

Counts time when the process is executing—including the time the kernel spends executing system calls on the behalf of the process, but not including any time spent processing interrupts on behalf of the process—and delivers a SIGPROF signal. Accounting for time spent processing interrupts would be so expensive that it would change the timings.

The combination of ITIMER_VIRTUAL and ITIMER_PROF is often used for profiling code.

Each of these timers generates its associated signal within one system clock tick (normally 1-10 milliseconds) of the timer expiring. If the process is currently running when the signal is generated, it is delivered immediately; otherwise, it is delivered soon afterward, depending on system load. Since ITIMER_VIRTUAL is tracked only when the process is running, it is always delivered immediately.

Use a struct itimerval to query and set itimers:

struct itimerval {
    struct timeval it_interval;
    struct timeval it_value;
};

The it_value member is the amount of time left until the next signal is sent. The it_interval member is the amount of time between signals; it_value is set to this value each time the timer goes off.

There are two system calls for dealing with interval timers. Both take a which argument that specifies which timer to manipulate.

int getitimer(int which, struct itimerval *val);

Fills in val with the current state of the which timer.

int setitimer(int which, struct itimerval *new,
                         struct itimerval *old);

Sets the which timer to new and fills in old with the previous setting if it is non-NULL.

Setting a timer’s it_value to zero immediately disables it; the timer will no longer be called. Setting a timer’s it_interval to zero disables it after the next time the timer triggers.

In the following example, a parent process starts a child process, runs a one-second ITIMER_REAL timer, sleeps for 10 seconds, and then kills the child process:

 1: /* itimer.c */
 2:
 3: #include <stdio.h>
 4: #include <stdlib.h>
 5: #include <sys/wait.h>
 6: #include <unistd.h>
 7: #include <string.h>
 8: #include <signal.h>
 9: #include <sys/time.h>
10:
11:
12: void catch_signal (int ignored) {
13:     static int iteration=0;
14:
15:     printf("caught interval timer signal, iteration %d
",
16:            iteration++);
17: }
18:
19: pid_t start_timer (int interval) {
20:     pid_t child;
21:     struct itimerval it;
22:     struct sigaction sa;
23:
24:     if (!(child = fork())) {
25:         memset(&sa, 0, sizeof(sa));
26:         sa.sa_handler = catch_signal;
27:         sigemptyset(&sa.sa_mask);
28:         sa.sa_flags = SA_RESTART;
29:
30:         sigaction(SIGALRM, &sa, NULL);
31:
32:         memset(&it, 0, sizeof(it));
33:         it.it_interval.tv_sec = interval;
34:         it.it_value.tv_sec = interval;
35:         setitimer(ITIMER_REAL, &it, NULL);
36:
37:         while (1) pause();
38:     }
39:
40:     return child;
41: }
42:
43: void stop_timer (pid_t child) {
44:     kill (child, SIGTERM);
45: }
46:
47: int main (int argc, const char **argv) {
48:     pid_t timer = 0;
49:
50:     printf("Demonstrating itimers for 10 seconds, "
51:            "please wait...
");
52:     timer = start_timer(1);
53:     sleep(10);
54:     stop_timer(timer);
55:     printf("Done.
");
56:
57:     return 0;
58: }


[1] UTC: Universal Coordinated Time, also sometimes improperly referred to as UCT; roughly equivalent to Greenwich Mean Time (GMT) and Zulu. Time zone designations involve technical detail far beyond the scope of this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.68.121