Blame src/gl/parse-datetime.y

Packit Service 4684c1
%{
Packit Service 4684c1
/* Parse a string into an internal timestamp.
Packit Service 4684c1
Packit Service 4684c1
   Copyright (C) 1999-2000, 2002-2020 Free Software Foundation, Inc.
Packit Service 4684c1
Packit Service 4684c1
   This program is free software: you can redistribute it and/or modify
Packit Service 4684c1
   it under the terms of the GNU General Public License as published by
Packit Service 4684c1
   the Free Software Foundation; either version 3 of the License, or
Packit Service 4684c1
   (at your option) any later version.
Packit Service 4684c1
Packit Service 4684c1
   This program is distributed in the hope that it will be useful,
Packit Service 4684c1
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 4684c1
   GNU General Public License for more details.
Packit Service 4684c1
Packit Service 4684c1
   You should have received a copy of the GNU General Public License
Packit Service 4684c1
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service 4684c1
Packit Service 4684c1
/* Originally written by Steven M. Bellovin <smb@research.att.com> while
Packit Service 4684c1
   at the University of North Carolina at Chapel Hill.  Later tweaked by
Packit Service 4684c1
   a couple of people on Usenet.  Completely overhauled by Rich $alz
Packit Service 4684c1
   <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
Packit Service 4684c1
Packit Service 4684c1
   Modified by Assaf Gordon <assafgordon@gmail.com> in 2016 to add
Packit Service 4684c1
   debug output.
Packit Service 4684c1
Packit Service 4684c1
   Modified by Paul Eggert <eggert@twinsun.com> in 1999 to do the
Packit Service 4684c1
   right thing about local DST.  Also modified by Paul Eggert
Packit Service 4684c1
   <eggert@cs.ucla.edu> in 2004 to support nanosecond-resolution
Packit Service 4684c1
   timestamps, in 2004 to support TZ strings in dates, and in 2017 to
Packit Service 4684c1
   check for integer overflow and to support longer-than-'long'
Packit Service 4684c1
   'time_t' and 'tv_nsec'.  */
Packit Service 4684c1
Packit Service 4684c1
#include <config.h>
Packit Service 4684c1
Packit Service 4684c1
#include "parse-datetime.h"
Packit Service 4684c1
Packit Service 4684c1
#include "intprops.h"
Packit Service 4684c1
#include "timespec.h"
Packit Service 4684c1
#include "verify.h"
Packit Service 4684c1
#include "strftime.h"
Packit Service 4684c1
Packit Service 4684c1
/* There's no need to extend the stack, so there's no need to involve
Packit Service 4684c1
   alloca.  */
Packit Service 4684c1
#define YYSTACK_USE_ALLOCA 0
Packit Service 4684c1
Packit Service 4684c1
/* Tell Bison how much stack space is needed.  20 should be plenty for
Packit Service 4684c1
   this grammar, which is not right recursive.  Beware setting it too
Packit Service 4684c1
   high, since that might cause problems on machines whose
Packit Service 4684c1
   implementations have lame stack-overflow checking.  */
Packit Service 4684c1
#define YYMAXDEPTH 20
Packit Service 4684c1
#define YYINITDEPTH YYMAXDEPTH
Packit Service 4684c1
Packit Service 4684c1
/* Since the code of parse-datetime.y is not included in the Emacs executable
Packit Service 4684c1
   itself, there is no need to #define static in this file.  Even if
Packit Service 4684c1
   the code were included in the Emacs executable, it probably
Packit Service 4684c1
   wouldn't do any harm to #undef it here; this will only cause
Packit Service 4684c1
   problems if we try to write to a static variable, which I don't
Packit Service 4684c1
   think this code needs to do.  */
Packit Service 4684c1
#ifdef emacs
Packit Service 4684c1
# undef static
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
#include <inttypes.h>
Packit Service 4684c1
#include <c-ctype.h>
Packit Service 4684c1
#include <limits.h>
Packit Service 4684c1
#include <stdarg.h>
Packit Service 4684c1
#include <stdio.h>
Packit Service 4684c1
#include <stdlib.h>
Packit Service 4684c1
#include <string.h>
Packit Service 4684c1
Packit Service 4684c1
#include "gettext.h"
Packit Service 4684c1
Packit Service 4684c1
#define _(str) gettext (str)
Packit Service 4684c1
Packit Service 4684c1
/* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
Packit Service 4684c1
   use _STDLIB_H_ as witness.  Map the latter to the one bison uses.  */
Packit Service 4684c1
/* FIXME: this is temporary.  Remove when we have a mechanism to ensure
Packit Service 4684c1
   that the version we're using is fixed, too.  */
Packit Service 4684c1
#ifdef _STDLIB_H_
Packit Service 4684c1
# undef _STDLIB_H
Packit Service 4684c1
# define _STDLIB_H 1
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
/* Shift A right by B bits portably, by dividing A by 2**B and
Packit Service 4684c1
   truncating towards minus infinity.  A and B should be free of side
Packit Service 4684c1
   effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
Packit Service 4684c1
   INT_BITS is the number of useful bits in an int.  GNU code can
Packit Service 4684c1
   assume that INT_BITS is at least 32.
Packit Service 4684c1
Packit Service 4684c1
   ISO C99 says that A >> B is implementation-defined if A < 0.  Some
Packit Service 4684c1
   implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
Packit Service 4684c1
   right in the usual way when A < 0, so SHR falls back on division if
Packit Service 4684c1
   ordinary A >> B doesn't seem to be the usual signed shift.  */
Packit Service 4684c1
#define SHR(a, b)       \
Packit Service 4684c1
  (-1 >> 1 == -1        \
Packit Service 4684c1
   ? (a) >> (b)         \
Packit Service 4684c1
   : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
Packit Service 4684c1
Packit Service 4684c1
#define HOUR(x) (60 * 60 * (x))
Packit Service 4684c1
Packit Service 4684c1
#define STREQ(a, b) (strcmp (a, b) == 0)
Packit Service 4684c1
Packit Service 4684c1
/* Verify that time_t is an integer as POSIX requires, and that every
Packit Service 4684c1
   time_t value fits in intmax_t.  Please file a bug report if these
Packit Service 4684c1
   assumptions are false on your platform.  */
Packit Service 4684c1
verify (TYPE_IS_INTEGER (time_t));
Packit Service 4684c1
verify (!TYPE_SIGNED (time_t) || INTMAX_MIN <= TYPE_MINIMUM (time_t));
Packit Service 4684c1
verify (TYPE_MAXIMUM (time_t) <= INTMAX_MAX);
Packit Service 4684c1
Packit Service 4684c1
/* True if N is out of range for time_t.  */
Packit Service 4684c1
static bool
Packit Service 4684c1
time_overflow (intmax_t n)
Packit Service 4684c1
{
Packit Service 4684c1
  return ! ((TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= n : 0 <= n)
Packit Service 4684c1
            && n <= TYPE_MAXIMUM (time_t));
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Convert a possibly-signed character to an unsigned character.  This is
Packit Service 4684c1
   a bit safer than casting to unsigned char, since it catches some type
Packit Service 4684c1
   errors that the cast doesn't.  */
Packit Service 4684c1
static unsigned char to_uchar (char ch) { return ch; }
Packit Service 4684c1
Packit Service 4684c1
static void _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2))
Packit Service 4684c1
dbg_printf (char const *msg, ...)
Packit Service 4684c1
{
Packit Service 4684c1
  va_list args;
Packit Service 4684c1
  /* TODO: use gnulib's 'program_name' instead?  */
Packit Service 4684c1
  fputs ("date: ", stderr);
Packit Service 4684c1
Packit Service 4684c1
  va_start (args, msg);
Packit Service 4684c1
  vfprintf (stderr, msg, args);
Packit Service 4684c1
  va_end (args);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
/* An integer value, and the number of digits in its textual
Packit Service 4684c1
   representation.  */
Packit Service 4684c1
typedef struct
Packit Service 4684c1
{
Packit Service 4684c1
  bool negative;
Packit Service 4684c1
  intmax_t value;
Packit Service 4684c1
  ptrdiff_t digits;
Packit Service 4684c1
} textint;
Packit Service 4684c1
Packit Service 4684c1
/* An entry in the lexical lookup table.  */
Packit Service 4684c1
typedef struct
Packit Service 4684c1
{
Packit Service 4684c1
  char const *name;
Packit Service 4684c1
  int type;
Packit Service 4684c1
  int value;
Packit Service 4684c1
} table;
Packit Service 4684c1
Packit Service 4684c1
/* Meridian: am, pm, or 24-hour style.  */
Packit Service 4684c1
enum { MERam, MERpm, MER24 };
Packit Service 4684c1
Packit Service 4684c1
/* A reasonable upper bound for the buffer used in debug output.  */
Packit Service 4684c1
enum { DBGBUFSIZE = 100 };
Packit Service 4684c1
Packit Service 4684c1
enum { BILLION = 1000000000, LOG10_BILLION = 9 };
Packit Service 4684c1
Packit Service 4684c1
/* Relative times.  */
Packit Service 4684c1
typedef struct
Packit Service 4684c1
{
Packit Service 4684c1
  /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
Packit Service 4684c1
  intmax_t year;
Packit Service 4684c1
  intmax_t month;
Packit Service 4684c1
  intmax_t day;
Packit Service 4684c1
  intmax_t hour;
Packit Service 4684c1
  intmax_t minutes;
Packit Service 4684c1
  intmax_t seconds;
Packit Service 4684c1
  int ns;
Packit Service 4684c1
} relative_time;
Packit Service 4684c1
Packit Service 4684c1
#if HAVE_COMPOUND_LITERALS
Packit Service 4684c1
# define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
Packit Service 4684c1
#else
Packit Service 4684c1
static relative_time const RELATIVE_TIME_0;
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
/* Information passed to and from the parser.  */
Packit Service 4684c1
typedef struct
Packit Service 4684c1
{
Packit Service 4684c1
  /* The input string remaining to be parsed.  */
Packit Service 4684c1
  const char *input;
Packit Service 4684c1
Packit Service 4684c1
  /* N, if this is the Nth Tuesday.  */
Packit Service 4684c1
  intmax_t day_ordinal;
Packit Service 4684c1
Packit Service 4684c1
  /* Day of week; Sunday is 0.  */
Packit Service 4684c1
  int day_number;
Packit Service 4684c1
Packit Service 4684c1
  /* tm_isdst flag for the local zone.  */
Packit Service 4684c1
  int local_isdst;
Packit Service 4684c1
Packit Service 4684c1
  /* Time zone, in seconds east of UT.  */
Packit Service 4684c1
  int time_zone;
Packit Service 4684c1
Packit Service 4684c1
  /* Style used for time.  */
Packit Service 4684c1
  int meridian;
Packit Service 4684c1
Packit Service 4684c1
  /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds.  */
Packit Service 4684c1
  textint year;
Packit Service 4684c1
  intmax_t month;
Packit Service 4684c1
  intmax_t day;
Packit Service 4684c1
  intmax_t hour;
Packit Service 4684c1
  intmax_t minutes;
Packit Service 4684c1
  struct timespec seconds; /* includes nanoseconds */
Packit Service 4684c1
Packit Service 4684c1
  /* Relative year, month, day, hour, minutes, seconds, and nanoseconds.  */
Packit Service 4684c1
  relative_time rel;
Packit Service 4684c1
Packit Service 4684c1
  /* Presence or counts of nonterminals of various flavors parsed so far.  */
Packit Service 4684c1
  bool timespec_seen;
Packit Service 4684c1
  bool rels_seen;
Packit Service 4684c1
  ptrdiff_t dates_seen;
Packit Service 4684c1
  ptrdiff_t days_seen;
Packit Service 4684c1
  ptrdiff_t local_zones_seen;
Packit Service 4684c1
  ptrdiff_t dsts_seen;
Packit Service 4684c1
  ptrdiff_t times_seen;
Packit Service 4684c1
  ptrdiff_t zones_seen;
Packit Service 4684c1
  bool year_seen;
Packit Service 4684c1
Packit Service 4684c1
  /* Print debugging output to stderr.  */
Packit Service 4684c1
  bool parse_datetime_debug;
Packit Service 4684c1
Packit Service 4684c1
  /* Which of the 'seen' parts have been printed when debugging.  */
Packit Service 4684c1
  bool debug_dates_seen;
Packit Service 4684c1
  bool debug_days_seen;
Packit Service 4684c1
  bool debug_local_zones_seen;
Packit Service 4684c1
  bool debug_times_seen;
Packit Service 4684c1
  bool debug_zones_seen;
Packit Service 4684c1
  bool debug_year_seen;
Packit Service 4684c1
Packit Service 4684c1
  /* The user specified explicit ordinal day value.  */
Packit Service 4684c1
  bool debug_ordinal_day_seen;
Packit Service 4684c1
Packit Service 4684c1
  /* Table of local time zone abbreviations, terminated by a null entry.  */
Packit Service 4684c1
  table local_time_zone_table[3];
Packit Service 4684c1
} parser_control;
Packit Service 4684c1
Packit Service 4684c1
union YYSTYPE;
Packit Service 4684c1
static int yylex (union YYSTYPE *, parser_control *);
Packit Service 4684c1
static int yyerror (parser_control const *, char const *);
Packit Service 4684c1
static bool time_zone_hhmm (parser_control *, textint, intmax_t);
Packit Service 4684c1
Packit Service 4684c1
/* Extract into *PC any date and time info from a string of digits
Packit Service 4684c1
   of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
Packit Service 4684c1
   YYYY, ...).  */
Packit Service 4684c1
static void
Packit Service 4684c1
digits_to_date_time (parser_control *pc, textint text_int)
Packit Service 4684c1
{
Packit Service 4684c1
  if (pc->dates_seen && ! pc->year.digits
Packit Service 4684c1
      && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
Packit Service 4684c1
    {
Packit Service 4684c1
      pc->year_seen = true;
Packit Service 4684c1
      pc->year = text_int;
Packit Service 4684c1
    }
Packit Service 4684c1
  else
Packit Service 4684c1
    {
Packit Service 4684c1
      if (4 < text_int.digits)
Packit Service 4684c1
        {
Packit Service 4684c1
          pc->dates_seen++;
Packit Service 4684c1
          pc->day = text_int.value % 100;
Packit Service 4684c1
          pc->month = (text_int.value / 100) % 100;
Packit Service 4684c1
          pc->year.value = text_int.value / 10000;
Packit Service 4684c1
          pc->year.digits = text_int.digits - 4;
Packit Service 4684c1
        }
Packit Service 4684c1
      else
Packit Service 4684c1
        {
Packit Service 4684c1
          pc->times_seen++;
Packit Service 4684c1
          if (text_int.digits <= 2)
Packit Service 4684c1
            {
Packit Service 4684c1
              pc->hour = text_int.value;
Packit Service 4684c1
              pc->minutes = 0;
Packit Service 4684c1
            }
Packit Service 4684c1
          else
Packit Service 4684c1
            {
Packit Service 4684c1
              pc->hour = text_int.value / 100;
Packit Service 4684c1
              pc->minutes = text_int.value % 100;
Packit Service 4684c1
            }
Packit Service 4684c1
          pc->seconds.tv_sec = 0;
Packit Service 4684c1
          pc->seconds.tv_nsec = 0;
Packit Service 4684c1
          pc->meridian = MER24;
Packit Service 4684c1
        }
Packit Service 4684c1
    }
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1).  Return true
Packit Service 4684c1
   if successful, false if an overflow occurred.  */
Packit Service 4684c1
static bool
Packit Service 4684c1
apply_relative_time (parser_control *pc, relative_time rel, int factor)
Packit Service 4684c1
{
Packit Service 4684c1
  if (factor < 0
Packit Service 4684c1
      ? (INT_SUBTRACT_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
Packit Service 4684c1
         | INT_SUBTRACT_WRAPV (pc->rel.year, rel.year, &pc->rel.year))
Packit Service 4684c1
      : (INT_ADD_WRAPV (pc->rel.ns, rel.ns, &pc->rel.ns)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.seconds, rel.seconds, &pc->rel.seconds)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.minutes, rel.minutes, &pc->rel.minutes)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.hour, rel.hour, &pc->rel.hour)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.day, rel.day, &pc->rel.day)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.month, rel.month, &pc->rel.month)
Packit Service 4684c1
         | INT_ADD_WRAPV (pc->rel.year, rel.year, &pc->rel.year)))
