/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
*/
-time_t tm_to_time_t(const struct tm *tm)
+static time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
}
/* Give years and months for 5 years or so */
if (diff < 1825) {
- unsigned long years = diff / 365;
- unsigned long months = (diff % 365 + 15) / 30;
+ unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
+ unsigned long years = totalmonths / 12;
+ unsigned long months = totalmonths % 12;
int n;
n = snprintf(timebuf, timebuf_size, "%lu year%s",
years, (years > 1 ? "s" : ""));
{ "GMT", 0, 0, }, /* Greenwich Mean */
{ "UTC", 0, 0, }, /* Universal (Coordinated) */
+ { "Z", 0, 0, }, /* Zulu, alias for UTC */
{ "WET", 0, 0, }, /* Western European */
{ "BST", 0, 1, }, /* British Summer */
for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
int match = match_string(date, timezone_names[i].name);
- if (match >= 3) {
+ if (match >= 3 || match == strlen(timezone_names[i].name)) {
int off = timezone_names[i].offset;
/* This is bogus, but we like summer */
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date(const char *date, char *result, int maxlen)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
{
struct tm tm;
- int offset, tm_gmt;
- time_t then;
+ int tm_gmt;
+ unsigned long dummy_timestamp;
+ int dummy_offset;
+
+ if (!timestamp)
+ timestamp = &dummy_timestamp;
+ if (!offset)
+ offset = &dummy_offset;
memset(&tm, 0, sizeof(tm));
tm.tm_year = -1;
tm.tm_hour = -1;
tm.tm_min = -1;
tm.tm_sec = -1;
- offset = -1;
+ *offset = -1;
tm_gmt = 0;
for (;;) {
break;
if (isalpha(c))
- match = match_alpha(date, &tm, &offset);
+ match = match_alpha(date, &tm, offset);
else if (isdigit(c))
- match = match_digit(date, &tm, &offset, &tm_gmt);
+ match = match_digit(date, &tm, offset, &tm_gmt);
else if ((c == '-' || c == '+') && isdigit(date[1]))
- match = match_tz(date, &offset);
+ match = match_tz(date, offset);
if (!match) {
/* BAD CRAP */
}
/* mktime uses local timezone */
- then = tm_to_time_t(&tm);
- if (offset == -1)
- offset = (then - mktime(&tm)) / 60;
+ *timestamp = tm_to_time_t(&tm);
+ if (*offset == -1)
+ *offset = ((time_t)*timestamp - mktime(&tm)) / 60;
- if (then == -1)
+ if (*timestamp == -1)
return -1;
if (!tm_gmt)
- then -= offset * 60;
- return date_string(then, offset, result, maxlen);
+ *timestamp -= *offset * 60;
+ return 0; /* success */
+}
+
+int parse_date(const char *date, char *result, int maxlen)
+{
+ unsigned long timestamp;
+ int offset;
+ if (parse_date_basic(date, ×tamp, &offset))
+ return -1;
+ return date_string(timestamp, offset, result, maxlen);
}
enum date_mode parse_date_format(const char *format)
return n;
}
+static void date_now(struct tm *tm, struct tm *now, int *num)
+{
+ update_tm(tm, now, 0);
+}
+
static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
update_tm(tm, now, 24*60*60);
{ "PM", date_pm },
{ "AM", date_am },
{ "never", date_never },
+ { "now", date_now },
{ NULL }
};
{ NULL }
};
-static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
{
const struct typelen *tl;
const struct special *s;
int match = match_string(date, month_names[i]);
if (match >= 3) {
tm->tm_mon = i;
+ *touched = 1;
return end;
}
}
int len = strlen(s->name);
if (match_string(date, s->name) == len) {
s->fn(tm, now, num);
+ *touched = 1;
return end;
}
}
int len = strlen(number_name[i]);
if (match_string(date, number_name[i]) == len) {
*num = i;
+ *touched = 1;
return end;
}
}
- if (match_string(date, "last") == 4)
+ if (match_string(date, "last") == 4) {
*num = 1;
+ *touched = 1;
+ }
return end;
}
if (match_string(date, tl->type) >= len-1) {
update_tm(tm, now, tl->length * *num);
*num = 0;
+ *touched = 1;
return end;
}
tl++;
diff += 7*n;
update_tm(tm, now, diff * 24 * 60 * 60);
+ *touched = 1;
return end;
}
}
tm->tm_year--;
}
tm->tm_mon = n;
+ *touched = 1;
return end;
}
update_tm(tm, now, 0); /* fill in date fields if needed */
tm->tm_year -= *num;
*num = 0;
+ *touched = 1;
return end;
}
}
}
-static unsigned long approxidate_str(const char *date, const struct timeval *tv)
+static unsigned long approxidate_str(const char *date,
+ const struct timeval *tv,
+ int *error_ret)
{
int number = 0;
+ int touched = 0;
struct tm tm, now;
time_t time_sec;
if (isdigit(c)) {
pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
+ touched = 1;
continue;
}
if (isalpha(c))
- date = approxidate_alpha(date-1, &tm, &now, &number);
+ date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
}
pending_number(&tm, &number);
+ if (!touched)
+ *error_ret = 1;
return update_tm(&tm, &now, 0);
}
unsigned long approxidate_relative(const char *date, const struct timeval *tv)
{
- char buffer[50];
-
- if (parse_date(date, buffer, sizeof(buffer)) > 0)
- return strtoul(buffer, NULL, 0);
+ unsigned long timestamp;
+ int offset;
+ int errors = 0;
- return approxidate_str(date, tv);
+ if (!parse_date_basic(date, ×tamp, &offset))
+ return timestamp;
+ return approxidate_str(date, tv, &errors);
}
-unsigned long approxidate(const char *date)
+unsigned long approxidate_careful(const char *date, int *error_ret)
{
struct timeval tv;
- char buffer[50];
+ unsigned long timestamp;
+ int offset;
+ int dummy = 0;
+ if (!error_ret)
+ error_ret = &dummy;
- if (parse_date(date, buffer, sizeof(buffer)) > 0)
- return strtoul(buffer, NULL, 0);
+ if (!parse_date_basic(date, ×tamp, &offset)) {
+ *error_ret = 0;
+ return timestamp;
+ }
gettimeofday(&tv, NULL);
- return approxidate_str(date, &tv);
+ return approxidate_str(date, &tv, error_ret);
}