[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Bug-gnulib] getdate.y question
From: |
Paul Eggert |
Subject: |
Re: [Bug-gnulib] getdate.y question |
Date: |
Fri, 29 Oct 2004 14:00:26 -0700 |
User-agent: |
Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux) |
Bruno Haible <address@hidden> writes:
> The getdate module thus should depend on the 'setenv' module. But I don't see
> a #include "setenv.h".
Thanks for catching that. I fixed that, and twiddled a few other things,
and installed the following into gnulib.
This patch adds a file doc/getdate.texi, which documents the module.
2004-10-29 Paul Eggert <address@hidden>
* doc/getdate.texi: New file, from coreutils with modifications for
the new TZ parsing.
* modules/getdate (Files): Add doc/getdate.texi.
(Depends-on): Add setenv, xalloc.
* lib/getdate.y: Add support for TZ="foo" within a date string.
Fix some bugs near time_t boundaries. Reject dates with
out-of-range components, e.g., "Sept 31".
Include <stdlib.h>, "setenv.h", "xalloc.h".
(ISDIGIT_LOCALE): Remove; unused.
Note that the TZ and time functions used here are not reentrant.
(mktime_ok, get_tz): New functions.
(TZBUFSIZE): New constant.
(get_date): Parse leading TZ="foo". Reject out-of-range components;.
This requires that we sometimes generate our own TZ="XXX..." setting.
Index: modules/getdate
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/getdate,v
retrieving revision 1.8
diff -p -u -r1.8 getdate
--- modules/getdate 4 Oct 2004 20:17:39 -0000 1.8
+++ modules/getdate 29 Oct 2004 20:56:48 -0000
@@ -2,6 +2,7 @@ Description:
Convert a date/time string to linear time.
Files:
+doc/getdate.texi
lib/getdate.h
lib/getdate.y
m4/bison.m4
@@ -13,6 +14,8 @@ timespec
stdbool
gettime
mktime
+setenv
+xalloc
alloca
configure.ac:
Index: lib/getdate.y
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/getdate.y,v
retrieving revision 1.85
diff -p -u -r1.85 getdate.y
--- lib/getdate.y 25 Oct 2004 05:50:16 -0000 1.85
+++ lib/getdate.y 29 Oct 2004 20:56:48 -0000
@@ -22,16 +22,13 @@
<address@hidden> and Jim Berets <address@hidden> in August, 1990.
Modified by Paul Eggert <address@hidden> in August 1999 to do
- the right thing about local DST, and in February 2004 to support
- nanosecond-resolution time stamps. Unlike previous versions, this
- version is reentrant. */
+ the right thing about local DST. Also modified by Paul Eggert
+ <address@hidden> in February 2004 to support
+ nanosecond-resolution time stamps, and in October 2004 to support
+ TZ strings in dates. */
/* FIXME: Check for arithmetic overflow in all cases, not just
- some of them.
-
- FIXME: The current code uses 'int' to count seconds; it should use
- something like 'intmax_t' to support time stamps that don't fit in
- 32 bits. */
+ some of them. */
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -53,6 +50,11 @@
#include <ctype.h>
#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "setenv.h"
+#include "xalloc.h"
#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
# define IN_CTYPE_DOMAIN(c) 1
@@ -63,19 +65,16 @@
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
-#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
-/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
+/* ISDIGIT differs from isdigit, as follows:
- Its arg may be any int or unsigned int; it need not be an unsigned char.
- It's guaranteed to evaluate its argument exactly once.
- It's typically faster.
POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
- ISDIGIT_LOCALE unless it's important to use the locale's definition
+ isdigit unless it's important to use the locale's definition
of `digit' even when the host does not conform to POSIX. */
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
-#include <string.h>
-
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
# define __attribute__(x)
#endif
@@ -167,7 +166,8 @@ static int yyerror (parser_control *, ch
%}
-/* We want a reentrant parser. */
+/* We want a reentrant parser, even if the TZ manipulation and the calls to
+ localtime and gmtime are not reentrant. */
%pure-parser
%parse-param { parser_control *pc }
%lex-param { parser_control *pc }
@@ -990,6 +990,51 @@ yyerror (parser_control *pc ATTRIBUTE_UN
return 0;
}
+/* If *TM0 is the old and *TM1 is the new value of a struct tm after
+ passing it to mktime, return true if it's OK that mktime returned T.
+ It's not OK if *TM0 has out-of-range members. */
+
+static bool
+mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
+{
+ if (t == (time_t) -1)
+ {
+ /* Guard against falsely reporting an error when parsing a time
+ stamp that happens to equal (time_t) -1, on a host that
+ supports such a time stamp. */
+ tm1 = localtime (&t);
+ if (!tm1)
+ return false;
+ }
+
+ return ! ((tm0->tm_sec ^ tm1->tm_sec)
+ | (tm0->tm_min ^ tm1->tm_min)
+ | (tm0->tm_hour ^ tm1->tm_hour)
+ | (tm0->tm_mday ^ tm1->tm_mday)
+ | (tm0->tm_mon ^ tm1->tm_mon)
+ | (tm0->tm_year ^ tm1->tm_year));
+}
+
+/* A reasonable upper bound for the size of ordinary TZ strings.
+ Use heap allocation if TZ's length exceeds this. */
+enum { TZBUFSIZE = 100 };
+
+/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
+ otherwise. */
+static char *
+get_tz (char tzbuf[TZBUFSIZE])
+{
+ char *tz = getenv ("TZ");
+ if (tz)
+ {
+ size_t tzsize = strlen (tz) + 1;
+ tz = (tzsize <= TZBUFSIZE
+ ? memcpy (tzbuf, tz, tzsize)
+ : xmemdup (tz, tzsize));
+ }
+ return tz;
+}
+
/* Parse a date/time string, storing the resulting time value into *RESULT.
The string itself is pointed to by P. Return true if successful.
P can be an incomplete or relative time specification; if so, use
@@ -1004,6 +1049,11 @@ get_date (struct timespec *result, char
struct tm tm0;
parser_control pc;
struct timespec gettime_buffer;
+ unsigned char c;
+ bool tz_was_altered = false;
+ char *tz0 = NULL;
+ char tz0buf[TZBUFSIZE];
+ bool ok = true;
if (! now)
{
@@ -1019,6 +1069,44 @@ get_date (struct timespec *result, char
if (! tmp)
return false;
+ while (c = *p, ISSPACE (c))
+ p++;
+
+ if (strncmp (p, "TZ=\"", 4) == 0)
+ {
+ char const *tzbase = p + 4;
+ size_t tzsize = 1;
+ char const *s;
+
+ for (s = tzbase; *s; s++, tzsize++)
+ if (*s == '\\')
+ {
+ s++;
+ if (! (*s == '\\' || *s == '"'))
+ break;
+ }
+ else if (*s == '"')
+ {
+ char *z;
+ char *tz1;
+ char tz1buf[TZBUFSIZE];
+ bool large_tz = TZBUFSIZE < tzsize;
+ bool setenv_ok;
+ tz0 = get_tz (tz0buf);
+ z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
+ for (s = tzbase; *s != '"'; s++)
+ *z++ = *(s += *s == '\\');
+ *z = '\0';
+ setenv_ok = setenv ("TZ", tz1, 1) == 0;
+ if (large_tz)
+ free (tz1);
+ if (!setenv_ok)
+ goto fail;
+ tz_was_altered = true;
+ p = s + 1;
+ }
+ }
+
pc.input = p;
pc.year.value = tmp->tm_year;
pc.year.value += TM_YEAR_BASE;
@@ -1106,142 +1194,173 @@ get_date (struct timespec *result, char
}
if (yyparse (&pc) != 0)
- return false;
+ goto fail;
if (pc.timespec_seen)
- {
- *result = pc.seconds;
- return true;
- }
-
- if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
- || 1 < (pc.local_zones_seen + pc.zones_seen)
- || (pc.local_zones_seen && 1 < pc.local_isdst))
- return false;
-
- tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
- tm.tm_mon = pc.month - 1 + pc.rel_month;
- tm.tm_mday = pc.day + pc.rel_day;
- if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
- {
- tm.tm_hour = to_hour (pc.hour, pc.meridian);
- if (tm.tm_hour < 0)
- return false;
- tm.tm_min = pc.minutes;
- tm.tm_sec = pc.seconds.tv_sec;
- }
+ *result = pc.seconds;
else
{
- tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- pc.seconds.tv_nsec = 0;
- }
-
- /* Let mktime deduce tm_isdst if we have an absolute time stamp,
- or if the relative time stamp mentions days, months, or years. */
- if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
- | pc.rel_month | pc.rel_year)
- tm.tm_isdst = -1;
-
- /* But if the input explicitly specifies local time with or without
- DST, give mktime that information. */
- if (pc.local_zones_seen)
- tm.tm_isdst = pc.local_isdst;
+ if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
+ || 1 < (pc.local_zones_seen + pc.zones_seen)
+ || (pc.local_zones_seen && 1 < pc.local_isdst))
+ goto fail;
+
+ tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
+ tm.tm_mon = pc.month - 1;
+ tm.tm_mday = pc.day;
+ if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
+ {
+ tm.tm_hour = to_hour (pc.hour, pc.meridian);
+ if (tm.tm_hour < 0)
+ goto fail;
+ tm.tm_min = pc.minutes;
+ tm.tm_sec = pc.seconds.tv_sec;
+ }
+ else
+ {
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ pc.seconds.tv_nsec = 0;
+ }
- tm0 = tm;
+ /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
+ if (pc.dates_seen | pc.days_seen | pc.times_seen)
+ tm.tm_isdst = -1;
+
+ /* But if the input explicitly specifies local time with or without
+ DST, give mktime that information. */
+ if (pc.local_zones_seen)
+ tm.tm_isdst = pc.local_isdst;
- Start = mktime (&tm);
+ tm0 = tm;
- if (Start == (time_t) -1)
- {
+ Start = mktime (&tm);
- /* Guard against falsely reporting errors near the time_t boundaries
- when parsing times in other time zones. For example, if the min
- time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
- of UTC, then the min localtime value is 1970-01-01 08:00:00; if
- we apply mktime to 1970-01-01 00:00:00 we will get an error, so
- we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
- zone by 24 hours to compensate. This algorithm assumes that
- there is no DST transition within a day of the time_t boundaries. */
- if (pc.zones_seen)
+ if (! mktime_ok (&tm0, &tm, Start))
{
- tm = tm0;
- if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
- {
- tm.tm_mday++;
- pc.time_zone += 24 * 60;
- }
+ if (! pc.zones_seen)
+ goto fail;
else
{
- tm.tm_mday--;
- pc.time_zone -= 24 * 60;
+ /* Guard against falsely reporting errors near the time_t
+ boundaries when parsing times in other time zones. For
+ example, suppose the input string "1969-12-31 23:00:00 -0100",
+ the current time zone is 8 hours ahead of UTC, and the min
+ time_t value is 1970-01-01 00:00:00 UTC. Then the min
+ localtime value is 1970-01-01 08:00:00, and mktime will
+ therefore fail on 1969-12-31 23:00:00. To work around the
+ problem, set the time zone to 1 hour behind UTC temporarily
+ by setting TZ="XXX1:00" and try mktime again. */
+
+ long int time_zone = pc.time_zone;
+ long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
+ long int abs_time_zone_hour = abs_time_zone / 60;
+ int abs_time_zone_min = abs_time_zone % 60;
+ char tz1buf[sizeof "XXX+0:00"
+ + sizeof pc.time_zone * CHAR_BIT / 3];
+ if (!tz_was_altered)
+ tz0 = get_tz (tz0buf);
+ sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
+ abs_time_zone_hour, abs_time_zone_min);
+ if (setenv ("TZ", tz1buf, 1) != 0)
+ goto fail;
+ tz_was_altered = true;
+ tm = tm0;
+ Start = mktime (&tm);
+ if (! mktime_ok (&tm0, &tm, Start))
+ goto fail;
}
- Start = mktime (&tm);
}
- if (Start == (time_t) -1)
- return false;
- }
-
- if (pc.days_seen && ! pc.dates_seen)
- {
- tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
- + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
- tm.tm_isdst = -1;
- Start = mktime (&tm);
- if (Start == (time_t) -1)
- return false;
- }
+ if (pc.days_seen && ! pc.dates_seen)
+ {
+ tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
+ + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
+ tm.tm_isdst = -1;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ goto fail;
+ }
- if (pc.zones_seen)
- {
- long int delta = pc.time_zone * 60;
- time_t t1;
+ if (pc.zones_seen)
+ {
+ long int delta = pc.time_zone * 60;
+ time_t t1;
#ifdef HAVE_TM_GMTOFF
- delta -= tm.tm_gmtoff;
+ delta -= tm.tm_gmtoff;
#else
- time_t t = Start;
- struct tm const *gmt = gmtime (&t);
- if (! gmt)
- return false;
- delta -= tm_diff (&tm, gmt);
+ time_t t = Start;
+ struct tm const *gmt = gmtime (&t);
+ if (! gmt)
+ goto fail;
+ delta -= tm_diff (&tm, gmt);
#endif
- t1 = Start - delta;
- if ((Start < t1) != (delta < 0))
- return false; /* time_t overflow */
- Start = t1;
+ t1 = Start - delta;
+ if ((Start < t1) != (delta < 0))
+ goto fail; /* time_t overflow */
+ Start = t1;
+ }
+
+ /* Add relative date. */
+ if (pc.rel_year | pc.rel_month | pc.rel_day)
+ {
+ int year = tm.tm_year + pc.rel_year;
+ int month = tm.tm_mon + pc.rel_month;
+ int day = tm.tm_mday + pc.rel_day;
+ if (((year < tm.tm_year) ^ (pc.rel_year < 0))
+ | (month < tm.tm_mon) ^ (pc.rel_month < 0)
+ | (day < tm.tm_mday) ^ (pc.rel_day < 0))
+ goto fail;
+ tm.tm_year = year;
+ tm.tm_mon = month;
+ tm.tm_mday = day;
+ Start = mktime (&tm);
+ if (Start == (time_t) -1)
+ goto fail;
+ }
+
+ /* Add relative hours, minutes, and seconds. On hosts that support
+ leap seconds, ignore the possibility of leap seconds; e.g.,
+ "+ 10 minutes" adds 600 seconds, even if one of them is a
+ leap second. Typically this is not what the user wants, but it's
+ too hard to do it the other way, because the time zone indicator
+ must be applied before relative times, and if mktime is applied
+ again the time zone will be lost. */
+ {
+ long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
+ long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
+ time_t t0 = Start;
+ long int d1 = 60 * 60 * pc.rel_hour;
+ time_t t1 = t0 + d1;
+ long int d2 = 60 * pc.rel_minutes;
+ time_t t2 = t1 + d2;
+ long int d3 = pc.rel_seconds;
+ time_t t3 = t2 + d3;
+ long int d4 = (sum_ns - normalized_ns) / BILLION;
+ time_t t4 = t3 + d4;
+
+ if ((d1 / (60 * 60) ^ pc.rel_hour)
+ | (d2 / 60 ^ pc.rel_minutes)
+ | ((t1 < t0) ^ (d1 < 0))
+ | ((t2 < t1) ^ (d2 < 0))
+ | ((t3 < t2) ^ (d3 < 0))
+ | ((t4 < t3) ^ (d4 < 0)))
+ goto fail;
+
+ result->tv_sec = t4;
+ result->tv_nsec = normalized_ns;
+ }
}
- /* Add relative hours, minutes, and seconds. Ignore leap seconds;
- i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
- leap second. Typically this is not what the user wants, but it's
- too hard to do it the other way, because the time zone indicator
- must be applied before relative times, and if mktime is applied
- again the time zone will be lost. */
- {
- long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns;
- long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
- time_t t0 = Start;
- long int d1 = 60 * 60 * pc.rel_hour;
- time_t t1 = t0 + d1;
- long int d2 = 60 * pc.rel_minutes;
- time_t t2 = t1 + d2;
- long int d3 = pc.rel_seconds;
- time_t t3 = t2 + d3;
- long int d4 = (sum_ns - normalized_ns) / BILLION;
- time_t t4 = t3 + d4;
-
- if ((d1 / (60 * 60) ^ pc.rel_hour)
- | (d2 / 60 ^ pc.rel_minutes)
- | ((t1 < t0) ^ (d1 < 0))
- | ((t2 < t1) ^ (d2 < 0))
- | ((t3 < t2) ^ (d3 < 0))
- | ((t4 < t3) ^ (d4 < 0)))
- return false;
-
- result->tv_sec = t4;
- result->tv_nsec = normalized_ns;
- return true;
- }
+ goto done;
+
+ fail:
+ ok = false;
+ done:
+ if (tz_was_altered)
+ ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
+ if (tz0 != tz0buf)
+ free (tz0);
+ return ok;
}
#if TEST
--- /dev/null 2003-03-18 13:55:57 -0800
+++ doc/getdate.texi 2004-10-29 13:47:12 -0700
@@ -0,0 +1,536 @@
address@hidden GNU date syntax documentation
+
address@hidden Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2002,
address@hidden 2003, 2004 Free Software Foundation, Inc.
+
address@hidden Permission is granted to copy, distribute and/or modify this
document
address@hidden under the terms of the GNU Free Documentation License, Version
1.1 or
address@hidden any later version published by the Free Software Foundation;
with no
address@hidden Invariant Sections, with no Front-Cover Texts, and with no
Back-Cover
address@hidden Texts. A copy of the license is included in the ``GNU Free
address@hidden Documentation License'' file as part of this distribution.
+
address@hidden Date input formats
address@hidden Date input formats
+
address@hidden date input formats
address@hidden get_date
+
+First, a quote:
+
address@hidden
+Our units of temporal measurement, from seconds on up to months, are so
+complicated, asymmetrical and disjunctive so as to make coherent mental
+reckoning in time all but impossible. Indeed, had some tyrannical god
+contrived to enslave our minds to time, to make it all but impossible
+for us to escape subjection to sodden routines and unpleasant surprises,
+he could hardly have done better than handing down our present system.
+It is like a set of trapezoidal building blocks, with no vertical or
+horizontal surfaces, like a language in which the simplest thought
+demands ornate constructions, useless particles and lengthy
+circumlocutions. Unlike the more successful patterns of language and
+science, which enable us to face experience boldly or at least
+level-headedly, our system of temporal calculation silently and
+persistently encourages our terror of time.
+
address@hidden It is as though architects had to measure length in feet, width
+in meters and height in ells; as though basic instruction manuals
+demanded a knowledge of five different languages. It is no wonder then
+that we often look into our own immediate past or future, last Tuesday
+or a week from Sunday, with feelings of helpless confusion. @dots{}
+
+--- Robert Grudin, @cite{Time and the Art of Living}.
address@hidden quotation
+
+This section describes the textual date representations that @sc{gnu}
+programs accept. These are the strings you, as a user, can supply as
+arguments to the various programs. The C interface (via the
address@hidden function) is not described here.
+
address@hidden
+* General date syntax:: Common rules.
+* Calendar date items:: 19 Dec 1994.
+* Time of day items:: 9:20pm.
+* Time zone items:: @sc{est}, @sc{pdt}, @sc{gmt}.
+* Day of week items:: Monday and others.
+* Relative items in date strings:: next tuesday, 2 years ago.
+* Pure numbers in date strings:: 19931219, 1440.
+* Seconds since the Epoch:: @@1078100502.
+* Specifying time zone rules:: TZ="America/New_York", TZ="UTC0".
+* Authors of get_date:: Bellovin, Eggert, Salz, Berets, et al.
address@hidden menu
+
+
address@hidden General date syntax
address@hidden General date syntax
+
address@hidden general date syntax
+
address@hidden items in date strings
+A @dfn{date} is a string, possibly empty, containing many items
+separated by whitespace. The whitespace may be omitted when no
+ambiguity arises. The empty string means the beginning of today (i.e.,
+midnight). Order of the items is immaterial. A date string may contain
+many flavors of items:
+
address@hidden @bullet
address@hidden calendar date items
address@hidden time of day items
address@hidden time zone items
address@hidden day of the week items
address@hidden relative items
address@hidden pure numbers.
address@hidden itemize
+
address@hidden We describe each of these item types in turn, below.
+
address@hidden numbers, written-out
address@hidden ordinal numbers
address@hidden first @r{in date strings}
address@hidden next @r{in date strings}
address@hidden last @r{in date strings}
+A few numbers may be written out in words in most contexts. This is
+most useful for specifying day of the week items or relative items (see
+below). Here is the list: @samp{first} for 1, @samp{next} for 2,
address@hidden for 3, @samp{fourth} for 4, @samp{fifth} for 5,
address@hidden for 6, @samp{seventh} for 7, @samp{eighth} for 8,
address@hidden for 9, @samp{tenth} for 10, @samp{eleventh} for 11 and
address@hidden for 12. Also, @samp{last} means exactly @math{-1}.
+
address@hidden months, written-out
+When a month is written this way, it is still considered to be written
+numerically, instead of being ``spelled in full''; this changes the
+allowed strings.
+
address@hidden language, in dates
+In the current implementation, only English is supported for words and
+abbreviations like @samp{AM}, @samp{DST}, @samp{EST}, @samp{first},
address@hidden, @samp{Sunday}, @samp{tomorrow}, and @samp{year}.
+
address@hidden language, in dates
address@hidden time zone item
+The output of the @command{date} command
+is not always acceptable as a date string,
+not only because of the language problem, but also because there is no
+standard meaning for time zone items like @samp{IST}. When using
address@hidden to generate a date string intended to be parsed later,
+specify a date format that is independent of language and that does not
+use time zone items other than @samp{UTC} and @samp{Z}. Here are some
+ways to do this:
+
address@hidden
+$ LC_ALL=C TZ=UTC0 date
+Mon Mar 1 00:21:42 UTC 2004
+$ TZ=UTC0 date +'%Y-%m-%d %H:%M:%SZ'
+2004-03-01 00:21:42Z
+$ date --iso-8601=ns # a GNU extension
+2004-02-29T16:21:42,692722128-0800
+$ date --rfc-2822 # a GNU extension
+Sun, 29 Feb 2004 16:21:42 -0800
+$ date +'%Y-%m-%d %H:%M:%S %z' # %z is a GNU extension.
+2004-02-29 16:21:42 -0800
+$ date +'@@%s.%N' # %s and %N are GNU extensions.
+@@1078100502.692722128
address@hidden example
+
address@hidden case, ignored in dates
address@hidden comments, in dates
+Alphabetic case is completely ignored in dates. Comments may be introduced
+between round parentheses, as long as included parentheses are properly
+nested. Hyphens not followed by a digit are currently ignored. Leading
+zeros on numbers are ignored.
+
+
address@hidden Calendar date items
address@hidden Calendar date items
+
address@hidden calendar date item
+
+A @dfn{calendar date item} specifies a day of the year. It is
+specified differently, depending on whether the month is specified
+numerically or literally. All these strings specify the same calendar date:
+
address@hidden
+1972-09-24 # @sc{iso} 8601.
+72-9-24 # Assume 19xx for 69 through 99,
+ # 20xx for 00 through 68.
+72-09-24 # Leading zeros are ignored.
+9/24/72 # Common U.S. writing.
+24 September 1972
+24 Sept 72 # September has a special abbreviation.
+24 Sep 72 # Three-letter abbreviations always allowed.
+Sep 24, 1972
+24-sep-72
+24sep72
address@hidden example
+
+The year can also be omitted. In this case, the last specified year is
+used, or the current year if none. For example:
+
address@hidden
+9/24
+sep 24
address@hidden example
+
+Here are the rules.
+
address@hidden @sc{iso} 8601 date format
address@hidden date format, @sc{iso} 8601
+For numeric months, the @sc{iso} 8601 format
address@hidden@address@hidden@var{day}} is allowed, where @var{year} is
+any positive number, @var{month} is a number between 01 and 12, and
address@hidden is a number between 01 and 31. A leading zero must be present
+if a number is less than ten. If @var{year} is 68 or smaller, then 2000
+is added to it; otherwise, if @var{year} is less than 100,
+then 1900 is added to it. The construct
address@hidden@var{month}/@var{day}/@var{year}}, popular in the United States,
+is accepted. Also @address@hidden/@var{day}}, omitting the year.
+
address@hidden month names in date strings
address@hidden abbreviations for months
+Literal months may be spelled out in full: @samp{January},
address@hidden, @samp{March}, @samp{April}, @samp{May}, @samp{June},
address@hidden, @samp{August}, @samp{September}, @samp{October},
address@hidden or @samp{December}. Literal months may be abbreviated
+to their first three letters, possibly followed by an abbreviating dot.
+It is also permitted to write @samp{Sept} instead of @samp{September}.
+
+When months are written literally, the calendar date may be given as any
+of the following:
+
address@hidden
address@hidden @var{month} @var{year}
address@hidden @var{month}
address@hidden @var{day} @var{year}
address@hidden@address@hidden
address@hidden example
+
+Or, omitting the year:
+
address@hidden
address@hidden @var{day}
address@hidden example
+
+
address@hidden Time of day items
address@hidden Time of day items
+
address@hidden time of day item
+
+A @dfn{time of day item} in date strings specifies the time on a given
+day. Here are some examples, all of which represent the same time:
+
address@hidden
+20:02:00.000000
+20:02
+8:02pm
+20:02-0500 # In @sc{est} (U.S. Eastern Standard Time).
address@hidden example
+
+More generally, the time of day may be given as
address@hidden@var{hour}:@var{minute}:@var{second}}, where @var{hour} is
+a number between 0 and 23, @var{minute} is a number between 0 and
+59, and @var{second} is a number between 0 and 59 possibly followed by
address@hidden or @samp{,} and a fraction containing one or more digits.
+Alternatively,
address@hidden:@var{second}} can be omitted, in which case it is taken to
+be zero.
+
address@hidden am @r{in date strings}
address@hidden pm @r{in date strings}
address@hidden midnight @r{in date strings}
address@hidden noon @r{in date strings}
+If the time is followed by @samp{am} or @samp{pm} (or @samp{a.m.}
+or @samp{p.m.}), @var{hour} is restricted to run from 1 to 12, and
address@hidden:@var{minute}} may be omitted (taken to be zero). @samp{am}
+indicates the first half of the day, @samp{pm} indicates the second
+half of the day. In this notation, 12 is the predecessor of 1:
+midnight is @samp{12am} while noon is @samp{12pm}.
+(This is the zero-oriented interpretation of @samp{12am} and @samp{12pm},
+as opposed to the old tradition derived from Latin
+which uses @samp{12m} for noon and @samp{12pm} for midnight.)
+
address@hidden time zone correction
address@hidden minutes, time zone correction by
+The time may alternatively be followed by a time zone correction,
+expressed as @address@hidden@address@hidden, where @var{s} is @samp{+}
+or @samp{-}, @var{hh} is a number of zone hours and @var{mm} is a number
+of zone minutes. When a time zone correction is given this way, it
+forces interpretation of the time relative to
+Coordinated Universal Time (@sc{utc}), overriding any previous
+specification for the time zone or the local time zone. The @var{minute}
+part of the time of day may not be elided when a time zone correction
+is used. This is the best way to specify a time zone correction by
+fractional parts of an hour.
+
+Either @samp{am}/@samp{pm} or a time zone correction may be specified,
+but not both.
+
+
address@hidden Time zone items
address@hidden Time zone items
+
address@hidden time zone item
+
+A @dfn{time zone item} specifies an international time zone, indicated
+by a small set of letters, e.g., @samp{UTC} or @samp{Z}
+for Coordinated Universal
+Time. Any included periods are ignored. By following a
+non-daylight-saving time zone by the string @samp{DST} in a separate
+word (that is, separated by some white space), the corresponding
+daylight saving time zone may be specified.
+
+Time zone items other than @samp{UTC} and @samp{Z}
+are obsolescent and are not recommended, because they
+are ambiguous; for example, @samp{EST} has a different meaning in
+Australia than in the United States. Instead, it's better to use
+unambiguous numeric time zone corrections like @samp{-0500}, as
+described in the previous section.
+
+If neither a time zone item nor a time zone correction is supplied,
+time stamps are interpreted using the rules of the default time zone
+(@pxref{Specifying time zone rules}).
+
+
address@hidden Day of week items
address@hidden Day of week items
+
address@hidden day of week item
+
+The explicit mention of a day of the week will forward the date
+(only if necessary) to reach that day of the week in the future.
+
+Days of the week may be spelled out in full: @samp{Sunday},
address@hidden, @samp{Tuesday}, @samp{Wednesday}, @samp{Thursday},
address@hidden or @samp{Saturday}. Days may be abbreviated to their
+first three letters, optionally followed by a period. The special
+abbreviations @samp{Tues} for @samp{Tuesday}, @samp{Wednes} for
address@hidden and @samp{Thur} or @samp{Thurs} for @samp{Thursday} are
+also allowed.
+
address@hidden next @var{day}
address@hidden last @var{day}
+A number may precede a day of the week item to move forward
+supplementary weeks. It is best used in expression like @samp{third
+monday}. In this context, @samp{last @var{day}} or @samp{next
address@hidden is also acceptable; they move one week before or after
+the day that @var{day} by itself would represent.
+
+A comma following a day of the week item is ignored.
+
+
address@hidden Relative items in date strings
address@hidden Relative items in date strings
+
address@hidden relative items in date strings
address@hidden displacement of dates
+
address@hidden items} adjust a date (or the current date if none) forward
+or backward. The effects of relative items accumulate. Here are some
+examples:
+
address@hidden
+1 year
+1 year ago
+3 years
+2 days
address@hidden example
+
address@hidden year @r{in date strings}
address@hidden month @r{in date strings}
address@hidden fortnight @r{in date strings}
address@hidden week @r{in date strings}
address@hidden day @r{in date strings}
address@hidden hour @r{in date strings}
address@hidden minute @r{in date strings}
+The unit of time displacement may be selected by the string @samp{year}
+or @samp{month} for moving by whole years or months. These are fuzzy
+units, as years and months are not all of equal duration. More precise
+units are @samp{fortnight} which is worth 14 days, @samp{week} worth 7
+days, @samp{day} worth 24 hours, @samp{hour} worth 60 minutes,
address@hidden or @samp{min} worth 60 seconds, and @samp{second} or
address@hidden worth one second. An @samp{s} suffix on these units is
+accepted and ignored.
+
address@hidden ago @r{in date strings}
+The unit of time may be preceded by a multiplier, given as an optionally
+signed number. Unsigned numbers are taken as positively signed. No
+number at all implies 1 for a multiplier. Following a relative item by
+the string @samp{ago} is equivalent to preceding the unit by a
+multiplier with value @math{-1}.
+
address@hidden day @r{in date strings}
address@hidden tomorrow @r{in date strings}
address@hidden yesterday @r{in date strings}
+The string @samp{tomorrow} is worth one day in the future (equivalent
+to @samp{day}), the string @samp{yesterday} is worth
+one day in the past (equivalent to @samp{day ago}).
+
address@hidden now @r{in date strings}
address@hidden today @r{in date strings}
address@hidden this @r{in date strings}
+The strings @samp{now} or @samp{today} are relative items corresponding
+to zero-valued time displacement, these strings come from the fact
+a zero-valued time displacement represents the current time when not
+otherwise changed by previous items. They may be used to stress other
+items, like in @samp{12:00 today}. The string @samp{this} also has
+the meaning of a zero-valued time displacement, but is preferred in
+date strings like @samp{this thursday}.
+
+When a relative item causes the resulting date to cross a boundary
+where the clocks were adjusted, typically for daylight saving time,
+the resulting date and time are adjusted accordingly.
+
+The fuzz in units can cause problems with relative items. For
+example, @samp{2003-07-31 -1 month} might evaluate to 2003-07-01,
+because 2003-06-31 is an invalid date. To determine the previous
+month more reliably, you can ask for the month before the 15th of the
+current month. For example:
+
address@hidden
+$ date -R
+Thu, 31 Jul 2003 13:02:39 -0700
+$ date --date='-1 month' +'Last month was %B?'
+Last month was July?
+$ date --date="$(date +%Y-%m-15) -1 month" +'Last month was %B!'
+Last month was June!
address@hidden example
+
+Also, take care when manipulating dates around clock changes such as
+daylight saving leaps. In a few cases these have added or subtracted
+as much as 24 hours from the clock, so it is often wise to adopt
+universal time by setting the @env{TZ} environment variable to
address@hidden before embarking on calendrical calculations.
+
address@hidden Pure numbers in date strings
address@hidden Pure numbers in date strings
+
address@hidden pure numbers in date strings
+
+The precise interpretation of a pure decimal number depends
+on the context in the date string.
+
+If the decimal number is of the form @address@hidden@var{dd} and no
+other calendar date item (@pxref{Calendar date items}) appears before it
+in the date string, then @var{yyyy} is read as the year, @var{mm} as the
+month number and @var{dd} as the day of the month, for the specified
+calendar date.
+
+If the decimal number is of the form @address@hidden and no other time
+of day item appears before it in the date string, then @var{hh} is read
+as the hour of the day and @var{mm} as the minute of the hour, for the
+specified time of day. @var{mm} can also be omitted.
+
+If both a calendar date and a time of day appear to the left of a number
+in the date string, but no relative item, then the number overrides the
+year.
+
+
address@hidden Seconds since the Epoch
address@hidden Seconds since the Epoch
+
+If you precede a number with @samp{@@}, it represents an internal time
+stamp as a count of seconds. The number can contain an internal
+decimal point (either @samp{.} or @samp{,}); any excess precision not
+supported by the internal representation is truncated toward minus
+infinity. Such a number cannot be combined with any other date
+item, as it specifies a complete time stamp.
+
address@hidden beginning of time, for @acronym{POSIX}
address@hidden epoch, for @acronym{POSIX}
+Internally, computer times are represented as a count of seconds since
+an epoch---a well-defined point of time. On @acronym{GNU} and
address@hidden systems, the epoch is 1970-01-01 00:00:00 @sc{utc}, so
address@hidden@@0} represents this time, @samp{@@1} represents 1970-01-01
+00:00:01 @sc{utc}, and so forth. @acronym{GNU} and most other
address@hidden systems support such times as an extension
+to @acronym{POSIX}, using negative counts, so that @samp{@@-1}
+represents 1969-12-31 23:59:59 @sc{utc}.
+
+Traditional Unix systems count seconds with 32-bit two's-complement
+integers and can represent times from 1901-12-13 20:45:52 through
+2038-01-19 03:14:07 @sc{utc}. More modern systems use 64-bit counts
+of seconds with nanosecond subcounts, and can represent all the times
+in the known lifetime of the universe to a resolution of 1 nanosecond.
+
+On most systems, these counts ignore the presence of leap seconds.
+For example, on most systems @samp{@@915148799} represents 1998-12-31
+23:59:59 @sc{utc}, @samp{@@915148800} represents 1999-01-01 00:00:00
address@hidden, and there is no way to represent the intervening leap second
+1998-12-31 23:59:60 @sc{utc}.
+
address@hidden Specifying time zone rules
address@hidden Specifying time zone rules
+
address@hidden TZ
+Normally, dates are interpreted using the rules of the current time
+zone, which in turn are specified by the @env{TZ} environment
+variable, or by a system default if @env{TZ} is not set. To specify a
+different set of default time zone rules that apply just to one date,
+start the date with a string of the form @samp{TZ="@var{rule}"}. The
+two quote characters (@samp{"}) must be present in the date, and any
+quotes or backslashes within @var{rule} must be escaped by a
+backslash.
+
+For example, with the @acronym{GNU} @command{date} command you can
+answer the question ``What time is it in New York when a Paris clock
+shows 6:30am on October 31, 2004?'' by using a date beginning with
address@hidden"Europe/Paris"} as shown in the following shell transcript:
+
address@hidden
+$ export TZ="America/New_York"
+$ date --date='TZ="Europe/Paris" 2004-10-31 06:30'
+Sun Oct 31 01:30:00 EDT 2004
address@hidden example
+
+In this example, the @option{--date} operand begins with its own
address@hidden setting, so the rest of that operand is processed according
+to @samp{Europe/Paris} rules, treating the string @samp{2004-10-31
+06:30} as if it were in Paris. However, since the output of the
address@hidden command is processed according to the overall time zone
+rules, it uses New York time. (Paris was normally six hours ahead of
+New York in 2004, but this example refers to a brief Halloween period
+when the gap was five hours.)
+
+A @env{TZ} value is a rule that typically names a location in the
address@hidden://www.twinsun.com/tz/tz-link.htm, @samp{tz} database}.
+A recent catalog of location names appears in the
address@hidden://twiki.org/cgi-bin/xtra/tzdate, TWiki Date and Time
+Gateway}. A few address@hidden hosts require a colon before a
+location name in a @env{TZ} setting, e.g.,
address@hidden":America/New_York"}.
+
+The @samp{tz} database includes a wide variety of locations ranging
+from @samp{Arctic/Longyearbyen} to @samp{Antarctica/South_Pole}, but
+if you are at sea and have your own private time zone, or if you are
+using a address@hidden host that does not support the @samp{tz}
+database, you may need to use a @acronym{POSIX} rule instead. Simple
address@hidden rules like @samp{UTC0} specify a time zone without
+daylight saving time; other rules can specify simple daylight saving
+regimes. @xref{TZ Variable,, Specifying the Time Zone with @code{TZ},
+libc, The GNU C Library}.
+
address@hidden Authors of get_date
address@hidden Authors of @code{get_date}
+
address@hidden authors of @code{get_date}
+
address@hidden Bellovin, Steven M.
address@hidden Salz, Rich
address@hidden Berets, Jim
address@hidden MacKenzie, David
address@hidden Meyering, Jim
address@hidden Eggert, Paul
address@hidden was originally implemented by Steven M. Bellovin
+(@email{smb@@research.att.com}) while at the University of North Carolina
+at Chapel Hill. The code was later tweaked by a couple of people on
+Usenet, then completely overhauled by Rich $alz (@email{rsalz@@bbn.com})
+and Jim Berets (@email{jberets@@bbn.com}) in August, 1990. Various
+revisions for the @sc{gnu} system were made by David MacKenzie, Jim Meyering,
+Paul Eggert and others.
+
address@hidden Pinard, F.
address@hidden Berry, K.
+This chapter was originally produced by Fran@,{c}ois Pinard
+(@email{pinard@@iro.umontreal.ca}) from the @file{getdate.y} source code,
+and then edited by K.@: Berry (@email{kb@@cs.umb.edu}).