diff options
Diffstat (limited to 'date.c')
-rw-r--r-- | date.c | 127 |
1 files changed, 75 insertions, 52 deletions
@@ -39,14 +39,24 @@ static const char *weekday_names[] = { "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays" }; -static time_t gm_time_t(unsigned long time, int tz) +static time_t gm_time_t(timestamp_t time, int tz) { int minutes; minutes = tz < 0 ? -tz : tz; minutes = (minutes / 100)*60 + (minutes % 100); minutes = tz < 0 ? -minutes : minutes; - return time + minutes * 60; + + if (minutes > 0) { + if (unsigned_add_overflows(time, minutes * 60)) + die("Timestamp+tz too large: %"PRItime" +%04d", + time, tz); + } else if (time < -minutes * 60) + die("Timestamp before Unix epoch: %"PRItime" %04d", time, tz); + time += minutes * 60; + if (date_overflows(time)) + die("Timestamp too large for this system: %"PRItime, time); + return (time_t)time; } /* @@ -54,23 +64,32 @@ static time_t gm_time_t(unsigned long time, int tz) * thing, which means that tz -0100 is passed in as the integer -100, * even though it means "sixty minutes off" */ -static struct tm *time_to_tm(unsigned long time, int tz) +static struct tm *time_to_tm(timestamp_t time, int tz) { time_t t = gm_time_t(time, tz); return gmtime(&t); } +static struct tm *time_to_tm_local(timestamp_t time) +{ + time_t t = time; + return localtime(&t); +} + /* * What value of "tz" was in effect back then at "time" in the * local timezone? */ -static int local_tzoffset(unsigned long time) +static int local_tzoffset(timestamp_t time) { time_t t, t_local; struct tm tm; int offset, eastwest; - t = time; + if (date_overflows(time)) + die("Timestamp too large for this system: %"PRItime, time); + + t = (time_t)time; localtime_r(&t, &tm); t_local = tm_to_time_t(&tm); @@ -88,11 +107,11 @@ static int local_tzoffset(unsigned long time) return offset * eastwest; } -void show_date_relative(unsigned long time, int tz, +void show_date_relative(timestamp_t time, int tz, const struct timeval *now, struct strbuf *timebuf) { - unsigned long diff; + timestamp_t diff; if (now->tv_sec < time) { strbuf_addstr(timebuf, _("in the future")); return; @@ -100,65 +119,65 @@ void show_date_relative(unsigned long time, int tz, diff = now->tv_sec - time; if (diff < 90) { strbuf_addf(timebuf, - Q_("%lu second ago", "%lu seconds ago", diff), diff); + Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff); return; } /* Turn it into minutes */ diff = (diff + 30) / 60; if (diff < 90) { strbuf_addf(timebuf, - Q_("%lu minute ago", "%lu minutes ago", diff), diff); + Q_("%"PRItime" minute ago", "%"PRItime" minutes ago", diff), diff); return; } /* Turn it into hours */ diff = (diff + 30) / 60; if (diff < 36) { strbuf_addf(timebuf, - Q_("%lu hour ago", "%lu hours ago", diff), diff); + Q_("%"PRItime" hour ago", "%"PRItime" hours ago", diff), diff); return; } /* We deal with number of days from here on */ diff = (diff + 12) / 24; if (diff < 14) { strbuf_addf(timebuf, - Q_("%lu day ago", "%lu days ago", diff), diff); + Q_("%"PRItime" day ago", "%"PRItime" days ago", diff), diff); return; } /* Say weeks for the past 10 weeks or so */ if (diff < 70) { strbuf_addf(timebuf, - Q_("%lu week ago", "%lu weeks ago", (diff + 3) / 7), + Q_("%"PRItime" week ago", "%"PRItime" weeks ago", (diff + 3) / 7), (diff + 3) / 7); return; } /* Say months for the past 12 months or so */ if (diff < 365) { strbuf_addf(timebuf, - Q_("%lu month ago", "%lu months ago", (diff + 15) / 30), + Q_("%"PRItime" month ago", "%"PRItime" months ago", (diff + 15) / 30), (diff + 15) / 30); return; } /* Give years and months for 5 years or so */ if (diff < 1825) { - unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2); - unsigned long years = totalmonths / 12; - unsigned long months = totalmonths % 12; + timestamp_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2); + timestamp_t years = totalmonths / 12; + timestamp_t months = totalmonths % 12; if (months) { struct strbuf sb = STRBUF_INIT; - strbuf_addf(&sb, Q_("%lu year", "%lu years", years), years); + strbuf_addf(&sb, Q_("%"PRItime" year", "%"PRItime" years", years), years); strbuf_addf(timebuf, /* TRANSLATORS: "%s" is "<n> years" */ - Q_("%s, %lu month ago", "%s, %lu months ago", months), + Q_("%s, %"PRItime" month ago", "%s, %"PRItime" months ago", months), sb.buf, months); strbuf_release(&sb); } else strbuf_addf(timebuf, - Q_("%lu year ago", "%lu years ago", years), years); + Q_("%"PRItime" year ago", "%"PRItime" years ago", years), years); return; } /* Otherwise, just years. Centuries is probably overkill. */ strbuf_addf(timebuf, - Q_("%lu year ago", "%lu years ago", (diff + 183) / 365), + Q_("%"PRItime" year ago", "%"PRItime" years ago", (diff + 183) / 365), (diff + 183) / 365); } @@ -172,14 +191,14 @@ struct date_mode *date_mode_from_type(enum date_mode_type type) return &mode; } -const char *show_date(unsigned long time, int tz, const struct date_mode *mode) +const char *show_date(timestamp_t time, int tz, const struct date_mode *mode) { struct tm *tm; static struct strbuf timebuf = STRBUF_INIT; if (mode->type == DATE_UNIX) { strbuf_reset(&timebuf); - strbuf_addf(&timebuf, "%lu", time); + strbuf_addf(&timebuf, "%"PRItime, time); return timebuf.buf; } @@ -188,7 +207,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) if (mode->type == DATE_RAW) { strbuf_reset(&timebuf); - strbuf_addf(&timebuf, "%lu %+05d", time, tz); + strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz); return timebuf.buf; } @@ -201,7 +220,10 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) return timebuf.buf; } - tm = time_to_tm(time, tz); + if (mode->local) + tm = time_to_tm_local(time); + else + tm = time_to_tm(time, tz); if (!tm) { tm = time_to_tm(0, 0); tz = 0; @@ -233,7 +255,8 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode) month_names[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, tz); else if (mode->type == DATE_STRFTIME) - strbuf_addftime(&timebuf, mode->strftime_fmt, tm); + strbuf_addftime(&timebuf, mode->strftime_fmt, tm, tz, + !mode->local); else strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d", weekday_names[tm->tm_wday], @@ -425,7 +448,7 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, return 0; } -static int match_multi_number(unsigned long num, char c, const char *date, +static int match_multi_number(timestamp_t num, char c, const char *date, char *end, struct tm *tm, time_t now) { struct tm now_tm; @@ -508,9 +531,9 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt { int n; char *end; - unsigned long num; + timestamp_t num; - num = strtoul(date, &end, 10); + num = parse_timestamp(date, &end, 10); /* * Seconds since 1970? We trigger on that for any numbers with @@ -635,7 +658,7 @@ static int match_tz(const char *date, int *offp) return end - date; } -static void date_string(unsigned long date, int offset, struct strbuf *buf) +static void date_string(timestamp_t date, int offset, struct strbuf *buf) { int sign = '+'; @@ -643,23 +666,23 @@ static void date_string(unsigned long date, int offset, struct strbuf *buf) offset = -offset; sign = '-'; } - strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60); + strbuf_addf(buf, "%"PRItime" %c%02d%02d", date, sign, offset / 60, offset % 60); } /* * Parse a string like "0 +0000" as ancient timestamp near epoch, but * only when it appears not as part of any other string. */ -static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset) +static int match_object_header_date(const char *date, timestamp_t *timestamp, int *offset) { char *end; - unsigned long stamp; + timestamp_t stamp; int ofs; if (*date < '0' || '9' < *date) return -1; - stamp = strtoul(date, &end, 10); - if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-')) + stamp = parse_timestamp(date, &end, 10); + if (*end != ' ' || stamp == TIME_MAX || (end[1] != '+' && end[1] != '-')) return -1; date = end + 2; ofs = strtol(date, &end, 10); @@ -675,11 +698,11 @@ static int match_object_header_date(const char *date, unsigned long *timestamp, /* 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_basic(const char *date, unsigned long *timestamp, int *offset) +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset) { struct tm tm; int tm_gmt; - unsigned long dummy_timestamp; + timestamp_t dummy_timestamp; int dummy_offset; if (!timestamp) @@ -747,7 +770,7 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset) return 0; /* success */ } -int parse_expiry_date(const char *date, unsigned long *timestamp) +int parse_expiry_date(const char *date, timestamp_t *timestamp) { int errors = 0; @@ -762,7 +785,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp) * of the past, and there is nothing from the future * to be kept. */ - *timestamp = ULONG_MAX; + *timestamp = TIME_MAX; else *timestamp = approxidate_careful(date, &errors); @@ -771,7 +794,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp) int parse_date(const char *date, struct strbuf *result) { - unsigned long timestamp; + timestamp_t timestamp; int offset; if (parse_date_basic(date, ×tamp, &offset)) return -1; @@ -845,7 +868,7 @@ void datestamp(struct strbuf *out) * Relative time update (eg "2 days ago"). If we haven't set the time * yet, we need to set it from current time. */ -static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec) +static time_t update_tm(struct tm *tm, struct tm *now, time_t sec) { time_t n; @@ -1066,7 +1089,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num, time_t now) { char *end; - unsigned long number = strtoul(date, &end, 10); + timestamp_t number = parse_timestamp(date, &end, 10); switch (*end) { case ':': @@ -1114,9 +1137,9 @@ static void pending_number(struct tm *tm, int *num) } } -static unsigned long approxidate_str(const char *date, - const struct timeval *tv, - int *error_ret) +static timestamp_t approxidate_str(const char *date, + const struct timeval *tv, + int *error_ret) { int number = 0; int touched = 0; @@ -1148,12 +1171,12 @@ static unsigned long approxidate_str(const char *date, pending_number(&tm, &number); if (!touched) *error_ret = 1; - return update_tm(&tm, &now, 0); + return (timestamp_t)update_tm(&tm, &now, 0); } -unsigned long approxidate_relative(const char *date, const struct timeval *tv) +timestamp_t approxidate_relative(const char *date, const struct timeval *tv) { - unsigned long timestamp; + timestamp_t timestamp; int offset; int errors = 0; @@ -1162,10 +1185,10 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv) return approxidate_str(date, tv, &errors); } -unsigned long approxidate_careful(const char *date, int *error_ret) +timestamp_t approxidate_careful(const char *date, int *error_ret) { struct timeval tv; - unsigned long timestamp; + timestamp_t timestamp; int offset; int dummy = 0; if (!error_ret) @@ -1180,12 +1203,12 @@ unsigned long approxidate_careful(const char *date, int *error_ret) return approxidate_str(date, &tv, error_ret); } -int date_overflows(unsigned long t) +int date_overflows(timestamp_t t) { time_t sys; - /* If we overflowed our unsigned long, that's bad... */ - if (t == ULONG_MAX) + /* If we overflowed our timestamp data type, that's bad... */ + if ((uintmax_t)t >= TIME_MAX) return 1; /* |