mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2025-08-08 17:22:05 +03:00
parse-datetime: add optional debug printing
Print parsing information, warnings, and errors to stderr. * lib/parse-datetime.h (parse_datetimte_debug): New global variable. * lib/parse-datetime.y: (DEBUG_*): Macros calling debug functions if debugging is enabled. (PROGRESS*): Same as DEBUG, for progress reporting. (dbg_printf): Print message to stderr, with 'date' prefix. (struct parser_control): Add 'debug_*_seen' variables. (str_days): Converts day ordinal/number to string (e.g. 'last wed'). (debug_print_current_time, debug_print_relateive_time): Prints the current/relative date/time value of parser_control. (YACC parser syntax): Print parsed parts with DEBUG_* macros. (to_year): Warn about 2-digit year parsing. (yylex): Warn about unrecognized words. (get_effective_timezone): Returns current timezone in minutes. (debug_strf{time,date,datetime}): Convert 'struct tm' to string as clearly and unambigiously as possible. (debug_mktime_not_ok): Print detailed information about failed date/time values. (parse_datetime): Add DEBUG messages for failures, warnings. Add PROGRESS messages for status messages. * modules/parse-datetime: Add 'timegm', 'gettext-h' dependencies.
This commit is contained in:
committed by
Pádraig Brady
parent
8318f4ae29
commit
12ad79069d
25
ChangeLog
25
ChangeLog
@@ -1,3 +1,28 @@
|
|||||||
|
2016-08-09 Assaf Gordon <assafgordon@gmail.com>
|
||||||
|
|
||||||
|
parse-datetime: add optional debug printing
|
||||||
|
Print parsing information, warnings, and errors to stderr.
|
||||||
|
* lib/parse-datetime.h (parse_datetimte_debug): New global variable.
|
||||||
|
* lib/parse-datetime.y:
|
||||||
|
(DEBUG_*): Macros calling debug functions if debugging is enabled.
|
||||||
|
(PROGRESS*): Same as DEBUG, for progress reporting.
|
||||||
|
(dbg_printf): Print message to stderr, with 'date' prefix.
|
||||||
|
(struct parser_control): Add 'debug_*_seen' variables.
|
||||||
|
(str_days): Converts day ordinal/number to string (e.g. 'last wed').
|
||||||
|
(debug_print_current_time, debug_print_relateive_time): Prints the
|
||||||
|
current/relative date/time value of parser_control.
|
||||||
|
(YACC parser syntax): Print parsed parts with DEBUG_* macros.
|
||||||
|
(to_year): Warn about 2-digit year parsing.
|
||||||
|
(yylex): Warn about unrecognized words.
|
||||||
|
(get_effective_timezone): Returns current timezone in minutes.
|
||||||
|
(debug_strf{time,date,datetime}): Convert 'struct tm' to string as
|
||||||
|
clearly and unambigiously as possible.
|
||||||
|
(debug_mktime_not_ok): Print detailed information about failed
|
||||||
|
date/time values.
|
||||||
|
(parse_datetime): Add DEBUG messages for failures, warnings. Add
|
||||||
|
PROGRESS messages for status messages.
|
||||||
|
* modules/parse-datetime: Add 'timegm', 'gettext-h' dependencies.
|
||||||
|
|
||||||
2016-08-06 Jim Meyering <meyering@fb.com>
|
2016-08-06 Jim Meyering <meyering@fb.com>
|
||||||
|
|
||||||
tests/init.sh: exclude dash with bad "local" semantics
|
tests/init.sh: exclude dash with bad "local" semantics
|
||||||
|
@@ -19,4 +19,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
extern bool parse_datetime_debug;
|
||||||
|
|
||||||
bool parse_datetime (struct timespec *, char const *, struct timespec const *);
|
bool parse_datetime (struct timespec *, char const *, struct timespec const *);
|
||||||
|
@@ -59,14 +59,18 @@
|
|||||||
# undef static
|
# undef static
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <c-ctype.h>
|
#include <c-ctype.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "gettext.h"
|
||||||
#include "xalloc.h"
|
#include "xalloc.h"
|
||||||
|
|
||||||
|
#define _(str) gettext (str)
|
||||||
|
|
||||||
/* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
|
/* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers
|
||||||
use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
|
use _STDLIB_H_ as witness. Map the latter to the one bison uses. */
|
||||||
/* FIXME: this is temporary. Remove when we have a mechanism to ensure
|
/* FIXME: this is temporary. Remove when we have a mechanism to ensure
|
||||||
@@ -105,6 +109,8 @@
|
|||||||
|
|
||||||
#define HOUR(x) ((x) * 60)
|
#define HOUR(x) ((x) * 60)
|
||||||
|
|
||||||
|
#define STREQ(a, b) (strcmp (a, b) == 0)
|
||||||
|
|
||||||
/* long_time_t is a signed integer type that contains all time_t values. */
|
/* long_time_t is a signed integer type that contains all time_t values. */
|
||||||
verify (TYPE_IS_INTEGER (time_t));
|
verify (TYPE_IS_INTEGER (time_t));
|
||||||
#if TIME_T_FITS_IN_LONG_INT
|
#if TIME_T_FITS_IN_LONG_INT
|
||||||
@@ -118,6 +124,64 @@ typedef time_t long_time_t;
|
|||||||
errors that the cast doesn't. */
|
errors that the cast doesn't. */
|
||||||
static unsigned char to_uchar (char ch) { return ch; }
|
static unsigned char to_uchar (char ch) { return ch; }
|
||||||
|
|
||||||
|
/* Enable diagnostic debug output to STDERR */
|
||||||
|
bool parse_datetime_debug = false;
|
||||||
|
|
||||||
|
/* Debug message without parameters */
|
||||||
|
#define DEBUG0(msg) \
|
||||||
|
do { \
|
||||||
|
if (parse_datetime_debug) \
|
||||||
|
dbg_printf (msg); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Debug message with printf-style parameters */
|
||||||
|
#define DEBUG(x,...) \
|
||||||
|
do { \
|
||||||
|
if (parse_datetime_debug) \
|
||||||
|
dbg_printf (x,__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Progress messages treated the same as debug messages */
|
||||||
|
#define PROGRESS DEBUG
|
||||||
|
#define PROGRESS0 DEBUG0
|
||||||
|
|
||||||
|
/* Print the date/time of the parser_control struct */
|
||||||
|
#define DEBUG_PRINT_CURRENT_TIME(item,pc) \
|
||||||
|
do { \
|
||||||
|
if (parse_datetime_debug) \
|
||||||
|
debug_print_current_time (item, pc); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Print the relative date/time of the parser control struct */
|
||||||
|
#define DEBUG_PRINT_RELATIVE_TIME(item,pc) \
|
||||||
|
do { \
|
||||||
|
if (parse_datetime_debug) \
|
||||||
|
debug_print_rel_time (item, pc); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* if 'mktime_ok' failed, print informative message with possible reasons */
|
||||||
|
#define DEBUG_MKTIME_NOT_OK(tm0,tm1,pc,zones_seen) \
|
||||||
|
do { \
|
||||||
|
if (parse_datetime_debug) \
|
||||||
|
debug_mktime_not_ok (tm0, tm1, pc, zones_seen); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbg_printf (const char *msg,...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
/* TODO: use gnulib's 'program_name' instead? */
|
||||||
|
fputs ("date: ", stderr);
|
||||||
|
|
||||||
|
va_start (args, msg);
|
||||||
|
vfprintf (stderr, msg, args);
|
||||||
|
va_end (args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Lots of this code assumes time_t and time_t-like values fit into
|
/* Lots of this code assumes time_t and time_t-like values fit into
|
||||||
long_time_t. */
|
long_time_t. */
|
||||||
verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
|
verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
|
||||||
@@ -209,6 +273,20 @@ typedef struct
|
|||||||
size_t times_seen;
|
size_t times_seen;
|
||||||
size_t zones_seen;
|
size_t zones_seen;
|
||||||
|
|
||||||
|
/* which of the 'seen' parts has been printed when debugging */
|
||||||
|
size_t debug_dates_seen;
|
||||||
|
size_t debug_days_seen;
|
||||||
|
size_t debug_local_zones_seen;
|
||||||
|
size_t debug_dsts_seen;
|
||||||
|
size_t debug_times_seen;
|
||||||
|
size_t debug_zones_seen;
|
||||||
|
|
||||||
|
/* true if the user specified explicit ordinal day value, */
|
||||||
|
bool debug_ordinal_day_seen;
|
||||||
|
|
||||||
|
/* the default input timezone, set by TZ value */
|
||||||
|
long int debug_default_input_timezone;
|
||||||
|
|
||||||
/* Table of local time zone abbreviations, terminated by a null entry. */
|
/* Table of local time zone abbreviations, terminated by a null entry. */
|
||||||
table local_time_zone_table[3];
|
table local_time_zone_table[3];
|
||||||
} parser_control;
|
} parser_control;
|
||||||
@@ -282,6 +360,201 @@ set_hhmmss (parser_control *pc, long int hour, long int minutes,
|
|||||||
pc->seconds.tv_nsec = nsec;
|
pc->seconds.tv_nsec = nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns a textual representation of the day ordinal/number values
|
||||||
|
in the parser_control struct (e.g. 'last wed', 'this tues', 'thu') */
|
||||||
|
static const char*
|
||||||
|
str_days (parser_control *pc, char* /*output*/ buffer, size_t n)
|
||||||
|
{
|
||||||
|
/* TODO: use the relative_time_table[] for reverse lookup */
|
||||||
|
static const char* ordinal_values[] = {
|
||||||
|
"last",
|
||||||
|
"this",
|
||||||
|
"next/first",
|
||||||
|
"(SECOND)", /* SECOND is commented out in relative_time_table[] */
|
||||||
|
"third",
|
||||||
|
"fourth",
|
||||||
|
"fifth",
|
||||||
|
"sixth",
|
||||||
|
"seventh",
|
||||||
|
"eight",
|
||||||
|
"ninth",
|
||||||
|
"tenth",
|
||||||
|
"eleventh",
|
||||||
|
"twelfth"};
|
||||||
|
|
||||||
|
static const char* days_values[] = {
|
||||||
|
"Sun",
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* don't add an ordinal prefix if the user didn't specify it
|
||||||
|
(e.g., "this wed" vs "wed") */
|
||||||
|
if (pc->debug_ordinal_day_seen)
|
||||||
|
{
|
||||||
|
/* use word description of possible (e.g. -1 = last, 3 = third) */
|
||||||
|
if (pc->day_ordinal>=-1 && pc->day_ordinal <=12)
|
||||||
|
{
|
||||||
|
strncpy (buffer, ordinal_values[ pc->day_ordinal+1 ], n);
|
||||||
|
buffer[n-1]='\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf (buffer,n,"%ld",pc->day_ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the day name */
|
||||||
|
if (pc->day_number>=0 && pc->day_number<=6)
|
||||||
|
{
|
||||||
|
size_t l = strlen (buffer);
|
||||||
|
if (l>0)
|
||||||
|
{
|
||||||
|
strncat (buffer," ",n-l);
|
||||||
|
++l;
|
||||||
|
}
|
||||||
|
strncat (buffer,days_values[pc->day_number],n-l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* invalid day_number value - should never happen */
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* debugging: print the current time in the parser_control structure.
|
||||||
|
The parser will increment "*_seen" members for those which were parsed.
|
||||||
|
This function will print only newly seen parts. */
|
||||||
|
static void
|
||||||
|
debug_print_current_time (const char* item, parser_control *pc)
|
||||||
|
{
|
||||||
|
char tmp[100] = {0};
|
||||||
|
int space = 0; /* if true, add space delimiter */
|
||||||
|
DEBUG (_("parsed %s part: "), item); /* no newline, more items printed below */
|
||||||
|
|
||||||
|
if (pc->dates_seen != pc->debug_dates_seen)
|
||||||
|
{
|
||||||
|
/*TODO: use pc->year.negative? */
|
||||||
|
fprintf (stderr,"(Y-M-D) %04ld-%02ld-%02ld",
|
||||||
|
pc->year.value, pc->month, pc->day);
|
||||||
|
pc->debug_dates_seen = pc->dates_seen;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->times_seen != pc->debug_times_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,"%02ld:%02ld:%02ld",
|
||||||
|
pc->hour, pc->minutes, pc->seconds.tv_sec);
|
||||||
|
if (pc->seconds.tv_nsec!=0)
|
||||||
|
fprintf (stderr,"%09ld", pc->seconds.tv_nsec);
|
||||||
|
if (pc->meridian==MERpm)
|
||||||
|
fputs ("pm",stderr);
|
||||||
|
|
||||||
|
pc->debug_times_seen = pc->times_seen;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->days_seen != pc->debug_days_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,_("%s (day ordinal=%ld number=%d)"),
|
||||||
|
str_days (pc,tmp,sizeof (tmp)),
|
||||||
|
pc->day_ordinal, pc->day_number);
|
||||||
|
pc->debug_days_seen = pc->days_seen ;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->dsts_seen != pc->debug_dsts_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,_("is-dst=%d"), pc->local_isdst);
|
||||||
|
pc->dsts_seen = pc->debug_dsts_seen;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: fix incorrect display of EST=2:08h? */
|
||||||
|
if (pc->zones_seen != pc->debug_zones_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,_("TZ=%+03d:%02d"), (int)(pc->time_zone/60),
|
||||||
|
abs ((int)pc->time_zone%60));
|
||||||
|
pc->debug_zones_seen = pc->zones_seen;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->local_zones_seen != pc->debug_local_zones_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,_("Local-TZ=%+03d:%02d"), (int)(pc->time_zone/60),
|
||||||
|
abs ((int)pc->time_zone%60));
|
||||||
|
pc->debug_local_zones_seen = pc->local_zones_seen;
|
||||||
|
space = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc->timespec_seen)
|
||||||
|
{
|
||||||
|
if (space)
|
||||||
|
fputc (' ',stderr);
|
||||||
|
fprintf (stderr,_("number of seconds: %ld"), pc->seconds.tv_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputc ('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* debugging: print the current relative values. */
|
||||||
|
static void
|
||||||
|
debug_print_rel_time (const char* item, const parser_control *pc)
|
||||||
|
{
|
||||||
|
int space = 0; /* if true, add space delimiter */
|
||||||
|
DEBUG (_("parsed %s part: "), item); /* no newline, more items printed below */
|
||||||
|
|
||||||
|
if (pc->rel.year==0 && pc->rel.month==0 && pc->rel.day==0
|
||||||
|
&& pc->rel.hour==0 && pc->rel.minutes==00 && pc->rel.seconds == 0
|
||||||
|
&& pc->rel.ns==0)
|
||||||
|
{
|
||||||
|
/* Special case: relative time of this/today/now */
|
||||||
|
fputs (_("today/this/now\n"),stderr);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRINT_REL_PART(x,name) \
|
||||||
|
do { \
|
||||||
|
if ( (pc->rel.x) != 0 ) \
|
||||||
|
{ \
|
||||||
|
if (space) \
|
||||||
|
fputc (' ',stderr); \
|
||||||
|
fprintf (stderr,"%+ld %s", pc->rel.x, name); \
|
||||||
|
space = 1; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
PRINT_REL_PART (year,"year(s)");
|
||||||
|
PRINT_REL_PART (month,"month(s)");
|
||||||
|
PRINT_REL_PART (day,"day(s)");
|
||||||
|
PRINT_REL_PART (hour,"hour(s)");
|
||||||
|
PRINT_REL_PART (minutes,"minutes");
|
||||||
|
PRINT_REL_PART (seconds,"seconds");
|
||||||
|
PRINT_REL_PART (ns,"nanoseconds");
|
||||||
|
|
||||||
|
fputc ('\n',stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
/* We want a reentrant parser, even if the TZ manipulation and the calls to
|
/* We want a reentrant parser, even if the TZ manipulation and the calls to
|
||||||
@@ -330,6 +603,7 @@ timespec:
|
|||||||
{
|
{
|
||||||
pc->seconds = $2;
|
pc->seconds = $2;
|
||||||
pc->timespec_seen = true;
|
pc->timespec_seen = true;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("number of seconds"), pc);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -340,20 +614,47 @@ items:
|
|||||||
|
|
||||||
item:
|
item:
|
||||||
datetime
|
datetime
|
||||||
{ pc->times_seen++; pc->dates_seen++; }
|
{
|
||||||
|
pc->times_seen++; pc->dates_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("datetime"), pc);
|
||||||
|
}
|
||||||
| time
|
| time
|
||||||
{ pc->times_seen++; }
|
{
|
||||||
|
pc->times_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("time"), pc);
|
||||||
|
}
|
||||||
| local_zone
|
| local_zone
|
||||||
{ pc->local_zones_seen++; }
|
{
|
||||||
|
pc->local_zones_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("local_zone"), pc);
|
||||||
|
}
|
||||||
| zone
|
| zone
|
||||||
{ pc->zones_seen++; }
|
{
|
||||||
|
pc->zones_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("zone"), pc);
|
||||||
|
}
|
||||||
| date
|
| date
|
||||||
{ pc->dates_seen++; }
|
{
|
||||||
|
pc->dates_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("date"), pc);
|
||||||
|
}
|
||||||
| day
|
| day
|
||||||
{ pc->days_seen++; }
|
{
|
||||||
|
pc->days_seen++;
|
||||||
|
DEBUG_PRINT_CURRENT_TIME (_("day"), pc);
|
||||||
|
}
|
||||||
| rel
|
| rel
|
||||||
|
{
|
||||||
|
DEBUG_PRINT_RELATIVE_TIME (_("relative"), pc);
|
||||||
|
}
|
||||||
| number
|
| number
|
||||||
|
{
|
||||||
|
DEBUG_PRINT_RELATIVE_TIME (_("number"), pc);
|
||||||
|
}
|
||||||
| hybrid
|
| hybrid
|
||||||
|
{
|
||||||
|
DEBUG_PRINT_RELATIVE_TIME (_("hybrid"), pc);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
datetime:
|
datetime:
|
||||||
@@ -463,11 +764,13 @@ day:
|
|||||||
{
|
{
|
||||||
pc->day_ordinal = $1;
|
pc->day_ordinal = $1;
|
||||||
pc->day_number = $2;
|
pc->day_number = $2;
|
||||||
|
pc->debug_ordinal_day_seen = true;
|
||||||
}
|
}
|
||||||
| tUNUMBER tDAY
|
| tUNUMBER tDAY
|
||||||
{
|
{
|
||||||
pc->day_ordinal = $1.value;
|
pc->day_ordinal = $1.value;
|
||||||
pc->day_number = $2;
|
pc->day_number = $2;
|
||||||
|
pc->debug_ordinal_day_seen = true;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -486,12 +789,18 @@ date:
|
|||||||
you want portability, use the ISO 8601 format. */
|
you want portability, use the ISO 8601 format. */
|
||||||
if (4 <= $1.digits)
|
if (4 <= $1.digits)
|
||||||
{
|
{
|
||||||
|
DEBUG (_("warning: value %ld has %"PRIuMAX" digits. " \
|
||||||
|
"Assuming YYYY/MM/DD\n"), $1.value, $1.digits);
|
||||||
|
|
||||||
pc->year = $1;
|
pc->year = $1;
|
||||||
pc->month = $3.value;
|
pc->month = $3.value;
|
||||||
pc->day = $5.value;
|
pc->day = $5.value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
DEBUG (_("warning: value %ld has less than 4 digits. " \
|
||||||
|
"Assuming MM/DD/YY[YY]\n"), $1.value);
|
||||||
|
|
||||||
pc->month = $1.value;
|
pc->month = $1.value;
|
||||||
pc->day = $3.value;
|
pc->day = $3.value;
|
||||||
pc->year = $5;
|
pc->year = $5;
|
||||||
@@ -905,7 +1214,11 @@ to_year (textint textyear)
|
|||||||
/* XPG4 suggests that years 00-68 map to 2000-2068, and
|
/* XPG4 suggests that years 00-68 map to 2000-2068, and
|
||||||
years 69-99 map to 1969-1999. */
|
years 69-99 map to 1969-1999. */
|
||||||
else if (textyear.digits == 2)
|
else if (textyear.digits == 2)
|
||||||
year += year < 69 ? 2000 : 1900;
|
{
|
||||||
|
year += year < 69 ? 2000 : 1900;
|
||||||
|
DEBUG (_("warning: adjusting year value %ld to %ld\n"),
|
||||||
|
textyear.value, year);
|
||||||
|
}
|
||||||
|
|
||||||
return year;
|
return year;
|
||||||
}
|
}
|
||||||
@@ -1170,7 +1483,10 @@ yylex (union YYSTYPE *lvalp, parser_control *pc)
|
|||||||
*p = '\0';
|
*p = '\0';
|
||||||
tp = lookup_word (pc, buff);
|
tp = lookup_word (pc, buff);
|
||||||
if (! tp)
|
if (! tp)
|
||||||
return '?';
|
{
|
||||||
|
DEBUG (_("error: unknown word '%s'\n"), buff);
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
lvalp->intval = tp->value;
|
lvalp->intval = tp->value;
|
||||||
return tp->type;
|
return tp->type;
|
||||||
}
|
}
|
||||||
@@ -1230,6 +1546,10 @@ mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
|
|||||||
Use heap allocation if TZ's length exceeds this. */
|
Use heap allocation if TZ's length exceeds this. */
|
||||||
enum { TZBUFSIZE = 100 };
|
enum { TZBUFSIZE = 100 };
|
||||||
|
|
||||||
|
/* A reasonable upper bound for the buffer used in debug print outs.
|
||||||
|
see days_to_name(), debug_strftime() and debug_mktime_not_ok() */
|
||||||
|
enum { DBGBUFSIZE = 100 };
|
||||||
|
|
||||||
/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
|
/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
|
||||||
otherwise. */
|
otherwise. */
|
||||||
static char *
|
static char *
|
||||||
@@ -1246,6 +1566,140 @@ get_tz (char tzbuf[TZBUFSIZE])
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* debugging: format a 'struct tm' into a buffer, taking the parser's
|
||||||
|
timezone information into account (if pc!=NULL). */
|
||||||
|
static const char*
|
||||||
|
debug_strfdatetime (const struct tm *tm, const parser_control *pc,
|
||||||
|
char* /*output*/ buf, size_t n)
|
||||||
|
{
|
||||||
|
/* TODO:
|
||||||
|
1. find an optimal way to print date string in a clear and unambiguous
|
||||||
|
format. Currently, always add '(Y-M-D)' prefix.
|
||||||
|
Consider '2016y01m10d' or 'year(2016) month(01) day(10)'.
|
||||||
|
|
||||||
|
If the user needs debug printing, it means he/she already having
|
||||||
|
issues with the parsing - better to avoid formats that could
|
||||||
|
be mis-interpreted (e.g. just YYYY-MM-DD).
|
||||||
|
|
||||||
|
2. Can strftime be used instead?
|
||||||
|
depends if it is portable and can print invalid dates on all systems.
|
||||||
|
|
||||||
|
3. Print timezone information ?
|
||||||
|
|
||||||
|
4. Print DST information ?
|
||||||
|
|
||||||
|
5. Print nanosecond information ?
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
Printed date/time values might not be valid, e.g. '2016-02-31'
|
||||||
|
or '2016-19-2016' . These are the values as parsed from the user
|
||||||
|
string, before validation.
|
||||||
|
*/
|
||||||
|
int m = snprintf (buf,n,"(Y-M-D) %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
|
||||||
|
/* if parser_control information was provided (for timezone),
|
||||||
|
and there's enough space in the buffer - add timezone info */
|
||||||
|
if (pc != NULL && ((n-m)>0))
|
||||||
|
{
|
||||||
|
const long int tz = (pc->zones_seen || pc->local_zones_seen)
|
||||||
|
? pc->time_zone
|
||||||
|
: pc->debug_default_input_timezone;
|
||||||
|
snprintf (&buf[m],n-m," TZ=%+03d:%02d", (int)(tz/60), abs ((int)tz)%60);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
debug_strfdate (const struct tm *tm, char* /*output*/ buf, size_t n)
|
||||||
|
{
|
||||||
|
snprintf (buf,n,"(Y-M-D) %04d-%02d-%02d",
|
||||||
|
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
debug_strftime (const struct tm *tm, char* /*output*/ buf, size_t n)
|
||||||
|
{
|
||||||
|
snprintf (buf,n,"%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If 'mktime_ok()' failed, display the failed time values,
|
||||||
|
and provide possible hints. Example output:
|
||||||
|
|
||||||
|
date: error: invalid date/time value:
|
||||||
|
date: user provided time: '(Y-M-D) 2006-04-02 02:45:00'
|
||||||
|
date: normalized time: '(Y-M-D) 2006-04-02 03:45:00'
|
||||||
|
date: __
|
||||||
|
date: possible reasons:
|
||||||
|
date: non-existing due to daylight-saving time;
|
||||||
|
date: numeric values overflow;
|
||||||
|
date: missing timezone;
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
debug_mktime_not_ok (struct tm const *tm0, struct tm const *tm1,
|
||||||
|
const parser_control *pc, bool time_zone_seen)
|
||||||
|
{
|
||||||
|
/* TODO: handle t==-1 (as in 'mktime_ok') */
|
||||||
|
char tmp[DBGBUFSIZE];
|
||||||
|
int i;
|
||||||
|
const bool eq_sec = (tm0->tm_sec == tm1->tm_sec);
|
||||||
|
const bool eq_min = (tm0->tm_min == tm1->tm_min);
|
||||||
|
const bool eq_hour = (tm0->tm_hour == tm1->tm_hour);
|
||||||
|
const bool eq_mday = (tm0->tm_mday == tm1->tm_mday);
|
||||||
|
const bool eq_month = (tm0->tm_mon == tm1->tm_mon);
|
||||||
|
const bool eq_year = (tm0->tm_year == tm1->tm_year);
|
||||||
|
|
||||||
|
const bool dst_shift = eq_sec && eq_min && !eq_hour
|
||||||
|
&& eq_mday && eq_month && eq_year;
|
||||||
|
|
||||||
|
DEBUG0 (_("error: invalid date/time value:\n"));
|
||||||
|
DEBUG (_(" user provided time: '%s'\n"),
|
||||||
|
debug_strfdatetime (tm0, pc, tmp, sizeof (tmp)));
|
||||||
|
DEBUG (_(" normalized time: '%s'\n"),
|
||||||
|
debug_strfdatetime (tm1, pc, tmp, sizeof (tmp)));
|
||||||
|
/* NOTEs: the format must be aligned with debug_strfdatetime() and the two
|
||||||
|
DEBUG statements above. this string is not translated. */
|
||||||
|
i = snprintf (tmp, sizeof(tmp),
|
||||||
|
" %4s %2s %2s %2s %2s %2s",
|
||||||
|
eq_year?"":"----", eq_month?"":"--", eq_mday?"":"--",
|
||||||
|
eq_hour?"":"--", eq_min?"":"--", eq_sec?"":"--");
|
||||||
|
/* Trim trailing whitespace */
|
||||||
|
if ((i>0) && (i<sizeof(tmp)))
|
||||||
|
{
|
||||||
|
while ((i>0) && (tmp[i-1]==' '))
|
||||||
|
--i;
|
||||||
|
tmp[i] = '\0';
|
||||||
|
}
|
||||||
|
DEBUG ("%s\n", tmp);
|
||||||
|
|
||||||
|
DEBUG0 (_(" possible reasons:\n"));
|
||||||
|
if (dst_shift)
|
||||||
|
DEBUG0 (_(" non-existing due to daylight-saving time;\n"));
|
||||||
|
if (!eq_mday && !eq_month)
|
||||||
|
DEBUG0 (_(" invalid day/month combination;\n"));
|
||||||
|
DEBUG0 (_(" numeric values overflow;\n"));
|
||||||
|
DEBUG (" %s\n",time_zone_seen?_("incorrect timezone")
|
||||||
|
:_("missing timezone"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Returns the effective local timezone, in minutes. */
|
||||||
|
static long int
|
||||||
|
get_effective_timezone (void)
|
||||||
|
{
|
||||||
|
/* TODO: check for failures */
|
||||||
|
const time_t z = 0;
|
||||||
|
time_t lz ;
|
||||||
|
struct tm *ltm;
|
||||||
|
ltm = localtime (&z);
|
||||||
|
lz = timegm (ltm)/60;
|
||||||
|
return (long int)lz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Parse a date/time string, storing the resulting time value into *RESULT.
|
/* Parse a date/time string, storing the resulting time value into *RESULT.
|
||||||
The string itself is pointed to by P. Return true if successful.
|
The string itself is pointed to by P. Return true if successful.
|
||||||
P can be an incomplete or relative time specification; if so, use
|
P can be an incomplete or relative time specification; if so, use
|
||||||
@@ -1266,6 +1720,8 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
char *tz0 = NULL;
|
char *tz0 = NULL;
|
||||||
char tz0buf[TZBUFSIZE];
|
char tz0buf[TZBUFSIZE];
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
char dbg_ord[DBGBUFSIZE];
|
||||||
|
char dbg_tm[DBGBUFSIZE];
|
||||||
|
|
||||||
if (! now)
|
if (! now)
|
||||||
{
|
{
|
||||||
@@ -1351,6 +1807,14 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
pc.local_zones_seen = 0;
|
pc.local_zones_seen = 0;
|
||||||
pc.dsts_seen = 0;
|
pc.dsts_seen = 0;
|
||||||
pc.zones_seen = 0;
|
pc.zones_seen = 0;
|
||||||
|
pc.debug_dates_seen = 0;
|
||||||
|
pc.debug_days_seen = 0;
|
||||||
|
pc.debug_times_seen = 0;
|
||||||
|
pc.debug_local_zones_seen = 0;
|
||||||
|
pc.debug_dsts_seen = 0;
|
||||||
|
pc.debug_zones_seen = 0;
|
||||||
|
pc.debug_ordinal_day_seen = false;
|
||||||
|
pc.debug_default_input_timezone = 0;
|
||||||
|
|
||||||
#if HAVE_STRUCT_TM_TM_ZONE
|
#if HAVE_STRUCT_TM_TM_ZONE
|
||||||
pc.local_time_zone_table[0].name = tmp->tm_zone;
|
pc.local_time_zone_table[0].name = tmp->tm_zone;
|
||||||
@@ -1410,8 +1874,60 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
pc.local_time_zone_table[1].name = NULL;
|
pc.local_time_zone_table[1].name = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pc.debug_default_input_timezone = get_effective_timezone ();
|
||||||
|
|
||||||
if (yyparse (&pc) != 0)
|
if (yyparse (&pc) != 0)
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG (_("error: parsing failed, stopped at '%s'\n"), pc.input);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine effective timezone source */
|
||||||
|
if (parse_datetime_debug)
|
||||||
|
{
|
||||||
|
long int tz = pc.debug_default_input_timezone;
|
||||||
|
const char* tz_env;
|
||||||
|
const char* tz_src;
|
||||||
|
|
||||||
|
if (pc.timespec_seen)
|
||||||
|
{
|
||||||
|
tz = 0 ;
|
||||||
|
tz_src = _("'@timespec' - always UTC0");
|
||||||
|
}
|
||||||
|
else if (pc.local_zones_seen || pc.zones_seen)
|
||||||
|
{
|
||||||
|
tz = pc.time_zone;
|
||||||
|
tz_src = _("parsed date/time string");
|
||||||
|
}
|
||||||
|
else if ((tz_env = getenv("TZ")))
|
||||||
|
{
|
||||||
|
if (tz_was_altered)
|
||||||
|
{
|
||||||
|
snprintf (dbg_tm, sizeof(dbg_tm), _("TZ=\"%s\" in date string"),
|
||||||
|
tz_env);
|
||||||
|
tz_src = dbg_tm;
|
||||||
|
}
|
||||||
|
else if (STREQ(tz_env,"UTC0"))
|
||||||
|
{
|
||||||
|
/* Special case: using 'date -u' simply set TZ=UTC0 */
|
||||||
|
tz_src = _("TZ=UTC0 environment value or -u");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf (dbg_tm, sizeof(dbg_tm),
|
||||||
|
_("TZ=\"%s\" environment value"), tz_env);
|
||||||
|
tz_src = dbg_tm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tz_src = _("system default");
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGRESS (_("input timezone: %+03d:%02d (set from %s)\n"),
|
||||||
|
(int)(tz/60), abs ((int)tz)%60, tz_src);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (pc.timespec_seen)
|
if (pc.timespec_seen)
|
||||||
*result = pc.seconds;
|
*result = pc.seconds;
|
||||||
@@ -1419,7 +1935,22 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
{
|
{
|
||||||
if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
|
if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
|
||||||
| (pc.local_zones_seen + pc.zones_seen)))
|
| (pc.local_zones_seen + pc.zones_seen)))
|
||||||
goto fail;
|
{
|
||||||
|
if (parse_datetime_debug)
|
||||||
|
{
|
||||||
|
if (pc.times_seen > 1)
|
||||||
|
DEBUG0 ("error: seen multiple time parts\n");
|
||||||
|
if (pc.dates_seen > 1)
|
||||||
|
DEBUG0 ("error: seen multiple date parts\n");
|
||||||
|
if (pc.days_seen > 1)
|
||||||
|
DEBUG0 ("error: seen multiple days parts\n");
|
||||||
|
if (pc.dsts_seen > 1)
|
||||||
|
DEBUG0 ("error: seen multiple daylight-saving parts\n");
|
||||||
|
if ( (pc.local_zones_seen + pc.zones_seen) > 1)
|
||||||
|
DEBUG0 ("error: seen multiple time-zone parts\n");
|
||||||
|
}
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
|
tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
|
||||||
tm.tm_mon = pc.month - 1;
|
tm.tm_mon = pc.month - 1;
|
||||||
@@ -1428,14 +1959,24 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
{
|
{
|
||||||
tm.tm_hour = to_hour (pc.hour, pc.meridian);
|
tm.tm_hour = to_hour (pc.hour, pc.meridian);
|
||||||
if (tm.tm_hour < 0)
|
if (tm.tm_hour < 0)
|
||||||
goto fail;
|
{
|
||||||
|
const char* mrd = (pc.meridian==MERam)?"am":
|
||||||
|
(pc.meridian==MERpm)?"pm":"";
|
||||||
|
DEBUG (_("error: invalid hour %ld%s\n"), pc.hour, mrd);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
tm.tm_min = pc.minutes;
|
tm.tm_min = pc.minutes;
|
||||||
tm.tm_sec = pc.seconds.tv_sec;
|
tm.tm_sec = pc.seconds.tv_sec;
|
||||||
|
PROGRESS (_("using %s time as starting value: '%s'\n"),
|
||||||
|
(pc.times_seen)?_("specified"):_("current"),
|
||||||
|
debug_strftime (&tm,dbg_tm,sizeof (dbg_tm)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
|
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
|
||||||
pc.seconds.tv_nsec = 0;
|
pc.seconds.tv_nsec = 0;
|
||||||
|
DEBUG0 ("warning: using midnight as starting time: 00:00:00\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Let mktime deduce tm_isdst if we have an absolute time stamp. */
|
/* Let mktime deduce tm_isdst if we have an absolute time stamp. */
|
||||||
@@ -1454,7 +1995,11 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
if (! mktime_ok (&tm0, &tm, Start))
|
if (! mktime_ok (&tm0, &tm, Start))
|
||||||
{
|
{
|
||||||
if (! pc.zones_seen)
|
if (! pc.zones_seen)
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG_MKTIME_NOT_OK (&tm0, &tm, &pc, pc.zones_seen);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Guard against falsely reporting errors near the time_t
|
/* Guard against falsely reporting errors near the time_t
|
||||||
@@ -1478,12 +2023,21 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
|
sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0],
|
||||||
abs_time_zone_hour, abs_time_zone_min);
|
abs_time_zone_hour, abs_time_zone_min);
|
||||||
if (setenv ("TZ", tz1buf, 1) != 0)
|
if (setenv ("TZ", tz1buf, 1) != 0)
|
||||||
goto fail;
|
{
|
||||||
|
/* TODO: was warn () + print errno? */
|
||||||
|
DEBUG (_("error: setenv('TZ','%s') failed\n"), tz1buf);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
tz_was_altered = true;
|
tz_was_altered = true;
|
||||||
tm = tm0;
|
tm = tm0;
|
||||||
Start = mktime (&tm);
|
Start = mktime (&tm);
|
||||||
if (! mktime_ok (&tm0, &tm, Start))
|
if (! mktime_ok (&tm0, &tm, Start))
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG_MKTIME_NOT_OK (&tm0, &tm, &pc, pc.zones_seen);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1496,19 +2050,58 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
tm.tm_isdst = -1;
|
tm.tm_isdst = -1;
|
||||||
Start = mktime (&tm);
|
Start = mktime (&tm);
|
||||||
if (Start == (time_t) -1)
|
if (Start == (time_t) -1)
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG (_("error: day '%s' (day ordinal=%ld number=%d) " \
|
||||||
|
"resulted in an invalid date: '%s'\n"),
|
||||||
|
str_days (&pc,dbg_ord,sizeof (dbg_ord)),
|
||||||
|
pc.day_ordinal,pc.day_number,
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGRESS (_("new start date: '%s' is '%s'\n"),
|
||||||
|
str_days (&pc,dbg_ord,sizeof (dbg_ord)),
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pc.dates_seen && !pc.days_seen)
|
||||||
|
PROGRESS (_("using current date as starting value: '%s'\n"),
|
||||||
|
debug_strfdate (&tm,dbg_tm,sizeof (dbg_tm)));
|
||||||
|
|
||||||
|
if (pc.days_seen && pc.dates_seen)
|
||||||
|
DEBUG (_("warning: day (%s) ignored when explicit dates are given\n"),
|
||||||
|
str_days (&pc,dbg_ord,sizeof (dbg_ord)));
|
||||||
|
|
||||||
|
PROGRESS (_("starting date/time: '%s'\n"),
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm,sizeof (dbg_tm)));
|
||||||
|
|
||||||
|
|
||||||
/* Add relative date. */
|
/* Add relative date. */
|
||||||
if (pc.rel.year | pc.rel.month | pc.rel.day)
|
if (pc.rel.year | pc.rel.month | pc.rel.day)
|
||||||
{
|
{
|
||||||
|
if ((pc.rel.year != 0 || pc.rel.month !=0) && tm.tm_mday==1)
|
||||||
|
DEBUG0 ("warning: when adding relative months/years, " \
|
||||||
|
"it is recommended to specify the 15th of the months\n");
|
||||||
|
|
||||||
|
|
||||||
|
if (pc.rel.day != 0 && tm.tm_hour==0)
|
||||||
|
DEBUG0 ("warning: when adding relative days, " \
|
||||||
|
"it is recommended to specify 12:00pm\n");
|
||||||
|
|
||||||
int year = tm.tm_year + pc.rel.year;
|
int year = tm.tm_year + pc.rel.year;
|
||||||
int month = tm.tm_mon + pc.rel.month;
|
int month = tm.tm_mon + pc.rel.month;
|
||||||
int day = tm.tm_mday + pc.rel.day;
|
int day = tm.tm_mday + pc.rel.day;
|
||||||
if (((year < tm.tm_year) ^ (pc.rel.year < 0))
|
if (((year < tm.tm_year) ^ (pc.rel.year < 0))
|
||||||
| ((month < tm.tm_mon) ^ (pc.rel.month < 0))
|
| ((month < tm.tm_mon) ^ (pc.rel.month < 0))
|
||||||
| ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
|
| ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
|
||||||
goto fail;
|
{
|
||||||
|
/* TODO: what is the actual error? int-value wrap-around? */
|
||||||
|
DEBUG (_("error: %s:%d\n"), __FILE__,__LINE__);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
tm.tm_year = year;
|
tm.tm_year = year;
|
||||||
tm.tm_mon = month;
|
tm.tm_mon = month;
|
||||||
tm.tm_mday = day;
|
tm.tm_mday = day;
|
||||||
@@ -1518,7 +2111,20 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
tm.tm_isdst = tm0.tm_isdst;
|
tm.tm_isdst = tm0.tm_isdst;
|
||||||
Start = mktime (&tm);
|
Start = mktime (&tm);
|
||||||
if (Start == (time_t) -1)
|
if (Start == (time_t) -1)
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG (_("error: adding relative date resulted " \
|
||||||
|
"in an invalid date: '%s'\n"),
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm, sizeof (dbg_tm)));
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGRESS (_("after date adjustment "\
|
||||||
|
"(%+ld years, %+ld months, %+ld days),\n"),
|
||||||
|
pc.rel.year, pc.rel.month, pc.rel.day);
|
||||||
|
PROGRESS (_(" new date/time = '%s'\n"),
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm, sizeof (dbg_tm)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The only "output" of this if-block is an updated Start value,
|
/* The only "output" of this if-block is an updated Start value,
|
||||||
@@ -1533,15 +2139,29 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
time_t t = Start;
|
time_t t = Start;
|
||||||
struct tm const *gmt = gmtime (&t);
|
struct tm const *gmt = gmtime (&t);
|
||||||
if (! gmt)
|
if (! gmt)
|
||||||
goto fail;
|
{
|
||||||
|
/* TODO: use 'warn(3)' + print errno ? */
|
||||||
|
DEBUG (_("error: gmtime failed for t=%ld\n"),t);
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
delta -= tm_diff (&tm, gmt);
|
delta -= tm_diff (&tm, gmt);
|
||||||
#endif
|
#endif
|
||||||
t1 = Start - delta;
|
t1 = Start - delta;
|
||||||
if ((Start < t1) != (delta < 0))
|
if ((Start < t1) != (delta < 0))
|
||||||
goto fail; /* time_t overflow */
|
{
|
||||||
|
DEBUG (_("error: timezone %ld caused time_t overflow\n"),
|
||||||
|
pc.time_zone);
|
||||||
|
|
||||||
|
goto fail; /* time_t overflow */
|
||||||
|
}
|
||||||
Start = t1;
|
Start = t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PROGRESS (_("'%s' = %ld epoch-seconds\n"),
|
||||||
|
debug_strfdatetime (&tm, &pc, dbg_tm, sizeof (dbg_tm)),
|
||||||
|
Start);
|
||||||
|
|
||||||
/* Add relative hours, minutes, and seconds. On hosts that support
|
/* Add relative hours, minutes, and seconds. On hosts that support
|
||||||
leap seconds, ignore the possibility of leap seconds; e.g.,
|
leap seconds, ignore the possibility of leap seconds; e.g.,
|
||||||
"+ 10 minutes" adds 600 seconds, even if one of them is a
|
"+ 10 minutes" adds 600 seconds, even if one of them is a
|
||||||
@@ -1570,7 +2190,19 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
| ((t3 < t2) ^ (d3 < 0))
|
| ((t3 < t2) ^ (d3 < 0))
|
||||||
| ((t4 < t3) ^ (d4 < 0))
|
| ((t4 < t3) ^ (d4 < 0))
|
||||||
| (t5 != t4))
|
| (t5 != t4))
|
||||||
goto fail;
|
{
|
||||||
|
DEBUG0 (" error: adding relative time caused an overflow\n");
|
||||||
|
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pc.rel.hour | pc.rel.minutes | pc.rel.seconds | pc.rel.ns )
|
||||||
|
{
|
||||||
|
PROGRESS (_("after time adjustment " \
|
||||||
|
"(%+ld hours, %+ld minutes, %+ld seconds, %+ld ns),\n"),
|
||||||
|
pc.rel.hour,pc.rel.minutes,pc.rel.seconds,pc.rel.ns);
|
||||||
|
PROGRESS (_(" new time = %ld epoch-seconds\n"),t5);
|
||||||
|
}
|
||||||
|
|
||||||
result->tv_sec = t5;
|
result->tv_sec = t5;
|
||||||
result->tv_nsec = normalized_ns;
|
result->tv_nsec = normalized_ns;
|
||||||
@@ -1586,6 +2218,49 @@ parse_datetime (struct timespec *result, char const *p,
|
|||||||
ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
|
ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
|
||||||
if (tz0 != tz0buf)
|
if (tz0 != tz0buf)
|
||||||
free (tz0);
|
free (tz0);
|
||||||
|
|
||||||
|
if (ok && parse_datetime_debug)
|
||||||
|
{
|
||||||
|
/* print local timezone AFTER restoring TZ (if tz_was_altered)*/
|
||||||
|
const long int otz = get_effective_timezone ();
|
||||||
|
const char* tz_src;
|
||||||
|
const char* tz_env;
|
||||||
|
|
||||||
|
if ((tz_env = getenv("TZ")))
|
||||||
|
{
|
||||||
|
/* Special case: using 'date -u' simply set TZ=UTC0 */
|
||||||
|
if (STREQ(tz_env,"UTC0"))
|
||||||
|
{
|
||||||
|
tz_src = _("TZ=UTC0 envionment value or -u");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf (dbg_tm, sizeof(dbg_tm),
|
||||||
|
_("TZ=\"%s\" environment value"), tz_env);
|
||||||
|
tz_src = dbg_tm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tz_src = _("system default");
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGRESS (_("output timezone: %+03d:%02d (set from %s)\n"),
|
||||||
|
(int)(otz/60), abs ((int)otz)%60, tz_src);
|
||||||
|
|
||||||
|
|
||||||
|
PROGRESS (_("final: %ld.%09ld (epoch-seconds)\n"),
|
||||||
|
result->tv_sec,result->tv_nsec);
|
||||||
|
|
||||||
|
struct tm const *gmt = gmtime (&result->tv_sec);
|
||||||
|
PROGRESS (_("final: %s (UTC0)\n"),
|
||||||
|
debug_strfdatetime (gmt, NULL, dbg_tm, sizeof (dbg_tm)));
|
||||||
|
struct tm const *lmt = localtime (&result->tv_sec);
|
||||||
|
PROGRESS (_("final: %s (output timezone TZ=%+03d:%02d)\n"),
|
||||||
|
debug_strfdatetime (lmt, NULL, dbg_tm, sizeof (dbg_tm)),
|
||||||
|
(int)(otz/60), abs ((int)otz)%60);
|
||||||
|
}
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,11 +13,13 @@ Depends-on:
|
|||||||
c-ctype
|
c-ctype
|
||||||
stdbool
|
stdbool
|
||||||
gettime
|
gettime
|
||||||
|
gettext-h
|
||||||
intprops
|
intprops
|
||||||
mktime
|
mktime
|
||||||
setenv
|
setenv
|
||||||
unsetenv
|
unsetenv
|
||||||
time
|
time
|
||||||
|
timegm
|
||||||
verify
|
verify
|
||||||
xalloc
|
xalloc
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user