Packit Service 4684c1
    return false;
Packit Service 4684c1
  pc->rels_seen = true;
Packit Service 4684c1
  return true;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Set PC-> hour, minutes, seconds and nanoseconds members from arguments.  */
Packit Service 4684c1
static void
Packit Service 4684c1
set_hhmmss (parser_control *pc, intmax_t hour, intmax_t minutes,
Packit Service 4684c1
            time_t sec, int nsec)
Packit Service 4684c1
{
Packit Service 4684c1
  pc->hour = hour;
Packit Service 4684c1
  pc->minutes = minutes;
Packit Service 4684c1
  pc->seconds.tv_sec = sec;
Packit Service 4684c1
  pc->seconds.tv_nsec = nsec;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Return a textual representation of the day ordinal/number values
Packit Service 4684c1
   in the parser_control struct (e.g., "last wed", "this tues", "thu").  */
Packit Service 4684c1
static const char *
Packit Service 4684c1
str_days (parser_control *pc, char *buffer, int n)
Packit Service 4684c1
{
Packit Service 4684c1
  /* TODO: use relative_time_table for reverse lookup.  */
Packit Service 4684c1
  static char const ordinal_values[][11] = {
Packit Service 4684c1
     "last",
Packit Service 4684c1
     "this",
Packit Service 4684c1
     "next/first",
Packit Service 4684c1
     "(SECOND)", /* SECOND is commented out in relative_time_table.  */
Packit Service 4684c1
     "third",
Packit Service 4684c1
     "fourth",
Packit Service 4684c1
     "fifth",
Packit Service 4684c1
     "sixth",
Packit Service 4684c1
     "seventh",
Packit Service 4684c1
     "eight",
Packit Service 4684c1
     "ninth",
Packit Service 4684c1
     "tenth",
Packit Service 4684c1
     "eleventh",
Packit Service 4684c1
     "twelfth"
Packit Service 4684c1
  };
Packit Service 4684c1
Packit Service 4684c1
  static char const days_values[][4] = {
Packit Service 4684c1
     "Sun",
Packit Service 4684c1
     "Mon",
Packit Service 4684c1
     "Tue",
Packit Service 4684c1
     "Wed",
Packit Service 4684c1
     "Thu",
Packit Service 4684c1
     "Fri",
Packit Service 4684c1
     "Sat"
Packit Service 4684c1
  };
Packit Service 4684c1
Packit Service 4684c1
  int len;
Packit Service 4684c1
Packit Service 4684c1
  /* Don't add an ordinal prefix if the user didn't specify it
Packit Service 4684c1
     (e.g., "this wed" vs "wed").  */
Packit Service 4684c1
  if (pc->debug_ordinal_day_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      /* Use word description if possible (e.g., -1 = last, 3 = third).  */
Packit Service 4684c1
      len = (-1 <= pc->day_ordinal && pc->day_ordinal <= 12
Packit Service 4684c1
             ? snprintf (buffer, n, "%s", ordinal_values[pc->day_ordinal + 1])
Packit Service 4684c1
             : snprintf (buffer, n, "%"PRIdMAX, pc->day_ordinal));
Packit Service 4684c1
    }
Packit Service 4684c1
  else
Packit Service 4684c1
    {
Packit Service 4684c1
      buffer[0] = '\0';
Packit Service 4684c1
      len = 0;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  /* Add the day name */
Packit Service 4684c1
  if (0 <= pc->day_number && pc->day_number <= 6 && 0 <= len && len < n)
Packit Service 4684c1
    snprintf (buffer + len, n - len, &" %s"[len == 0],
Packit Service 4684c1
              days_values[pc->day_number]);
Packit Service 4684c1
  else
Packit Service 4684c1
    {
Packit Service 4684c1
      /* invalid day_number value - should never happen */
Packit Service 4684c1
    }
Packit Service 4684c1
  return buffer;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Convert a time zone to its string representation.  */
Packit Service 4684c1
Packit Service 4684c1
enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
Packit Service 4684c1
Packit Service 4684c1
static char const *
Packit Service 4684c1
time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
Packit Service 4684c1
{
Packit Service 4684c1
  char *p = time_zone_buf;
Packit Service 4684c1
  char sign = time_zone < 0 ? '-' : '+';
Packit Service 4684c1
  int hour = abs (time_zone / (60 * 60));
Packit Service 4684c1
  p += sprintf (time_zone_buf, "%c%02d", sign, hour);
Packit Service 4684c1
  int offset_from_hour = abs (time_zone % (60 * 60));
Packit Service 4684c1
  if (offset_from_hour != 0)
Packit Service 4684c1
    {
Packit Service 4684c1
      int mm = offset_from_hour / 60;
Packit Service 4684c1
      int ss = offset_from_hour % 60;
Packit Service 4684c1
      *p++ = ':';
Packit Service 4684c1
      *p++ = '0' + mm / 10;
Packit Service 4684c1
      *p++ = '0' + mm % 10;
Packit Service 4684c1
      if (ss)
Packit Service 4684c1
        {
Packit Service 4684c1
          *p++ = ':';
Packit Service 4684c1
          *p++ = '0' + ss / 10;
Packit Service 4684c1
          *p++ = '0' + ss % 10;
Packit Service 4684c1
        }
Packit Service 4684c1
      *p = '\0';
Packit Service 4684c1
    }
Packit Service 4684c1
  return time_zone_buf;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* debugging: print the current time in the parser_control structure.
Packit Service 4684c1
   The parser will increment "*_seen" members for those which were parsed.
Packit Service 4684c1
   This function will print only newly seen parts.  */
Packit Service 4684c1
static void
Packit Service 4684c1
debug_print_current_time (char const *item, parser_control *pc)
Packit Service 4684c1
{
Packit Service 4684c1
  bool space = false;
Packit Service 4684c1
Packit Service 4684c1
  if (!pc->parse_datetime_debug)
Packit Service 4684c1
    return;
Packit Service 4684c1
Packit Service 4684c1
  /* no newline, more items printed below */
Packit Service 4684c1
  dbg_printf (_("parsed %s part: "), item);
Packit Service 4684c1
Packit Service 4684c1
  if (pc->dates_seen && !pc->debug_dates_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      /*TODO: use pc->year.negative?  */
Packit Service 4684c1
      fprintf (stderr, "(Y-M-D) %04"PRIdMAX"-%02"PRIdMAX"-%02"PRIdMAX,
Packit Service 4684c1
              pc->year.value, pc->month, pc->day);
Packit Service 4684c1
      pc->debug_dates_seen = true;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc->year_seen != pc->debug_year_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      if (space)
Packit Service 4684c1
        fputc (' ', stderr);
Packit Service 4684c1
      fprintf (stderr, _("year: %04"PRIdMAX), pc->year.value);
Packit Service 4684c1
Packit Service 4684c1
      pc->debug_year_seen = pc->year_seen;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc->times_seen && !pc->debug_times_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      intmax_t sec = pc->seconds.tv_sec;
Packit Service 4684c1
      fprintf (stderr, &" %02"PRIdMAX":%02"PRIdMAX":%02"PRIdMAX[!space],
Packit Service 4684c1
               pc->hour, pc->minutes, sec);
Packit Service 4684c1
      if (pc->seconds.tv_nsec != 0)
Packit Service 4684c1
        {
Packit Service 4684c1
          int nsec = pc->seconds.tv_nsec;
Packit Service 4684c1
          fprintf (stderr, ".%09d", nsec);
Packit Service 4684c1
        }
Packit Service 4684c1
      if (pc->meridian == MERpm)
Packit Service 4684c1
        fputs ("pm", stderr);
Packit Service 4684c1
Packit Service 4684c1
      pc->debug_times_seen = true;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc->days_seen && !pc->debug_days_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      if (space)
Packit Service 4684c1
        fputc (' ', stderr);
Packit Service 4684c1
      char tmp[DBGBUFSIZE];
Packit Service 4684c1
      fprintf (stderr, _("%s (day ordinal=%"PRIdMAX" number=%d)"),
Packit Service 4684c1
               str_days (pc, tmp, sizeof tmp),
Packit Service 4684c1
               pc->day_ordinal, pc->day_number);
Packit Service 4684c1
      pc->debug_days_seen = true;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  /* local zone strings only change the DST settings,
Packit Service 4684c1
     not the timezone value.  If seen, inform about the DST.  */
Packit Service 4684c1
  if (pc->local_zones_seen && !pc->debug_local_zones_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      fprintf (stderr, &" isdst=%d%s"[!space],
Packit Service 4684c1
               pc->local_isdst, pc->dsts_seen ? " DST" : "");
Packit Service 4684c1
      pc->debug_local_zones_seen = true;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc->zones_seen && !pc->debug_zones_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      char time_zone_buf[TIME_ZONE_BUFSIZE];
Packit Service 4684c1
      fprintf (stderr, &" UTC%s"[!space],
Packit Service 4684c1
               time_zone_str (pc->time_zone, time_zone_buf));
Packit Service 4684c1
      pc->debug_zones_seen = true;
Packit Service 4684c1
      space = true;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc->timespec_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      intmax_t sec = pc->seconds.tv_sec;
Packit Service 4684c1
      if (space)
Packit Service 4684c1
        fputc (' ', stderr);
Packit Service 4684c1
      fprintf (stderr, _("number of seconds: %"PRIdMAX), sec);
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  fputc ('\n', stderr);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Debugging: print the current relative values.  */
Packit Service 4684c1
Packit Service 4684c1
static bool
Packit Service 4684c1
print_rel_part (bool space, intmax_t val, char const *name)
Packit Service 4684c1
{
Packit Service 4684c1
  if (val == 0)
Packit Service 4684c1
    return space;
Packit Service 4684c1
  fprintf (stderr, &" %+"PRIdMAX" %s"[!space], val, name);
Packit Service 4684c1
  return true;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static void
Packit Service 4684c1
debug_print_relative_time (char const *item, parser_control const *pc)
Packit Service 4684c1
{
Packit Service 4684c1
  bool space = false;
Packit Service 4684c1
Packit Service 4684c1
  if (!pc->parse_datetime_debug)
Packit Service 4684c1
    return;
Packit Service 4684c1
Packit Service 4684c1
  /* no newline, more items printed below */
Packit Service 4684c1
  dbg_printf (_("parsed %s part: "), item);
Packit Service 4684c1
Packit Service 4684c1
  if (pc->rel.year == 0 && pc->rel.month == 0 && pc->rel.day == 0
Packit Service 4684c1
      && pc->rel.hour == 0 && pc->rel.minutes == 0 && pc->rel.seconds == 0
Packit Service 4684c1
      && pc->rel.ns == 0)
Packit Service 4684c1
    {
Packit Service 4684c1
      /* Special case: relative time of this/today/now */
Packit Service 4684c1
      fputs (_("today/this/now\n"), stderr);
Packit Service 4684c1
      return;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.year, "year(s)");
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.month, "month(s)");
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.day, "day(s)");
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.hour, "hour(s)");
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.minutes, "minutes");
Packit Service 4684c1
  space = print_rel_part (space, pc->rel.seconds, "seconds");
Packit Service 4684c1
  print_rel_part (space, pc->rel.ns, "nanoseconds");
Packit Service 4684c1
Packit Service 4684c1
  fputc ('\n', stderr);
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
%}
Packit Service 4684c1
Packit Service 4684c1
/* We want a reentrant parser, even if the TZ manipulation and the calls to
Packit Service 4684c1
   localtime and gmtime are not reentrant.  */
Packit Service 4684c1
%define api.pure
Packit Service 4684c1
%parse-param { parser_control *pc }
Packit Service 4684c1
%lex-param { parser_control *pc }
Packit Service 4684c1
Packit Service 4684c1
/* This grammar has 31 shift/reduce conflicts.  */
Packit Service 4684c1
%expect 31
Packit Service 4684c1
Packit Service 4684c1
%union
Packit Service 4684c1
{
Packit Service 4684c1
  intmax_t intval;
Packit Service 4684c1
  textint textintval;
Packit Service 4684c1
  struct timespec timespec;
Packit Service 4684c1
  relative_time rel;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
%token <intval> tAGO
Packit Service 4684c1
%token tDST
Packit Service 4684c1
Packit Service 4684c1
%token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
Packit Service 4684c1
%token <intval> tDAY_UNIT tDAY_SHIFT
Packit Service 4684c1
Packit Service 4684c1
%token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
Packit Service 4684c1
%token <intval> tMONTH tORDINAL tZONE
Packit Service 4684c1
Packit Service 4684c1
%token <textintval> tSNUMBER tUNUMBER
Packit Service 4684c1
%token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
Packit Service 4684c1
Packit Service 4684c1
%type <intval> o_colon_minutes
Packit Service 4684c1
%type <timespec> seconds signed_seconds unsigned_seconds
Packit Service 4684c1
Packit Service 4684c1
%type <rel> relunit relunit_snumber dayshift
Packit Service 4684c1
Packit Service 4684c1
%%
Packit Service 4684c1
Packit Service 4684c1
spec:
Packit Service 4684c1
    timespec
Packit Service 4684c1
  | items
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
timespec:
Packit Service 4684c1
    '@' seconds
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->seconds = $2;
Packit Service 4684c1
        pc->timespec_seen = true;
Packit Service 4684c1
        debug_print_current_time (_("number of seconds"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
items:
Packit Service 4684c1
    /* empty */
Packit Service 4684c1
  | items item
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
item:
Packit Service 4684c1
    datetime
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->times_seen++; pc->dates_seen++;
Packit Service 4684c1
        debug_print_current_time (_("datetime"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | time
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->times_seen++;
Packit Service 4684c1
        debug_print_current_time (_("time"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | local_zone
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->local_zones_seen++;
Packit Service 4684c1
        debug_print_current_time (_("local_zone"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | zone
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->zones_seen++;
Packit Service 4684c1
        debug_print_current_time (_("zone"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | date
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->dates_seen++;
Packit Service 4684c1
        debug_print_current_time (_("date"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | day
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->days_seen++;
Packit Service 4684c1
        debug_print_current_time (_("day"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | rel
Packit Service 4684c1
      {
Packit Service 4684c1
        debug_print_relative_time (_("relative"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | number
Packit Service 4684c1
      {
Packit Service 4684c1
        debug_print_current_time (_("number"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | hybrid
Packit Service 4684c1
      {
Packit Service 4684c1
        debug_print_relative_time (_("hybrid"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
datetime:
Packit Service 4684c1
    iso_8601_datetime
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
iso_8601_datetime:
Packit Service 4684c1
    iso_8601_date 'T' iso_8601_time
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
time:
Packit Service 4684c1
    tUNUMBER tMERIDIAN
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, 0, 0, 0);
Packit Service 4684c1
        pc->meridian = $2;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER ':' tUNUMBER tMERIDIAN
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, $3.value, 0, 0);
Packit Service 4684c1
        pc->meridian = $4;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tMERIDIAN
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
Packit Service 4684c1
        pc->meridian = $6;
Packit Service 4684c1
      }
Packit Service 4684c1
  | iso_8601_time
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
iso_8601_time:
Packit Service 4684c1
    tUNUMBER zone_offset
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, 0, 0, 0);
Packit Service 4684c1
        pc->meridian = MER24;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER ':' tUNUMBER o_zone_offset
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, $3.value, 0, 0);
Packit Service 4684c1
        pc->meridian = MER24;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_zone_offset
Packit Service 4684c1
      {
Packit Service 4684c1
        set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
Packit Service 4684c1
        pc->meridian = MER24;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
o_zone_offset:
Packit Service 4684c1
  /* empty */
Packit Service 4684c1
  | zone_offset
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
zone_offset:
Packit Service 4684c1
    tSNUMBER o_colon_minutes
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->zones_seen++;
Packit Service 4684c1
        if (! time_zone_hhmm (pc, $1, $2)) YYABORT;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
/* Local zone strings affect only the DST setting, and take effect
Packit Service 4684c1
   only if the current TZ setting is relevant.
Packit Service 4684c1
Packit Service 4684c1
   Example 1:
Packit Service 4684c1
   'EEST' is parsed as tLOCAL_ZONE, as it relates to the effective TZ:
Packit Service 4684c1
        TZ='Europe/Helsinki' date -d '2016-06-30 EEST'
Packit Service 4684c1
Packit Service 4684c1
   Example 2:
Packit Service 4684c1
   'EEST' is parsed as tDAYZONE:
Packit Service 4684c1
        TZ='Asia/Tokyo' date -d '2016-06-30 EEST'
Packit Service 4684c1
Packit Service 4684c1
   This is implemented by probing the next three calendar quarters
Packit Service 4684c1
   of the effective timezone and looking for DST changes -
Packit Service 4684c1
   if found, the timezone name (EEST) is inserted into
Packit Service 4684c1
   the lexical lookup table with type tLOCAL_ZONE.
Packit Service 4684c1
   (Search for 'quarter' comment in  'parse_datetime2'.)
Packit Service 4684c1
*/
Packit Service 4684c1
local_zone:
Packit Service 4684c1
    tLOCAL_ZONE
Packit Service 4684c1
      { pc->local_isdst = $1; }
Packit Service 4684c1
  | tLOCAL_ZONE tDST
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->local_isdst = 1;
Packit Service 4684c1
        pc->dsts_seen++;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
/* Note 'T' is a special case, as it is used as the separator in ISO
Packit Service 4684c1
   8601 date and time of day representation.  */
Packit Service 4684c1
zone:
Packit Service 4684c1
    tZONE
Packit Service 4684c1
      { pc->time_zone = $1; }
Packit Service 4684c1
  | 'T'
Packit Service 4684c1
      { pc->time_zone = -HOUR (7); }
Packit Service 4684c1
  | tZONE relunit_snumber
Packit Service 4684c1
      { pc->time_zone = $1;
Packit Service 4684c1
        if (! apply_relative_time (pc, $2, 1)) YYABORT;
Packit Service 4684c1
        debug_print_relative_time (_("relative"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | 'T' relunit_snumber
Packit Service 4684c1
      { pc->time_zone = -HOUR (7);
Packit Service 4684c1
        if (! apply_relative_time (pc, $2, 1)) YYABORT;
Packit Service 4684c1
        debug_print_relative_time (_("relative"), pc);
Packit Service 4684c1
      }
Packit Service 4684c1
  | tZONE tSNUMBER o_colon_minutes
Packit Service 4684c1
      { if (! time_zone_hhmm (pc, $2, $3)) YYABORT;
Packit Service 4684c1
        if (INT_ADD_WRAPV (pc->time_zone, $1, &pc->time_zone)) YYABORT; }
Packit Service 4684c1
  | tDAYZONE
Packit Service 4684c1
      { pc->time_zone = $1 + 60 * 60; }
Packit Service 4684c1
  | tZONE tDST
Packit Service 4684c1
      { pc->time_zone = $1 + 60 * 60; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
day:
Packit Service 4684c1
    tDAY
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day_ordinal = 0;
Packit Service 4684c1
        pc->day_number = $1;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tDAY ','
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day_ordinal = 0;
Packit Service 4684c1
        pc->day_number = $1;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tORDINAL tDAY
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day_ordinal = $1;
Packit Service 4684c1
        pc->day_number = $2;
Packit Service 4684c1
        pc->debug_ordinal_day_seen = true;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER tDAY
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day_ordinal = $1.value;
Packit Service 4684c1
        pc->day_number = $2;
Packit Service 4684c1
        pc->debug_ordinal_day_seen = true;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
date:
Packit Service 4684c1
    tUNUMBER '/' tUNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->month = $1.value;
Packit Service 4684c1
        pc->day = $3.value;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER '/' tUNUMBER '/' tUNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
Packit Service 4684c1
           otherwise as MM/DD/YY.
Packit Service 4684c1
           The goal in recognizing YYYY/MM/DD is solely to support legacy
Packit Service 4684c1
           machine-generated dates like those in an RCS log listing.  If
Packit Service 4684c1
           you want portability, use the ISO 8601 format.  */
Packit Service 4684c1
        if (4 <= $1.digits)
Packit Service 4684c1
          {
Packit Service 4684c1
            if (pc->parse_datetime_debug)
Packit Service 4684c1
              {
Packit Service 4684c1
                intmax_t digits = $1.digits;
Packit Service 4684c1
                dbg_printf (_("warning: value %"PRIdMAX" has %"PRIdMAX" digits. "
Packit Service 4684c1
                              "Assuming YYYY/MM/DD\n"),
Packit Service 4684c1
                            $1.value, digits);
Packit Service 4684c1
              }
Packit Service 4684c1
Packit Service 4684c1
            pc->year = $1;
Packit Service 4684c1
            pc->month = $3.value;
Packit Service 4684c1
            pc->day = $5.value;
Packit Service 4684c1
          }
Packit Service 4684c1
        else
Packit Service 4684c1
          {
Packit Service 4684c1
            if (pc->parse_datetime_debug)
Packit Service 4684c1
              dbg_printf (_("warning: value %"PRIdMAX" has less than 4 digits. "
Packit Service 4684c1
                            "Assuming MM/DD/YY[YY]\n"),
Packit Service 4684c1
                          $1.value);
Packit Service 4684c1
Packit Service 4684c1
            pc->month = $1.value;
Packit Service 4684c1
            pc->day = $3.value;
Packit Service 4684c1
            pc->year = $5;
Packit Service 4684c1
          }
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER tMONTH tSNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        /* E.g., 17-JUN-1992.  */
Packit Service 4684c1
        pc->day = $1.value;
Packit Service 4684c1
        pc->month = $2;
Packit Service 4684c1
        if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
Packit Service 4684c1
        pc->year.digits = $3.digits;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tMONTH tSNUMBER tSNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        /* E.g., JUN-17-1992.  */
Packit Service 4684c1
        pc->month = $1;
Packit Service 4684c1
        if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->day)) YYABORT;
Packit Service 4684c1
        if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->year.value)) YYABORT;
Packit Service 4684c1
        pc->year.digits = $3.digits;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tMONTH tUNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->month = $1;
Packit Service 4684c1
        pc->day = $2.value;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tMONTH tUNUMBER ',' tUNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->month = $1;
Packit Service 4684c1
        pc->day = $2.value;
Packit Service 4684c1
        pc->year = $4;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER tMONTH
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day = $1.value;
Packit Service 4684c1
        pc->month = $2;
Packit Service 4684c1
      }
Packit Service 4684c1
  | tUNUMBER tMONTH tUNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        pc->day = $1.value;
Packit Service 4684c1
        pc->month = $2;
Packit Service 4684c1
        pc->year = $3;
Packit Service 4684c1
      }
Packit Service 4684c1
  | iso_8601_date
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
iso_8601_date:
Packit Service 4684c1
    tUNUMBER tSNUMBER tSNUMBER
Packit Service 4684c1
      {
Packit Service 4684c1
        /* ISO 8601 format.  YYYY-MM-DD.  */
Packit Service 4684c1
        pc->year = $1;
Packit Service 4684c1
        if (INT_SUBTRACT_WRAPV (0, $2.value, &pc->month)) YYABORT;
Packit Service 4684c1
        if (INT_SUBTRACT_WRAPV (0, $3.value, &pc->day)) YYABORT;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
rel:
Packit Service 4684c1
    relunit tAGO
Packit Service 4684c1
      { if (! apply_relative_time (pc, $1, $2)) YYABORT; }
Packit Service 4684c1
  | relunit
Packit Service 4684c1
      { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
Packit Service 4684c1
  | dayshift
Packit Service 4684c1
      { if (! apply_relative_time (pc, $1, 1)) YYABORT; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
relunit:
Packit Service 4684c1
    tORDINAL tYEAR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.year = $1; }
Packit Service 4684c1
  | tUNUMBER tYEAR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
Packit Service 4684c1
  | tYEAR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.year = 1; }
Packit Service 4684c1
  | tORDINAL tMONTH_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.month = $1; }
Packit Service 4684c1
  | tUNUMBER tMONTH_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
Packit Service 4684c1
  | tMONTH_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.month = 1; }
Packit Service 4684c1
  | tORDINAL tDAY_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0;
Packit Service 4684c1
        if (INT_MULTIPLY_WRAPV ($1, $2, &$$.day)) YYABORT; }
Packit Service 4684c1
  | tUNUMBER tDAY_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0;
Packit Service 4684c1
        if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
Packit Service 4684c1
  | tDAY_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.day = $1; }
Packit Service 4684c1
  | tORDINAL tHOUR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.hour = $1; }
Packit Service 4684c1
  | tUNUMBER tHOUR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
Packit Service 4684c1
  | tHOUR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.hour = 1; }
Packit Service 4684c1
  | tORDINAL tMINUTE_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
Packit Service 4684c1
  | tUNUMBER tMINUTE_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
Packit Service 4684c1
  | tMINUTE_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
Packit Service 4684c1
  | tORDINAL tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
Packit Service 4684c1
  | tUNUMBER tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
Packit Service 4684c1
  | tSDECIMAL_NUMBER tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
Packit Service 4684c1
  | tUDECIMAL_NUMBER tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
Packit Service 4684c1
  | tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
Packit Service 4684c1
  | relunit_snumber
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
relunit_snumber:
Packit Service 4684c1
    tSNUMBER tYEAR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
Packit Service 4684c1
  | tSNUMBER tMONTH_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
Packit Service 4684c1
  | tSNUMBER tDAY_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0;
Packit Service 4684c1
        if (INT_MULTIPLY_WRAPV ($1.value, $2, &$$.day)) YYABORT; }
Packit Service 4684c1
  | tSNUMBER tHOUR_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
Packit Service 4684c1
  | tSNUMBER tMINUTE_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
Packit Service 4684c1
  | tSNUMBER tSEC_UNIT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
dayshift:
Packit Service 4684c1
    tDAY_SHIFT
Packit Service 4684c1
      { $$ = RELATIVE_TIME_0; $$.day = $1; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
seconds: signed_seconds | unsigned_seconds;
Packit Service 4684c1
Packit Service 4684c1
signed_seconds:
Packit Service 4684c1
    tSDECIMAL_NUMBER
Packit Service 4684c1
  | tSNUMBER
Packit Service 4684c1
      { if (time_overflow ($1.value)) YYABORT;
Packit Service 4684c1
        $$.tv_sec = $1.value; $$.tv_nsec = 0; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
unsigned_seconds:
Packit Service 4684c1
    tUDECIMAL_NUMBER
Packit Service 4684c1
  | tUNUMBER
Packit Service 4684c1
      { if (time_overflow ($1.value)) YYABORT;
Packit Service 4684c1
        $$.tv_sec = $1.value; $$.tv_nsec = 0; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
number:
Packit Service 4684c1
    tUNUMBER
Packit Service 4684c1
      { digits_to_date_time (pc, $1); }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
hybrid:
Packit Service 4684c1
    tUNUMBER relunit_snumber
Packit Service 4684c1
      {
Packit Service 4684c1
        /* Hybrid all-digit and relative offset, so that we accept e.g.,
Packit Service 4684c1
           "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
Packit Service 4684c1
        digits_to_date_time (pc, $1);
Packit Service 4684c1
        if (! apply_relative_time (pc, $2, 1)) YYABORT;
Packit Service 4684c1
      }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
o_colon_minutes:
Packit Service 4684c1
    /* empty */
Packit Service 4684c1
      { $$ = -1; }
Packit Service 4684c1
  | ':' tUNUMBER
Packit Service 4684c1
      { $$ = $2.value; }
Packit Service 4684c1
  ;
Packit Service 4684c1
Packit Service 4684c1
%%
Packit Service 4684c1
Packit Service 4684c1
static table const meridian_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "AM",   tMERIDIAN, MERam },
Packit Service 4684c1
  { "A.M.", tMERIDIAN, MERam },
Packit Service 4684c1
  { "PM",   tMERIDIAN, MERpm },
Packit Service 4684c1
  { "P.M.", tMERIDIAN, MERpm },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
static table const dst_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "DST", tDST, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
static table const month_and_day_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "JANUARY",  tMONTH,  1 },
Packit Service 4684c1
  { "FEBRUARY", tMONTH,  2 },
Packit Service 4684c1
  { "MARCH",    tMONTH,  3 },
Packit Service 4684c1
  { "APRIL",    tMONTH,  4 },
Packit Service 4684c1
  { "MAY",      tMONTH,  5 },
Packit Service 4684c1
  { "JUNE",     tMONTH,  6 },
Packit Service 4684c1
  { "JULY",     tMONTH,  7 },
Packit Service 4684c1
  { "AUGUST",   tMONTH,  8 },
Packit Service 4684c1
  { "SEPTEMBER",tMONTH,  9 },
Packit Service 4684c1
  { "SEPT",     tMONTH,  9 },
Packit Service 4684c1
  { "OCTOBER",  tMONTH, 10 },
Packit Service 4684c1
  { "NOVEMBER", tMONTH, 11 },
Packit Service 4684c1
  { "DECEMBER", tMONTH, 12 },
Packit Service 4684c1
  { "SUNDAY",   tDAY,    0 },
Packit Service 4684c1
  { "MONDAY",   tDAY,    1 },
Packit Service 4684c1
  { "TUESDAY",  tDAY,    2 },
Packit Service 4684c1
  { "TUES",     tDAY,    2 },
Packit Service 4684c1
  { "WEDNESDAY",tDAY,    3 },
Packit Service 4684c1
  { "WEDNES",   tDAY,    3 },
Packit Service 4684c1
  { "THURSDAY", tDAY,    4 },
Packit Service 4684c1
  { "THUR",     tDAY,    4 },
Packit Service 4684c1
  { "THURS",    tDAY,    4 },
Packit Service 4684c1
  { "FRIDAY",   tDAY,    5 },
Packit Service 4684c1
  { "SATURDAY", tDAY,    6 },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
static table const time_units_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "YEAR",     tYEAR_UNIT,      1 },
Packit Service 4684c1
  { "MONTH",    tMONTH_UNIT,     1 },
Packit Service 4684c1
  { "FORTNIGHT",tDAY_UNIT,      14 },
Packit Service 4684c1
  { "WEEK",     tDAY_UNIT,       7 },
Packit Service 4684c1
  { "DAY",      tDAY_UNIT,       1 },
Packit Service 4684c1
  { "HOUR",     tHOUR_UNIT,      1 },
Packit Service 4684c1
  { "MINUTE",   tMINUTE_UNIT,    1 },
Packit Service 4684c1
  { "MIN",      tMINUTE_UNIT,    1 },
Packit Service 4684c1
  { "SECOND",   tSEC_UNIT,       1 },
Packit Service 4684c1
  { "SEC",      tSEC_UNIT,       1 },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
/* Assorted relative-time words.  */
Packit Service 4684c1
static table const relative_time_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "TOMORROW", tDAY_SHIFT,      1 },
Packit Service 4684c1
  { "YESTERDAY",tDAY_SHIFT,     -1 },
Packit Service 4684c1
  { "TODAY",    tDAY_SHIFT,      0 },
Packit Service 4684c1
  { "NOW",      tDAY_SHIFT,      0 },
Packit Service 4684c1
  { "LAST",     tORDINAL,       -1 },
Packit Service 4684c1
  { "THIS",     tORDINAL,        0 },
Packit Service 4684c1
  { "NEXT",     tORDINAL,        1 },
Packit Service 4684c1
  { "FIRST",    tORDINAL,        1 },
Packit Service 4684c1
/*{ "SECOND",   tORDINAL,        2 }, */
Packit Service 4684c1
  { "THIRD",    tORDINAL,        3 },
Packit Service 4684c1
  { "FOURTH",   tORDINAL,        4 },
Packit Service 4684c1
  { "FIFTH",    tORDINAL,        5 },
Packit Service 4684c1
  { "SIXTH",    tORDINAL,        6 },
Packit Service 4684c1
  { "SEVENTH",  tORDINAL,        7 },
Packit Service 4684c1
  { "EIGHTH",   tORDINAL,        8 },
Packit Service 4684c1
  { "NINTH",    tORDINAL,        9 },
Packit Service 4684c1
  { "TENTH",    tORDINAL,       10 },
Packit Service 4684c1
  { "ELEVENTH", tORDINAL,       11 },
Packit Service 4684c1
  { "TWELFTH",  tORDINAL,       12 },
Packit Service 4684c1
  { "AGO",      tAGO,           -1 },
Packit Service 4684c1
  { "HENCE",    tAGO,            1 },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
/* The universal time zone table.  These labels can be used even for
Packit Service 4684c1
   timestamps that would not otherwise be valid, e.g., GMT timestamps
Packit Service 4684c1
   oin London during summer.  */
Packit Service 4684c1
static table const universal_time_zone_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "GMT",      tZONE,     HOUR ( 0) }, /* Greenwich Mean */
Packit Service 4684c1
  { "UT",       tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
Packit Service 4684c1
  { "UTC",      tZONE,     HOUR ( 0) },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
/* The time zone table.  This table is necessarily incomplete, as time
Packit Service 4684c1
   zone abbreviations are ambiguous; e.g., Australians interpret "EST"
Packit Service 4684c1
   as Eastern time in Australia, not as US Eastern Standard Time.
Packit Service 4684c1
   You cannot rely on parse_datetime to handle arbitrary time zone
Packit Service 4684c1
   abbreviations; use numeric abbreviations like "-0500" instead.  */
Packit Service 4684c1
static table const time_zone_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "WET",      tZONE,     HOUR ( 0) }, /* Western European */
Packit Service 4684c1
  { "WEST",     tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
Packit Service 4684c1
  { "BST",      tDAYZONE,  HOUR ( 0) }, /* British Summer */
Packit Service 4684c1
  { "ART",      tZONE,    -HOUR ( 3) }, /* Argentina */
Packit Service 4684c1
  { "BRT",      tZONE,    -HOUR ( 3) }, /* Brazil */
Packit Service 4684c1
  { "BRST",     tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
Packit Service 4684c1
  { "NST",      tZONE,   -(HOUR ( 3) + 30 * 60) }, /* Newfoundland Standard */
Packit Service 4684c1
  { "NDT",      tDAYZONE,-(HOUR ( 3) + 30 * 60) }, /* Newfoundland Daylight */
Packit Service 4684c1
  { "AST",      tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
Packit Service 4684c1
  { "ADT",      tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
Packit Service 4684c1
  { "CLT",      tZONE,    -HOUR ( 4) }, /* Chile */
Packit Service 4684c1
  { "CLST",     tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
Packit Service 4684c1
  { "EST",      tZONE,    -HOUR ( 5) }, /* Eastern Standard */
Packit Service 4684c1
  { "EDT",      tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
Packit Service 4684c1
  { "CST",      tZONE,    -HOUR ( 6) }, /* Central Standard */
Packit Service 4684c1
  { "CDT",      tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
Packit Service 4684c1
  { "MST",      tZONE,    -HOUR ( 7) }, /* Mountain Standard */
Packit Service 4684c1
  { "MDT",      tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
Packit Service 4684c1
  { "PST",      tZONE,    -HOUR ( 8) }, /* Pacific Standard */
Packit Service 4684c1
  { "PDT",      tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
Packit Service 4684c1
  { "AKST",     tZONE,    -HOUR ( 9) }, /* Alaska Standard */
Packit Service 4684c1
  { "AKDT",     tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
Packit Service 4684c1
  { "HST",      tZONE,    -HOUR (10) }, /* Hawaii Standard */
Packit Service 4684c1
  { "HAST",     tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
Packit Service 4684c1
  { "HADT",     tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
Packit Service 4684c1
  { "SST",      tZONE,    -HOUR (12) }, /* Samoa Standard */
Packit Service 4684c1
  { "WAT",      tZONE,     HOUR ( 1) }, /* West Africa */
Packit Service 4684c1
  { "CET",      tZONE,     HOUR ( 1) }, /* Central European */
Packit Service 4684c1
  { "CEST",     tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
Packit Service 4684c1
  { "MET",      tZONE,     HOUR ( 1) }, /* Middle European */
Packit Service 4684c1
  { "MEZ",      tZONE,     HOUR ( 1) }, /* Middle European */
Packit Service 4684c1
  { "MEST",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
Packit Service 4684c1
  { "MESZ",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
Packit Service 4684c1
  { "EET",      tZONE,     HOUR ( 2) }, /* Eastern European */
Packit Service 4684c1
  { "EEST",     tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
Packit Service 4684c1
  { "CAT",      tZONE,     HOUR ( 2) }, /* Central Africa */
Packit Service 4684c1
  { "SAST",     tZONE,     HOUR ( 2) }, /* South Africa Standard */
Packit Service 4684c1
  { "EAT",      tZONE,     HOUR ( 3) }, /* East Africa */
Packit Service 4684c1
  { "MSK",      tZONE,     HOUR ( 3) }, /* Moscow */
Packit Service 4684c1
  { "MSD",      tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
Packit Service 4684c1
  { "IST",      tZONE,    (HOUR ( 5) + 30 * 60) }, /* India Standard */
Packit Service 4684c1
  { "SGT",      tZONE,     HOUR ( 8) }, /* Singapore */
Packit Service 4684c1
  { "KST",      tZONE,     HOUR ( 9) }, /* Korea Standard */
Packit Service 4684c1
  { "JST",      tZONE,     HOUR ( 9) }, /* Japan Standard */
Packit Service 4684c1
  { "GST",      tZONE,     HOUR (10) }, /* Guam Standard */
Packit Service 4684c1
  { "NZST",     tZONE,     HOUR (12) }, /* New Zealand Standard */
Packit Service 4684c1
  { "NZDT",     tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
/* Military time zone table.
Packit Service 4684c1
Packit Service 4684c1
   RFC 822 got these backwards, but RFC 5322 makes the incorrect
Packit Service 4684c1
   treatment optional, so do them the right way here.
Packit Service 4684c1
Packit Service 4684c1
   Note 'T' is a special case, as it is used as the separator in ISO
Packit Service 4684c1
   8601 date and time of day representation.  */
Packit Service 4684c1
static table const military_table[] =
Packit Service 4684c1
{
Packit Service 4684c1
  { "A", tZONE,  HOUR ( 1) },
Packit Service 4684c1
  { "B", tZONE,  HOUR ( 2) },
Packit Service 4684c1
  { "C", tZONE,  HOUR ( 3) },
Packit Service 4684c1
  { "D", tZONE,  HOUR ( 4) },
Packit Service 4684c1
  { "E", tZONE,  HOUR ( 5) },
Packit Service 4684c1
  { "F", tZONE,  HOUR ( 6) },
Packit Service 4684c1
  { "G", tZONE,  HOUR ( 7) },
Packit Service 4684c1
  { "H", tZONE,  HOUR ( 8) },
Packit Service 4684c1
  { "I", tZONE,  HOUR ( 9) },
Packit Service 4684c1
  { "K", tZONE,  HOUR (10) },
Packit Service 4684c1
  { "L", tZONE,  HOUR (11) },
Packit Service 4684c1
  { "M", tZONE,  HOUR (12) },
Packit Service 4684c1
  { "N", tZONE, -HOUR ( 1) },
Packit Service 4684c1
  { "O", tZONE, -HOUR ( 2) },
Packit Service 4684c1
  { "P", tZONE, -HOUR ( 3) },
Packit Service 4684c1
  { "Q", tZONE, -HOUR ( 4) },
Packit Service 4684c1
  { "R", tZONE, -HOUR ( 5) },
Packit Service 4684c1
  { "S", tZONE, -HOUR ( 6) },
Packit Service 4684c1
  { "T", 'T',    0 },
Packit Service 4684c1
  { "U", tZONE, -HOUR ( 8) },
Packit Service 4684c1
  { "V", tZONE, -HOUR ( 9) },
Packit Service 4684c1
  { "W", tZONE, -HOUR (10) },
Packit Service 4684c1
  { "X", tZONE, -HOUR (11) },
Packit Service 4684c1
  { "Y", tZONE, -HOUR (12) },
Packit Service 4684c1
  { "Z", tZONE,  HOUR ( 0) },
Packit Service 4684c1
  { NULL, 0, 0 }
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1

Packit Service 4684c1
Packit Service 4684c1
/* Convert a time zone expressed as HH:MM into an integer count of
Packit Service 4684c1
   seconds.  If MM is negative, then S is of the form HHMM and needs
Packit Service 4684c1
   to be picked apart; otherwise, S is of the form HH.  As specified in
Packit Service 4684c1
   https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03, allow
Packit Service 4684c1
   only valid TZ range, and consider first two digits as hours, if no
Packit Service 4684c1
   minutes specified.  Return true if successful.  */
Packit Service 4684c1
Packit Service 4684c1
static bool
Packit Service 4684c1
time_zone_hhmm (parser_control *pc, textint s, intmax_t mm)
Packit Service 4684c1
{
Packit Service 4684c1
  intmax_t n_minutes;
Packit Service 4684c1
  bool overflow = false;
Packit Service 4684c1
Packit Service 4684c1
  /* If the length of S is 1 or 2 and no minutes are specified,
Packit Service 4684c1
     interpret it as a number of hours.  */
Packit Service 4684c1
  if (s.digits <= 2 && mm < 0)
Packit Service 4684c1
    s.value *= 100;
Packit Service 4684c1
Packit Service 4684c1
  if (mm < 0)
Packit Service 4684c1
    n_minutes = (s.value / 100) * 60 + s.value % 100;
Packit Service 4684c1
  else
Packit Service 4684c1
    {
Packit Service 4684c1
      overflow |= INT_MULTIPLY_WRAPV (s.value, 60, &n_minutes);
Packit Service 4684c1
      overflow |= (s.negative
Packit Service 4684c1
                   ? INT_SUBTRACT_WRAPV (n_minutes, mm, &n_minutes)
Packit Service 4684c1
                   : INT_ADD_WRAPV (n_minutes, mm, &n_minutes));
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (overflow || ! (-24 * 60 <= n_minutes && n_minutes <= 24 * 60))
Packit Service 4684c1
    return false;
Packit Service 4684c1
  pc->time_zone = n_minutes * 60;
Packit Service 4684c1
  return true;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
to_hour (intmax_t hours, int meridian)
Packit Service 4684c1
{
Packit Service 4684c1
  switch (meridian)
Packit Service 4684c1
    {
Packit Service 4684c1
    default: /* Pacify GCC.  */
Packit Service 4684c1
    case MER24:
Packit Service 4684c1
      return 0 <= hours && hours < 24 ? hours : -1;
Packit Service 4684c1
    case MERam:
Packit Service 4684c1
      return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
Packit Service 4684c1
    case MERpm:
Packit Service 4684c1
      return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
Packit Service 4684c1
    }
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
enum { TM_YEAR_BASE = 1900 };
Packit Service 4684c1
enum { TM_YEAR_BUFSIZE = INT_BUFSIZE_BOUND (int) + 1 };
Packit Service 4684c1
Packit Service 4684c1
/* Convert TM_YEAR, a year minus 1900, to a string that is numerically
Packit Service 4684c1
   correct even if subtracting 1900 would overflow.  */
Packit Service 4684c1
Packit Service 4684c1
static char const *
Packit Service 4684c1
tm_year_str (int tm_year, char buf[TM_YEAR_BUFSIZE])
Packit Service 4684c1
{
Packit Service 4684c1
  verify (TM_YEAR_BASE % 100 == 0);
Packit Service 4684c1
  sprintf (buf, &"-%02d%02d"[-TM_YEAR_BASE <= tm_year],
Packit Service 4684c1
           abs (tm_year / 100 + TM_YEAR_BASE / 100),
Packit Service 4684c1
           abs (tm_year % 100));
Packit Service 4684c1
  return buf;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Convert a text year number to a year minus 1900, working correctly
Packit Service 4684c1
   even if the input is in the range INT_MAX .. INT_MAX + 1900 - 1.  */
Packit Service 4684c1
Packit Service 4684c1
static bool
Packit Service 4684c1
to_tm_year (textint textyear, bool debug, int *tm_year)
Packit Service 4684c1
{
Packit Service 4684c1
  intmax_t year = textyear.value;
Packit Service 4684c1
Packit Service 4684c1
  /* XPG4 suggests that years 00-68 map to 2000-2068, and
Packit Service 4684c1
     years 69-99 map to 1969-1999.  */
Packit Service 4684c1
  if (0 <= year && textyear.digits == 2)
Packit Service 4684c1
    {
Packit Service 4684c1
      year += year < 69 ? 2000 : 1900;
Packit Service 4684c1
      if (debug)
Packit Service 4684c1
        dbg_printf (_("warning: adjusting year value %"PRIdMAX
Packit Service 4684c1
                      " to %"PRIdMAX"\n"),
Packit Service 4684c1
                    textyear.value, year);
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (year < 0
Packit Service 4684c1
      ? INT_SUBTRACT_WRAPV (-TM_YEAR_BASE, year, tm_year)
Packit Service 4684c1
      : INT_SUBTRACT_WRAPV (year, TM_YEAR_BASE, tm_year))
Packit Service 4684c1
    {
Packit Service 4684c1
      if (debug)
Packit Service 4684c1
        dbg_printf (_("error: out-of-range year %"PRIdMAX"\n"), year);
Packit Service 4684c1
      return false;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  return true;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static table const * _GL_ATTRIBUTE_PURE
Packit Service 4684c1
lookup_zone (parser_control const *pc, char const *name)
Packit Service 4684c1
{
Packit Service 4684c1
  table const *tp;
Packit Service 4684c1
Packit Service 4684c1
  for (tp = universal_time_zone_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (name, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  /* Try local zone abbreviations before those in time_zone_table, as
Packit Service 4684c1
     the local ones are more likely to be right.  */
Packit Service 4684c1
  for (tp = pc->local_time_zone_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (name, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  for (tp = time_zone_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (name, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  return NULL;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
#if ! HAVE_TM_GMTOFF
Packit Service 4684c1
/* Yield the difference between *A and *B,
Packit Service 4684c1
   measured in seconds, ignoring leap seconds.
Packit Service 4684c1
   The body of this function is taken directly from the GNU C Library;
Packit Service 4684c1
   see strftime.c.  */
Packit Service 4684c1
static int
Packit Service 4684c1
tm_diff (const struct tm *a, const struct tm *b)
Packit Service 4684c1
{
Packit Service 4684c1
  /* Compute intervening leap days correctly even if year is negative.
Packit Service 4684c1
     Take care to avoid int overflow in leap day calculations,
Packit Service 4684c1
     but it's OK to assume that A and B are close to each other.  */
Packit Service 4684c1
  int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
Packit Service 4684c1
  int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
Packit Service 4684c1
  int a100 = a4 / 25 - (a4 % 25 < 0);
Packit Service 4684c1
  int b100 = b4 / 25 - (b4 % 25 < 0);
Packit Service 4684c1
  int a400 = SHR (a100, 2);
Packit Service 4684c1
  int b400 = SHR (b100, 2);
Packit Service 4684c1
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
Packit Service 4684c1
  int years = a->tm_year - b->tm_year;
Packit Service 4684c1
  int days = (365 * years + intervening_leap_days
Packit Service 4684c1
              + (a->tm_yday - b->tm_yday));
Packit Service 4684c1
  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
Packit Service 4684c1
                + (a->tm_min - b->tm_min))
Packit Service 4684c1
          + (a->tm_sec - b->tm_sec));
Packit Service 4684c1
}
Packit Service 4684c1
#endif /* ! HAVE_TM_GMTOFF */
Packit Service 4684c1
Packit Service 4684c1
static table const *
Packit Service 4684c1
lookup_word (parser_control const *pc, char *word)
Packit Service 4684c1
{
Packit Service 4684c1
  char *p;
Packit Service 4684c1
  char *q;
Packit Service 4684c1
  ptrdiff_t wordlen;
Packit Service 4684c1
  table const *tp;
Packit Service 4684c1
  bool period_found;
Packit Service 4684c1
  bool abbrev;
Packit Service 4684c1
Packit Service 4684c1
  /* Make it uppercase.  */
Packit Service 4684c1
  for (p = word; *p; p++)
Packit Service 4684c1
    *p = c_toupper (to_uchar (*p));
Packit Service 4684c1
Packit Service 4684c1
  for (tp = meridian_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (word, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  /* See if we have an abbreviation for a month.  */
Packit Service 4684c1
  wordlen = strlen (word);
Packit Service 4684c1
  abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
Packit Service 4684c1
Packit Service 4684c1
  for (tp = month_and_day_table; tp->name; tp++)
Packit Service 4684c1
    if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  if ((tp = lookup_zone (pc, word)))
Packit Service 4684c1
    return tp;
Packit Service 4684c1
Packit Service 4684c1
  if (strcmp (word, dst_table[0].name) == 0)
Packit Service 4684c1
    return dst_table;
Packit Service 4684c1
Packit Service 4684c1
  for (tp = time_units_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (word, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  /* Strip off any plural and try the units table again.  */
Packit Service 4684c1
  if (word[wordlen - 1] == 'S')
Packit Service 4684c1
    {
Packit Service 4684c1
      word[wordlen - 1] = '\0';
Packit Service 4684c1
      for (tp = time_units_table; tp->name; tp++)
Packit Service 4684c1
        if (strcmp (word, tp->name) == 0)
Packit Service 4684c1
          return tp;
Packit Service 4684c1
      word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  for (tp = relative_time_table; tp->name; tp++)
Packit Service 4684c1
    if (strcmp (word, tp->name) == 0)
Packit Service 4684c1
      return tp;
Packit Service 4684c1
Packit Service 4684c1
  /* Military time zones.  */
Packit Service 4684c1
  if (wordlen == 1)
Packit Service 4684c1
    for (tp = military_table; tp->name; tp++)
Packit Service 4684c1
      if (word[0] == tp->name[0])
Packit Service 4684c1
        return tp;
Packit Service 4684c1
Packit Service 4684c1
  /* Drop out any periods and try the time zone table again.  */
Packit Service 4684c1
  for (period_found = false, p = q = word; (*p = *q); q++)
Packit Service 4684c1
    if (*q == '.')
Packit Service 4684c1
      period_found = true;
Packit Service 4684c1
    else
Packit Service 4684c1
      p++;
Packit Service 4684c1
  if (period_found && (tp = lookup_zone (pc, word)))
Packit Service 4684c1
    return tp;
Packit Service 4684c1
Packit Service 4684c1
  return NULL;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int
Packit Service 4684c1
yylex (union YYSTYPE *lvalp, parser_control *pc)
Packit Service 4684c1
{
Packit Service 4684c1
  unsigned char c;
Packit Service 4684c1
Packit Service 4684c1
  for (;;)
Packit Service 4684c1
    {
Packit Service 4684c1
      while (c = *pc->input, c_isspace (c))
Packit Service 4684c1
        pc->input++;
Packit Service 4684c1
Packit Service 4684c1
      if (c_isdigit (c) || c == '-' || c == '+')
Packit Service 4684c1
        {
Packit Service 4684c1
          char const *p;
Packit Service 4684c1
          int sign;
Packit Service 4684c1
          intmax_t value = 0;
Packit Service 4684c1
          if (c == '-' || c == '+')
Packit Service 4684c1
            {
Packit Service 4684c1
              sign = c == '-' ? -1 : 1;
Packit Service 4684c1
              while (c = *++pc->input, c_isspace (c))
Packit Service 4684c1
                continue;
Packit Service 4684c1
              if (! c_isdigit (c))
Packit Service 4684c1
                /* skip the '-' sign */
Packit Service 4684c1
                continue;
Packit Service 4684c1
            }
Packit Service 4684c1
          else
Packit Service 4684c1
            sign = 0;
Packit Service 4684c1
          p = pc->input;
Packit Service 4684c1
Packit Service 4684c1
          do
Packit Service 4684c1
            {
Packit Service 4684c1
              if (INT_MULTIPLY_WRAPV (value, 10, &value))
Packit Service 4684c1
                return '?';
Packit Service 4684c1
              if (INT_ADD_WRAPV (value, sign < 0 ? '0' - c : c - '0', &value))
Packit Service 4684c1
                return '?';
Packit Service 4684c1
              c = *++p;
Packit Service 4684c1
            }
Packit Service 4684c1
          while (c_isdigit (c));
Packit Service 4684c1
Packit Service 4684c1
          if ((c == '.' || c == ',') && c_isdigit (p[1]))
Packit Service 4684c1
            {
Packit Service 4684c1
              time_t s;
Packit Service 4684c1
              int ns;
Packit Service 4684c1
              int digits;
Packit Service 4684c1
Packit Service 4684c1
              if (time_overflow (value))
Packit Service 4684c1
                return '?';
Packit Service 4684c1
              s = value;
Packit Service 4684c1
Packit Service 4684c1
              /* Accumulate fraction, to ns precision.  */
Packit Service 4684c1
              p++;
Packit Service 4684c1
              ns = *p++ - '0';
Packit Service 4684c1
              for (digits = 2; digits <= LOG10_BILLION; digits++)
Packit Service 4684c1
                {
Packit Service 4684c1
                  ns *= 10;
Packit Service 4684c1
                  if (c_isdigit (*p))
Packit Service 4684c1
                    ns += *p++ - '0';
Packit Service 4684c1
                }
Packit Service 4684c1
Packit Service 4684c1
              /* Skip excess digits, truncating toward -Infinity.  */
Packit Service 4684c1
              if (sign < 0)
Packit Service 4684c1
                for (; c_isdigit (*p); p++)
Packit Service 4684c1
                  if (*p != '0')
Packit Service 4684c1
                    {
Packit Service 4684c1
                      ns++;
Packit Service 4684c1
                      break;
Packit Service 4684c1
                    }
Packit Service 4684c1
              while (c_isdigit (*p))
Packit Service 4684c1
                p++;
Packit Service 4684c1
Packit Service 4684c1
              /* Adjust to the timespec convention, which is that
Packit Service 4684c1
                 tv_nsec is always a positive offset even if tv_sec is
Packit Service 4684c1
                 negative.  */
Packit Service 4684c1
              if (sign < 0 && ns)
Packit Service 4684c1
                {
Packit Service 4684c1
                  if (s == TYPE_MINIMUM (time_t))
Packit Service 4684c1
                    return '?';
Packit Service 4684c1
                  s--;
Packit Service 4684c1
                  ns = BILLION - ns;
Packit Service 4684c1
                }
Packit Service 4684c1
Packit Service 4684c1
              lvalp->timespec.tv_sec = s;
Packit Service 4684c1
              lvalp->timespec.tv_nsec = ns;
Packit Service 4684c1
              pc->input = p;
Packit Service 4684c1
              return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
Packit Service 4684c1
            }
Packit Service 4684c1
          else
Packit Service 4684c1
            {
Packit Service 4684c1
              lvalp->textintval.negative = sign < 0;
Packit Service 4684c1
              lvalp->textintval.value = value;
Packit Service 4684c1
              lvalp->textintval.digits = p - pc->input;
Packit Service 4684c1
              pc->input = p;
Packit Service 4684c1
              return sign ? tSNUMBER : tUNUMBER;
Packit Service 4684c1
            }
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      if (c_isalpha (c))
Packit Service 4684c1
        {
Packit Service 4684c1
          char buff[20];
Packit Service 4684c1
          char *p = buff;
Packit Service 4684c1
          table const *tp;
Packit Service 4684c1
Packit Service 4684c1
          do
Packit Service 4684c1
            {
Packit Service 4684c1
              if (p < buff + sizeof buff - 1)
Packit Service 4684c1
                *p++ = c;
Packit Service 4684c1
              c = *++pc->input;
Packit Service 4684c1
            }
Packit Service 4684c1
          while (c_isalpha (c) || c == '.');
Packit Service 4684c1
Packit Service 4684c1
          *p = '\0';
Packit Service 4684c1
          tp = lookup_word (pc, buff);
Packit Service 4684c1
          if (! tp)
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc->parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: unknown word '%s'\n"), buff);
Packit Service 4684c1
              return '?';
Packit Service 4684c1
            }
Packit Service 4684c1
          lvalp->intval = tp->value;
Packit Service 4684c1
          return tp->type;
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      if (c != '(')
Packit Service 4684c1
        return to_uchar (*pc->input++);
Packit Service 4684c1
Packit Service 4684c1
      ptrdiff_t count = 0;
Packit Service 4684c1
      do
Packit Service 4684c1
        {
Packit Service 4684c1
          c = *pc->input++;
Packit Service 4684c1
          if (c == '\0')
Packit Service 4684c1
            return c;
Packit Service 4684c1
          if (c == '(')
Packit Service 4684c1
            count++;
Packit Service 4684c1
          else if (c == ')')
Packit Service 4684c1
            count--;
Packit Service 4684c1
        }
Packit Service 4684c1
      while (count != 0);
Packit Service 4684c1
    }
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Do nothing if the parser reports an error.  */
Packit Service 4684c1
static int
Packit Service 4684c1
yyerror (parser_control const *pc _GL_UNUSED,
Packit Service 4684c1
         char const *s _GL_UNUSED)
Packit Service 4684c1
{
Packit Service 4684c1
  return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* If *TM0 is the old and *TM1 is the new value of a struct tm after
Packit Service 4684c1
   passing it to mktime_z, return true if it's OK.  It's not OK if
Packit Service 4684c1
   mktime failed or if *TM0 has out-of-range mainline members.
Packit Service 4684c1
   The caller should set TM1->tm_wday to -1 before calling mktime,
Packit Service 4684c1
   as a negative tm_wday is how mktime failure is inferred.  */
Packit Service 4684c1
Packit Service 4684c1
static bool
Packit Service 4684c1
mktime_ok (struct tm const *tm0, struct tm const *tm1)
Packit Service 4684c1
{
Packit Service 4684c1
  if (tm1->tm_wday < 0)
Packit Service 4684c1
    return false;
Packit Service 4684c1
Packit Service 4684c1
  return ! ((tm0->tm_sec ^ tm1->tm_sec)
Packit Service 4684c1
            | (tm0->tm_min ^ tm1->tm_min)
Packit Service 4684c1
            | (tm0->tm_hour ^ tm1->tm_hour)
Packit Service 4684c1
            | (tm0->tm_mday ^ tm1->tm_mday)
Packit Service 4684c1
            | (tm0->tm_mon ^ tm1->tm_mon)
Packit Service 4684c1
            | (tm0->tm_year ^ tm1->tm_year));
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Debugging: format a 'struct tm' into a buffer, taking the parser's
Packit Service 4684c1
   timezone information into account (if pc != NULL).  */
Packit Service 4684c1
static char const *
Packit Service 4684c1
debug_strfdatetime (struct tm const *tm, parser_control const *pc,
Packit Service 4684c1
                    char *buf, int n)
Packit Service 4684c1
{
Packit Service 4684c1
  /* TODO:
Packit Service 4684c1
     1. find an optimal way to print date string in a clear and unambiguous
Packit Service 4684c1
        format.  Currently, always add '(Y-M-D)' prefix.
Packit Service 4684c1
        Consider '2016y01m10d'  or 'year(2016) month(01) day(10)'.
Packit Service 4684c1
Packit Service 4684c1
        If the user needs debug printing, it means he/she already having
Packit Service 4684c1
        issues with the parsing - better to avoid formats that could
Packit Service 4684c1
        be mis-interpreted (e.g., just YYYY-MM-DD).
Packit Service 4684c1
Packit Service 4684c1
     2. Can strftime be used instead?
Packit Service 4684c1
        depends if it is portable and can print invalid dates on all systems.
Packit Service 4684c1
Packit Service 4684c1
     3. Print timezone information ?
Packit Service 4684c1
Packit Service 4684c1
     4. Print DST information ?
Packit Service 4684c1
Packit Service 4684c1
     5. Print nanosecond information ?
Packit Service 4684c1
Packit Service 4684c1
     NOTE:
Packit Service 4684c1
     Printed date/time values might not be valid, e.g., '2016-02-31'
Packit Service 4684c1
     or '2016-19-2016' .  These are the values as parsed from the user
Packit Service 4684c1
     string, before validation.
Packit Service 4684c1
  */
Packit Service 4684c1
  int m = nstrftime (buf, n, "(Y-M-D) %Y-%m-%d %H:%M:%S", tm, 0, 0);
Packit Service 4684c1
Packit Service 4684c1
  /* If parser_control information was provided (for timezone),
Packit Service 4684c1
     and there's enough space in the buffer, add timezone info.  */
Packit Service 4684c1
  if (pc && m < n && pc->zones_seen)
Packit Service 4684c1
    {
Packit Service 4684c1
      int tz = pc->time_zone;
Packit Service 4684c1
Packit Service 4684c1
      /* Account for DST if tLOCAL_ZONE was seen.  */
Packit Service 4684c1
      if (pc->local_zones_seen && !pc->zones_seen && 0 < pc->local_isdst)
Packit Service 4684c1
        tz += 60 * 60;
Packit Service 4684c1
Packit Service 4684c1
      char time_zone_buf[TIME_ZONE_BUFSIZE];
Packit Service 4684c1
      snprintf (&buf[m], n - m, " TZ=%s", time_zone_str (tz, time_zone_buf));
Packit Service 4684c1
    }
Packit Service 4684c1
  return buf;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static char const *
Packit Service 4684c1
debug_strfdate (struct tm const *tm, char *buf, int n)
Packit Service 4684c1
{
Packit Service 4684c1
  char tm_year_buf[TM_YEAR_BUFSIZE];
Packit Service 4684c1
  snprintf (buf, n, "(Y-M-D) %s-%02d-%02d",
Packit Service 4684c1
            tm_year_str (tm->tm_year, tm_year_buf),
Packit Service 4684c1
            tm->tm_mon + 1, tm->tm_mday);
Packit Service 4684c1
  return buf;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static char const *
Packit Service 4684c1
debug_strftime (struct tm const *tm, char *buf, int n)
Packit Service 4684c1
{
Packit Service 4684c1
  snprintf (buf, n, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
Packit Service 4684c1
  return buf;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* If mktime_ok failed, display the failed time values,
Packit Service 4684c1
   and provide possible hints.  Example output:
Packit Service 4684c1
Packit Service 4684c1
    date: error: invalid date/time value:
Packit Service 4684c1
    date:     user provided time: '(Y-M-D) 2006-04-02 02:45:00'
Packit Service 4684c1
    date:        normalized time: '(Y-M-D) 2006-04-02 03:45:00'
Packit Service 4684c1
    date:                                             __
Packit Service 4684c1
    date:      possible reasons:
Packit Service 4684c1
    date:        non-existing due to daylight-saving time;
Packit Service 4684c1
    date:        numeric values overflow;
Packit Service 4684c1
    date:        missing timezone;
Packit Service 4684c1
 */
Packit Service 4684c1
static void
Packit Service 4684c1
debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
Packit Service 4684c1
                     parser_control const *pc, bool time_zone_seen)
Packit Service 4684c1
{
Packit Service 4684c1
  /* TODO: handle t == -1 (as in 'mktime_ok').  */
Packit Service 4684c1
  char tmp[DBGBUFSIZE];
Packit Service 4684c1
  int i;
Packit Service 4684c1
  const bool eq_sec   = (tm0->tm_sec  == tm1->tm_sec);
Packit Service 4684c1
  const bool eq_min   = (tm0->tm_min  == tm1->tm_min);
Packit Service 4684c1
  const bool eq_hour  = (tm0->tm_hour == tm1->tm_hour);
Packit Service 4684c1
  const bool eq_mday  = (tm0->tm_mday == tm1->tm_mday);
Packit Service 4684c1
  const bool eq_month = (tm0->tm_mon  == tm1->tm_mon);
Packit Service 4684c1
  const bool eq_year  = (tm0->tm_year == tm1->tm_year);
Packit Service 4684c1
Packit Service 4684c1
  const bool dst_shift = eq_sec && eq_min && !eq_hour
Packit Service 4684c1
                         && eq_mday && eq_month && eq_year;
Packit Service 4684c1
Packit Service 4684c1
  if (!pc->parse_datetime_debug)
Packit Service 4684c1
    return;
Packit Service 4684c1
Packit Service 4684c1
  dbg_printf (_("error: invalid date/time value:\n"));
Packit Service 4684c1
  dbg_printf (_("    user provided time: '%s'\n"),
Packit Service 4684c1
              debug_strfdatetime (tm0, pc, tmp, sizeof tmp));
Packit Service 4684c1
  dbg_printf (_("       normalized time: '%s'\n"),
Packit Service 4684c1
              debug_strfdatetime (tm1, pc, tmp, sizeof tmp));
Packit Service 4684c1
  /* The format must be aligned with debug_strfdatetime and the two
Packit Service 4684c1
     DEBUG statements above.  This string is not translated.  */
Packit Service 4684c1
  i = snprintf (tmp, sizeof tmp,
Packit Service 4684c1
                "                                 %4s %2s %2s %2s %2s %2s",
Packit Service 4684c1
                eq_year ? "" : "----",
Packit Service 4684c1
                eq_month ? "" : "--",
Packit Service 4684c1
                eq_mday ? "" : "--",
Packit Service 4684c1
                eq_hour ? "" : "--",
Packit Service 4684c1
                eq_min ? "" : "--",
Packit Service 4684c1
                eq_sec ? "" : "--");
Packit Service 4684c1
  /* Trim trailing whitespace.  */
Packit Service 4684c1
  if (0 <= i)
Packit Service 4684c1
    {
Packit Service 4684c1
      if (sizeof tmp - 1 < i)
Packit Service 4684c1
        i = sizeof tmp - 1;
Packit Service 4684c1
      while (0 < i && tmp[i - 1] == ' ')
Packit Service 4684c1
        --i;
Packit Service 4684c1
      tmp[i] = '\0';
Packit Service 4684c1
    }
Packit Service 4684c1
  dbg_printf ("%s\n", tmp);
Packit Service 4684c1
Packit Service 4684c1
  dbg_printf (_("     possible reasons:\n"));
Packit Service 4684c1
  if (dst_shift)
Packit Service 4684c1
    dbg_printf (_("       non-existing due to daylight-saving time;\n"));
Packit Service 4684c1
  if (!eq_mday && !eq_month)
Packit Service 4684c1
    dbg_printf (_("       invalid day/month combination;\n"));
Packit Service 4684c1
  dbg_printf (_("       numeric values overflow;\n"));
Packit Service 4684c1
  dbg_printf ("       %s\n", (time_zone_seen ? _("incorrect timezone")
Packit Service 4684c1
                              : _("missing timezone")));
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* The original interface: run with debug=false and the default timezone.   */
Packit Service 4684c1
bool
Packit Service 4684c1
parse_datetime (struct timespec *result, char const *p,
Packit Service 4684c1
                struct timespec const *now)
Packit Service 4684c1
{
Packit Service 4684c1
  char const *tzstring = getenv ("TZ");
Packit Service 4684c1
  timezone_t tz = tzalloc (tzstring);
Packit Service 4684c1
  if (!tz)
Packit Service 4684c1
    return false;
Packit Service 4684c1
  bool ok = parse_datetime2 (result, p, now, 0, tz, tzstring);
Packit Service 4684c1
  tzfree (tz);
Packit Service 4684c1
  return ok;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
/* Parse a date/time string, storing the resulting time value into *RESULT.
Packit Service 4684c1
   The string itself is pointed to by P.  Return true if successful.
Packit Service 4684c1
   P can be an incomplete or relative time specification; if so, use
Packit Service 4684c1
   *NOW as the basis for the returned time.  Default to timezone
Packit Service 4684c1
   TZDEFAULT, which corresponds to tzalloc (TZSTRING).  */
Packit Service 4684c1
bool
Packit Service 4684c1
parse_datetime2 (struct timespec *result, char const *p,
Packit Service 4684c1
                 struct timespec const *now, unsigned int flags,
Packit Service 4684c1
                 timezone_t tzdefault, char const *tzstring)
Packit Service 4684c1
{
Packit Service 4684c1
  struct tm tm;
Packit Service 4684c1
  struct tm tm0;
Packit Service 4684c1
  char time_zone_buf[TIME_ZONE_BUFSIZE];
Packit Service 4684c1
  char dbg_tm[DBGBUFSIZE];
Packit Service 4684c1
  bool ok = false;
Packit Service 4684c1
  char const *input_sentinel = p + strlen (p);
Packit Service 4684c1
  char *tz1alloc = NULL;
Packit Service 4684c1
Packit Service 4684c1
  /* A reasonable upper bound for the size of ordinary TZ strings.
Packit Service 4684c1
     Use heap allocation if TZ's length exceeds this.  */
Packit Service 4684c1
  enum { TZBUFSIZE = 100 };
Packit Service 4684c1
  char tz1buf[TZBUFSIZE];
Packit Service 4684c1
Packit Service 4684c1
  struct timespec gettime_buffer;
Packit Service 4684c1
  if (! now)
Packit Service 4684c1
    {
Packit Service 4684c1
      gettime (&gettime_buffer);
Packit Service 4684c1
      now = &gettime_buffer;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  time_t Start = now->tv_sec;
Packit Service 4684c1
  int Start_ns = now->tv_nsec;
Packit Service 4684c1
Packit Service 4684c1
  unsigned char c;
Packit Service 4684c1
  while (c = *p, c_isspace (c))
Packit Service 4684c1
    p++;
Packit Service 4684c1
Packit Service 4684c1
  timezone_t tz = tzdefault;
Packit Service 4684c1
Packit Service 4684c1
  /* Store a local copy prior to first "goto".  Without this, a prior use
Packit Service 4684c1
     below of RELATIVE_TIME_0 on the RHS might translate to an assignment-
Packit Service 4684c1
     to-temporary, which would trigger a -Wjump-misses-init warning.  */
Packit Service 4684c1
  const relative_time rel_time_0 = RELATIVE_TIME_0;
Packit Service 4684c1
Packit Service 4684c1
  if (strncmp (p, "TZ=\"", 4) == 0)
Packit Service 4684c1
    {
Packit Service 4684c1
      char const *tzbase = p + 4;
Packit Service 4684c1
      ptrdiff_t tzsize = 1;
Packit Service 4684c1
      char const *s;
Packit Service 4684c1
Packit Service 4684c1
      for (s = tzbase; *s; s++, tzsize++)
Packit Service 4684c1
        if (*s == '\\')
Packit Service 4684c1
          {
Packit Service 4684c1
            s++;
Packit Service 4684c1
            if (! (*s == '\\' || *s == '"'))
Packit Service 4684c1
              break;
Packit Service 4684c1
          }
Packit Service 4684c1
        else if (*s == '"')
Packit Service 4684c1
          {
Packit Service 4684c1
            timezone_t tz1;
Packit Service 4684c1
            char *tz1string = tz1buf;
Packit Service 4684c1
            char *z;
Packit Service 4684c1
            if (TZBUFSIZE < tzsize)
Packit Service 4684c1
              {
Packit Service 4684c1
                tz1alloc = malloc (tzsize);
Packit Service 4684c1
                if (!tz1alloc)
Packit Service 4684c1
                  goto fail;
Packit Service 4684c1
                tz1string = tz1alloc;
Packit Service 4684c1
              }
Packit Service 4684c1
            z = tz1string;
Packit Service 4684c1
            for (s = tzbase; *s != '"'; s++)
Packit Service 4684c1
              *z++ = *(s += *s == '\\');
Packit Service 4684c1
            *z = '\0';
Packit Service 4684c1
            tz1 = tzalloc (tz1string);
Packit Service 4684c1
            if (!tz1)
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            tz = tz1;
Packit Service 4684c1
            tzstring = tz1string;
Packit Service 4684c1
Packit Service 4684c1
            p = s + 1;
Packit Service 4684c1
            while (c = *p, c_isspace (c))
Packit Service 4684c1
              p++;
Packit Service 4684c1
Packit Service 4684c1
            break;
Packit Service 4684c1
          }
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  struct tm tmp;
Packit Service 4684c1
  if (! localtime_rz (tz, &now->tv_sec, &tmp))
Packit Service 4684c1
    goto fail;
Packit Service 4684c1
Packit Service 4684c1
  /* As documented, be careful to treat the empty string just like
Packit Service 4684c1
     a date string of "0".  Without this, an empty string would be
Packit Service 4684c1
     declared invalid when parsed during a DST transition.  */
Packit Service 4684c1
  if (*p == '\0')
Packit Service 4684c1
    p = "0";
Packit Service 4684c1
Packit Service 4684c1
  parser_control pc;
Packit Service 4684c1
  pc.input = p;
Packit Service 4684c1
  pc.parse_datetime_debug = (flags & PARSE_DATETIME_DEBUG) != 0;
Packit Service 4684c1
  if (INT_ADD_WRAPV (tmp.tm_year, TM_YEAR_BASE, &pc.year.value))
Packit Service 4684c1
    {
Packit Service 4684c1
      if (pc.parse_datetime_debug)
Packit Service 4684c1
        dbg_printf (_("error: initial year out of range\n"));
Packit Service 4684c1
      goto fail;
Packit Service 4684c1
    }
Packit Service 4684c1
  pc.year.digits = 0;
Packit Service 4684c1
  pc.month = tmp.tm_mon + 1;
Packit Service 4684c1
  pc.day = tmp.tm_mday;
Packit Service 4684c1
  pc.hour = tmp.tm_hour;
Packit Service 4684c1
  pc.minutes = tmp.tm_min;
Packit Service 4684c1
  pc.seconds.tv_sec = tmp.tm_sec;
Packit Service 4684c1
  pc.seconds.tv_nsec = Start_ns;
Packit Service 4684c1
  tm.tm_isdst = tmp.tm_isdst;
Packit Service 4684c1
Packit Service 4684c1
  pc.meridian = MER24;
Packit Service 4684c1
  pc.rel = rel_time_0;
Packit Service 4684c1
  pc.timespec_seen = false;
Packit Service 4684c1
  pc.rels_seen = false;
Packit Service 4684c1
  pc.dates_seen = 0;
Packit Service 4684c1
  pc.days_seen = 0;
Packit Service 4684c1
  pc.times_seen = 0;
Packit Service 4684c1
  pc.local_zones_seen = 0;
Packit Service 4684c1
  pc.dsts_seen = 0;
Packit Service 4684c1
  pc.zones_seen = 0;
Packit Service 4684c1
  pc.year_seen = false;
Packit Service 4684c1
  pc.debug_dates_seen = false;
Packit Service 4684c1
  pc.debug_days_seen = false;
Packit Service 4684c1
  pc.debug_times_seen = false;
Packit Service 4684c1
  pc.debug_local_zones_seen = false;
Packit Service 4684c1
  pc.debug_zones_seen = false;
Packit Service 4684c1
  pc.debug_year_seen = false;
Packit Service 4684c1
  pc.debug_ordinal_day_seen = false;
Packit Service 4684c1
Packit Service 4684c1
#if HAVE_STRUCT_TM_TM_ZONE
Packit Service 4684c1
  pc.local_time_zone_table[0].name = tmp.tm_zone;
Packit Service 4684c1
  pc.local_time_zone_table[0].type = tLOCAL_ZONE;
Packit Service 4684c1
  pc.local_time_zone_table[0].value = tmp.tm_isdst;
Packit Service 4684c1
  pc.local_time_zone_table[1].name = NULL;
Packit Service 4684c1
Packit Service 4684c1
  /* Probe the names used in the next three calendar quarters, looking
Packit Service 4684c1
     for a tm_isdst different from the one we already have.  */
Packit Service 4684c1
  {
Packit Service 4684c1
    int quarter;
Packit Service 4684c1
    for (quarter = 1; quarter <= 3; quarter++)
Packit Service 4684c1
      {
Packit Service 4684c1
        intmax_t iprobe;
Packit Service 4684c1
        if (INT_ADD_WRAPV (Start, quarter * (90 * 24 * 60 * 60), &iprobe)
Packit Service 4684c1
            || time_overflow (iprobe))
Packit Service 4684c1
          break;
Packit Service 4684c1
        time_t probe = iprobe;
Packit Service 4684c1
        struct tm probe_tm;
Packit Service 4684c1
        if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
Packit Service 4684c1
            && probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
Packit Service 4684c1
          {
Packit Service 4684c1
              {
Packit Service 4684c1
                pc.local_time_zone_table[1].name = probe_tm.tm_zone;
Packit Service 4684c1
                pc.local_time_zone_table[1].type = tLOCAL_ZONE;
Packit Service 4684c1
                pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
Packit Service 4684c1
                pc.local_time_zone_table[2].name = NULL;
Packit Service 4684c1
              }
Packit Service 4684c1
            break;
Packit Service 4684c1
          }
Packit Service 4684c1
      }
Packit Service 4684c1
  }
Packit Service 4684c1
#else
Packit Service 4684c1
#if HAVE_TZNAME
Packit Service 4684c1
  {
Packit Service 4684c1
# if !HAVE_DECL_TZNAME
Packit Service 4684c1
    extern char *tzname[];
Packit Service 4684c1
# endif
Packit Service 4684c1
    int i;
Packit Service 4684c1
    for (i = 0; i < 2; i++)
Packit Service 4684c1
      {
Packit Service 4684c1
        pc.local_time_zone_table[i].name = tzname[i];
Packit Service 4684c1
        pc.local_time_zone_table[i].type = tLOCAL_ZONE;
Packit Service 4684c1
        pc.local_time_zone_table[i].value = i;
Packit Service 4684c1
      }
Packit Service 4684c1
    pc.local_time_zone_table[i].name = NULL;
Packit Service 4684c1
  }
Packit Service 4684c1
#else
Packit Service 4684c1
  pc.local_time_zone_table[0].name = NULL;
Packit Service 4684c1
#endif
Packit Service 4684c1
#endif
Packit Service 4684c1
Packit Service 4684c1
  if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
Packit Service 4684c1
      && ! strcmp (pc.local_time_zone_table[0].name,
Packit Service 4684c1
                   pc.local_time_zone_table[1].name))
Packit Service 4684c1
    {
Packit Service 4684c1
      /* This locale uses the same abbreviation for standard and
Packit Service 4684c1
         daylight times.  So if we see that abbreviation, we don't
Packit Service 4684c1
         know whether it's daylight time.  */
Packit Service 4684c1
      pc.local_time_zone_table[0].value = -1;
Packit Service 4684c1
      pc.local_time_zone_table[1].name = NULL;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (yyparse (&pc) != 0)
Packit Service 4684c1
    {
Packit Service 4684c1
      if (pc.parse_datetime_debug)
Packit Service 4684c1
        dbg_printf ((input_sentinel <= pc.input
Packit Service 4684c1
                     ? _("error: parsing failed\n")
Packit Service 4684c1
                     : _("error: parsing failed, stopped at '%s'\n")),
Packit Service 4684c1
                    pc.input);
Packit Service 4684c1
      goto fail;
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
  /* Determine effective timezone source.  */
Packit Service 4684c1
Packit Service 4684c1
  if (pc.parse_datetime_debug)
Packit Service 4684c1
    {
Packit Service 4684c1
      dbg_printf (_("input timezone: "));
Packit Service 4684c1
Packit Service 4684c1
      if (pc.timespec_seen)
Packit Service 4684c1
        fprintf (stderr, _("'@timespec' - always UTC"));
Packit Service 4684c1
      else if (pc.zones_seen)
Packit Service 4684c1
        fprintf (stderr, _("parsed date/time string"));
Packit Service 4684c1
      else if (tzstring)
Packit Service 4684c1
        {
Packit Service 4684c1
          if (tz != tzdefault)
Packit Service 4684c1
            fprintf (stderr, _("TZ=\"%s\" in date string"), tzstring);
Packit Service 4684c1
          else if (STREQ (tzstring, "UTC0"))
Packit Service 4684c1
            {
Packit Service 4684c1
              /* Special case: 'date -u' sets TZ="UTC0".  */
Packit Service 4684c1
              fprintf (stderr, _("TZ=\"UTC0\" environment value or -u"));
Packit Service 4684c1
            }
Packit Service 4684c1
          else
Packit Service 4684c1
            fprintf (stderr, _("TZ=\"%s\" environment value"), tzstring);
Packit Service 4684c1
        }
Packit Service 4684c1
      else
Packit Service 4684c1
        fprintf (stderr, _("system default"));
Packit Service 4684c1
Packit Service 4684c1
      /* Account for DST changes if tLOCAL_ZONE was seen.
Packit Service 4684c1
         local timezone only changes DST and is relative to the
Packit Service 4684c1
         default timezone.*/
Packit Service 4684c1
      if (pc.local_zones_seen && !pc.zones_seen && 0 < pc.local_isdst)
Packit Service 4684c1
        fprintf (stderr, ", dst");
Packit Service 4684c1
Packit Service 4684c1
      if (pc.zones_seen)
Packit Service 4684c1
        fprintf (stderr, " (%s)", time_zone_str (pc.time_zone, time_zone_buf));
Packit Service 4684c1
Packit Service 4684c1
      fputc ('\n', stderr);
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc.timespec_seen)
Packit Service 4684c1
    *result = pc.seconds;
Packit Service 4684c1
  else
Packit Service 4684c1
    {
Packit Service 4684c1
      if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
Packit Service 4684c1
               | (pc.local_zones_seen + pc.zones_seen)))
Packit Service 4684c1
        {
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc.times_seen > 1)
Packit Service 4684c1
                dbg_printf ("error: seen multiple time parts\n");
Packit Service 4684c1
              if (pc.dates_seen > 1)
Packit Service 4684c1
                dbg_printf ("error: seen multiple date parts\n");
Packit Service 4684c1
              if (pc.days_seen > 1)
Packit Service 4684c1
                dbg_printf ("error: seen multiple days parts\n");
Packit Service 4684c1
              if (pc.dsts_seen > 1)
Packit Service 4684c1
                dbg_printf ("error: seen multiple daylight-saving parts\n");
Packit Service 4684c1
              if ((pc.local_zones_seen + pc.zones_seen) > 1)
Packit Service 4684c1
                dbg_printf ("error: seen multiple time-zone parts\n");
Packit Service 4684c1
            }
Packit Service 4684c1
          goto fail;
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      if (! to_tm_year (pc.year, pc.parse_datetime_debug, &tm.tm_year)
Packit Service 4684c1
          || INT_ADD_WRAPV (pc.month, -1, &tm.tm_mon)
Packit Service 4684c1
          || INT_ADD_WRAPV (pc.day, 0, &tm.tm_mday))
Packit Service 4684c1
        {
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            dbg_printf (_("error: year, month, or day overflow\n"));
Packit Service 4684c1
          goto fail;
Packit Service 4684c1
        }
Packit Service 4684c1
      if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
Packit Service 4684c1
        {
Packit Service 4684c1
          tm.tm_hour = to_hour (pc.hour, pc.meridian);
Packit Service 4684c1
          if (tm.tm_hour < 0)
Packit Service 4684c1
            {
Packit Service 4684c1
              char const *mrd = (pc.meridian == MERam ? "am"
Packit Service 4684c1
                                 : pc.meridian == MERpm ?"pm" : "");
Packit Service 4684c1
              if (pc.parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: invalid hour %"PRIdMAX"%s\n"),
Packit Service 4684c1
                            pc.hour, mrd);
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
          tm.tm_min = pc.minutes;
Packit Service 4684c1
          tm.tm_sec = pc.seconds.tv_sec;
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            dbg_printf ((pc.times_seen
Packit Service 4684c1
                         ? _("using specified time as starting value: '%s'\n")
Packit Service 4684c1
                         : _("using current time as starting value: '%s'\n")),
Packit Service 4684c1
                        debug_strftime (&tm, dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
        }
Packit Service 4684c1
      else
Packit Service 4684c1
        {
Packit Service 4684c1
          tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
Packit Service 4684c1
          pc.seconds.tv_nsec = 0;
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            dbg_printf ("warning: using midnight as starting time: 00:00:00\n");
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      /* Let mktime deduce tm_isdst if we have an absolute timestamp.  */
Packit Service 4684c1
      if (pc.dates_seen | pc.days_seen | pc.times_seen)
Packit Service 4684c1
        tm.tm_isdst = -1;
Packit Service 4684c1
Packit Service 4684c1
      /* But if the input explicitly specifies local time with or without
Packit Service 4684c1
         DST, give mktime that information.  */
Packit Service 4684c1
      if (pc.local_zones_seen)
Packit Service 4684c1
        tm.tm_isdst = pc.local_isdst;
Packit Service 4684c1
Packit Service 4684c1
      tm0.tm_sec = tm.tm_sec;
Packit Service 4684c1
      tm0.tm_min = tm.tm_min;
Packit Service 4684c1
      tm0.tm_hour = tm.tm_hour;
Packit Service 4684c1
      tm0.tm_mday = tm.tm_mday;
Packit Service 4684c1
      tm0.tm_mon = tm.tm_mon;
Packit Service 4684c1
      tm0.tm_year = tm.tm_year;
Packit Service 4684c1
      tm0.tm_isdst = tm.tm_isdst;
Packit Service 4684c1
      tm.tm_wday = -1;
Packit Service 4684c1
Packit Service 4684c1
      Start = mktime_z (tz, &tm;;
Packit Service 4684c1
Packit Service 4684c1
      if (! mktime_ok (&tm0, &tm))
Packit Service 4684c1
        {
Packit Service 4684c1
          bool repaired = false;
Packit Service 4684c1
          bool time_zone_seen = pc.zones_seen != 0;
Packit Service 4684c1
          if (time_zone_seen)
Packit Service 4684c1
            {
Packit Service 4684c1
              /* Guard against falsely reporting errors near the time_t
Packit Service 4684c1
                 boundaries when parsing times in other time zones.  For
Packit Service 4684c1
                 example, suppose the input string "1969-12-31 23:00:00 -0100",
Packit Service 4684c1
                 the current time zone is 8 hours ahead of UTC, and the min
Packit Service 4684c1
                 time_t value is 1970-01-01 00:00:00 UTC.  Then the min
Packit Service 4684c1
                 localtime value is 1970-01-01 08:00:00, and mktime will
Packit Service 4684c1
                 therefore fail on 1969-12-31 23:00:00.  To work around the
Packit Service 4684c1
                 problem, set the time zone to 1 hour behind UTC temporarily
Packit Service 4684c1
                 by setting TZ="XXX1:00" and try mktime again.  */
Packit Service 4684c1
Packit Service 4684c1
              char tz2buf[sizeof "XXX" - 1 + TIME_ZONE_BUFSIZE];
Packit Service 4684c1
              tz2buf[0] = tz2buf[1] = tz2buf[2] = 'X';
Packit Service 4684c1
              time_zone_str (pc.time_zone, &tz2buf[3]);
Packit Service 4684c1
              timezone_t tz2 = tzalloc (tz2buf);
Packit Service 4684c1
              if (!tz2)
Packit Service 4684c1
                {
Packit Service 4684c1
                  if (pc.parse_datetime_debug)
Packit Service 4684c1
                    dbg_printf (_("error: tzalloc (\"%s\") failed\n"), tz2buf);
Packit Service 4684c1
                  goto fail;
Packit Service 4684c1
                }
Packit Service 4684c1
              tm.tm_sec = tm0.tm_sec;
Packit Service 4684c1
              tm.tm_min = tm0.tm_min;
Packit Service 4684c1
              tm.tm_hour = tm0.tm_hour;
Packit Service 4684c1
              tm.tm_mday = tm0.tm_mday;
Packit Service 4684c1
              tm.tm_mon = tm0.tm_mon;
Packit Service 4684c1
              tm.tm_year = tm0.tm_year;
Packit Service 4684c1
              tm.tm_isdst = tm0.tm_isdst;
Packit Service 4684c1
              tm.tm_wday = -1;
Packit Service 4684c1
              Start = mktime_z (tz2, &tm;;
Packit Service 4684c1
              repaired = mktime_ok (&tm0, &tm;;
Packit Service 4684c1
              tzfree (tz2);
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
          if (! repaired)
Packit Service 4684c1
            {
Packit Service 4684c1
              debug_mktime_not_ok (&tm0, &tm, &pc, time_zone_seen);
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      char dbg_ord[DBGBUFSIZE];
Packit Service 4684c1
Packit Service 4684c1
      if (pc.days_seen && ! pc.dates_seen)
Packit Service 4684c1
        {
Packit Service 4684c1
          intmax_t dayincr;
Packit Service 4684c1
          if (INT_MULTIPLY_WRAPV ((pc.day_ordinal
Packit Service 4684c1
                                   - (0 < pc.day_ordinal
Packit Service 4684c1
                                      && tm.tm_wday != pc.day_number)),
Packit Service 4684c1
                                  7, &dayincr)
Packit Service 4684c1
              || INT_ADD_WRAPV ((pc.day_number - tm.tm_wday + 7) % 7,
Packit Service 4684c1
                                dayincr, &dayincr)
Packit Service 4684c1
              || INT_ADD_WRAPV (dayincr, tm.tm_mday, &tm.tm_mday))
Packit Service 4684c1
            Start = -1;
Packit Service 4684c1
          else
Packit Service 4684c1
            {
Packit Service 4684c1
              tm.tm_isdst = -1;
Packit Service 4684c1
              Start = mktime_z (tz, &tm;;
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
          if (Start == (time_t) -1)
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc.parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: day '%s' "
Packit Service 4684c1
                              "(day ordinal=%"PRIdMAX" number=%d) "
Packit Service 4684c1
                              "resulted in an invalid date: '%s'\n"),
Packit Service 4684c1
                            str_days (&pc, dbg_ord, sizeof dbg_ord),
Packit Service 4684c1
                            pc.day_ordinal, pc.day_number,
Packit Service 4684c1
                            debug_strfdatetime (&tm, &pc, dbg_tm,
Packit Service 4684c1
                                                sizeof dbg_tm));
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            dbg_printf (_("new start date: '%s' is '%s'\n"),
Packit Service 4684c1
                        str_days (&pc, dbg_ord, sizeof dbg_ord),
Packit Service 4684c1
                        debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      if (pc.parse_datetime_debug)
Packit Service 4684c1
        {
Packit Service 4684c1
          if (!pc.dates_seen && !pc.days_seen)
Packit Service 4684c1
            dbg_printf (_("using current date as starting value: '%s'\n"),
Packit Service 4684c1
                        debug_strfdate (&tm, dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
Packit Service 4684c1
          if (pc.days_seen && pc.dates_seen)
Packit Service 4684c1
            dbg_printf (_("warning: day (%s) ignored when explicit dates "
Packit Service 4684c1
                          "are given\n"),
Packit Service 4684c1
                        str_days (&pc, dbg_ord, sizeof dbg_ord));
Packit Service 4684c1
Packit Service 4684c1
          dbg_printf (_("starting date/time: '%s'\n"),
Packit Service 4684c1
                      debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      /* Add relative date.  */
Packit Service 4684c1
      if (pc.rel.year | pc.rel.month | pc.rel.day)
Packit Service 4684c1
        {
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            {
Packit Service 4684c1
              if ((pc.rel.year != 0 || pc.rel.month != 0) && tm.tm_mday != 15)
Packit Service 4684c1
                dbg_printf (_("warning: when adding relative months/years, "
Packit Service 4684c1
                              "it is recommended to specify the 15th of the "
Packit Service 4684c1
                              "months\n"));
Packit Service 4684c1
Packit Service 4684c1
              if (pc.rel.day != 0 && tm.tm_hour != 12)
Packit Service 4684c1
                dbg_printf (_("warning: when adding relative days, "
Packit Service 4684c1
                              "it is recommended to specify noon\n"));
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
          int year, month, day;
Packit Service 4684c1
          if (INT_ADD_WRAPV (tm.tm_year, pc.rel.year, &year)
Packit Service 4684c1
              || INT_ADD_WRAPV (tm.tm_mon, pc.rel.month, &month)
Packit Service 4684c1
              || INT_ADD_WRAPV (tm.tm_mday, pc.rel.day, &day))
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc.parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: %s:%d\n"), __FILE__, __LINE__);
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
          tm.tm_year = year;
Packit Service 4684c1
          tm.tm_mon = month;
Packit Service 4684c1
          tm.tm_mday = day;
Packit Service 4684c1
          tm.tm_hour = tm0.tm_hour;
Packit Service 4684c1
          tm.tm_min = tm0.tm_min;
Packit Service 4684c1
          tm.tm_sec = tm0.tm_sec;
Packit Service 4684c1
          tm.tm_isdst = tm0.tm_isdst;
Packit Service 4684c1
          Start = mktime_z (tz, &tm;;
Packit Service 4684c1
          if (Start == (time_t) -1)
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc.parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: adding relative date resulted "
Packit Service 4684c1
                              "in an invalid date: '%s'\n"),
Packit Service 4684c1
                            debug_strfdatetime (&tm, &pc, dbg_tm,
Packit Service 4684c1
                                                sizeof dbg_tm));
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
          if (pc.parse_datetime_debug)
Packit Service 4684c1
            {
Packit Service 4684c1
              dbg_printf (_("after date adjustment "
Packit Service 4684c1
                            "(%+"PRIdMAX" years, %+"PRIdMAX" months, "
Packit Service 4684c1
                            "%+"PRIdMAX" days),\n"),
Packit Service 4684c1
                          pc.rel.year, pc.rel.month, pc.rel.day);
Packit Service 4684c1
              dbg_printf (_("    new date/time = '%s'\n"),
Packit Service 4684c1
                          debug_strfdatetime (&tm, &pc, dbg_tm,
Packit Service 4684c1
                                              sizeof dbg_tm));
Packit Service 4684c1
Packit Service 4684c1
              /* Warn about crossing DST due to time adjustment.
Packit Service 4684c1
                 Example: https://bugs.gnu.org/8357
Packit Service 4684c1
                 env TZ=Europe/Helsinki \
Packit Service 4684c1
                   date --debug \
Packit Service 4684c1
                        -d 'Mon Mar 28 00:36:07 2011 EEST 1 day ago'
Packit Service 4684c1
Packit Service 4684c1
                 This case is different than DST changes due to time adjustment,
Packit Service 4684c1
                 i.e., "1 day ago" vs "24 hours ago" are calculated in different
Packit Service 4684c1
                 places.
Packit Service 4684c1
Packit Service 4684c1
                 'tm0.tm_isdst' contains the DST of the input date,
Packit Service 4684c1
                 'tm.tm_isdst' is the normalized result after calling
Packit Service 4684c1
                 mktime (&tm).
Packit Service 4684c1
              */
Packit Service 4684c1
              if (tm0.tm_isdst != -1 && tm.tm_isdst != tm0.tm_isdst)
Packit Service 4684c1
                dbg_printf (_("warning: daylight saving time changed after "
Packit Service 4684c1
                              "date adjustment\n"));
Packit Service 4684c1
Packit Service 4684c1
              /* Warn if the user did not ask to adjust days but mday changed,
Packit Service 4684c1
                 or
Packit Service 4684c1
                 user did not ask to adjust months/days but the month changed.
Packit Service 4684c1
Packit Service 4684c1
                 Example for first case:
Packit Service 4684c1
                 2016-05-31 + 1 month => 2016-06-31 => 2016-07-01.
Packit Service 4684c1
                 User asked to adjust month, but the day changed from 31 to 01.
Packit Service 4684c1
Packit Service 4684c1
                 Example for second case:
Packit Service 4684c1
                 2016-02-29 + 1 year => 2017-02-29 => 2017-03-01.
Packit Service 4684c1
                 User asked to adjust year, but the month changed from 02 to 03.
Packit Service 4684c1
              */
Packit Service 4684c1
              if (pc.rel.day == 0
Packit Service 4684c1
                  && (tm.tm_mday != day
Packit Service 4684c1
                      || (pc.rel.month == 0 && tm.tm_mon != month)))
Packit Service 4684c1
                {
Packit Service 4684c1
                  dbg_printf (_("warning: month/year adjustment resulted in "
Packit Service 4684c1
                                "shifted dates:\n"));
Packit Service 4684c1
                  char tm_year_buf[TM_YEAR_BUFSIZE];
Packit Service 4684c1
                  dbg_printf (_("     adjusted Y M D: %s %02d %02d\n"),
Packit Service 4684c1
                              tm_year_str (year, tm_year_buf), month + 1, day);
Packit Service 4684c1
                  dbg_printf (_("   normalized Y M D: %s %02d %02d\n"),
Packit Service 4684c1
                              tm_year_str (tm.tm_year, tm_year_buf),
Packit Service 4684c1
                              tm.tm_mon + 1, tm.tm_mday);
Packit Service 4684c1
                }
Packit Service 4684c1
            }
Packit Service 4684c1
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      /* The only "output" of this if-block is an updated Start value,
Packit Service 4684c1
         so this block must follow others that clobber Start.  */
Packit Service 4684c1
      if (pc.zones_seen)
Packit Service 4684c1
        {
Packit Service 4684c1
          intmax_t delta = pc.time_zone, t1;
Packit Service 4684c1
          bool overflow = false;
Packit Service 4684c1
#ifdef HAVE_TM_GMTOFF
Packit Service 4684c1
          long int utcoff = tm.tm_gmtoff;
Packit Service 4684c1
#else
Packit Service 4684c1
          time_t t = Start;
Packit Service 4684c1
          struct tm gmt;
Packit Service 4684c1
          int utcoff = (gmtime_r (&t, &gmt)
Packit Service 4684c1
                        ? tm_diff (&tm, &gmt)
Packit Service 4684c1
                        : (overflow = true, 0));
Packit Service 4684c1
#endif
Packit Service 4684c1
          overflow |= INT_SUBTRACT_WRAPV (delta, utcoff, &delta);
Packit Service 4684c1
          overflow |= INT_SUBTRACT_WRAPV (Start, delta, &t1;;
Packit Service 4684c1
          if (overflow || time_overflow (t1))
Packit Service 4684c1
            {
Packit Service 4684c1
              if (pc.parse_datetime_debug)
Packit Service 4684c1
                dbg_printf (_("error: timezone %d caused time_t overflow\n"),
Packit Service 4684c1
                            pc.time_zone);
Packit Service 4684c1
              goto fail;
Packit Service 4684c1
            }
Packit Service 4684c1
          Start = t1;
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
      if (pc.parse_datetime_debug)
Packit Service 4684c1
        {
Packit Service 4684c1
          intmax_t Starti = Start;
Packit Service 4684c1
          dbg_printf (_("'%s' = %"PRIdMAX" epoch-seconds\n"),
Packit Service 4684c1
                      debug_strfdatetime (&tm, &pc, dbg_tm, sizeof dbg_tm),
Packit Service 4684c1
                      Starti);
Packit Service 4684c1
        }
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
      /* Add relative hours, minutes, and seconds.  On hosts that support
Packit Service 4684c1
         leap seconds, ignore the possibility of leap seconds; e.g.,
Packit Service 4684c1
         "+ 10 minutes" adds 600 seconds, even if one of them is a
Packit Service 4684c1
         leap second.  Typically this is not what the user wants, but it's
Packit Service 4684c1
         too hard to do it the other way, because the time zone indicator
Packit Service 4684c1
         must be applied before relative times, and if mktime is applied
Packit Service 4684c1
         again the time zone will be lost.  */
Packit Service 4684c1
      {
Packit Service 4684c1
        intmax_t orig_ns = pc.seconds.tv_nsec;
Packit Service 4684c1
        intmax_t sum_ns = orig_ns + pc.rel.ns;
Packit Service 4684c1
        int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
Packit Service 4684c1
        int d4 = (sum_ns - normalized_ns) / BILLION;
Packit Service 4684c1
        intmax_t d1, t1, d2, t2, t3, t4;
Packit Service 4684c1
        if (INT_MULTIPLY_WRAPV (pc.rel.hour, 60 * 60, &d1)
Packit Service 4684c1
            || INT_ADD_WRAPV (Start, d1, &t1)
Packit Service 4684c1
            || INT_MULTIPLY_WRAPV (pc.rel.minutes, 60, &d2)
Packit Service 4684c1
            || INT_ADD_WRAPV (t1, d2, &t2)
Packit Service 4684c1
            || INT_ADD_WRAPV (t2, pc.rel.seconds, &t3)
Packit Service 4684c1
            || INT_ADD_WRAPV (t3, d4, &t4)
Packit Service 4684c1
            || time_overflow (t4))
Packit Service 4684c1
          {
Packit Service 4684c1
            if (pc.parse_datetime_debug)
Packit Service 4684c1
              dbg_printf (_("error: adding relative time caused an "
Packit Service 4684c1
                            "overflow\n"));
Packit Service 4684c1
            goto fail;
Packit Service 4684c1
          }
Packit Service 4684c1
Packit Service 4684c1
        result->tv_sec = t4;
Packit Service 4684c1
        result->tv_nsec = normalized_ns;
Packit Service 4684c1
Packit Service 4684c1
        if (pc.parse_datetime_debug
Packit Service 4684c1
            && (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns))
Packit Service 4684c1
          {
Packit Service 4684c1
            dbg_printf (_("after time adjustment (%+"PRIdMAX" hours, "
Packit Service 4684c1
                          "%+"PRIdMAX" minutes, "
Packit Service 4684c1
                          "%+"PRIdMAX" seconds, %+d ns),\n"),
Packit Service 4684c1
                        pc.rel.hour, pc.rel.minutes, pc.rel.seconds,
Packit Service 4684c1
                        pc.rel.ns);
Packit Service 4684c1
            dbg_printf (_("    new time = %"PRIdMAX" epoch-seconds\n"), t4);
Packit Service 4684c1
Packit Service 4684c1
            /* Warn about crossing DST due to time adjustment.
Packit Service 4684c1
               Example: https://bugs.gnu.org/8357
Packit Service 4684c1
               env TZ=Europe/Helsinki           \
Packit Service 4684c1
               date --debug                                             \
Packit Service 4684c1
               -d 'Mon Mar 28 00:36:07 2011 EEST 24 hours ago'
Packit Service 4684c1
Packit Service 4684c1
               This case is different than DST changes due to days adjustment,
Packit Service 4684c1
               i.e., "1 day ago" vs "24 hours ago" are calculated in different
Packit Service 4684c1
               places.
Packit Service 4684c1
Packit Service 4684c1
               'tm.tm_isdst' contains the date after date adjustment.  */
Packit Service 4684c1
            struct tm lmt;
Packit Service 4684c1
            if (tm.tm_isdst != -1 && localtime_rz (tz, &result->tv_sec, &lmt)
Packit Service 4684c1
                && tm.tm_isdst != lmt.tm_isdst)
Packit Service 4684c1
              dbg_printf (_("warning: daylight saving time changed after "
Packit Service 4684c1
                            "time adjustment\n"));
Packit Service 4684c1
          }
Packit Service 4684c1
      }
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  if (pc.parse_datetime_debug)
Packit Service 4684c1
    {
Packit Service 4684c1
      /* Special case: using 'date -u' simply set TZ=UTC0 */
Packit Service 4684c1
      if (! tzstring)
Packit Service 4684c1
        dbg_printf (_("timezone: system default\n"));
Packit Service 4684c1
      else if (STREQ (tzstring, "UTC0"))
Packit Service 4684c1
        dbg_printf (_("timezone: Universal Time\n"));
Packit Service 4684c1
      else
Packit Service 4684c1
        dbg_printf (_("timezone: TZ=\"%s\" environment value\n"), tzstring);
Packit Service 4684c1
Packit Service 4684c1
      intmax_t sec = result->tv_sec;
Packit Service 4684c1
      int nsec = result->tv_nsec;
Packit Service 4684c1
      dbg_printf (_("final: %"PRIdMAX".%09d (epoch-seconds)\n"),
Packit Service 4684c1
                  sec, nsec);
Packit Service 4684c1
Packit Service 4684c1
      struct tm gmt, lmt;
Packit Service 4684c1
      bool got_utc = !!gmtime_r (&result->tv_sec, &gmt);
Packit Service 4684c1
      if (got_utc)
Packit Service 4684c1
        dbg_printf (_("final: %s (UTC)\n"),
Packit Service 4684c1
                    debug_strfdatetime (&gmt, NULL,
Packit Service 4684c1
                                        dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
      if (localtime_rz (tz, &result->tv_sec, &lmt))
Packit Service 4684c1
        {
Packit Service 4684c1
#ifdef HAVE_TM_GMTOFF
Packit Service 4684c1
          bool got_utcoff = true;
Packit Service 4684c1
          long int utcoff = lmt.tm_gmtoff;
Packit Service 4684c1
#else
Packit Service 4684c1
          bool got_utcoff = got_utc;
Packit Service 4684c1
          int utcoff;
Packit Service 4684c1
          if (got_utcoff)
Packit Service 4684c1
            utcoff = tm_diff (&lmt, &gmt);
Packit Service 4684c1
#endif
Packit Service 4684c1
          if (got_utcoff)
Packit Service 4684c1
            dbg_printf (_("final: %s (UTC%s)\n"),
Packit Service 4684c1
                        debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm),
Packit Service 4684c1
                        time_zone_str (utcoff, time_zone_buf));
Packit Service 4684c1
          else
Packit Service 4684c1
            dbg_printf (_("final: %s (unknown time zone offset)\n"),
Packit Service 4684c1
                        debug_strfdatetime (&lmt, NULL, dbg_tm, sizeof dbg_tm));
Packit Service 4684c1
        }
Packit Service 4684c1
    }
Packit Service 4684c1
Packit Service 4684c1
  ok = true;
Packit Service 4684c1
Packit Service 4684c1
 fail:
Packit Service 4684c1
  if (tz != tzdefault)
Packit Service 4684c1
    tzfree (tz);
Packit Service 4684c1
  free (tz1alloc);
Packit Service 4684c1
  return ok;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
#if TEST
Packit Service 4684c1
Packit Service 4684c1
int
Packit Service 4684c1
main (int ac, char **av)
Packit Service 4684c1
{
Packit Service 4684c1
  char buff[BUFSIZ];
Packit Service 4684c1
Packit Service 4684c1
  printf ("Enter date, or blank line to exit.\n\t> ");
Packit Service 4684c1
  fflush (stdout);
Packit Service 4684c1
Packit Service 4684c1
  buff[BUFSIZ - 1] = '\0';
Packit Service 4684c1
  while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
Packit Service 4684c1
    {
Packit Service 4684c1
      struct timespec d;
Packit Service 4684c1
      struct tm const *tm;
Packit Service 4684c1
      if (! parse_datetime (&d, buff, NULL))
Packit Service 4684c1
        printf ("Bad format - couldn't convert.\n");
Packit Service 4684c1
      else if (! (tm = localtime (&d.tv_sec)))
Packit Service 4684c1
        {
Packit Service 4684c1
          intmax_t sec = d.tv_sec;
Packit Service 4684c1
          printf ("localtime (%"PRIdMAX") failed\n", sec);
Packit Service 4684c1
        }
Packit Service 4684c1
      else
Packit Service 4684c1
        {
Packit Service 4684c1
          int ns = d.tv_nsec;
Packit Service 4684c1
          char tm_year_buf[TM_YEAR_BUFSIZE];
Packit Service 4684c1
          printf ("%s-%02d-%02d %02d:%02d:%02d.%09d\n",
Packit Service 4684c1
                  tm_year_str (tm->tm_year, tm_year_buf),
Packit Service 4684c1
                  tm->tm_mon + 1, tm->tm_mday,
Packit Service 4684c1
                  tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
Packit Service 4684c1
        }
Packit Service 4684c1
      printf ("\t> ");
Packit Service 4684c1
      fflush (stdout);
Packit Service 4684c1
    }
Packit Service 4684c1
  return 0;
Packit Service 4684c1
}
Packit Service 4684c1
#endif /* TEST */