# define _STDLIB_H 1
#endif
-/**
- * ISDIGIT differs from isdigit, as follows:
- * - Its arg may be any int or unsigned int; it need not be an unsigned char
- * or EOF.
- * - It's typically faster.
- * POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
- * 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)
-
/**
* Shift A right by B bits portably, by dividing A by 2**B and
* truncating towards minus infinity. A and B should be free of side
? (a) >> (b) \
: (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
-#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
#define HOUR(x) ((x) * 60)
*/
typedef struct {
int negative;
- long int value;
+ intmax_t value;
size_t digits;
} textint;
/* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
typedef struct {
- long int year;
- long int month;
- long int day;
- long int hour;
- long int minutes;
+ intmax_t year;
+ intmax_t month;
+ intmax_t day;
+ intmax_t hour;
+ intmax_t minutes;
time_t seconds;
- long int ns;
+ int ns;
} relative_time;
#if HAVE_COMPOUND_LITERALS
const char *input;
/* N, if this is the Nth Tuesday. */
- long int day_ordinal;
+ intmax_t day_ordinal;
/* Day of week; Sunday is 0. */
int day_number;
int local_isdst;
/* Time zone, in minutes east of UTC. */
- long int time_zone;
+ int time_zone;
/* Style used for time. */
int meridian;
/* Gregorian year, month, day, hour, minutes, seconds, and ns. */
textint year;
- long int month;
- long int day;
- long int hour;
- long int minutes;
+ intmax_t month;
+ intmax_t day;
+ intmax_t hour;
+ intmax_t minutes;
struct timespec seconds; /* includes nanoseconds */
/* Relative year, month, day, hour, minutes, seconds, and ns. */
size_t dsts_seen;
size_t times_seen;
size_t zones_seen;
- size_t year_seen;
-
- /* 1 if the user specified explicit ordinal day value, */
- int ordinal_day_seen;
/* Table of local time zone abbreviations, null terminated. */
table local_time_zone_table[3];
union YYSTYPE;
static int yylex (union YYSTYPE *, parser_control *);
static int yyerror (parser_control const *, char const *);
-static long int time_zone_hhmm (parser_control *, textint, long int);
+static int time_zone_hhmm (parser_control *, textint, textint);
/**
* Extract into *PC any date and time info from a string of digits
{
if (pc->dates_seen && ! pc->year.digits
&& ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits)) {
- pc->year_seen++;
pc->year = text_int;
} else {
if (4 < text_int.digits) {
/* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
static void
-set_hhmmss(parser_control *pc, long int hour, long int minutes,
- time_t sec, long int nsec)
+set_hhmmss(parser_control *pc, intmax_t hour, intmax_t minutes,
+ time_t sec, int nsec)
{
pc->hour = hour;
pc->minutes = minutes;
%expect 31
%union {
- long int intval;
+ intmax_t intval;
textint textintval;
struct timespec timespec;
relative_time rel;
%token <textintval> tSNUMBER tUNUMBER
%token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
-%type <intval> o_colon_minutes
+%type <textintval> o_colon_minutes
%type <timespec> seconds signed_seconds unsigned_seconds
%type <rel> relunit relunit_snumber dayshift
zone_offset:
tSNUMBER o_colon_minutes {
pc->zones_seen++;
- pc->time_zone = time_zone_hhmm (pc, $1, $2);
+ if (! time_zone_hhmm (pc, $1, $2)) YYABORT;
}
;
apply_relative_time (pc, $2, 1);
}
| tZONE tSNUMBER o_colon_minutes {
- pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3);
+ if (! time_zone_hhmm (pc, $2, $3)) YYABORT;
+ pc->time_zone += $1;
}
| tDAYZONE {
pc->time_zone = $1 + 60;
| tORDINAL tDAY {
pc->day_ordinal = $1;
pc->day_number = $2;
- pc->ordinal_day_seen = 1;
}
| tUNUMBER tDAY {
pc->day_ordinal = $1.value;
pc->day_number = $2;
- pc->ordinal_day_seen = 1;
}
;
o_colon_minutes:
/* empty */
- { $$ = -1; }
- | ':' tUNUMBER
- { $$ = $2.value; }
+ { $$.value = $$.digits = 0; }
+ | ':' tUNUMBER {
+ $$ = $2;
+ }
;
%%
};
/**
- * Convert a time zone expressed as HH:MM into an integer count of
- * minutes. If MM is negative, then S is of the form HHMM and needs
- * to be picked apart; otherwise, S is of the form HH. As specified in
- * http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
- * only valid TZ range, and consider first two digits as hours, if no
- * minutes specified.
+ * Convert a time offset expressed as HH:MM or HHMM into an integer count of
+ * minutes. If hh is more than 2 digits then it is of the form HHMM and must be
+ * delimited; in that case 'mm' is required to be absent. Otherwise, hh and mm
+ * are used ('mm' contains digits that were prefixed with a colon).
+ *
+ * POSIX TZ and ISO 8601 both define the maximum offset as 24:59. POSIX also
+ * allows seconds, but currently the parser rejects them. Both require minutes
+ * to be zero padded (2 digits). ISO requires hours to be zero padded, POSIX
+ * does not, either is accepted; which means an invalid ISO offset could pass.
*/
-static long int time_zone_hhmm(parser_control *pc, textint s, long int mm)
+static int time_zone_hhmm(parser_control *pc, textint hh, textint mm)
{
- long int n_minutes;
-
- /**
- * If the length of S is 1 or 2 and no minutes are specified,
- * interpret it as a number of hours.
- */
- if (s.digits <= 2 && mm < 0)
- s.value *= 100;
-
- if (mm < 0)
- n_minutes = (s.value / 100) * 60 + s.value % 100;
- else
- n_minutes = s.value * 60 + (s.negative ? -mm : mm);
+ int h, m;
+
+ if (hh.digits > 2 && hh.digits < 5 && mm.digits == 0) {
+ h = hh.value / 100;
+ m = hh.value % 100;
+ } else if (hh.digits < 3 && (mm.digits == 0 || mm.digits == 2)) {
+ h = hh.value;
+ m = hh.negative ? -mm.value : mm.value;
+ } else
+ return 0;
- /**
- * If the absolute number of minutes is larger than 24 hours,
- * arrange to reject it by incrementing pc->zones_seen. Thus,
- * we allow only values in the range UTC-24:00 to UTC+24:00.
- */
- if (24 * 60 < abs (n_minutes))
- pc->zones_seen++;
+ if (abs(h) > 24 || abs(m) > 59)
+ return 0;
- return n_minutes;
+ pc->time_zone = h * 60 + m;
+ return 1;
}
-static int to_hour(long int hours, int meridian)
+static int to_hour(intmax_t hours, int meridian)
{
switch (meridian) {
default: /* Pacify GCC. */
static long int to_year(textint textyear)
{
- long int year = textyear.value;
+ intmax_t year = textyear.value;
if (year < 0)
year = -year;
* The body of this function is taken directly from the GNU C Library;
* see src/strftime.c.
*/
-static long int tm_diff(struct tm const *a, struct tm const *b)
+static int tm_diff(struct tm const *a, struct tm const *b)
{
/**
* Compute intervening leap days correctly even if year is negative.
int a400 = SHR (a100, 2);
int b400 = SHR (b100, 2);
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
- long int ayear = a->tm_year;
- long int years = ayear - b->tm_year;
- long int days = (365 * years + intervening_leap_days
+ int years = a->tm_year - b->tm_year;
+ int days = (365 * years + intervening_leap_days
+ (a->tm_yday - b->tm_yday));
return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+ (a->tm_min - b->tm_min))
int abbrev;
/* Make it uppercase. */
- for (p = word; *p; p++) {
- unsigned char ch = *p;
- *p = c_toupper (ch);
- }
+ for (p = word; *p; p++)
+ *p = c_toupper (to_uchar (*p));
for (tp = meridian_table; tp->name; tp++)
if (strcmp (word, tp->name) == 0)
while (c = *pc->input, c_isspace (c))
pc->input++;
- if (ISDIGIT (c) || c == '-' || c == '+') {
+ if (c_isdigit (c) || c == '-' || c == '+') {
char const *p;
int sign;
- unsigned long int value;
+ uintmax_t value;
if (c == '-' || c == '+') {
sign = c == '-' ? -1 : 1;
while (c = *++pc->input, c_isspace (c))
continue;
- if (! ISDIGIT (c))
+ if (! c_isdigit (c))
/* skip the '-' sign */
continue;
} else
sign = 0;
p = pc->input;
for (value = 0; ; value *= 10) {
- unsigned long int value1 = value + (c - '0');
+ uintmax_t value1 = value + (c - '0');
if (value1 < value)
return '?';
value = value1;
c = *++p;
- if (! ISDIGIT (c))
+ if (! c_isdigit (c))
break;
- if (ULONG_MAX / 10 < value)
+ if (UINTMAX_MAX / 10 < value)
return '?';
}
- if ((c == '.' || c == ',') && ISDIGIT (p[1])) {
+ if ((c == '.' || c == ',') && c_isdigit (p[1])) {
time_t s;
int ns;
int digits;
- unsigned long int value1;
+ uintmax_t value1;
/* Check for overflow when converting value to
* time_t.
for (digits = 2;
digits <= LOG10_BILLION; digits++) {
ns *= 10;
- if (ISDIGIT (*p))
+ if (c_isdigit (*p))
ns += *p++ - '0';
}
* -Infinity.
*/
if (sign < 0)
- for (; ISDIGIT (*p); p++)
+ for (; c_isdigit (*p); p++)
if (*p != '0') {
ns++;
break;
}
- while (ISDIGIT (*p))
+ while (c_isdigit (*p))
p++;
/* Adjust to the timespec convention, which is
struct timespec const *now)
{
time_t Start;
- long int Start_ns;
+ intmax_t Start_ns;
struct tm const *tmp;
struct tm tm;
struct tm tm0;
pc.local_zones_seen = 0;
pc.dsts_seen = 0;
pc.zones_seen = 0;
- pc.year_seen = 0;
- pc.ordinal_day_seen = 0;
#if HAVE_STRUCT_TM_TM_ZONE
pc.local_time_zone_table[0].name = tmp->tm_zone;
* TZ="XXX1:00" and try mktime again.
*/
- long int time_zone = pc.time_zone;
+ intmax_t time_zone = pc.time_zone;
- long int abs_time_zone = time_zone < 0
+ intmax_t abs_time_zone = time_zone < 0
? - time_zone : time_zone;
- long int abs_time_zone_hour
+ intmax_t abs_time_zone_hour
= abs_time_zone / 60;
int abs_time_zone_min = abs_time_zone % 60;
if (!tz_was_altered)
tz0 = get_tz (tz0buf);
- sprintf (tz1buf, "XXX%s%ld:%02d",
+ sprintf (tz1buf, "XXX%s%jd:%02d",
&"-"[time_zone < 0],
abs_time_zone_hour,
abs_time_zone_min);
* so this block must follow others that clobber Start.
*/
if (pc.zones_seen) {
- long int delta = pc.time_zone * 60;
+ intmax_t delta = pc.time_zone * 60;
time_t t1;
#ifdef HAVE_TM_GMTOFF
delta -= tm.tm_gmtoff;
* 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;
+ intmax_t sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
+ intmax_t normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
time_t t0 = Start;
- long int d1 = 60 * 60 * pc.rel.hour;
+ intmax_t d1 = 60 * 60 * pc.rel.hour;
time_t t1 = t0 + d1;
- long int d2 = 60 * pc.rel.minutes;
+ intmax_t d2 = 60 * pc.rel.minutes;
time_t t2 = t1 + d2;
time_t d3 = pc.rel.seconds;
time_t t3 = t2 + d3;
- long int d4 = (sum_ns - normalized_ns) / BILLION;
+ intmax_t d4 = (sum_ns - normalized_ns) / BILLION;
time_t t4 = t3 + d4;
time_t t5 = t4;