timestamp_t: a new data type for timestamps

Git's source code assumes that unsigned long is at least as precise as
time_t. Which is incorrect, and causes a lot of problems, in particular
where unsigned long is only 32-bit (notably on Windows, even in 64-bit
versions).

So let's just use a more appropriate data type instead. In preparation
for this, we introduce the new `timestamp_t` data type.

By necessity, this is a very, very large patch, as it has to replace all
timestamps' data type in one go.

As we will use a data type that is not necessarily identical to `time_t`,
we need to be very careful to use `time_t` whenever we interact with the
system functions, and `timestamp_t` everywhere else.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johannes Schindelin
2017-04-26 21:29:31 +02:00
committed by Junio C Hamano
parent cb71f8bdb5
commit dddbad728c
49 changed files with 170 additions and 158 deletions

66
date.c
View File

@ -39,7 +39,7 @@ 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;
@ -54,7 +54,7 @@ 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);
@ -64,7 +64,7 @@ static struct tm *time_to_tm(unsigned long time, int tz)
* 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;
@ -88,11 +88,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;
@ -140,9 +140,9 @@ void show_date_relative(unsigned long time, int tz,
}
/* 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_("%"PRItime" year", "%"PRItime" years", years), years);
@ -172,7 +172,7 @@ 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;
@ -425,7 +425,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,7 +508,7 @@ 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 = parse_timestamp(date, &end, 10);
@ -635,7 +635,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 = '+';
@ -650,16 +650,16 @@ static void date_string(unsigned long date, int offset, struct strbuf *buf)
* 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 = parse_timestamp(date, &end, 10);
if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
if (*end != ' ' || stamp == TIME_MAX || (end[1] != '+' && end[1] != '-'))
return -1;
date = end + 2;
ofs = strtol(date, &end, 10);
@ -675,11 +675,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 +747,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 +762,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 +771,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, &timestamp, &offset))
return -1;
@ -845,7 +845,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 +1066,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num,
time_t now)
{
char *end;
unsigned long number = parse_timestamp(date, &end, 10);
timestamp_t number = parse_timestamp(date, &end, 10);
switch (*end) {
case ':':
@ -1114,9 +1114,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 +1148,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 +1162,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 +1180,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;
/*