git@vger.kernel.org mailing list mirror (one of many)
 help / Atom feed
* [PATCH 0/6] Use time_t
@ 2017-02-27 21:30 Johannes Schindelin
  2017-02-27 21:30 ` [PATCH 1/6] t0006 & t5000: prepare for 64-bit " Johannes Schindelin
                   ` (8 more replies)
  0 siblings, 9 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:30 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as `time_t` exists, and at
least on 64-bit Windows it is 64-bit. Meaning: we *can* support these
absurd future dates on those platforms.

So let's do this.

One notable fallout of this patch series is that on 64-bit Linux (and
other platforms where `unsigned long` is 64-bit), we now limit the range
of dates to LONG_MAX (i.e. the *signed* maximum value). This needs to be
done as `time_t` can be signed (and indeed is at least on my Ubuntu
setup).

Obviously, I think that we can live with that, and I hope that all
interested parties agree.


Johannes Schindelin (6):
  t0006 & t5000: prepare for 64-bit time_t
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Prepare for timestamps to use 64-bit signed types
  ref-filter: avoid using `unsigned long` for catch-all data type
  Use time_t where appropriate

 Documentation/technical/api-parse-options.txt |  8 +--
 Makefile                                      |  4 ++
 archive-tar.c                                 |  5 +-
 builtin/am.c                                  |  4 +-
 builtin/blame.c                               | 14 ++---
 builtin/fsck.c                                |  6 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  4 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +-
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        | 10 +--
 builtin/reflog.c                              | 24 +++----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  4 +-
 cache.h                                       | 14 ++---
 commit.c                                      | 16 ++---
 commit.h                                      |  2 +-
 config.mak.uname                              |  2 +
 credential-cache--daemon.c                    | 12 ++--
 date.c                                        | 90 +++++++++++++--------------
 fetch-pack.c                                  |  8 +--
 fsck.c                                        |  2 +-
 git-compat-util.h                             | 10 +++
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  4 +-
 reachable.c                                   | 10 ++-
 reachable.h                                   |  4 +-
 ref-filter.c                                  | 22 +++----
 reflog-walk.c                                 |  8 +--
 refs.c                                        | 14 ++---
 refs.h                                        |  8 +--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +-
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +-
 t/helper/test-date.c                          | 11 ++--
 t/helper/test-parse-options.c                 |  4 +-
 t/t0006-date.sh                               |  4 +-
 t/t5000-tar-tree.sh                           |  6 +-
 t/test-lib.sh                                 |  2 +
 tag.c                                         |  4 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  8 +--
 vcs-svn/fast_export.c                         |  8 +--
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 51 files changed, 221 insertions(+), 199 deletions(-)


base-commit: e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v1
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v1

-- 
2.11.1.windows.1.379.g44ae0bc


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 1/6] t0006 & t5000: prepare for 64-bit time_t
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
@ 2017-02-27 21:30 ` " Johannes Schindelin
  2017-02-27 22:55   ` Junio C Hamano
  2017-02-27 21:30 ` [PATCH 2/6] Specify explicitly where we parse timestamps Johannes Schindelin
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:30 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply does not use `time_t`, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) forced the test cases
to be skipped that require a 64-bit (or larger) data type to be used to
represent the time.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, when we
will have switched to using `time_t` where appropriate, we can flip that
prerequisite to test `time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 86d77c16dd3..6151a3d70f8 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1163,3 +1163,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.11.1.windows.1.379.g44ae0bc



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 2/6] Specify explicitly where we parse timestamps
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
  2017-02-27 21:30 ` [PATCH 1/6] t0006 & t5000: prepare for 64-bit " Johannes Schindelin
@ 2017-02-27 21:30 ` Johannes Schindelin
  2017-02-27 22:37   ` Junio C Hamano
  2017-02-27 21:31 ` [PATCH 3/6] Introduce a new "printf format" for " Johannes Schindelin
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:30 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using `time_t` instead, let's introduce a
symbol `parse_timestamp` (currently being defined to `strtoul`) where
appropriate, so that we can later easily switch to use `strtoull()`
instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c         | 2 +-
 bundle.c             | 2 +-
 commit.c             | 4 ++--
 date.c               | 6 +++---
 fsck.c               | 2 +-
 git-compat-util.h    | 2 ++
 pretty.c             | 2 +-
 ref-filter.c         | 2 +-
 t/helper/test-date.c | 2 +-
 tag.c                | 2 +-
 upload-pack.c        | 2 +-
 11 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 31fb60578f6..75e2d939036 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -882,7 +882,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 2cf85158b48..7f56b643704 100644
--- a/commit.c
+++ b/commit.c
@@ -90,7 +90,7 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 	if (buf >= tail)
 		return 0;
 	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -608,7 +608,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..a8848f6e141 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	time_t number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index 939792752bf..33a66e68a83 100644
--- a/fsck.c
+++ b/fsck.c
@@ -690,7 +690,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index ef6d560e156..5eff97bea2e 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index 5e683830d9d..6d1e1e87e7d 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 3820b21cc75..6fab0db5e0d 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -637,7 +637,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..98637053760 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -33,7 +33,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..55d07725777 100644
--- a/tag.c
+++ b/tag.c
@@ -111,7 +111,7 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 	if (buf >= tail)
 		return 0;
 	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index 7597ba3405e..8c47dc1707a 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.11.1.windows.1.379.g44ae0bc



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 3/6] Introduce a new "printf format" for timestamps
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
  2017-02-27 21:30 ` [PATCH 1/6] t0006 & t5000: prepare for 64-bit " Johannes Schindelin
  2017-02-27 21:30 ` [PATCH 2/6] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-02-27 21:31 ` " Johannes Schindelin
  2017-03-01 18:20   ` Junio C Hamano
  2017-02-27 21:31 ` [PATCH 4/6] Prepare for timestamps to use 64-bit signed types Johannes Schindelin
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:31 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
to using time_t instead.

So let's introduce the pseudo format "PRItime" (currently simply being
"lu") so that it is easy later on to change the data type to time_t.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 11 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index cffc6265408..c9486dd580b 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1736,11 +1736,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1853,7 +1853,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 1a5caccd0f5..5413c76e7a6 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index 55d20cc2d88..24612c2299a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -903,7 +903,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 1dbb8a06922..4a878645847 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -456,12 +456,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/date.c b/date.c
index a8848f6e141..97ab5fcc349 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index 601f0779a19..54fb35e39c5 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -357,7 +357,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index 5eff97bea2e..4365012c536 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 98637053760..ba309ec1760 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -52,7 +52,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/upload-pack.c b/upload-pack.c
index 8c47dc1707a..c2be661f6d4 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -859,7 +859,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.11.1.windows.1.379.g44ae0bc



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 4/6] Prepare for timestamps to use 64-bit signed types
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (2 preceding siblings ...)
  2017-02-27 21:31 ` [PATCH 3/6] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-02-27 21:31 ` Johannes Schindelin
  2017-02-27 21:31 ` [PATCH 5/6] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:31 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Currently, Git's source code uses the unsigned long type to represent
timestamps. However, this type is limited to 32-bit e.g. on 64-bit
Windows. Hence it is a suboptimal type for this use case.

In any case, we need to use the time_t type to represent timestamps
since we often send those values to system functions which are declared
to accept time_t parameters.

So let's prepare for the case where timestamps are represented as 64-bit
signed integers by introducing the Makefile option TIME_T_IS_INT64.

As we have to resort to using `strtoull()` (and casting the parsed,
unsigned value to an `int64_t`), the check in the `date_overflows()`
helper has to be relaxed: a value of ULLONG_MAX (cast to `int64_t`)
now *also* indicates an overflow.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Makefile                   | 4 ++++
 archive-tar.c              | 5 ++++-
 builtin/name-rev.c         | 2 +-
 builtin/prune.c            | 2 +-
 builtin/worktree.c         | 2 +-
 credential-cache--daemon.c | 2 +-
 date.c                     | 8 ++++----
 git-compat-util.h          | 7 +++++++
 ref-filter.c               | 2 +-
 9 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile
index 8e4081e0619..0232cf62d33 100644
--- a/Makefile
+++ b/Makefile
@@ -1518,6 +1518,10 @@ ifdef HAVE_GETDELIM
 	BASIC_CFLAGS += -DHAVE_GETDELIM
 endif
 
+ifdef TIME_T_IS_INT64
+	BASIC_CFLAGS += -DTIME_T_IS_INT64
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK = NoThanks
 endif
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..695339a2369 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
 #define USTAR_MAX_SIZE 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index cd89d48b65e..a0f16407b93 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -145,7 +145,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	unsigned long taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 8f4f0522856..1e5eb0292b1 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 831fe058a53..3df95e112e5 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..b298ac01e4f 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -52,7 +52,7 @@ static int check_expirations(void)
 	static unsigned long wait_for_entry_until;
 	int i = 0;
 	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	unsigned long next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
diff --git a/date.c b/date.c
index 97ab5fcc349..23dee2964c1 100644
--- a/date.c
+++ b/date.c
@@ -659,7 +659,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 	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);
@@ -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);
 
@@ -1184,8 +1184,8 @@ int date_overflows(unsigned long 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;
 
 	/*
diff --git a/git-compat-util.h b/git-compat-util.h
index 4365012c536..5cf1133532d 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,15 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#ifdef TIME_T_IS_INT64
+#define PRItime PRId64
+#define parse_timestamp strtoull
+#define TIME_MAX INT64_MAX
+#else
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
+#endif
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/ref-filter.c b/ref-filter.c
index 6fab0db5e0d..07c1f372351 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -638,7 +638,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
-- 
2.11.1.windows.1.379.g44ae0bc



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 5/6] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (3 preceding siblings ...)
  2017-02-27 21:31 ` [PATCH 4/6] Prepare for timestamps to use 64-bit signed types Johannes Schindelin
@ 2017-02-27 21:31 ` Johannes Schindelin
  2017-02-27 21:31 ` [PATCH 6/6] Use time_t where appropriate Johannes Schindelin
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:31 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply use `uintmax_t` instead.

Unfortunately, this patch is larger than that because the field's name
was tied to its data type.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 07c1f372351..b8b34d4dd9e 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -236,7 +236,7 @@ struct atom_value {
 		struct align align;
 	} u;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 };
 
 /*
@@ -492,7 +492,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -539,8 +539,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -644,11 +644,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1583,9 +1583,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.11.1.windows.1.379.g44ae0bc



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH 6/6] Use time_t where appropriate
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (4 preceding siblings ...)
  2017-02-27 21:31 ` [PATCH 5/6] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-02-27 21:31 ` Johannes Schindelin
  2017-02-27 22:48 ` [PATCH 0/6] Use time_t Junio C Hamano
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-27 21:31 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git's source code assumes that unsigned long is at least as precise as
time_t. That 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 time_t instead.

Note that in some systems (most notably 32-bit Linux), time_t is *still*
only 32-bit.

Therefore, it might seem desirable to simply replace unsigned long by
int64_t when working with timestamps, but that comes with its own set of
problems, as we often interact with the system's date functions that
*do* use time_t.

So let's just stick with time_t.

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

As `time_t` can be signed, we now have to switch to using LONG_MAX as
the maximum timestamp lest expressions like `timestamp < TIME_MAX`
evaluate to false. Technically, this introduces a limitation on
platforms where we used 64-bit unsigned longs to represent timestamps
before. Practically, however, this simply unifies the behavior with
platforms where `time_t` is signed (and where sending too large
timestamps to libc functions would fail).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/technical/api-parse-options.txt |  8 ++---
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++---
 builtin/fsck.c                                |  4 +--
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 ++--
 builtin/pack-objects.c                        |  4 +--
 builtin/prune.c                               |  2 +-
 builtin/receive-pack.c                        |  6 ++--
 builtin/reflog.c                              | 24 ++++++-------
 builtin/show-branch.c                         |  4 +--
 builtin/worktree.c                            |  2 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 ++++----
 commit.c                                      | 12 +++----
 commit.h                                      |  2 +-
 config.mak.uname                              |  2 ++
 credential-cache--daemon.c                    | 12 +++----
 date.c                                        | 50 +++++++++++++--------------
 fetch-pack.c                                  |  6 ++--
 git-compat-util.h                             |  2 +-
 http-backend.c                                |  4 +--
 parse-options-cb.c                            |  4 +--
 pretty.c                                      |  2 +-
 reachable.c                                   | 10 +++---
 reachable.h                                   |  4 +--
 ref-filter.c                                  |  2 +-
 reflog-walk.c                                 |  8 ++---
 refs.c                                        | 14 ++++----
 refs.h                                        |  8 ++---
 refs/files-backend.c                          |  4 +--
 revision.c                                    |  6 ++--
 revision.h                                    |  4 +--
 sha1_name.c                                   |  6 ++--
 t/helper/test-date.c                          |  4 +--
 t/helper/test-parse-options.c                 |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +--
 vcs-svn/fast_export.c                         |  4 +--
 vcs-svn/fast_export.h                         |  4 +--
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 45 files changed, 140 insertions(+), 140 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 27bd701c0d6..28c9a64fc11 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -178,13 +178,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &time_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `time_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &time_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `time_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/builtin/am.c b/builtin/am.c
index 75e2d939036..c3e0a4816e4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			time_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index c9486dd580b..33701a1353e 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1570,13 +1570,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	time_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	time_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1587,7 +1587,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	time_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1846,7 +1846,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(time_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 5413c76e7a6..42452744d67 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
-	unsigned long timestamp)
+	time_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
 }
 
 static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, time_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index 331f2192607..7be9d106c0b 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -67,7 +67,7 @@ static void git_config_date_string(const char *key, const char **output)
 	if (git_config_get_string_const(key, output))
 		return;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		time_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/builtin/log.c b/builtin/log.c
index 24612c2299a..c0d3143d0e3 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -904,7 +904,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (time_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index b572a37c261..5a9580230c8 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, time_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index a0f16407b93..5faafeda96f 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	time_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, time_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -145,7 +145,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = TIME_MAX;
+	time_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 8841f8b366b..e46ed73f51b 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static time_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2593,7 +2593,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct sha1_array recent_objects;
 
 static int loosened_object_can_be_discarded(const unsigned char *sha1,
-					    unsigned long mtime)
+					    time_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 1e5eb0292b1..a8babb1f201 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static time_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 4a878645847..1b21617c601 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -77,7 +77,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static time_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -451,7 +451,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, time_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -493,7 +493,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	time_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 7a7136e53e2..331c874174e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static time_t default_reflog_expire;
+static time_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	time_t expire_total;
+	time_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	time_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, time_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	time_t expire_total;
+	time_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, time_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	time_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	time_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, time_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 974f3403abe..512b5e3db34 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -742,7 +742,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				time_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -753,7 +753,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			time_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 3df95e112e5..f12a0e4689c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static time_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..75b82e6b653 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	time_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index 61fc86e6d71..a276d1ea1b0 100644
--- a/cache.h
+++ b/cache.h
@@ -1361,18 +1361,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(time_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(time_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, time_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, time_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+time_t approxidate_careful(const char *, int *);
+time_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(time_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 7f56b643704..ab379e9d6f4 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static time_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -474,8 +474,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	time_t a_date = ((const struct commit_list *)a)->item->date;
+	time_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -599,7 +599,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	time_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -622,8 +622,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	time_t a_date = *(author_date_slab_at(author_date, a));
+	time_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 9c12abb9111..7b2d3d0b8a8 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	time_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.mak.uname b/config.mak.uname
index 447f36ac2e3..ea1a71a936b 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -370,6 +370,7 @@ ifeq ($(uname_S),Windows)
 	NO_INET_PTON = YesPlease
 	NO_INET_NTOP = YesPlease
 	NO_POSIX_GOODIES = UnfortunatelyYes
+	TIME_T_IS_INT64 = YesItIs
 	NATIVE_CRLF = YesPlease
 	DEFAULT_HELP_FORMAT = html
 
@@ -520,6 +521,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
 	NO_INET_PTON = YesPlease
 	NO_INET_NTOP = YesPlease
 	NO_POSIX_GOODIES = UnfortunatelyYes
+	TIME_T_IS_INT64 = YesItIs
 	DEFAULT_HELP_FORMAT = html
 	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index b298ac01e4f..364e5e0c5be 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	time_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static time_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static time_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = TIME_MAX;
+	time_t now = time(NULL);
+	time_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	time_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 23dee2964c1..dad2d0c807e 100644
--- a/date.c
+++ b/date.c
@@ -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(time_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(time_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(time_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(time_t time, int tz,
 			       const struct timeval *now,
 			       struct strbuf *timebuf)
 {
-	unsigned long diff;
+	time_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;
+		time_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
+		time_t years = totalmonths / 12;
+		time_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(time_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(time_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;
+	time_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(time_t date, int offset, struct strbuf *buf)
 {
 	int sign = '+';
 
@@ -650,10 +650,10 @@ 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, time_t *timestamp, int *offset)
 {
 	char *end;
-	unsigned long stamp;
+	time_t stamp;
 	int ofs;
 
 	if (*date < '0' || '9' < *date)
@@ -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, time_t *timestamp, int *offset)
 {
 	struct tm tm;
 	int tm_gmt;
-	unsigned long dummy_timestamp;
+	time_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, time_t *timestamp)
 {
 	int errors = 0;
 
@@ -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;
+	time_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;
 
@@ -1114,7 +1114,7 @@ static void pending_number(struct tm *tm, int *num)
 	}
 }
 
-static unsigned long approxidate_str(const char *date,
+static time_t approxidate_str(const char *date,
 				     const struct timeval *tv,
 				     int *error_ret)
 {
@@ -1151,9 +1151,9 @@ static unsigned long approxidate_str(const char *date,
 	return update_tm(&tm, &now, 0);
 }
 
-unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+time_t approxidate_relative(const char *date, const struct timeval *tv)
 {
-	unsigned long timestamp;
+	time_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)
+time_t approxidate_careful(const char *date, int *error_ret)
 {
 	struct timeval tv;
-	unsigned long timestamp;
+	time_t timestamp;
 	int offset;
 	int dummy = 0;
 	if (!error_ret)
@@ -1180,7 +1180,7 @@ 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(time_t t)
 {
 	time_t sys;
 
diff --git a/fetch-pack.c b/fetch-pack.c
index 54fb35e39c5..d3401733b05 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -356,7 +356,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		time_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -545,7 +545,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 time_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -630,7 +630,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	time_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index 5cf1133532d..027f3b2f8eb 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -326,7 +326,7 @@ extern char *gitdirname(char *);
 #else
 #define PRItime "lu"
 #define parse_timestamp strtoul
-#define TIME_MAX ULONG_MAX
+#define TIME_MAX LONG_MAX
 #endif
 
 #ifndef PATH_SEP
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..4e88735a9e3 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, time_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	time_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index b7d8f7dcb2c..a560d8d0b8c 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(time_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (time_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 6d1e1e87e7d..ab689009536 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	time_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index d0199cace4a..f4d2be6beb7 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,12 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	time_t timestamp;
 };
 
 static void add_recent_object(const unsigned char *sha1,
-			      unsigned long mtime,
-			      struct recent_data *data)
+			      time_t mtime, struct recent_data *data)
 {
 	struct object *obj;
 	enum object_type type;
@@ -139,7 +138,7 @@ static int add_recent_packed(const unsigned char *sha1,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   time_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +155,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    time_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..2a7f6588941 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  time_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   time_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index b8b34d4dd9e..265b7c03845 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -618,7 +618,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	time_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
diff --git a/reflog-walk.c b/reflog-walk.c
index a246af27678..d1a8673c348 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		unsigned char osha1[20], nsha1[20];
 		char *email;
-		unsigned long timestamp;
+		time_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, time_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	time_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	time_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index cd36b64ed93..546e9080555 100644
--- a/refs.c
+++ b/refs.c
@@ -658,7 +658,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	time_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -667,15 +667,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	time_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	time_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, time_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -722,7 +722,7 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
 }
 
 static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
-				  const char *email, unsigned long timestamp,
+				  const char *email, time_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -742,9 +742,9 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, time_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		time_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 9fbff90e79b..1748827a7ba 100644
--- a/refs.h
+++ b/refs.h
@@ -262,9 +262,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		time_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		time_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int reflog_exists(const char *refname);
@@ -293,7 +293,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		unsigned char *old_sha1, unsigned char *new_sha1,
-		const char *committer, unsigned long timestamp,
+		const char *committer, time_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
@@ -536,7 +536,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  time_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index c041d4ba21a..4316de3a692 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3115,7 +3115,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	unsigned char osha1[20], nsha1[20];
 	char *email_end, *message;
-	unsigned long timestamp;
+	time_t timestamp;
 	int tz;
 
 	/* old SP new SP name <email> SP time TAB msg LF */
@@ -3940,7 +3940,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, time_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index b37dbec378f..419d5fa954e 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, time_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	time_t date = ~0ul;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
 }
 
 static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, time_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(osha1, cb_data);
diff --git a/revision.h b/revision.h
index 9fac1a607de..e39e5b524bb 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	time_t max_age;
+	time_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index 73a915ff1b3..9520586139e 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -658,8 +658,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		time_t at_time;
+		time_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1052,7 +1052,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, time_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ba309ec1760..371f9d259c6 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -47,7 +47,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		time_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -95,7 +95,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..8af62dd7501 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static time_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/tag.c b/tag.c
index 55d07725777..a0b22c788b6 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static time_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..aaf56849de8 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	time_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index c2be661f6d4..4dcc6aa8d95 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static time_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	time_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5df87ce6bb4 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, time_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			time_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..864dada2b31 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, time_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			time_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..9e1eb8d4176 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	time_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index d47012048f8..da261363551 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1374,7 +1374,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, time_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.11.1.windows.1.379.g44ae0bc

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 2/6] Specify explicitly where we parse timestamps
  2017-02-27 21:30 ` [PATCH 2/6] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-02-27 22:37   ` Junio C Hamano
  2017-02-27 22:51     ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-02-27 22:37 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> Currently, Git's source code represents all timestamps as `unsigned
> long`. In preparation for using `time_t` instead, let's introduce a
> symbol `parse_timestamp` (currently being defined to `strtoul`) where
> appropriate, so that we can later easily switch to use `strtoull()`
> instead.

This definitely is a very good thing to do as a separate step.

> diff --git a/date.c b/date.c
> index a996331f5b3..a8848f6e141 100644
> --- a/date.c
> +++ b/date.c
> ...
> @@ -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 = strtoul(date, &end, 10);
> +	time_t number = parse_timestamp(date, &end, 10);

This hunk does not belong to this step.  Everybody else in this step
still receives parse_timestamp()'s return value in ulong, not time_t.
I presume that that will happen in the final step 6/6 (which could
be a huge patch that exceeds 100k?)

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (5 preceding siblings ...)
  2017-02-27 21:31 ` [PATCH 6/6] Use time_t where appropriate Johannes Schindelin
@ 2017-02-27 22:48 ` Junio C Hamano
  2017-02-28 11:32   ` Johannes Schindelin
  2017-02-28 14:28 ` Jeff King
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
  8 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-02-27 22:48 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> One notable fallout of this patch series is that on 64-bit Linux (and
> other platforms where `unsigned long` is 64-bit), we now limit the range
> of dates to LONG_MAX (i.e. the *signed* maximum value). This needs to be
> done as `time_t` can be signed (and indeed is at least on my Ubuntu
> setup).
>
> Obviously, I think that we can live with that, and I hope that all
> interested parties agree.

s/ulong/time_t/ is definintely a good change, and it will take us to
a place we would want to be in in some future.  

As long as there remains no platform we care about whose time_t and
long are still 32-bit signed integer, there will be a fallout to
them with this change.  Probably it is of a larger impact than
losing the upper half of a 64-bit timestamp range on larger boxes.
Hopefully those platforms have died out (or at least we don't mind
breaking them)?

It appears that we use uint64_t in many places in our code.  So
while philosophically time_t is the right type, uint64_t might be
practically a safer alternative type to use at the endgame patch in
this series.  I haven't seen it yet, but presumably the last one 6/6
is the endgame?



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 2/6] Specify explicitly where we parse timestamps
  2017-02-27 22:37   ` Junio C Hamano
@ 2017-02-27 22:51     ` Junio C Hamano
  2017-02-28 10:49       ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-02-27 22:51 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

>> -	unsigned long number = strtoul(date, &end, 10);
>> +	time_t number = parse_timestamp(date, &end, 10);
>
> This hunk does not belong to this step.  Everybody else in this step

obviously I meant "the left half of this hunk" ;-)

> still receives parse_timestamp()'s return value in ulong, not time_t.
> I presume that that will happen in the final step 6/6 (which could
> be a huge patch that exceeds 100k?)

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 1/6] t0006 & t5000: prepare for 64-bit time_t
  2017-02-27 21:30 ` [PATCH 1/6] t0006 & t5000: prepare for 64-bit " Johannes Schindelin
@ 2017-02-27 22:55   ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-02-27 22:55 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> This quick fix, however, tests for *long* to be 64-bit or not. What we
> need, though, is a test that says whether *whatever data type we use for
> timestamps* is 64-bit or not.
>
> The same quick fix was used to handle the similar problem where Git's
> source code uses `unsigned long` to represent size, instead of `size_t`.
>
> So let's just add another prerequisite to test specifically whether
> timestamps are represented by a 64-bit data type or not. Later, when we
> will have switched to using `time_t` where appropriate, we can flip that
> prerequisite to test `time_t` instead of `long`.

The changes that move from LONG_IS to TIME_IS in this patch are all
about time (iow, LONG_IS_64BIT prereq is still used in the check on
sizes).  The patch looks sensible.


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 2/6] Specify explicitly where we parse timestamps
  2017-02-27 22:51     ` Junio C Hamano
@ 2017-02-28 10:49       ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-28 10:49 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi Junio,

On Mon, 27 Feb 2017, Junio C Hamano wrote:

> Junio C Hamano <gitster@pobox.com> writes:
> 
> >> -	unsigned long number = strtoul(date, &end, 10);
> >> +	time_t number = parse_timestamp(date, &end, 10);
> >
> > This hunk does not belong to this step.  Everybody else in this step
> 
> obviously I meant "the left half of this hunk" ;-)

Obviously ;-)

Ciao,
Johannes

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-27 22:48 ` [PATCH 0/6] Use time_t Junio C Hamano
@ 2017-02-28 11:32   ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-28 11:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi Junio,

On Mon, 27 Feb 2017, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > One notable fallout of this patch series is that on 64-bit Linux (and
> > other platforms where `unsigned long` is 64-bit), we now limit the
> > range of dates to LONG_MAX (i.e. the *signed* maximum value). This
> > needs to be done as `time_t` can be signed (and indeed is at least on
> > my Ubuntu setup).
> >
> > Obviously, I think that we can live with that, and I hope that all
> > interested parties agree.
> 
> s/ulong/time_t/ is definintely a good change, and it will take us to a
> place we would want to be in in some future.  

Actually. I have to take back the part where I hoped that all interested
parties would agree. The problem is 32-bit Linux:

	$ cat >a1.c <<-\EOF
	#include <stdio.h>
	#include <limits.h>
	#include <time.h>

	int main(int argc, char **argv)
	{
		printf("sizeof(long): %d, sizeof(time_t): %d, ulong_max: %lu\n",
		       (int)sizeof(long), (int)sizeof(time_t), ULONG_MAX);
		return 0;
	}
	EOF

	$ gcc -m32 -Wall -o a1 a1.c

	$ ./a1
	sizeof(long): 4, sizeof(time_t): 4, ulong_max: 4294967295

So. Not only is `long` a 32-bit on 32-bit Linux, but so is `time_t`. And
with that, switching from `ULONG_MAX` as the maximal time we can represent
in Git to `LONG_MAX` is kind of a serious problem.

> As long as there remains no platform we care about whose time_t and long
> are still 32-bit signed integer, there will be a fallout to them with
> this change.

Sorry, I do not understand the verb "remains" in conjunction with "no
platform"...

Do you mean to say that currently no platform we care about has 32-bit
signed time_t/long?

If so, I just demonstrated this to be unfortunately incorrect.

> It appears that we use uint64_t in many places in our code.  So
> while philosophically time_t is the right type, uint64_t might be
> practically a safer alternative type to use at the endgame patch in
> this series.

Yes, I think you are right. We should use uint64_t instead of time_t, but
*semantically* we should not even use uint64_t. We should introduce our
own data type instead of repeating the mistake to use a data type that
does not convey its role to the reader.

Currently, I am favoring timestamp_t.

> I haven't seen it yet, but presumably the last one 6/6 is the endgame?

Maybe it took a while to get sent out, but it made it into public inbox:
http://public-inbox.org/git/75efe76cbb0636741a7c3aec9e21459bc1dc3cbe.1488231002.git.johannes.schindelin@gmx.de/

Ciao,
Johannes

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (6 preceding siblings ...)
  2017-02-27 22:48 ` [PATCH 0/6] Use time_t Junio C Hamano
@ 2017-02-28 14:28 ` Jeff King
  2017-02-28 15:01   ` Johannes Schindelin
                     ` (2 more replies)
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
  8 siblings, 3 replies; 113+ messages in thread
From: Jeff King @ 2017-02-28 14:28 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano

On Mon, Feb 27, 2017 at 10:30:20PM +0100, Johannes Schindelin wrote:

> One notable fallout of this patch series is that on 64-bit Linux (and
> other platforms where `unsigned long` is 64-bit), we now limit the range
> of dates to LONG_MAX (i.e. the *signed* maximum value). This needs to be
> done as `time_t` can be signed (and indeed is at least on my Ubuntu
> setup).
> 
> Obviously, I think that we can live with that, and I hope that all
> interested parties agree.

I do not just agree, but I think the move to a signed timestamp is a big
improvement. Git's object format is happy to represent times before
1970, but the code is not. I know this has been a pain for people who
import ancient histories into Git.

It looks from the discussion like the sanest path forward is our own
signed-64bit timestamp_t. That's unfortunate compared to using the
standard time_t, but hopefully it would reduce the number of knobs (like
TIME_T_IS_INT64) in the long run.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 14:28 ` Jeff King
@ 2017-02-28 15:01   ` Johannes Schindelin
  2017-02-28 16:38   ` René Scharfe
  2017-02-28 17:26   ` Junio C Hamano
  2 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-28 15:01 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Junio C Hamano

Hi Peff,

On Tue, 28 Feb 2017, Jeff King wrote:

> On Mon, Feb 27, 2017 at 10:30:20PM +0100, Johannes Schindelin wrote:
> 
> > One notable fallout of this patch series is that on 64-bit Linux (and
> > other platforms where `unsigned long` is 64-bit), we now limit the
> > range of dates to LONG_MAX (i.e. the *signed* maximum value). This
> > needs to be done as `time_t` can be signed (and indeed is at least on
> > my Ubuntu setup).
> > 
> > Obviously, I think that we can live with that, and I hope that all
> > interested parties agree.
> 
> I do not just agree, but I think the move to a signed timestamp is a big
> improvement. Git's object format is happy to represent times before
> 1970, but the code is not. I know this has been a pain for people who
> import ancient histories into Git.
> 
> It looks from the discussion like the sanest path forward is our own
> signed-64bit timestamp_t. That's unfortunate compared to using the
> standard time_t, but hopefully it would reduce the number of knobs (like
> TIME_T_IS_INT64) in the long run.

Boy am I happy that I did not go ahead and changed the code to use
uint64_t yet...

I'll let the dust settle a bit and then make further changes and send out
v2.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 14:28 ` Jeff King
  2017-02-28 15:01   ` Johannes Schindelin
@ 2017-02-28 16:38   ` René Scharfe
  2017-02-28 18:55     ` Junio C Hamano
  2017-02-28 17:26   ` Junio C Hamano
  2 siblings, 1 reply; 113+ messages in thread
From: René Scharfe @ 2017-02-28 16:38 UTC (permalink / raw)
  To: Jeff King, Johannes Schindelin; +Cc: git, Junio C Hamano

Am 28.02.2017 um 15:28 schrieb Jeff King:
> On Mon, Feb 27, 2017 at 10:30:20PM +0100, Johannes Schindelin wrote:
>
>> One notable fallout of this patch series is that on 64-bit Linux (and
>> other platforms where `unsigned long` is 64-bit), we now limit the range
>> of dates to LONG_MAX (i.e. the *signed* maximum value). This needs to be
>> done as `time_t` can be signed (and indeed is at least on my Ubuntu
>> setup).
>>
>> Obviously, I think that we can live with that, and I hope that all
>> interested parties agree.
>
> I do not just agree, but I think the move to a signed timestamp is a big
> improvement. Git's object format is happy to represent times before
> 1970, but the code is not. I know this has been a pain for people who
> import ancient histories into Git.
>
> It looks from the discussion like the sanest path forward is our own
> signed-64bit timestamp_t. That's unfortunate compared to using the
> standard time_t, but hopefully it would reduce the number of knobs (like
> TIME_T_IS_INT64) in the long run.

Glibc will get a way to enable 64-bit time_t on 32-bit platforms 
eventually (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign). 
Can platforms that won't provide a 64-bit time_t by 2038 be actually 
used at that point?  How would we get time information on them?  How 
would a custom timestamp_t help us?

Regarding the need for knobs: We could let the compiler chose between 
strtoll() and strtol() based on the size of time_t, in an inline 
function.  The maximum value can be calculated using its size as well. 
And we could use PRIdMAX and cast to intmax_t for printing.

René

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 14:28 ` Jeff King
  2017-02-28 15:01   ` Johannes Schindelin
  2017-02-28 16:38   ` René Scharfe
@ 2017-02-28 17:26   ` Junio C Hamano
  2017-02-28 20:01     ` Jeff King
  2 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-02-28 17:26 UTC (permalink / raw)
  To: Jeff King; +Cc: Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> I do not just agree, but I think the move to a signed timestamp is a big
> improvement. Git's object format is happy to represent times before
> 1970, but the code is not. I know this has been a pain for people who
> import ancient histories into Git.
>
> It looks from the discussion like the sanest path forward is our own
> signed-64bit timestamp_t. That's unfortunate compared to using the
> standard time_t, but hopefully it would reduce the number of knobs (like
> TIME_T_IS_INT64) in the long run.

Keeping it unsigned is safer in the short-term.  There are some
places that uses 0 as "impossible time" (e.g. somebody tried to
parse a string as time and returns a failure) and these places need
to be found and be replaced with probably the most negative value
that timestamp_t cn represent.  Another possible special value we
may use is for "expiring everything" but I think we tend to just use
the timestamp of the present time for that purpose and not UONG_MAX,
so we should be OK there.

But we need to cross the bridge to signed timestamp sometime, and I
do not see any reason why that somtime should not be now.



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 16:38   ` René Scharfe
@ 2017-02-28 18:55     ` Junio C Hamano
  2017-02-28 20:04       ` Jeff King
  2017-02-28 20:54       ` Johannes Schindelin
  0 siblings, 2 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-02-28 18:55 UTC (permalink / raw)
  To: René Scharfe; +Cc: Jeff King, Johannes Schindelin, git

René Scharfe <l.s.r@web.de> writes:

> Am 28.02.2017 um 15:28 schrieb Jeff King:
>
>> It looks from the discussion like the sanest path forward is our own
>> signed-64bit timestamp_t. That's unfortunate compared to using the
>> standard time_t, but hopefully it would reduce the number of knobs (like
>> TIME_T_IS_INT64) in the long run.
>
> Glibc will get a way to enable 64-bit time_t on 32-bit platforms
> eventually
> (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign). Can
> platforms that won't provide a 64-bit time_t by 2038 be actually used
> at that point?  How would we get time information on them?  How would
> a custom timestamp_t help us?

That's a sensible "wait, let's step back a bit".  I take it that you
are saying "time_t is just fine", and I am inclined to agree.

Right now, they may be able to have future timestamps ranging to
year 2100 and switching to time_t would limit their ability to
express future time to 2038 but they would be able to express
timestamp in the past to cover most of 20th century.  Given that
these 32-bit time_t software platforms will die off before year 2038
(either by underlying hardware getting obsolete, or software updated
to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
would not be too big a deal to warrant additional complexity.

> Regarding the need for knobs: We could let the compiler chose between
> strtoll() and strtol() based on the size of time_t, in an inline
> function.  The maximum value can be calculated using its size as
> well. And we could use PRIdMAX and cast to intmax_t for printing.

Thanks.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 17:26   ` Junio C Hamano
@ 2017-02-28 20:01     ` Jeff King
  2017-02-28 22:27       ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Jeff King @ 2017-02-28 20:01 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git

On Tue, Feb 28, 2017 at 09:26:23AM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > I do not just agree, but I think the move to a signed timestamp is a big
> > improvement. Git's object format is happy to represent times before
> > 1970, but the code is not. I know this has been a pain for people who
> > import ancient histories into Git.
> >
> > It looks from the discussion like the sanest path forward is our own
> > signed-64bit timestamp_t. That's unfortunate compared to using the
> > standard time_t, but hopefully it would reduce the number of knobs (like
> > TIME_T_IS_INT64) in the long run.
> 
> Keeping it unsigned is safer in the short-term.  There are some
> places that uses 0 as "impossible time" (e.g. somebody tried to
> parse a string as time and returns a failure) and these places need
> to be found and be replaced with probably the most negative value
> that timestamp_t cn represent.  Another possible special value we
> may use is for "expiring everything" but I think we tend to just use
> the timestamp of the present time for that purpose and not UONG_MAX,
> so we should be OK there.

Yeah. I think I was the one who invented the "0 is impossible"
convention. We can certainly stick with it for now (it's awkward if you
really do have an entry on Jan 1 1970, but other than that it's an OK
marker). I agree that the most negatively value is probably a saner
choice, but we can switch to it after the dust settles.

> But we need to cross the bridge to signed timestamp sometime, and I
> do not see any reason why that somtime should not be now.

Yep.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 18:55     ` Junio C Hamano
@ 2017-02-28 20:04       ` Jeff King
  2017-02-28 20:54       ` Johannes Schindelin
  1 sibling, 0 replies; 113+ messages in thread
From: Jeff King @ 2017-02-28 20:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: René Scharfe, Johannes Schindelin, git

On Tue, Feb 28, 2017 at 10:55:49AM -0800, Junio C Hamano wrote:

> > Glibc will get a way to enable 64-bit time_t on 32-bit platforms
> > eventually
> > (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign). Can
> > platforms that won't provide a 64-bit time_t by 2038 be actually used
> > at that point?  How would we get time information on them?  How would
> > a custom timestamp_t help us?
> 
> That's a sensible "wait, let's step back a bit".  I take it that you
> are saying "time_t is just fine", and I am inclined to agree.
> 
> Right now, they may be able to have future timestamps ranging to
> year 2100 and switching to time_t would limit their ability to
> express future time to 2038 but they would be able to express
> timestamp in the past to cover most of 20th century.  Given that
> these 32-bit time_t software platforms will die off before year 2038
> (either by underlying hardware getting obsolete, or software updated
> to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
> would not be too big a deal to warrant additional complexity.

For what it's worth, I'm on board with just using time_t if it reduces
the overall complexity. I agree that the "loss" of far-future timestamp
handling is unlikely to matter between now and 2038, and those systems
will have to figure out their time_t problems by then. By actually using
time_t we get to piggy-back on their solution.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 18:55     ` Junio C Hamano
  2017-02-28 20:04       ` Jeff King
@ 2017-02-28 20:54       ` Johannes Schindelin
  2017-02-28 21:31         ` Jeff King
  2017-02-28 21:31         ` René Scharfe
  1 sibling, 2 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-28 20:54 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: René Scharfe, Jeff King, git

[-- Attachment #1: Type: text/plain, Size: 1715 bytes --]

Hi Junio,

On Tue, 28 Feb 2017, Junio C Hamano wrote:

> René Scharfe <l.s.r@web.de> writes:
> 
> > Am 28.02.2017 um 15:28 schrieb Jeff King:
> >
> >> It looks from the discussion like the sanest path forward is our own
> >> signed-64bit timestamp_t. That's unfortunate compared to using the
> >> standard time_t, but hopefully it would reduce the number of knobs
> >> (like TIME_T_IS_INT64) in the long run.
> >
> > Glibc will get a way to enable 64-bit time_t on 32-bit platforms
> > eventually (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign).
> > Can platforms that won't provide a 64-bit time_t by 2038 be actually
> > used at that point?  How would we get time information on them?  How
> > would a custom timestamp_t help us?
> 
> That's a sensible "wait, let's step back a bit".  I take it that you are
> saying "time_t is just fine", and I am inclined to agree.
> 
> Right now, they may be able to have future timestamps ranging to
> year 2100 and switching to time_t would limit their ability to
> express future time to 2038 but they would be able to express
> timestamp in the past to cover most of 20th century.  Given that
> these 32-bit time_t software platforms will die off before year 2038
> (either by underlying hardware getting obsolete, or software updated
> to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
> would not be too big a deal to warrant additional complexity.

You seem to assume that time_t is required to be signed. But from my
understanding that is only guaranteed by POSIX, not by ISO C.

We may very well buy ourselves a ton of trouble if we decide to switch to
`time_t` rather than to `int64_t`.

Ciao,
Johannes

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 20:54       ` Johannes Schindelin
@ 2017-02-28 21:31         ` Jeff King
  2017-02-28 21:31         ` René Scharfe
  1 sibling, 0 replies; 113+ messages in thread
From: Jeff King @ 2017-02-28 21:31 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, René Scharfe, git

On Tue, Feb 28, 2017 at 09:54:58PM +0100, Johannes Schindelin wrote:

> > Right now, they may be able to have future timestamps ranging to
> > year 2100 and switching to time_t would limit their ability to
> > express future time to 2038 but they would be able to express
> > timestamp in the past to cover most of 20th century.  Given that
> > these 32-bit time_t software platforms will die off before year 2038
> > (either by underlying hardware getting obsolete, or software updated
> > to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
> > would not be too big a deal to warrant additional complexity.
> 
> You seem to assume that time_t is required to be signed. But from my
> understanding that is only guaranteed by POSIX, not by ISO C.

I wonder how common that is in practice, and whether it is worth
treating it as a quality-of-implementation issue. IOW, to say "your
platform time_t doesn't handle negative times, so you get Jan 1 1970 for
any dates before then. Complain to your platform vendor".

I'm not sure how much complexity it would add to the code.  Either way,
when we parse an ascii-decimal timestamp from an object, we need to do
bounds checking. Whether that bound is at "0" or "LONG_MIN", I don't
think that it changes much.

Meanwhile, if we were to have a negative timestamp_t but the system
time_t is unsigned, we have to do a bounds-check any time we use a
system function like gmtime(), or risk funny wrap-around bugs.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 20:54       ` Johannes Schindelin
  2017-02-28 21:31         ` Jeff King
@ 2017-02-28 21:31         ` René Scharfe
  2017-02-28 23:10           ` Johannes Schindelin
  1 sibling, 1 reply; 113+ messages in thread
From: René Scharfe @ 2017-02-28 21:31 UTC (permalink / raw)
  To: Johannes Schindelin, Junio C Hamano; +Cc: Jeff King, git

Am 28.02.2017 um 21:54 schrieb Johannes Schindelin:
> Hi Junio,
>
> On Tue, 28 Feb 2017, Junio C Hamano wrote:
>
>> René Scharfe <l.s.r@web.de> writes:
>>
>>> Am 28.02.2017 um 15:28 schrieb Jeff King:
>>>
>>>> It looks from the discussion like the sanest path forward is our own
>>>> signed-64bit timestamp_t. That's unfortunate compared to using the
>>>> standard time_t, but hopefully it would reduce the number of knobs
>>>> (like TIME_T_IS_INT64) in the long run.
>>>
>>> Glibc will get a way to enable 64-bit time_t on 32-bit platforms
>>> eventually (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign).
>>> Can platforms that won't provide a 64-bit time_t by 2038 be actually
>>> used at that point?  How would we get time information on them?  How
>>> would a custom timestamp_t help us?
>>
>> That's a sensible "wait, let's step back a bit".  I take it that you are
>> saying "time_t is just fine", and I am inclined to agree.
>>
>> Right now, they may be able to have future timestamps ranging to
>> year 2100 and switching to time_t would limit their ability to
>> express future time to 2038 but they would be able to express
>> timestamp in the past to cover most of 20th century.  Given that
>> these 32-bit time_t software platforms will die off before year 2038
>> (either by underlying hardware getting obsolete, or software updated
>> to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
>> would not be too big a deal to warrant additional complexity.
>
> You seem to assume that time_t is required to be signed. But from my
> understanding that is only guaranteed by POSIX, not by ISO C.
>
> We may very well buy ourselves a ton of trouble if we decide to switch to
> `time_t` rather than to `int64_t`.

True, and time_t doesn't even have to be an integer type.  But which 
platforms capable of running git use something else than int32_t or int64_t?

René

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 20:01     ` Jeff King
@ 2017-02-28 22:27       ` Junio C Hamano
  2017-02-28 22:33         ` Jeff King
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-02-28 22:27 UTC (permalink / raw)
  To: Jeff King; +Cc: Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> ... We can certainly stick with it for now (it's awkward if you
> really do have an entry on Jan 1 1970, but other than that it's an OK
> marker). I agree that the most negatively value is probably a saner
> choice, but we can switch to it after the dust settles.

I was trying to suggest that we should strive to switch to the most
negative or whatever the most implausible value in the new range
(and leave it as a possible bug to be fixed if we missed a place
that still used "0 is impossible") while doing the ulong to time_t
(or timestamp_t that is i64).  

"safer in the short term" wasn't meant to be "let's not spend time
to do quality work".  As long as we are switching, we should follow
it through.

>> But we need to cross the bridge to signed timestamp sometime, and I
>> do not see any reason why that somtime should not be now.
>
> Yep.
>
> -Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 22:27       ` Junio C Hamano
@ 2017-02-28 22:33         ` Jeff King
  2017-03-01 17:23           ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Jeff King @ 2017-02-28 22:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git

On Tue, Feb 28, 2017 at 02:27:22PM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > ... We can certainly stick with it for now (it's awkward if you
> > really do have an entry on Jan 1 1970, but other than that it's an OK
> > marker). I agree that the most negatively value is probably a saner
> > choice, but we can switch to it after the dust settles.
> 
> I was trying to suggest that we should strive to switch to the most
> negative or whatever the most implausible value in the new range
> (and leave it as a possible bug to be fixed if we missed a place
> that still used "0 is impossible") while doing the ulong to time_t
> (or timestamp_t that is i64).  
> 
> "safer in the short term" wasn't meant to be "let's not spend time
> to do quality work".  As long as we are switching, we should follow
> it through.

Sure, I'd be much happier to see it done now. I just didn't want to pile
on the requirements to the point that step 1 doesn't get done. But I
haven't even looked at the code changes needed for time_t. I suspect
Dscho has a better feel for it at this point.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 21:31         ` René Scharfe
@ 2017-02-28 23:10           ` Johannes Schindelin
  2017-03-01  0:59             ` René Scharfe
  0 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-02-28 23:10 UTC (permalink / raw)
  To: René Scharfe; +Cc: Junio C Hamano, Jeff King, git

[-- Attachment #1: Type: text/plain, Size: 3521 bytes --]

Hi René,

On Tue, 28 Feb 2017, René Scharfe wrote:

> Am 28.02.2017 um 21:54 schrieb Johannes Schindelin:
> >
> > On Tue, 28 Feb 2017, Junio C Hamano wrote:
> >
> > > René Scharfe <l.s.r@web.de> writes:
> > >
> > > > Am 28.02.2017 um 15:28 schrieb Jeff King:
> > > >
> > > > > It looks from the discussion like the sanest path forward is our
> > > > > own signed-64bit timestamp_t. That's unfortunate compared to
> > > > > using the standard time_t, but hopefully it would reduce the
> > > > > number of knobs (like TIME_T_IS_INT64) in the long run.
> > > >
> > > > Glibc will get a way to enable 64-bit time_t on 32-bit platforms
> > > > eventually
> > > > (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign).  Can
> > > > platforms that won't provide a 64-bit time_t by 2038 be actually
> > > > used at that point?  How would we get time information on them?
> > > > How would a custom timestamp_t help us?
> > >
> > > That's a sensible "wait, let's step back a bit".  I take it that you
> > > are saying "time_t is just fine", and I am inclined to agree.
> > >
> > > Right now, they may be able to have future timestamps ranging to
> > > year 2100 and switching to time_t would limit their ability to
> > > express future time to 2038 but they would be able to express
> > > timestamp in the past to cover most of 20th century.  Given that
> > > these 32-bit time_t software platforms will die off before year 2038
> > > (either by underlying hardware getting obsolete, or software updated
> > > to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
> > > would not be too big a deal to warrant additional complexity.
> >
> > You seem to assume that time_t is required to be signed. But from my
> > understanding that is only guaranteed by POSIX, not by ISO C.
> >
> > We may very well buy ourselves a ton of trouble if we decide to switch
> > to `time_t` rather than to `int64_t`.
> 
> True, and time_t doesn't even have to be an integer type.  But which
> platforms capable of running git use something else than int32_t or
> int64_t?

That kind of thinking is dangerous. We don't know what platforms are
running Git, and we have a very clear example how we got it very wrong
recently, when we broke building with musl by requiring REG_STARTEND
support [*1*].

So why gamble? If we switch to uint64_t, it would definitely provide the
smoothest upgrade path. It is what the code assumed implicitly when we
broke 32-bit in v2.9.1.

If anybody really wants to support negative timestamps, it should be done
on top of my work. My current patch series does not even start to try to
address the ramifications of negative timestamps (see e.g. the use of
strtoull() for parsing). It is quite unreasonable to ask for such a
fundamental design change when it could very easily be done incrementally
instead, when needed, by someone who needs it.

My work would pave the way for that effort, of course. But this is really
as far as I can go with this patch series, given that I have bigger fish
to fry than to support negative timestamps.

Ciao,
Dscho

Footnote *1*: I still deeply regret deviating from my v1 that did *not*
require REG_STARTEND, but would have kept things working for platforms
without REG_STARTEND by simulating it.

But our thinking was: who would want to run Git in an environment so
ridiculously old that it does not have that clearly useful REG_STARTEND
support? Our answer was "nobody". And it was incorrect.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 23:10           ` Johannes Schindelin
@ 2017-03-01  0:59             ` René Scharfe
  0 siblings, 0 replies; 113+ messages in thread
From: René Scharfe @ 2017-03-01  0:59 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, Jeff King, git

Am 01.03.2017 um 00:10 schrieb Johannes Schindelin:
> Hi René,
>
> On Tue, 28 Feb 2017, René Scharfe wrote:
>
>> Am 28.02.2017 um 21:54 schrieb Johannes Schindelin:
>>>
>>> On Tue, 28 Feb 2017, Junio C Hamano wrote:
>>>
>>>> René Scharfe <l.s.r@web.de> writes:
>>>>
>>>>> Am 28.02.2017 um 15:28 schrieb Jeff King:
>>>>>
>>>>>> It looks from the discussion like the sanest path forward is our
>>>>>> own signed-64bit timestamp_t. That's unfortunate compared to
>>>>>> using the standard time_t, but hopefully it would reduce the
>>>>>> number of knobs (like TIME_T_IS_INT64) in the long run.
>>>>>
>>>>> Glibc will get a way to enable 64-bit time_t on 32-bit platforms
>>>>> eventually
>>>>> (https://sourceware.org/glibc/wiki/Y2038ProofnessDesign).  Can
>>>>> platforms that won't provide a 64-bit time_t by 2038 be actually
>>>>> used at that point?  How would we get time information on them?
>>>>> How would a custom timestamp_t help us?
>>>>
>>>> That's a sensible "wait, let's step back a bit".  I take it that you
>>>> are saying "time_t is just fine", and I am inclined to agree.
>>>>
>>>> Right now, they may be able to have future timestamps ranging to
>>>> year 2100 and switching to time_t would limit their ability to
>>>> express future time to 2038 but they would be able to express
>>>> timestamp in the past to cover most of 20th century.  Given that
>>>> these 32-bit time_t software platforms will die off before year 2038
>>>> (either by underlying hardware getting obsolete, or software updated
>>>> to handle 64-bit time_t), the (temporary) loss of 2038-2100 range
>>>> would not be too big a deal to warrant additional complexity.
>>>
>>> You seem to assume that time_t is required to be signed. But from my
>>> understanding that is only guaranteed by POSIX, not by ISO C.
>>>
>>> We may very well buy ourselves a ton of trouble if we decide to switch
>>> to `time_t` rather than to `int64_t`.
>>
>> True, and time_t doesn't even have to be an integer type.  But which
>> platforms capable of running git use something else than int32_t or
>> int64_t?
>
> That kind of thinking is dangerous. We don't know what platforms are
> running Git, and we have a very clear example how we got it very wrong
> recently, when we broke building with musl by requiring REG_STARTEND
> support [*1*].

In general that's true, and if nobody can add to the list (glibc: 
int32_t on 32-bit platforms, int64_t on 64-bit platforms for now; 
NetBSD, OpenBSD, Windows int64_t) then we shouldn't make assumptions 
about time_t that go beyond the C standard -- at least not without 
verifying them, e.g. with asserts or tests.  I'd especially be 
interested in hearing about platforms that use a floating point type.

Systems lacking REG_STARTEND can use compat/regex/ -- that's doesn't 
sound too bad.  (I didn't follow the original discussion too closely, 
and don't mean to open it up again.)

> So why gamble? If we switch to uint64_t, it would definitely provide the
> smoothest upgrade path. It is what the code assumed implicitly when we
> broke 32-bit in v2.9.1.

We can assume that time_t exists everywhere and is used by functions 
like gettimeofday(2) and localtime(3).  Why invent our own layer on top? 
  What would it be able to do that time_t alone can't?  I understand a 
need to handle times before 1970 for historical documents, but what good 
would it do to have the ability to handle dates beyond 2038, today?

Platforms that don't use the next two decades to provide a time_t that 
can store the current time by then will have bigger problems than a 
crippled git.

> If anybody really wants to support negative timestamps, it should be done
> on top of my work. My current patch series does not even start to try to
> address the ramifications of negative timestamps (see e.g. the use of
> strtoull() for parsing). It is quite unreasonable to ask for such a
> fundamental design change when it could very easily be done incrementally
> instead, when needed, by someone who needs it.

I'm confused.  Your patch series converts to time_t.  Why not use it? 
That would prepare ourselves for the bright future when we'll have a 
64-bit time_t in every libc. :)

René



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 0/6] Use time_t
  2017-02-28 22:33         ` Jeff King
@ 2017-03-01 17:23           ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-03-01 17:23 UTC (permalink / raw)
  To: Jeff King; +Cc: Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> On Tue, Feb 28, 2017 at 02:27:22PM -0800, Junio C Hamano wrote:
>
>> Jeff King <peff@peff.net> writes:
>> 
>> > ... We can certainly stick with it for now (it's awkward if you
>> > really do have an entry on Jan 1 1970, but other than that it's an OK
>> > marker). I agree that the most negatively value is probably a saner
>> > choice, but we can switch to it after the dust settles.
>> 
>> I was trying to suggest that we should strive to switch to the most
>> negative or whatever the most implausible value in the new range
>> (and leave it as a possible bug to be fixed if we missed a place
>> that still used "0 is impossible") while doing the ulong to time_t
>> (or timestamp_t that is i64).  
>> 
>> "safer in the short term" wasn't meant to be "let's not spend time
>> to do quality work".  As long as we are switching, we should follow
>> it through.
>
> Sure, I'd be much happier to see it done now. I just didn't want to pile
> on the requirements to the point that step 1 doesn't get done.

Ah, that was what you meant.

I was assuming that we are switching to a longer _signed_ type.  It
felt silly to tell users "you can use timestamps before the epoch
now with this change, but you cannot express time exactly at the
epoch".

I am perfectly OK with switching to a longer _unsigned_ type with
the "0 is impossible" [*1*] intact, aka "safer in the short term",
if we want to it make our first step.  That may be a smaller step,
but still a step in the right direction.


[Footnote]

*1* It could be "-1 is impossible", I didn't actually check.
    Funnily enough, ISO C99 uses (time_t)(-1) to signal an error
    when returning value from mktime() and time().


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 3/6] Introduce a new "printf format" for timestamps
  2017-02-27 21:31 ` [PATCH 3/6] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-03-01 18:20   ` Junio C Hamano
  2017-03-01 19:53     ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-03-01 18:20 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> So let's introduce the pseudo format "PRItime" (currently simply being
> "lu") so that it is easy later on to change the data type to time_t.

The problem being solved is a good thing to solve, and 

> -	printf("author-time %lu\n", ci.author_time);
> +	printf("author-time %"PRItime"\n", ci.author_time);

is one of the two ingredients to the solution for this line.  But
the final form would require casting ci.author_time to the type
expected by %PRItime format specifier.  With this change alone, you
could define PRItime to expect an winder type in the next step but
that would be a bad conversion.  IOW, changing only the format
without introducing an explicit cast appears to invite future
mistakes.

It would be better to introduce the timestamp_t we discussed earlier
before (or at) this step, and typedef it to ulong first, and then in
this step, change the above to

	printf("author-time %"PRItime"\n", (timestamp_t)ci.author_time);

to keep them in sync.  And at a later step in the series, you can
update definition of PRItime and timestamp_t to make them wider at
the same time, and the changes in this patch like the above line
would not need to be touched again.



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH 3/6] Introduce a new "printf format" for timestamps
  2017-03-01 18:20   ` Junio C Hamano
@ 2017-03-01 19:53     ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-03-01 19:53 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Junio C Hamano <gitster@pobox.com> writes:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
>
>> So let's introduce the pseudo format "PRItime" (currently simply being
>> "lu") so that it is easy later on to change the data type to time_t.
>
> The problem being solved is a good thing to solve, and 
>
>> -	printf("author-time %lu\n", ci.author_time);
>> +	printf("author-time %"PRItime"\n", ci.author_time);
> ...
> It would be better to introduce the timestamp_t we discussed earlier
> before (or at) this step, and typedef it to ulong first, and then in
> this step, change the above to
>
> 	printf("author-time %"PRItime"\n", (timestamp_t)ci.author_time);
>
> to keep them in sync.

Nah, ignore me.  This was just me being silly.

I was somehow expecting (incorrecty) that we would pick one single
PRItime for everybody and end up doing an equivalent of

	printf("%llu", (unsigned long long)(something_that_is_time_t))

But as long as the plan is to configure PRItime for the platform's
time_t (or whatever the final type for timestamp_t is), we do not
have to have any extra cast here.  The endgame will use the type
that is consistent with %PRItime for variables and structure fields,
and we do not want an extra cast.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 0/8] Introduce timestamp_t for timestamps
  2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
                   ` (7 preceding siblings ...)
  2017-02-28 14:28 ` Jeff King
@ 2017-04-02 19:06 ` Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                     ` (8 more replies)
  8 siblings, 9 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as we already make use of
64-bit data types internally. There is no good reason why we should not
use the same for timestamps. Hence, let's use uintmax_t for timestamps.

Note: while the `time_t` data type exists and is meant to be used for
timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
used `time_t` for that reason, but it came with a few serious downsides:
as `time_t` can be signed (and indeed, on Windows it is an int64_t),
Git's expectation that 0 is the minimal value does no longer hold true,
introducing its own set of interesting challenges. Besides, if we *can*
handle far in the future timestamps (except for formatting them using
the system libraries), it is more consistent to do so.

The upside of using `uintmax_t` for timestamps is that we do a much
better job to support far in the future timestamps across all platforms,
including 32-bit ones. The downside is that those platforms that use a
32-bit `time_t` will barf when parsing or formatting those timestamps.

Changes since v1 (sorry, the interdiff is huge due to the switch from
time_t to uintmax_t):

- moved an `unsigned long` -> `time_t` change into 6/6 (it inadvertently
  was done as part of the patch that wanted to introduce
  parse_timestamp()).

- using `uintmax_t` for timestamps instead of `time_t` now.

- as 32-bit Linux still uses 32-bit time_t, while we may be able to
  represent timestamps far in the future internally, as soon as we try
  to format them using strftime() it will fail. Added safeguards against
  that and prepared t0006 and t5000 for this scenario (i.e. skip those
  tests that require strftime() to handle 64-bit timestamps).

- found a couple more places where strtoul() was still hardcoded for
  parsing timestamps, and fixed them.


Johannes Schindelin (8):
  ref-filter: avoid using `unsigned long` for catch-all data type
  t0006 & t5000: prepare for 64-bit timestamps
  t0006 & t5000: skip "far in the future" test when time_t is too
    limited
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Introduce a new data type for timestamps
  Abort if the system time cannot handle one of our timestamps
  Use uintmax_t for timestamps

 Documentation/technical/api-parse-options.txt |   8 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |   6 +-
 archive.h                                     |   2 +-
 builtin/am.c                                  |   4 +-
 builtin/blame.c                               |  14 ++--
 builtin/fsck.c                                |   6 +-
 builtin/gc.c                                  |   2 +-
 builtin/log.c                                 |   4 +-
 builtin/merge-base.c                          |   2 +-
 builtin/name-rev.c                            |   6 +-
 builtin/pack-objects.c                        |   4 +-
 builtin/prune.c                               |   4 +-
 builtin/receive-pack.c                        |  14 ++--
 builtin/reflog.c                              |  24 +++---
 builtin/rev-list.c                            |   2 +-
 builtin/rev-parse.c                           |   3 +-
 builtin/show-branch.c                         |   4 +-
 builtin/worktree.c                            |   4 +-
 bundle.c                                      |   4 +-
 cache.h                                       |  14 ++--
 commit.c                                      |  18 ++--
 commit.h                                      |   2 +-
 config.c                                      |   2 +-
 credential-cache--daemon.c                    |  12 +--
 date.c                                        | 113 ++++++++++++++------------
 fetch-pack.c                                  |   8 +-
 fsck.c                                        |   2 +-
 git-compat-util.h                             |   9 ++
 http-backend.c                                |   4 +-
 parse-options-cb.c                            |   4 +-
 pretty.c                                      |   4 +-
 reachable.c                                   |   9 +-
 reachable.h                                   |   4 +-
 ref-filter.c                                  |  22 ++---
 reflog-walk.c                                 |   8 +-
 refs.c                                        |  14 ++--
 refs.h                                        |   8 +-
 refs/files-backend.c                          |   8 +-
 revision.c                                    |   6 +-
 revision.h                                    |   4 +-
 sha1_name.c                                   |   6 +-
 t/helper/test-date.c                          |  18 ++--
 t/helper/test-parse-options.c                 |   4 +-
 t/t0006-date.sh                               |   4 +-
 t/t5000-tar-tree.sh                           |   6 +-
 t/test-lib.sh                                 |   3 +
 tag.c                                         |   6 +-
 tag.h                                         |   2 +-
 upload-pack.c                                 |   8 +-
 vcs-svn/fast_export.c                         |   8 +-
 vcs-svn/fast_export.h                         |   4 +-
 vcs-svn/svndump.c                             |   2 +-
 wt-status.c                                   |   2 +-
 54 files changed, 252 insertions(+), 218 deletions(-)


base-commit: b14f27f91770e0f99f64135348977a0ce1c7993a
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v2
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v2

Interdiff vs v1:

 diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
 index ed16519da8f..829b5581105 100644
 --- a/Documentation/technical/api-parse-options.txt
 +++ b/Documentation/technical/api-parse-options.txt
 @@ -183,13 +183,13 @@ There are some macros to easily define options:
  	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
  	The scaled value is put into `unsigned_long_var`.
  
 -`OPT_DATE(short, long, &time_t_var, description)`::
 +`OPT_DATE(short, long, &timestamp_t_var, description)`::
  	Introduce an option with date argument, see `approxidate()`.
 -	The timestamp is put into `time_t_var`.
 +	The timestamp is put into `timestamp_t_var`.
  
 -`OPT_EXPIRY_DATE(short, long, &time_t_var, description)`::
 +`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
  	Introduce an option with expiry date argument, see `parse_expiry_date()`.
 -	The timestamp is put into `time_t_var`.
 +	The timestamp is put into `timestamp_t_var`.
  
  `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
  	Introduce an option with argument.
 diff --git a/Makefile b/Makefile
 index 7dafef138c0..9b36068ac5e 100644
 --- a/Makefile
 +++ b/Makefile
 @@ -1530,10 +1530,6 @@ ifdef HAVE_GETDELIM
  	BASIC_CFLAGS += -DHAVE_GETDELIM
  endif
  
 -ifdef TIME_T_IS_INT64
 -	BASIC_CFLAGS += -DTIME_T_IS_INT64
 -endif
 -
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
 diff --git a/archive-zip.c b/archive-zip.c
 index b429a8d974a..4f715d40450 100644
 --- a/archive-zip.c
 +++ b/archive-zip.c
 @@ -545,10 +545,12 @@ static void write_zip_trailer(const unsigned char *sha1)
  		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
  }
  
 -static void dos_time(time_t *time, int *dos_date, int *dos_time)
 +static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
  {
 -	struct tm *t = localtime(time);
 +	time_t time;
 +	struct tm *t = localtime(&time);
  
 +	*timestamp = time;
  	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
  	            (t->tm_year + 1900 - 1980) * 512;
  	*dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
 diff --git a/archive.h b/archive.h
 index 415e0152e2c..62d1d82c1af 100644
 --- a/archive.h
 +++ b/archive.h
 @@ -9,7 +9,7 @@ struct archiver_args {
  	struct tree *tree;
  	const unsigned char *commit_sha1;
  	const struct commit *commit;
 -	time_t time;
 +	timestamp_t time;
  	struct pathspec pathspec;
  	unsigned int verbose : 1;
  	unsigned int worktree_attributes : 1;
 diff --git a/builtin/am.c b/builtin/am.c
 index d683268790e..89914ed8757 100644
 --- a/builtin/am.c
 +++ b/builtin/am.c
 @@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
  		if (skip_prefix(sb.buf, "# User ", &str))
  			fprintf(out, "From: %s\n", str);
  		else if (skip_prefix(sb.buf, "# Date ", &str)) {
 -			time_t timestamp;
 +			timestamp_t timestamp;
  			long tz, tz2;
  			char *end;
  
 diff --git a/builtin/blame.c b/builtin/blame.c
 index 9a7e9a2fe52..e508694f9ab 100644
 --- a/builtin/blame.c
 +++ b/builtin/blame.c
 @@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
  struct commit_info {
  	struct strbuf author;
  	struct strbuf author_mail;
 -	time_t author_time;
 +	timestamp_t author_time;
  	struct strbuf author_tz;
  
  	/* filled only when asked for details */
  	struct strbuf committer;
  	struct strbuf committer_mail;
 -	time_t committer_time;
 +	timestamp_t committer_time;
  	struct strbuf committer_tz;
  
  	struct strbuf summary;
 @@ -1578,7 +1578,7 @@ struct commit_info {
   */
  static void get_ac_line(const char *inbuf, const char *what,
  	struct strbuf *name, struct strbuf *mail,
 -	time_t *time, struct strbuf *tz)
 +	timestamp_t *time, struct strbuf *tz)
  {
  	struct ident_split ident;
  	size_t len, maillen, namelen;
 @@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
  	stop_progress(&pi.progress);
  }
  
 -static const char *format_time(time_t time, const char *tz_str,
 +static const char *format_time(timestamp_t time, const char *tz_str,
  			       int show_raw_time)
  {
  	static struct strbuf time_buf = STRBUF_INIT;
 diff --git a/builtin/gc.c b/builtin/gc.c
 index c2c61a57bb3..d38677cf44c 100644
 --- a/builtin/gc.c
 +++ b/builtin/gc.c
 @@ -33,7 +33,7 @@ static int aggressive_window = 250;
  static int gc_auto_threshold = 6700;
  static int gc_auto_pack_limit = 50;
  static int detach_auto = 1;
 -static unsigned long gc_log_expire_time;
 +static timestamp_t gc_log_expire_time;
  static const char *gc_log_expire = "1.day.ago";
  static const char *prune_expire = "2.weeks.ago";
  static const char *prune_worktrees_expire = "3.months.ago";
 diff --git a/builtin/log.c b/builtin/log.c
 index 84230d946d3..6d457f3b54a 100644
 --- a/builtin/log.c
 +++ b/builtin/log.c
 @@ -904,7 +904,7 @@ static void gen_message_id(struct rev_info *info, char *base)
  {
  	struct strbuf buf = STRBUF_INIT;
  	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 -		    (time_t) time(NULL),
 +		    (timestamp_t) time(NULL),
  		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
  	info->message_id = strbuf_detach(&buf, NULL);
  }
 diff --git a/builtin/name-rev.c b/builtin/name-rev.c
 index cf4faf27730..ee4b6950e5a 100644
 --- a/builtin/name-rev.c
 +++ b/builtin/name-rev.c
 @@ -10,7 +10,7 @@
  
  typedef struct rev_name {
  	const char *tip_name;
 -	time_t taggerdate;
 +	timestamp_t taggerdate;
  	int generation;
  	int distance;
  } rev_name;
 @@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
  #define MERGE_TRAVERSAL_WEIGHT 65535
  
  static void name_rev(struct commit *commit,
 -		const char *tip_name, time_t taggerdate,
 +		const char *tip_name, timestamp_t taggerdate,
  		int generation, int distance,
  		int deref)
  {
 @@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
  	struct name_ref_data *data = cb_data;
  	int can_abbreviate_output = data->tags_only && data->name_only;
  	int deref = 0;
 -	time_t taggerdate = TIME_MAX;
 +	timestamp_t taggerdate = TIME_MAX;
  
  	if (data->tags_only && !starts_with(path, "refs/tags/"))
  		return 0;
 diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
 index 0ab7a0e6c2e..9d0fef2e49a 100644
 --- a/builtin/pack-objects.c
 +++ b/builtin/pack-objects.c
 @@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
  static int non_empty;
  static int reuse_delta = 1, reuse_object = 1;
  static int keep_unreachable, unpack_unreachable, include_tag;
 -static time_t unpack_unreachable_expiration;
 +static timestamp_t unpack_unreachable_expiration;
  static int pack_loose_unreachable;
  static int local;
  static int have_non_local_packs;
 @@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
  static struct sha1_array recent_objects;
  
  static int loosened_object_can_be_discarded(const unsigned char *sha1,
 -					    time_t mtime)
 +					    timestamp_t mtime)
  {
  	if (!unpack_unreachable_expiration)
  		return 0;
 diff --git a/builtin/prune.c b/builtin/prune.c
 index a28d2c71b28..8dcfecde0f3 100644
 --- a/builtin/prune.c
 +++ b/builtin/prune.c
 @@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
  };
  static int show_only;
  static int verbose;
 -static time_t expire;
 +static timestamp_t expire;
  static int show_progress = -1;
  
  static int prune_tmp_file(const char *fullpath)
 diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
 index eba38524351..f8f4242719b 100644
 --- a/builtin/receive-pack.c
 +++ b/builtin/receive-pack.c
 @@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
  static const char *NONCE_SLOP = "SLOP";
  static const char *nonce_status;
  static long nonce_stamp_slop;
 -static time_t nonce_stamp_slop_limit;
 +static timestamp_t nonce_stamp_slop_limit;
  static struct ref_transaction *transaction;
  
  static enum {
 @@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
  	git_SHA1_Final(out, &ctx);
  }
  
 -static char *prepare_push_cert_nonce(const char *path, time_t stamp)
 +static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
  {
  	struct strbuf buf = STRBUF_INIT;
  	unsigned char sha1[20];
 @@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
  static const char *check_nonce(const char *buf, size_t len)
  {
  	char *nonce = find_header(buf, len, "nonce");
 -	time_t stamp, ostamp;
 +	timestamp_t stamp, ostamp;
  	char *bohmac, *expect = NULL;
  	const char *retval = NONCE_BAD;
  
 @@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
  		retval = NONCE_BAD;
  		goto leave;
  	}
 -	stamp = strtoul(nonce, &bohmac, 10);
 +	stamp = parse_timestamp(nonce, &bohmac, 10);
  	if (bohmac == nonce || bohmac[0] != '-') {
  		retval = NONCE_BAD;
  		goto leave;
 @@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
  	 * would mean it was issued by another server with its clock
  	 * skewed in the future.
  	 */
 -	ostamp = strtoul(push_cert_nonce, NULL, 10);
 +	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
  	nonce_stamp_slop = (long)ostamp - (long)stamp;
  
  	if (nonce_stamp_slop_limit &&
 diff --git a/builtin/reflog.c b/builtin/reflog.c
 index 2dc05121b77..4228d9ff4db 100644
 --- a/builtin/reflog.c
 +++ b/builtin/reflog.c
 @@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
  static const char reflog_exists_usage[] =
  "git reflog exists <ref>";
  
 -static time_t default_reflog_expire;
 -static time_t default_reflog_expire_unreachable;
 +static timestamp_t default_reflog_expire;
 +static timestamp_t default_reflog_expire_unreachable;
  
  struct cmd_reflog_expire_cb {
  	struct rev_info revs;
  	int stalefix;
 -	time_t expire_total;
 -	time_t expire_unreachable;
 +	timestamp_t expire_total;
 +	timestamp_t expire_unreachable;
  	int recno;
  };
  
 @@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
  static void mark_reachable(struct expire_reflog_policy_cb *cb)
  {
  	struct commit_list *pending;
 -	time_t expire_limit = cb->mark_limit;
 +	timestamp_t expire_limit = cb->mark_limit;
  	struct commit_list *leftover = NULL;
  
  	for (pending = cb->mark_list; pending; pending = pending->next)
 @@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
   * Return true iff the specified reflog entry should be expired.
   */
  static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 -				    const char *email, time_t timestamp, int tz,
 +				    const char *email, timestamp_t timestamp, int tz,
  				    const char *message, void *cb_data)
  {
  	struct expire_reflog_policy_cb *cb = cb_data;
 @@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
  
  static struct reflog_expire_cfg {
  	struct reflog_expire_cfg *next;
 -	time_t expire_total;
 -	time_t expire_unreachable;
 +	timestamp_t expire_total;
 +	timestamp_t expire_unreachable;
  	char pattern[FLEX_ARRAY];
  } *reflog_expire_cfg, **reflog_expire_cfg_tail;
  
 @@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
  	return ent;
  }
  
 -static int parse_expire_cfg_value(const char *var, const char *value, time_t *expire)
 +static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
  {
  	if (!value)
  		return config_error_nonbool(var);
 @@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
  {
  	const char *pattern, *key;
  	int pattern_len;
 -	time_t expire;
 +	timestamp_t expire;
  	int slot;
  	struct reflog_expire_cfg *ent;
  
 @@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
  static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
  {
  	struct expire_reflog_policy_cb cb;
 -	time_t now = time(NULL);
 +	timestamp_t now = time(NULL);
  	int i, status, do_all;
  	int explicit_expiry = 0;
  	unsigned int flags = 0;
 diff --git a/builtin/rev-list.c b/builtin/rev-list.c
 index 0aa93d58919..a30fbce3341 100644
 --- a/builtin/rev-list.c
 +++ b/builtin/rev-list.c
 @@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
  	}
  
  	if (info->show_timestamp)
 -		printf("%lu ", commit->date);
 +		printf("%"PRItime" ", commit->date);
  	if (info->header_prefix)
  		fputs(info->header_prefix, stdout);
  
 diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
 index 9e53a1a7ca4..f1c26ca6e62 100644
 --- a/builtin/rev-parse.c
 +++ b/builtin/rev-parse.c
 @@ -218,7 +218,8 @@ static void show_datestring(const char *flag, const char *datestr)
  	/* date handling requires both flags and revs */
  	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
  		return;
 -	snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
 +	snprintf(buffer, sizeof(buffer), "%s%"PRItime,
 +		 flag, approxidate(datestr));
  	show(buffer);
  }
  
 diff --git a/builtin/show-branch.c b/builtin/show-branch.c
 index ebd46fe6363..8860f429b06 100644
 --- a/builtin/show-branch.c
 +++ b/builtin/show-branch.c
 @@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
  			base = strtoul(reflog_base, &ep, 10);
  			if (*ep) {
  				/* Ah, that is a date spec... */
 -				time_t at;
 +				timestamp_t at;
  				at = approxidate(reflog_base);
  				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
  					    NULL, NULL, &base);
 @@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
  			char *logmsg;
  			char *nth_desc;
  			const char *msg;
 -			time_t timestamp;
 +			timestamp_t timestamp;
  			int tz;
  
  			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
 diff --git a/builtin/worktree.c b/builtin/worktree.c
 index 15e29e9e385..74f9b18d40c 100644
 --- a/builtin/worktree.c
 +++ b/builtin/worktree.c
 @@ -30,7 +30,7 @@ struct add_opts {
  
  static int show_only;
  static int verbose;
 -static time_t expire;
 +static timestamp_t expire;
  
  static int prune_worktree(const char *id, struct strbuf *reason)
  {
 diff --git a/bundle.c b/bundle.c
 index 75b82e6b653..05e014fc5ab 100644
 --- a/bundle.c
 +++ b/bundle.c
 @@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
  	unsigned long size;
  	enum object_type type;
  	char *buf = NULL, *line, *lineend;
 -	time_t date;
 +	timestamp_t date;
  	int result = 1;
  
  	if (revs->max_age == -1 && revs->min_age == -1)
 diff --git a/cache.h b/cache.h
 index 6226634bf23..38f5c7dd3a5 100644
 --- a/cache.h
 +++ b/cache.h
 @@ -1472,18 +1472,18 @@ struct date_mode {
  #define DATE_MODE(t) date_mode_from_type(DATE_##t)
  struct date_mode *date_mode_from_type(enum date_mode_type type);
  
 -const char *show_date(time_t time, int timezone, const struct date_mode *mode);
 -void show_date_relative(time_t time, int tz, const struct timeval *now,
 +const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
 +void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
  			struct strbuf *timebuf);
  int parse_date(const char *date, struct strbuf *out);
 -int parse_date_basic(const char *date, time_t *timestamp, int *offset);
 -int parse_expiry_date(const char *date, time_t *timestamp);
 +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
 +int parse_expiry_date(const char *date, timestamp_t *timestamp);
  void datestamp(struct strbuf *out);
  #define approxidate(s) approxidate_careful((s), NULL)
 -time_t approxidate_careful(const char *, int *);
 -time_t approxidate_relative(const char *date, const struct timeval *now);
 +timestamp_t approxidate_careful(const char *, int *);
 +timestamp_t approxidate_relative(const char *date, const struct timeval *now);
  void parse_date_format(const char *format, struct date_mode *mode);
 -int date_overflows(time_t date);
 +int date_overflows(timestamp_t date);
  
  #define IDENT_STRICT	       1
  #define IDENT_NO_DATE	       2
 diff --git a/commit.c b/commit.c
 index 5ddaa939ce4..99a62b90ee2 100644
 --- a/commit.c
 +++ b/commit.c
 @@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
  	return commit;
  }
  
 -static time_t parse_commit_date(const char *buf, const char *tail)
 +static timestamp_t parse_commit_date(const char *buf, const char *tail)
  {
  	const char *dateptr;
  
 @@ -89,7 +89,7 @@ static time_t parse_commit_date(const char *buf, const char *tail)
  		/* nada */;
  	if (buf >= tail)
  		return 0;
 -	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
 +	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
  	return parse_timestamp(dateptr, NULL, 10);
  }
  
 @@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
  
  static int commit_list_compare_by_date(const void *a, const void *b)
  {
 -	time_t a_date = ((const struct commit_list *)a)->item->date;
 -	time_t b_date = ((const struct commit_list *)b)->item->date;
 +	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
 +	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
  	if (a_date < b_date)
  		return 1;
  	if (a_date > b_date)
 @@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
  	const char *ident_line;
  	size_t ident_len;
  	char *date_end;
 -	time_t date;
 +	timestamp_t date;
  
  	ident_line = find_commit_header(buffer, "author", &ident_len);
  	if (!ident_line)
 @@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
  {
  	const struct commit *a = a_, *b = b_;
  	struct author_date_slab *author_date = cb_data;
 -	time_t a_date = *(author_date_slab_at(author_date, a));
 -	time_t b_date = *(author_date_slab_at(author_date, b));
 +	timestamp_t a_date = *(author_date_slab_at(author_date, a));
 +	timestamp_t b_date = *(author_date_slab_at(author_date, b));
  
  	/* newer commits with larger date first */
  	if (a_date < b_date)
 diff --git a/commit.h b/commit.h
 index f445fbea49b..5f2a837b067 100644
 --- a/commit.h
 +++ b/commit.h
 @@ -17,7 +17,7 @@ struct commit {
  	struct object object;
  	void *util;
  	unsigned int index;
 -	time_t date;
 +	timestamp_t date;
  	struct commit_list *parents;
  	struct tree *tree;
  };
 diff --git a/config.c b/config.c
 index 1a4d85537b3..3247bfaa020 100644
 --- a/config.c
 +++ b/config.c
 @@ -1926,7 +1926,7 @@ int git_config_get_expiry(const char *key, const char **output)
  	if (ret)
  		return ret;
  	if (strcmp(*output, "now")) {
 -		unsigned long now = approxidate("now");
 +		timestamp_t now = approxidate("now");
  		if (approxidate(*output) >= now)
  			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
  	}
 diff --git a/config.mak.uname b/config.mak.uname
 index aa18ed0af4d..399fe192719 100644
 --- a/config.mak.uname
 +++ b/config.mak.uname
 @@ -357,7 +357,6 @@ ifeq ($(uname_S),Windows)
  	NO_INET_PTON = YesPlease
  	NO_INET_NTOP = YesPlease
  	NO_POSIX_GOODIES = UnfortunatelyYes
 -	TIME_T_IS_INT64 = YesItIs
  	NATIVE_CRLF = YesPlease
  	DEFAULT_HELP_FORMAT = html
  
 @@ -504,7 +503,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
  	NO_INET_PTON = YesPlease
  	NO_INET_NTOP = YesPlease
  	NO_POSIX_GOODIES = UnfortunatelyYes
 -	TIME_T_IS_INT64 = YesItIs
  	DEFAULT_HELP_FORMAT = html
  	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
  	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
 index 364e5e0c5be..f3814cc47a0 100644
 --- a/credential-cache--daemon.c
 +++ b/credential-cache--daemon.c
 @@ -8,7 +8,7 @@ static struct tempfile socket_file;
  
  struct credential_cache_entry {
  	struct credential item;
 -	time_t expiration;
 +	timestamp_t expiration;
  };
  static struct credential_cache_entry *entries;
  static int entries_nr;
 @@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
  		e->expiration = 0;
  }
  
 -static time_t check_expirations(void)
 +static timestamp_t check_expirations(void)
  {
 -	static time_t wait_for_entry_until;
 +	static timestamp_t wait_for_entry_until;
  	int i = 0;
 -	time_t now = time(NULL);
 -	time_t next = TIME_MAX;
 +	timestamp_t now = time(NULL);
 +	timestamp_t next = TIME_MAX;
  
  	/*
  	 * Initially give the client 30 seconds to actually contact us
 @@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
  static int serve_cache_loop(int fd)
  {
  	struct pollfd pfd;
 -	time_t wakeup;
 +	timestamp_t wakeup;
  
  	wakeup = check_expirations();
  	if (!wakeup)
 diff --git a/date.c b/date.c
 index dad2d0c807e..db3435df3e4 100644
 --- a/date.c
 +++ b/date.c
 @@ -39,14 +39,17 @@ static const char *weekday_names[] = {
  	"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
  };
  
 -static time_t gm_time_t(time_t time, int tz)
 +static time_t gm_time_t(timestamp_t time, int tz)
  {
  	int minutes;
  
 +	if (date_overflows(time))
 +		die("Timestamp too large for this system: %"PRItime, time);
 +
  	minutes = tz < 0 ? -tz : tz;
  	minutes = (minutes / 100)*60 + (minutes % 100);
  	minutes = tz < 0 ? -minutes : minutes;
 -	return time + minutes * 60;
 +	return (time_t)time + minutes * 60;
  }
  
  /*
 @@ -54,9 +57,14 @@ static time_t gm_time_t(time_t 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(time_t time, int tz)
 +static struct tm *time_to_tm(timestamp_t time, int tz)
  {
 -	time_t t = gm_time_t(time, tz);
 +	time_t t;
 +
 +	if (date_overflows(time))
 +		die("Timestamp too large for this system: %"PRItime, time);
 +
 +	t = gm_time_t((time_t)time, tz);
  	return gmtime(&t);
  }
  
 @@ -64,13 +72,16 @@ static struct tm *time_to_tm(time_t time, int tz)
   * What value of "tz" was in effect back then at "time" in the
   * local timezone?
   */
 -static int local_tzoffset(time_t 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 +99,11 @@ static int local_tzoffset(time_t time)
  	return offset * eastwest;
  }
  
 -void show_date_relative(time_t time, int tz,
 +void show_date_relative(timestamp_t time, int tz,
  			       const struct timeval *now,
  			       struct strbuf *timebuf)
  {
 -	time_t diff;
 +	timestamp_t diff;
  	if (now->tv_sec < time) {
  		strbuf_addstr(timebuf, _("in the future"));
  		return;
 @@ -140,9 +151,9 @@ void show_date_relative(time_t time, int tz,
  	}
  	/* Give years and months for 5 years or so */
  	if (diff < 1825) {
 -		time_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
 -		time_t years = totalmonths / 12;
 -		time_t 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 +183,7 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
  	return &mode;
  }
  
 -const char *show_date(time_t 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 +436,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(time_t 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 +519,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
  {
  	int n;
  	char *end;
 -	time_t num;
 +	timestamp_t num;
  
  	num = parse_timestamp(date, &end, 10);
  
 @@ -635,7 +646,7 @@ static int match_tz(const char *date, int *offp)
  	return end - date;
  }
  
 -static void date_string(time_t date, int offset, struct strbuf *buf)
 +static void date_string(timestamp_t date, int offset, struct strbuf *buf)
  {
  	int sign = '+';
  
 @@ -650,10 +661,10 @@ static void date_string(time_t 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, time_t *timestamp, int *offset)
 +static int match_object_header_date(const char *date, timestamp_t *timestamp, int *offset)
  {
  	char *end;
 -	time_t stamp;
 +	timestamp_t stamp;
  	int ofs;
  
  	if (*date < '0' || '9' < *date)
 @@ -675,11 +686,11 @@ static int match_object_header_date(const char *date, time_t *timestamp, int *of
  
  /* 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, time_t *timestamp, int *offset)
 +int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
  {
  	struct tm tm;
  	int tm_gmt;
 -	time_t dummy_timestamp;
 +	timestamp_t dummy_timestamp;
  	int dummy_offset;
  
  	if (!timestamp)
 @@ -747,7 +758,7 @@ int parse_date_basic(const char *date, time_t *timestamp, int *offset)
  	return 0; /* success */
  }
  
 -int parse_expiry_date(const char *date, time_t *timestamp)
 +int parse_expiry_date(const char *date, timestamp_t *timestamp)
  {
  	int errors = 0;
  
 @@ -771,7 +782,7 @@ int parse_expiry_date(const char *date, time_t *timestamp)
  
  int parse_date(const char *date, struct strbuf *result)
  {
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	int offset;
  	if (parse_date_basic(date, &timestamp, &offset))
  		return -1;
 @@ -1066,7 +1077,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num,
  				     time_t now)
  {
  	char *end;
 -	time_t number = parse_timestamp(date, &end, 10);
 +	timestamp_t number = parse_timestamp(date, &end, 10);
  
  	switch (*end) {
  	case ':':
 @@ -1114,9 +1125,9 @@ static void pending_number(struct tm *tm, int *num)
  	}
  }
  
 -static time_t 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 +1159,12 @@ static time_t 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);
  }
  
 -time_t approxidate_relative(const char *date, const struct timeval *tv)
 +timestamp_t approxidate_relative(const char *date, const struct timeval *tv)
  {
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	int offset;
  	int errors = 0;
  
 @@ -1162,10 +1173,10 @@ time_t approxidate_relative(const char *date, const struct timeval *tv)
  	return approxidate_str(date, tv, &errors);
  }
  
 -time_t approxidate_careful(const char *date, int *error_ret)
 +timestamp_t approxidate_careful(const char *date, int *error_ret)
  {
  	struct timeval tv;
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	int offset;
  	int dummy = 0;
  	if (!error_ret)
 @@ -1180,7 +1191,7 @@ time_t approxidate_careful(const char *date, int *error_ret)
  	return approxidate_str(date, &tv, error_ret);
  }
  
 -int date_overflows(time_t t)
 +int date_overflows(timestamp_t t)
  {
  	time_t sys;
  
 diff --git a/fetch-pack.c b/fetch-pack.c
 index bea2f59299d..b4b2475e7a6 100644
 --- a/fetch-pack.c
 +++ b/fetch-pack.c
 @@ -392,7 +392,7 @@ static int find_common(struct fetch_pack_args *args,
  	if (args->depth > 0)
  		packet_buf_write(&req_buf, "deepen %d", args->depth);
  	if (args->deepen_since) {
 -		time_t max_age = approxidate(args->deepen_since);
 +		timestamp_t max_age = approxidate(args->deepen_since);
  		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
  	}
  	if (args->deepen_not) {
 @@ -581,7 +581,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
  }
  
  static void mark_recent_complete_commits(struct fetch_pack_args *args,
 -					 time_t cutoff)
 +					 timestamp_t cutoff)
  {
  	while (complete && cutoff <= complete->item->date) {
  		print_verbose(args, _("Marking %s as complete"),
 @@ -668,7 +668,7 @@ static int everything_local(struct fetch_pack_args *args,
  {
  	struct ref *ref;
  	int retval;
 -	time_t cutoff = 0;
 +	timestamp_t cutoff = 0;
  
  	save_commit_buffer = 0;
  
 diff --git a/git-compat-util.h b/git-compat-util.h
 index cb68e700d5e..c678ca94b8f 100644
 --- a/git-compat-util.h
 +++ b/git-compat-util.h
 @@ -319,14 +319,13 @@ extern char *gitdirname(char *);
  #define PRIo32 "o"
  #endif
  
 -#ifdef TIME_T_IS_INT64
 -#define PRItime PRId64
 -#define parse_timestamp strtoull
 -#define TIME_MAX INT64_MAX
 +typedef uintmax_t timestamp_t;
 +#define PRItime PRIuMAX
 +#define parse_timestamp strtoumax
 +#ifdef ULLONG_MAX
 +#define TIME_MAX ULLONG_MAX
  #else
 -#define PRItime "lu"
 -#define parse_timestamp strtoul
 -#define TIME_MAX LONG_MAX
 +#define TIME_MAX ULONG_MAX
  #endif
  
  #ifndef PATH_SEP
 diff --git a/http-backend.c b/http-backend.c
 index 4e88735a9e3..d6ea6075339 100644
 --- a/http-backend.c
 +++ b/http-backend.c
 @@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
  	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
  }
  
 -static void hdr_date(struct strbuf *hdr, const char *name, time_t when)
 +static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
  {
  	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
  	hdr_str(hdr, name, value);
 @@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
  
  static void hdr_cache_forever(struct strbuf *hdr)
  {
 -	time_t now = time(NULL);
 +	timestamp_t now = time(NULL);
  	hdr_date(hdr, "Date", now);
  	hdr_date(hdr, "Expires", now + 31536000);
  	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
 diff --git a/parse-options-cb.c b/parse-options-cb.c
 index a560d8d0b8c..ff3b7fa7aaa 100644
 --- a/parse-options-cb.c
 +++ b/parse-options-cb.c
 @@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
  int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
  			     int unset)
  {
 -	*(time_t *)(opt->value) = approxidate(arg);
 +	*(timestamp_t *)(opt->value) = approxidate(arg);
  	return 0;
  }
  
  int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
  			     int unset)
  {
 -	return parse_expiry_date(arg, (time_t *)opt->value);
 +	return parse_expiry_date(arg, (timestamp_t *)opt->value);
  }
  
  int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
 diff --git a/pretty.c b/pretty.c
 index 703eaf66586..587d48371b0 100644
 --- a/pretty.c
 +++ b/pretty.c
 @@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
  const char *show_ident_date(const struct ident_split *ident,
  			    const struct date_mode *mode)
  {
 -	time_t date = 0;
 +	timestamp_t date = 0;
  	long tz = 0;
  
  	if (ident->date_begin && ident->date_end)
 diff --git a/reachable.c b/reachable.c
 index f6285f66726..682418f5d23 100644
 --- a/reachable.c
 +++ b/reachable.c
 @@ -55,7 +55,7 @@ static void mark_commit(struct commit *c, void *data)
  
  struct recent_data {
  	struct rev_info *revs;
 -	time_t timestamp;
 +	timestamp_t timestamp;
  };
  
  static void add_recent_object(const struct object_id *oid,
 @@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
  }
  
  int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 -					   time_t timestamp)
 +					   timestamp_t timestamp)
  {
  	struct recent_data data;
  	int r;
 @@ -156,7 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
  }
  
  void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 -			    time_t mark_recent, struct progress *progress)
 +			    timestamp_t mark_recent, struct progress *progress)
  {
  	struct connectivity_progress cp;
  
 diff --git a/reachable.h b/reachable.h
 index 2a7f6588941..3c00fa0526c 100644
 --- a/reachable.h
 +++ b/reachable.h
 @@ -3,8 +3,8 @@
  
  struct progress;
  extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 -						  time_t timestamp);
 +						  timestamp_t timestamp);
  extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 -				   time_t mark_recent, struct progress *);
 +				   timestamp_t mark_recent, struct progress *);
  
  #endif
 diff --git a/ref-filter.c b/ref-filter.c
 index 08ded90f363..4fdacda0887 100644
 --- a/ref-filter.c
 +++ b/ref-filter.c
 @@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
  {
  	const char *eoemail = strstr(buf, "> ");
  	char *zone;
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	long tz;
  	struct date_mode date_mode = { DATE_NORMAL };
  	const char *formatp;
 diff --git a/reflog-walk.c b/reflog-walk.c
 index 674930e47d3..3ca5ed8415a 100644
 --- a/reflog-walk.c
 +++ b/reflog-walk.c
 @@ -12,7 +12,7 @@ struct complete_reflogs {
  	struct reflog_info {
  		struct object_id ooid, noid;
  		char *email;
 -		time_t timestamp;
 +		timestamp_t timestamp;
  		int tz;
  		char *message;
  	} *items;
 @@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
  }
  
  static int get_reflog_recno_by_time(struct complete_reflogs *array,
 -	time_t timestamp)
 +	timestamp_t timestamp)
  {
  	int i;
  	for (i = array->nr - 1; i >= 0; i--)
 @@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
  int add_reflog_for_walk(struct reflog_walk_info *info,
  		struct commit *commit, const char *name)
  {
 -	time_t timestamp = 0;
 +	timestamp_t timestamp = 0;
  	int recno = -1;
  	struct string_list_item *item;
  	struct complete_reflogs *reflogs;
 diff --git a/refs.c b/refs.c
 index 2549caa9403..6018b3104c6 100644
 --- a/refs.c
 +++ b/refs.c
 @@ -659,7 +659,7 @@ int is_branch(const char *refname)
  
  struct read_ref_at_cb {
  	const char *refname;
 -	time_t at_time;
 +	timestamp_t at_time;
  	int cnt;
  	int reccnt;
  	unsigned char *sha1;
 @@ -668,9 +668,9 @@ struct read_ref_at_cb {
  	unsigned char osha1[20];
  	unsigned char nsha1[20];
  	int tz;
 -	time_t date;
 +	timestamp_t date;
  	char **msg;
 -	time_t *cutoff_time;
 +	timestamp_t *cutoff_time;
  	int *cutoff_tz;
  	int *cutoff_cnt;
  };
 @@ -743,9 +743,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
  	return 1;
  }
  
 -int read_ref_at(const char *refname, unsigned int flags, time_t at_time, int cnt,
 +int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
  		unsigned char *sha1, char **msg,
 -		time_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 +		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
  {
  	struct read_ref_at_cb cb;
  
 diff --git a/refs.h b/refs.h
 index 747cd10af46..cc79ae4b8ed 100644
 --- a/refs.h
 +++ b/refs.h
 @@ -262,9 +262,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
  
  /** Reads log for the value of ref during at_time. **/
  int read_ref_at(const char *refname, unsigned int flags,
 -		time_t at_time, int cnt,
 +		timestamp_t at_time, int cnt,
  		unsigned char *sha1, char **msg,
 -		time_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 +		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
  
  /** Check if a particular reflog exists */
  int reflog_exists(const char *refname);
 @@ -537,7 +537,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
  typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
  					  unsigned char *nsha1,
  					  const char *email,
 -					  time_t timestamp, int tz,
 +					  timestamp_t timestamp, int tz,
  					  const char *message, void *cb_data);
  typedef void reflog_expiry_cleanup_fn(void *cb_data);
  
 diff --git a/refs/files-backend.c b/refs/files-backend.c
 index a5486577b47..a27a67dd2ad 100644
 --- a/refs/files-backend.c
 +++ b/refs/files-backend.c
 @@ -3104,7 +3104,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
  {
  	struct object_id ooid, noid;
  	char *email_end, *message;
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	int tz;
  	const char *p = sb->buf;
  
 @@ -3114,7 +3114,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
  	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
  	    !(email_end = strchr(p, '>')) ||
  	    email_end[1] != ' ' ||
 -	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
 +	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
  	    !message || message[0] != ' ' ||
  	    (message[1] != '+' && message[1] != '-') ||
  	    !isdigit(message[2]) || !isdigit(message[3]) ||
 @@ -3976,7 +3976,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
  			printf("prune %s", message);
  	} else {
  		if (cb->newlog) {
 -			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
 +			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
  				oid_to_hex(ooid), oid_to_hex(noid),
  				email, timestamp, tz, message);
  			oidcpy(&cb->last_kept_oid, noid);
 diff --git a/revision.c b/revision.c
 index e95d680ca68..8a8c1789c7b 100644
 --- a/revision.c
 +++ b/revision.c
 @@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
  /* How many extra uninteresting commits we want to see.. */
  #define SLOP 5
  
 -static int still_interesting(struct commit_list *src, time_t date, int slop,
 +static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
  			     struct commit **interesting_cache)
  {
  	/*
 @@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
  static int limit_list(struct rev_info *revs)
  {
  	int slop = SLOP;
 -	time_t date = ~0ul;
 +	timestamp_t date = TIME_MAX;
  	struct commit_list *list = revs->commits;
  	struct commit_list *newlist = NULL;
  	struct commit_list **p = &newlist;
 diff --git a/revision.h b/revision.h
 index 623bb588cb1..0d9e68b36e9 100644
 --- a/revision.h
 +++ b/revision.h
 @@ -181,8 +181,8 @@ struct rev_info {
  	/* special limits */
  	int skip_count;
  	int max_count;
 -	time_t max_age;
 -	time_t min_age;
 +	timestamp_t max_age;
 +	timestamp_t min_age;
  	int min_parents;
  	int max_parents;
  	int (*include_check)(struct commit *, void *);
 diff --git a/sha1_name.c b/sha1_name.c
 index 42e794d4c0b..8ab1744ebab 100644
 --- a/sha1_name.c
 +++ b/sha1_name.c
 @@ -658,8 +658,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
  
  	if (reflog_len) {
  		int nth, i;
 -		time_t at_time;
 -		time_t co_time;
 +		timestamp_t at_time;
 +		timestamp_t co_time;
  		int co_tz, co_cnt;
  
  		/* Is it asking for N-th entry, or approxidate? */
 diff --git a/t/helper/test-date.c b/t/helper/test-date.c
 index 371f9d259c6..f414a3ac670 100644
 --- a/t/helper/test-date.c
 +++ b/t/helper/test-date.c
 @@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
  "  test-date show:<format> [time_t]...\n"
  "  test-date parse [date]...\n"
  "  test-date approxidate [date]...\n"
 -"  test-date is64bit\n";
 +"  test-date is64bit\n"
 +"  test-date time_t-is64bit\n";
  
  static void show_relative_dates(const char **argv, struct timeval *now)
  {
 @@ -26,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
  	parse_date_format(format, &mode);
  	for (; *argv; argv++) {
  		char *arg;
 -		time_t t;
 +		timestamp_t t;
  		int tz;
  
  		/*
 @@ -47,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
  	struct strbuf result = STRBUF_INIT;
  
  	for (; *argv; argv++) {
 -		time_t t;
 +		timestamp_t t;
  		int tz;
  
  		strbuf_reset(&result);
 @@ -64,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
  static void parse_approxidate(const char **argv, struct timeval *now)
  {
  	for (; *argv; argv++) {
 -		time_t t;
 +		timestamp_t t;
  		t = approxidate_relative(*argv, now);
  		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
  	}
 @@ -95,6 +96,8 @@ int cmd_main(int argc, const char **argv)
  	else if (!strcmp(*argv, "approxidate"))
  		parse_approxidate(argv+1, &now);
  	else if (!strcmp(*argv, "is64bit"))
 +		return sizeof(timestamp_t) == 8 ? 0 : 1;
 +	else if (!strcmp(*argv, "time_t-is64bit"))
  		return sizeof(time_t) == 8 ? 0 : 1;
  	else
  		usage(usage_msg);
 diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
 index 8af62dd7501..75fe883aac1 100644
 --- a/t/helper/test-parse-options.c
 +++ b/t/helper/test-parse-options.c
 @@ -5,7 +5,7 @@
  static int boolean = 0;
  static int integer = 0;
  static unsigned long magnitude = 0;
 -static time_t timestamp;
 +static timestamp_t timestamp;
  static int abbrev = 7;
  static int verbose = -1; /* unspecified */
  static int dry_run = 0, quiet = 0;
 diff --git a/t/t0006-date.sh b/t/t0006-date.sh
 index 9539b425ffb..42d4ea61ef5 100755
 --- a/t/t0006-date.sh
 +++ b/t/t0006-date.sh
 @@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
  
  # arbitrary time absurdly far in the future
  FUTURE="5758122296 -0400"
 -check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
 -check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 +check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
 +check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
  
  check_parse() {
  	echo "$1 -> $2" >expect
 diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
 index 997aa9dea28..fe2d4f15a73 100755
 --- a/t/t5000-tar-tree.sh
 +++ b/t/t5000-tar-tree.sh
 @@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
  	git archive HEAD >future.tar
  '
  
 -test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 +test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
  	echo 4147 >expect &&
  	tar_info future.tar | cut -d" " -f2 >actual &&
  	test_cmp expect actual
 diff --git a/t/test-lib.sh b/t/test-lib.sh
 index beee1d847ff..8d25cb7c183 100644
 --- a/t/test-lib.sh
 +++ b/t/test-lib.sh
 @@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
  '
  
  test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
 +test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
 diff --git a/tag.c b/tag.c
 index a0b22c788b6..d71b67e8d83 100644
 --- a/tag.c
 +++ b/tag.c
 @@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
  	return object_as_type(obj, OBJ_TAG, 0);
  }
  
 -static time_t parse_tag_date(const char *buf, const char *tail)
 +static timestamp_t parse_tag_date(const char *buf, const char *tail)
  {
  	const char *dateptr;
  
 @@ -110,7 +110,7 @@ static time_t parse_tag_date(const char *buf, const char *tail)
  		/* nada */;
  	if (buf >= tail)
  		return 0;
 -	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
 +	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
  	return parse_timestamp(dateptr, NULL, 10);
  }
  
 diff --git a/tag.h b/tag.h
 index aaf56849de8..2abb3726fb5 100644
 --- a/tag.h
 +++ b/tag.h
 @@ -9,7 +9,7 @@ struct tag {
  	struct object object;
  	struct object *tagged;
  	char *tag;
 -	time_t date;
 +	timestamp_t date;
  };
  
  extern struct tag *lookup_tag(const unsigned char *sha1);
 diff --git a/upload-pack.c b/upload-pack.c
 index df9b8476991..97da13e6a54 100644
 --- a/upload-pack.c
 +++ b/upload-pack.c
 @@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
  #define CLIENT_SHALLOW	(1u << 18)
  #define HIDDEN_REF	(1u << 19)
  
 -static time_t oldest_have;
 +static timestamp_t oldest_have;
  
  static int deepen_relative;
  static int multi_ack;
 @@ -735,7 +735,7 @@ static void receive_needs(void)
  	struct string_list deepen_not = STRING_LIST_INIT_DUP;
  	int depth = 0;
  	int has_non_tip = 0;
 -	time_t deepen_since = 0;
 +	timestamp_t deepen_since = 0;
  	int deepen_rev_list = 0;
  
  	shallow_nr = 0;
 diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
 index 5df87ce6bb4..5a89db30e3f 100644
 --- a/vcs-svn/fast_export.c
 +++ b/vcs-svn/fast_export.c
 @@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
  }
  
  void fast_export_begin_note(uint32_t revision, const char *author,
 -		const char *log, time_t timestamp, const char *note_ref)
 +		const char *log, timestamp_t timestamp, const char *note_ref)
  {
  	static int firstnote = 1;
  	size_t loglen = strlen(log);
 @@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
  void fast_export_begin_commit(uint32_t revision, const char *author,
  			const struct strbuf *log,
  			const char *uuid, const char *url,
 -			time_t timestamp, const char *local_ref)
 +			timestamp_t timestamp, const char *local_ref)
  {
  	static const struct strbuf empty = STRBUF_INIT;
  	if (!log)
 diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
 index 864dada2b31..b9a3b71c99f 100644
 --- a/vcs-svn/fast_export.h
 +++ b/vcs-svn/fast_export.h
 @@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
  void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
  void fast_export_note(const char *committish, const char *dataref);
  void fast_export_begin_note(uint32_t revision, const char *author,
 -		const char *log, time_t timestamp, const char *note_ref);
 +		const char *log, timestamp_t timestamp, const char *note_ref);
  void fast_export_begin_commit(uint32_t revision, const char *author,
  			const struct strbuf *log, const char *uuid,const char *url,
 -			time_t timestamp, const char *local_ref);
 +			timestamp_t timestamp, const char *local_ref);
  void fast_export_end_commit(uint32_t revision);
  void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
  void fast_export_buf_to_data(const struct strbuf *data);
 diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
 index 9e1eb8d4176..1846685a21a 100644
 --- a/vcs-svn/svndump.c
 +++ b/vcs-svn/svndump.c
 @@ -47,7 +47,7 @@ static struct {
  
  static struct {
  	uint32_t revision;
 -	time_t timestamp;
 +	timestamp_t timestamp;
  	struct strbuf log, author, note;
  } rev_ctx;
  

-- 
2.12.2.windows.1


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
@ 2017-04-02 19:06   ` Johannes Schindelin
  2017-04-03  4:22     ` Torsten Bögershausen
  2017-04-02 19:06   ` [PATCH v2 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply change that field to use `uintmax_t` instead.

This patch is a bit larger than the mere change of the data type
because the field's name was tied to its data type, which has been fixed
at the same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 9c82b5b9d63..8538328fc7f 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -351,7 +351,7 @@ struct ref_formatting_state {
 struct atom_value {
 	const char *s;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 	struct used_atom *atom;
 };
 
@@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1934,9 +1934,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 2/8] t0006 & t5000: prepare for 64-bit timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-02 19:06   ` Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply uses an incorrect data type for timestamps, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) papered over this
issue simply by skipping the respective test cases on platforms where
they would fail due to the data type in use.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`,
conflating the two issues.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, after we
switch to a larger data type, we can flip that prerequisite to test
`time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822d..beee1d847ff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1164,3 +1164,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
@ 2017-04-02 19:06   ` Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Git's source code refers to timestamps as unsigned long, which is
ill-defined, as there is no guarantee about the number of bits that
data type has.

In preparation of switching to another data type that is large enough
to hold "far in the future" dates, we need to prepare the t0006-date.sh
script for the case where we *still* cannot format those dates if the
system library uses 32-bit time_t.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 2 +-
 t/test-lib.sh        | 1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..ac7c66c733b 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
-"  test-date is64bit\n";
+"  test-date is64bit\n"
+"  test-date time_t-is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv)
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
 		return sizeof(unsigned long) == 8 ? 0 : 1;
+	else if (!strcmp(*argv, "time_t-is64bit"))
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 9539b425ffb..42d4ea61ef5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 997aa9dea28..fe2d4f15a73 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index beee1d847ff..8d25cb7c183 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
 '
 
 test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 4/8] Specify explicitly where we parse timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (2 preceding siblings ...)
  2017-04-02 19:06   ` [PATCH v2 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
@ 2017-04-02 19:06   ` Johannes Schindelin
  2017-04-03  4:26     ` Torsten Bögershausen
  2017-04-02 19:06   ` [PATCH v2 5/8] Introduce a new "printf format" for " Johannes Schindelin
                     ` (4 subsequent siblings)
  8 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using a more appropriate data type, let's
introduce a symbol `parse_timestamp` (currently being defined to
`strtoul`) where appropriate, so that we can later easily switch to,
say, use `strtoull()` instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c           | 2 +-
 builtin/receive-pack.c | 4 ++--
 bundle.c               | 2 +-
 commit.c               | 6 +++---
 date.c                 | 6 +++---
 fsck.c                 | 2 +-
 git-compat-util.h      | 2 ++
 pretty.c               | 2 +-
 ref-filter.c           | 2 +-
 refs/files-backend.c   | 2 +-
 t/helper/test-date.c   | 2 +-
 tag.c                  | 4 ++--
 upload-pack.c          | 2 +-
 13 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index f7a7a971fbe..2c93adc69c3 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -882,7 +882,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index aca9c33d8d8..fd8a24dd47e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
 		retval = NONCE_BAD;
 		goto leave;
 	}
-	stamp = strtoul(nonce, &bohmac, 10);
+	stamp = parse_timestamp(nonce, &bohmac, 10);
 	if (bohmac == nonce || bohmac[0] != '-') {
 		retval = NONCE_BAD;
 		goto leave;
@@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
 	 * would mean it was issued by another server with its clock
 	 * skewed in the future.
 	 */
-	ostamp = strtoul(push_cert_nonce, NULL, 10);
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
 	nonce_stamp_slop = (long)ostamp - (long)stamp;
 
 	if (nonce_stamp_slop_limit &&
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 73c78c2b80c..0d2d0fa1984 100644
--- a/commit.c
+++ b/commit.c
@@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..495c207c64f 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	unsigned long number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index 939792752bf..33a66e68a83 100644
--- a/fsck.c
+++ b/fsck.c
@@ -690,7 +690,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index 8a4a3f85e7b..fc1b5fe1a6c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index d0f86f5d85c..24fb0c79062 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 8538328fc7f..d81dd938579 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 50188e92f9f..fe3e2ac8afe 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3114,7 +3114,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 	    !(email_end = strchr(p, '>')) ||
 	    email_end[1] != ' ' ||
-	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
 	    !message || message[0] != ' ' ||
 	    (message[1] != '+' && message[1] != '-') ||
 	    !isdigit(message[2]) || !isdigit(message[3]) ||
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ac7c66c733b..52d1fc34454 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..9b6725e02c9 100644
--- a/tag.c
+++ b/tag.c
@@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d6231..f17f4dd1233 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 5/8] Introduce a new "printf format" for timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (3 preceding siblings ...)
  2017-04-02 19:06   ` [PATCH v2 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-02 19:06   ` " Johannes Schindelin
  2017-04-02 19:06   ` [PATCH v2 6/8] Introduce a new data type " Johannes Schindelin
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
away from the ill-specified `unsigned long` data type.

So let's introduce the pseudo format "PRItime" (currently simply being
defined to "lu") to make it easier to change the data type used for
timestamps.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 builtin/rev-list.c            |  2 +-
 builtin/rev-parse.c           |  3 ++-
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 refs/files-backend.c          |  2 +-
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 14 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index f7aa95f4bab..ff7b2df023b 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f76e4163abb..af7b985c6eb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index 670229cbb4c..079c659c754 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -903,7 +903,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index fd8a24dd47e..8814e49893e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 0aa93d58919..a30fbce3341 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
 	}
 
 	if (info->show_timestamp)
-		printf("%lu ", commit->date);
+		printf("%"PRItime" ", commit->date);
 	if (info->header_prefix)
 		fputs(info->header_prefix, stdout);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 9e53a1a7ca4..f1c26ca6e62 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -218,7 +218,8 @@ static void show_datestring(const char *flag, const char *datestr)
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
+	snprintf(buffer, sizeof(buffer), "%s%"PRItime,
+		 flag, approxidate(datestr));
 	show(buffer);
 }
 
diff --git a/date.c b/date.c
index 495c207c64f..5c33dfd8ee7 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index d07d85ce302..fb268e328a4 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -393,7 +393,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index fc1b5fe1a6c..cd522903eda 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/refs/files-backend.c b/refs/files-backend.c
index fe3e2ac8afe..9b486980722 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3976,7 +3976,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			printf("prune %s", message);
 	} else {
 		if (cb->newlog) {
-			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
 				oid_to_hex(ooid), oid_to_hex(noid),
 				email, timestamp, tz, message);
 			oidcpy(&cb->last_kept_oid, noid);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 52d1fc34454..269040f028f 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/upload-pack.c b/upload-pack.c
index f17f4dd1233..4966f0b8a09 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -863,7 +863,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 6/8] Introduce a new data type for timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (4 preceding siblings ...)
  2017-04-02 19:06   ` [PATCH v2 5/8] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-04-02 19:06   ` " Johannes Schindelin
  2017-04-02 19:07   ` [PATCH v2 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:06 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

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>
---
 Documentation/technical/api-parse-options.txt |  8 ++--
 archive-tar.c                                 |  5 +-
 archive-zip.c                                 |  6 ++-
 archive.h                                     |  2 +-
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++--
 builtin/fsck.c                                |  4 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +--
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        |  6 +--
 builtin/reflog.c                              | 24 +++++-----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 +++---
 commit.c                                      | 12 ++---
 commit.h                                      |  2 +-
 config.c                                      |  2 +-
 credential-cache--daemon.c                    | 12 ++---
 date.c                                        | 66 +++++++++++++--------------
 fetch-pack.c                                  |  6 +--
 git-compat-util.h                             |  2 +
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  2 +-
 reachable.c                                   |  9 ++--
 reachable.h                                   |  4 +-
 ref-filter.c                                  |  4 +-
 reflog-walk.c                                 |  8 ++--
 refs.c                                        | 14 +++---
 refs.h                                        |  8 ++--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +--
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +--
 t/helper/test-date.c                          |  8 ++--
 t/helper/test-parse-options.c                 |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +-
 vcs-svn/fast_export.c                         |  4 +-
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 48 files changed, 162 insertions(+), 156 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e1..829b5581105 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..695339a2369 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
 #define USTAR_MAX_SIZE 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974a..4f715d40450 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -545,10 +545,12 @@ static void write_zip_trailer(const unsigned char *sha1)
 		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 {
-	struct tm *t = localtime(time);
+	time_t time;
+	struct tm *t = localtime(&time);
 
+	*timestamp = time;
 	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 	            (t->tm_year + 1900 - 1980) * 512;
 	*dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
diff --git a/archive.h b/archive.h
index 415e0152e2c..62d1d82c1af 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
 	struct tree *tree;
 	const unsigned char *commit_sha1;
 	const struct commit *commit;
-	time_t time;
+	timestamp_t time;
 	struct pathspec pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
diff --git a/builtin/am.c b/builtin/am.c
index 2c93adc69c3..89914ed8757 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index ff7b2df023b..e508694f9ab 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	timestamp_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	timestamp_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1578,7 +1578,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	timestamp_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index af7b985c6eb..ea3a9f8a70a 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 }
 
 static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index c2c61a57bb3..d38677cf44c 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/log.c b/builtin/log.c
index 079c659c754..6d457f3b54a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -904,7 +904,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (timestamp_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f85..8ed96391c1d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, timestamp_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 8bdc3eaa6fa..ee4b6950e5a 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	timestamp_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, timestamp_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	timestamp_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 84af7c2324b..9d0fef2e49a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct sha1_array recent_objects;
 
 static int loosened_object_can_be_discarded(const unsigned char *sha1,
-					    unsigned long mtime)
+					    timestamp_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e6..8dcfecde0f3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 8814e49893e..f8f4242719b 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 74727757785..4228d9ff4db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	timestamp_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, timestamp_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	timestamp_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d57..8860f429b06 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				timestamp_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9993ded41aa..74f9b18d40c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..05e014fc5ab 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	timestamp_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index 5c8078291c4..38f5c7dd3a5 100644
--- a/cache.h
+++ b/cache.h
@@ -1472,18 +1472,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 0d2d0fa1984..99a62b90ee2 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	timestamp_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	timestamp_t a_date = *(author_date_slab_at(author_date, a));
+	timestamp_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 528272ac9ba..5f2a837b067 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	timestamp_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.c b/config.c
index 1a4d85537b3..3247bfaa020 100644
--- a/config.c
+++ b/config.c
@@ -1926,7 +1926,7 @@ int git_config_get_expiry(const char *key, const char **output)
 	if (ret)
 		return ret;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		timestamp_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..f3814cc47a0 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	timestamp_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static timestamp_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	timestamp_t now = time(NULL);
+	timestamp_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	timestamp_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 5c33dfd8ee7..92ab31aa441 100644
--- a/date.c
+++ b/date.c
@@ -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;
 
 	/*
diff --git a/fetch-pack.c b/fetch-pack.c
index fb268e328a4..b4b2475e7a6 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -392,7 +392,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		timestamp_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -581,7 +581,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 timestamp_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -668,7 +668,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	timestamp_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index cd522903eda..72c12173a14 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+typedef unsigned long timestamp_t;
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..d6ea6075339 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index b7d8f7dcb2c..ff3b7fa7aaa 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(timestamp_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (timestamp_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 24fb0c79062..587d48371b0 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	timestamp_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index a8a979bd4fc..682418f5d23 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 };
 
 static void add_recent_object(const struct object_id *oid,
-			      unsigned long mtime,
+			      timestamp_t mtime,
 			      struct recent_data *data)
 {
 	struct object *obj;
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   timestamp_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    timestamp_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..3c00fa0526c 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  timestamp_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index d81dd938579..4fdacda0887 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
@@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
diff --git a/reflog-walk.c b/reflog-walk.c
index 99679f58255..3ca5ed8415a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
-		unsigned long timestamp;
+		timestamp_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	timestamp_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index 0272e332cc9..6018b3104c6 100644
--- a/refs.c
+++ b/refs.c
@@ -659,7 +659,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	timestamp_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -668,15 +668,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	timestamp_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	timestamp_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -723,7 +723,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp,
+				  const char *email, timestamp_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -743,9 +743,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 3df0d45ebb6..cc79ae4b8ed 100644
--- a/refs.h
+++ b/refs.h
@@ -262,9 +262,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int reflog_exists(const char *refname);
@@ -293,7 +293,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		struct object_id *old_oid, struct object_id *new_oid,
-		const char *committer, unsigned long timestamp,
+		const char *committer, timestamp_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
@@ -537,7 +537,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  timestamp_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 9b486980722..a27a67dd2ad 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3104,7 +3104,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	struct object_id ooid, noid;
 	char *email_end, *message;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	int tz;
 	const char *p = sb->buf;
 
@@ -3959,7 +3959,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index 7ff61ff5f73..8a8c1789c7b 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	timestamp_t date = TIME_MAX;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 }
 
 static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(ooid, cb_data);
diff --git a/revision.h b/revision.h
index 14886ec92b4..0d9e68b36e9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	timestamp_t max_age;
+	timestamp_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index d9d1b2fce8d..8ab1744ebab 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -658,8 +658,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		timestamp_t at_time;
+		timestamp_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1052,7 +1052,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, timestamp_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 269040f028f..f414a3ac670 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
 	parse_date_format(format, &mode);
 	for (; *argv; argv++) {
 		char *arg;
-		time_t t;
+		timestamp_t t;
 		int tz;
 
 		/*
@@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		timestamp_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 static void parse_approxidate(const char **argv, struct timeval *now)
 {
 	for (; *argv; argv++) {
-		time_t t;
+		timestamp_t t;
 		t = approxidate_relative(*argv, now);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
@@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(timestamp_t) == 8 ? 0 : 1;
 	else if (!strcmp(*argv, "time_t-is64bit"))
 		return sizeof(time_t) == 8 ? 0 : 1;
 	else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..75fe883aac1 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/tag.c b/tag.c
index 9b6725e02c9..d71b67e8d83 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..2abb3726fb5 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	timestamp_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index 4966f0b8a09..97da13e6a54 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	timestamp_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5a89db30e3f 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, timestamp_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			timestamp_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..b9a3b71c99f 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, timestamp_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			timestamp_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..1846685a21a 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index 308cf3779eb..c2a9cba50aa 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1374,7 +1374,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, timestamp_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 7/8] Abort if the system time cannot handle one of our timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (5 preceding siblings ...)
  2017-04-02 19:06   ` [PATCH v2 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-04-02 19:07   ` " Johannes Schindelin
  2017-04-02 19:07   ` [PATCH v2 8/8] Use uintmax_t for " Johannes Schindelin
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

We are about to switch to a new data type for time stamps that is
definitely not smaller or equal, but larger or equal to time_t.

So before using the system functions to process or format timestamps,
let's make extra certain that they can handle what we feed them.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 date.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/date.c b/date.c
index 92ab31aa441..db3435df3e4 100644
--- a/date.c
+++ b/date.c
@@ -43,10 +43,13 @@ static time_t gm_time_t(timestamp_t time, int tz)
 {
 	int minutes;
 
+	if (date_overflows(time))
+		die("Timestamp too large for this system: %"PRItime, time);
+
 	minutes = tz < 0 ? -tz : tz;
 	minutes = (minutes / 100)*60 + (minutes % 100);
 	minutes = tz < 0 ? -minutes : minutes;
-	return time + minutes * 60;
+	return (time_t)time + minutes * 60;
 }
 
 /*
@@ -56,7 +59,12 @@ static time_t gm_time_t(timestamp_t time, int tz)
  */
 static struct tm *time_to_tm(timestamp_t time, int tz)
 {
-	time_t t = gm_time_t(time, tz);
+	time_t t;
+
+	if (date_overflows(time))
+		die("Timestamp too large for this system: %"PRItime, time);
+
+	t = gm_time_t((time_t)time, tz);
 	return gmtime(&t);
 }
 
@@ -70,7 +78,10 @@ static int local_tzoffset(timestamp_t time)
 	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);
 
-- 
2.12.2.windows.1



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v2 8/8] Use uintmax_t for timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (6 preceding siblings ...)
  2017-04-02 19:07   ` [PATCH v2 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
@ 2017-04-02 19:07   ` " Johannes Schindelin
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
  8 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-02 19:07 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Previously, we used `unsigned long` for timestamps. This was only a good
choice on Linux, where we know implicitly that `unsigned long` is what is
used for `time_t`.

However, we want to use a different data type for timestamps for two
reasons:

- there is nothing that says that `unsigned long` should be the same data
  type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
  `unsigned long` is 32-bit but `time_t` is 64-bit.

- even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
  32-bit, we *want* to be able to encode timestamps in Git that are
  currently absurdly far in the future, *even if* the system library is
  not able to format those timestamps into date strings.

So let's just switch to the maximal integer type available, which should
be at least 64-bit for all practical purposes these days. It certainly
cannot be worse than `unsigned long`, so...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 git-compat-util.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 72c12173a14..c678ca94b8f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,10 +319,14 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
-typedef unsigned long timestamp_t;
-#define PRItime "lu"
-#define parse_timestamp strtoul
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#ifdef ULLONG_MAX
+#define TIME_MAX ULLONG_MAX
+#else
 #define TIME_MAX ULONG_MAX
+#endif
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
-- 
2.12.2.windows.1

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-02 19:06   ` [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-03  4:22     ` Torsten Bögershausen
  2017-04-03 22:47       ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Torsten Bögershausen @ 2017-04-03  4:22 UTC (permalink / raw)
  To: Johannes Schindelin, git; +Cc: Junio C Hamano



On 02/04/17 21:06, Johannes Schindelin wrote:
> In its `atom_value` struct, the ref-filter source code wants to store
> different values in a field called `ul` (for `unsigned long`), e.g.
> timestamps.
>
> However, as we are about to switch the data type of timestamps away from
> `unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
> that data type is not large enough.
>
> Simply change that field to use `uintmax_t` instead.
>
> This patch is a bit larger than the mere change of the data type
> because the field's name was tied to its data type, which has been fixed
> at the same time.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>  ref-filter.c | 16 ++++++++--------
>  1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/ref-filter.c b/ref-filter.c
> index 9c82b5b9d63..8538328fc7f 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -351,7 +351,7 @@ struct ref_formatting_state {
>  struct atom_value {
>  	const char *s;
>  	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
> -	unsigned long ul; /* used for sorting when not FIELD_STR */
> +	uintmax_t value; /* used for sorting when not FIELD_STR */
>  	struct used_atom *atom;
>  };
>
> @@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
>  		if (!strcmp(name, "objecttype"))
>  			v->s = typename(obj->type);
>  		else if (!strcmp(name, "objectsize")) {
> -			v->ul = sz;
> +			v->value = sz;
>  			v->s = xstrfmt("%lu", sz);
>  		}
>  		else if (deref)
> @@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
>  			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
>  		}
>  		else if (!strcmp(name, "numparent")) {
> -			v->ul = commit_list_count(commit->parents);
> -			v->s = xstrfmt("%lu", v->ul);
> +			v->value = commit_list_count(commit->parents);
> +			v->s = xstrfmt("%lu", (unsigned long)v->value);

If we want to get rid of "%lu" at some day, we can do like this:
v->s = xstrfmt("%" PRIuMAX, v->value);
Or, to make clear that under all circumstances an unsigned long is big enough to
hold the counter, for readers in the future, use something like this:
			v->s = xstrfmt("%lu", (xulong_t)v->value);

(this is more a reminder to myself, to send such a patch )

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v2 4/8] Specify explicitly where we parse timestamps
  2017-04-02 19:06   ` [PATCH v2 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-03  4:26     ` Torsten Bögershausen
  2017-04-03 22:50       ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Torsten Bögershausen @ 2017-04-03  4:26 UTC (permalink / raw)
  To: Johannes Schindelin, git; +Cc: Junio C Hamano

[]
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -319,6 +319,8 @@ extern char *gitdirname(char *);
>  #define PRIo32 "o"
>  #endif
>
> +#define parse_timestamp strtoul
> +

Would
#define parse_timestamp(a,b,c)  strtoul((a),(b),(c))
be more strict ?


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-03  4:22     ` Torsten Bögershausen
@ 2017-04-03 22:47       ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-03 22:47 UTC (permalink / raw)
  To: Torsten Bögershausen; +Cc: git, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2950 bytes --]

Hi Torsten,

On Mon, 3 Apr 2017, Torsten Bögershausen wrote:

> On 02/04/17 21:06, Johannes Schindelin wrote:
> > In its `atom_value` struct, the ref-filter source code wants to store
> > different values in a field called `ul` (for `unsigned long`), e.g.
> > timestamps.
> >
> > However, as we are about to switch the data type of timestamps away from
> > `unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
> > that data type is not large enough.
> >
> > Simply change that field to use `uintmax_t` instead.
> >
> > This patch is a bit larger than the mere change of the data type
> > because the field's name was tied to its data type, which has been fixed
> > at the same time.
> >
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > ---
> >  ref-filter.c | 16 ++++++++--------
> >  1 file changed, 8 insertions(+), 8 deletions(-)
> >
> > diff --git a/ref-filter.c b/ref-filter.c
> > index 9c82b5b9d63..8538328fc7f 100644
> > --- a/ref-filter.c
> > +++ b/ref-filter.c
> > @@ -351,7 +351,7 @@ struct ref_formatting_state {
> >  struct atom_value {
> >   const char *s;
> >   void (*handler)(struct atom_value *atomv, struct ref_formatting_state
> >   *state);
> > -	unsigned long ul; /* used for sorting when not FIELD_STR */
> > +	uintmax_t value; /* used for sorting when not FIELD_STR */
> >  	struct used_atom *atom;
> >  };
> >
> > @@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val,
> > int deref, struct object
> >    if (!strcmp(name, "objecttype"))
> >    	v->s = typename(obj->type);
> > 		else if (!strcmp(name, "objectsize")) {
> > -			v->ul = sz;
> > +			v->value = sz;
> >    	v->s = xstrfmt("%lu", sz);
> >    }
> >    else if (deref)
> > @@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val,
> > int deref, struct object
> >    	v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
> >    }
> >    else if (!strcmp(name, "numparent")) {
> > -			v->ul = commit_list_count(commit->parents);
> > -			v->s = xstrfmt("%lu", v->ul);
> > +			v->value = commit_list_count(commit->parents);
> > +			v->s = xstrfmt("%lu", (unsigned long)v->value);
> 
> If we want to get rid of "%lu" at some day, we can do like this:
> v->s = xstrfmt("%" PRIuMAX, v->value);
> Or, to make clear that under all circumstances an unsigned long is big enough
> to
> hold the counter, for readers in the future, use something like this:
> 			v->s = xstrfmt("%lu", (xulong_t)v->value);

We could do that, yes.

But part of my patch series is to clarify in a semantic way what the
purpose of the code is. Your solution would keep it syntactically correct,
of course, but it would also make it semantically unclear again.

By writing "%"PRIutime *instead of* "%"PRIuMAX, we are saying: look, we
are talking about a timestamp here. That would not at all be clear if we
wrote "%"PRIuMAX.

Ciao,
Johannes

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v2 4/8] Specify explicitly where we parse timestamps
  2017-04-03  4:26     ` Torsten Bögershausen
@ 2017-04-03 22:50       ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-03 22:50 UTC (permalink / raw)
  To: Torsten Bögershausen; +Cc: git, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 736 bytes --]

Hi Torsten,

On Mon, 3 Apr 2017, Torsten Bögershausen wrote:

> []
> > --- a/git-compat-util.h
> > +++ b/git-compat-util.h
> > @@ -319,6 +319,8 @@ extern char *gitdirname(char *);
> >  #define PRIo32 "o"
> >  #endif
> >
> > +#define parse_timestamp strtoul
> > +
> 
> Would
> #define parse_timestamp(a,b,c)  strtoul((a),(b),(c))
> be more strict ?

It would be more pedantic. But I fail to see how it would be more strict:
once the preprocessor substituted `parse_timestamp` for whatever we
#define'd it to, the C compiler will then validate the calls against the
signature of the real function. That validation will be much more strict
than merely testing for the correct number of parameters.

Ciao,
Johannes

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 0/8] Introduce timestamp_t for timestamps
  2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
                     ` (7 preceding siblings ...)
  2017-04-02 19:07   ` [PATCH v2 8/8] Use uintmax_t for " Johannes Schindelin
@ 2017-04-20 20:52   ` " Johannes Schindelin
  2017-04-20 20:52     ` [PATCH v3 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                       ` (9 more replies)
  8 siblings, 10 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:52 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as we already make use of
64-bit data types internally. There is no good reason why we should not
use the same for timestamps. Hence, let's use uintmax_t for timestamps.

Note: while the `time_t` data type exists and is meant to be used for
timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
used `time_t` for that reason, but it came with a few serious downsides:
as `time_t` can be signed (and indeed, on Windows it is an int64_t),
Git's expectation that 0 is the minimal value does no longer hold true,
introducing its own set of interesting challenges. Besides, if we *can*
handle far in the future timestamps (except for formatting them using
the system libraries), it is more consistent to do so.

The upside of using `uintmax_t` for timestamps is that we do a much
better job to support far in the future timestamps across all platforms,
including 32-bit ones. The downside is that those platforms that use a
32-bit `time_t` will barf when parsing or formatting those timestamps.

This iteration has no new changes, only conflict resolutions necessary
due to `master` introducing nearby changes. Maybe this iteration will
be noticed enough to make it into `pu`?

Changes since v2:

- resolved merge conflicts while rebasing to the current `master`

- fixed a bug where archive-zip.c's dos_time() was passing an
  uninitialized time_t to localtime()


Johannes Schindelin (8):
  ref-filter: avoid using `unsigned long` for catch-all data type
  t0006 & t5000: prepare for 64-bit timestamps
  t0006 & t5000: skip "far in the future" test when time_t is too
    limited
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Introduce a new data type for timestamps
  Abort if the system time cannot handle one of our timestamps
  Use uintmax_t for timestamps

 Documentation/technical/api-parse-options.txt |   8 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |  11 ++-
 archive.h                                     |   2 +-
 builtin/am.c                                  |   4 +-
 builtin/blame.c                               |  14 ++--
 builtin/fsck.c                                |   6 +-
 builtin/gc.c                                  |   2 +-
 builtin/log.c                                 |   4 +-
 builtin/merge-base.c                          |   2 +-
 builtin/name-rev.c                            |   6 +-
 builtin/pack-objects.c                        |   4 +-
 builtin/prune.c                               |   4 +-
 builtin/receive-pack.c                        |  14 ++--
 builtin/reflog.c                              |  24 +++---
 builtin/rev-list.c                            |   2 +-
 builtin/rev-parse.c                           |   2 +-
 builtin/show-branch.c                         |   4 +-
 builtin/worktree.c                            |   4 +-
 bundle.c                                      |   4 +-
 cache.h                                       |  14 ++--
 commit.c                                      |  18 ++--
 commit.h                                      |   2 +-
 config.c                                      |   2 +-
 credential-cache--daemon.c                    |  12 +--
 date.c                                        | 113 ++++++++++++++------------
 fetch-pack.c                                  |   8 +-
 fsck.c                                        |   2 +-
 git-compat-util.h                             |   9 ++
 http-backend.c                                |   4 +-
 parse-options-cb.c                            |   4 +-
 pretty.c                                      |   4 +-
 reachable.c                                   |   9 +-
 reachable.h                                   |   4 +-
 ref-filter.c                                  |  22 ++---
 reflog-walk.c                                 |   8 +-
 refs.c                                        |  14 ++--
 refs.h                                        |   8 +-
 refs/files-backend.c                          |   8 +-
 revision.c                                    |   6 +-
 revision.h                                    |   4 +-
 sha1_name.c                                   |   6 +-
 t/helper/test-date.c                          |  18 ++--
 t/helper/test-parse-options.c                 |   4 +-
 t/t0006-date.sh                               |   4 +-
 t/t5000-tar-tree.sh                           |   6 +-
 t/test-lib.sh                                 |   3 +
 tag.c                                         |   6 +-
 tag.h                                         |   2 +-
 upload-pack.c                                 |   8 +-
 vcs-svn/fast_export.c                         |   8 +-
 vcs-svn/fast_export.h                         |   4 +-
 vcs-svn/svndump.c                             |   2 +-
 wt-status.c                                   |   2 +-
 54 files changed, 256 insertions(+), 218 deletions(-)


base-commit: 6a2c2f8d34fa1e8f3bb85d159d354810ed63692e
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v3
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v3

Interdiff vs v2:

 diff --git a/archive-zip.c b/archive-zip.c
 index 4f715d40450..bb117a74d45 100644
 --- a/archive-zip.c
 +++ b/archive-zip.c
 @@ -548,9 +548,14 @@ static void write_zip_trailer(const unsigned char *sha1)
  static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
  {
  	time_t time;
 -	struct tm *t = localtime(&time);
 +	struct tm *t;
  
 +	if (date_overflows(*timestamp))
 +		die("Timestamp too large for this system: %"PRItime, time);
 +	time = (time_t)*timestamp;
 +	t = localtime(&time);
  	*timestamp = time;
 +
  	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
  	            (t->tm_year + 1900 - 1980) * 512;
  	*dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;

-- 
2.12.2.windows.2.406.gd14a8f8640f


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
@ 2017-04-20 20:52     ` Johannes Schindelin
  2017-04-20 20:52     ` [PATCH v3 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
                       ` (8 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:52 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply change that field to use `uintmax_t` instead.

This patch is a bit larger than the mere change of the data type
because the field's name was tied to its data type, which has been fixed
at the same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 3a640448fd8..92871266001 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -351,7 +351,7 @@ struct ref_formatting_state {
 struct atom_value {
 	const char *s;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 	struct used_atom *atom;
 };
 
@@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1941,9 +1941,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 2/8] t0006 & t5000: prepare for 64-bit timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
  2017-04-20 20:52     ` [PATCH v3 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-20 20:52     ` Johannes Schindelin
  2017-04-20 20:58     ` [PATCH v3 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
                       ` (7 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:52 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply uses an incorrect data type for timestamps, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) papered over this
issue simply by skipping the respective test cases on platforms where
they would fail due to the data type in use.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`,
conflating the two issues.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, after we
switch to a larger data type, we can flip that prerequisite to test
`time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822d..beee1d847ff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1164,3 +1164,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
  2017-04-20 20:52     ` [PATCH v3 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
  2017-04-20 20:52     ` [PATCH v3 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
@ 2017-04-20 20:58     ` Johannes Schindelin
  2017-04-20 20:58     ` [PATCH v3 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
                       ` (6 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned long, which is
ill-defined, as there is no guarantee about the number of bits that
data type has.

In preparation of switching to another data type that is large enough
to hold "far in the future" dates, we need to prepare the t0006-date.sh
script for the case where we *still* cannot format those dates if the
system library uses 32-bit time_t.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 2 +-
 t/test-lib.sh        | 1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..ac7c66c733b 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
-"  test-date is64bit\n";
+"  test-date is64bit\n"
+"  test-date time_t-is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv)
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
 		return sizeof(unsigned long) == 8 ? 0 : 1;
+	else if (!strcmp(*argv, "time_t-is64bit"))
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 9539b425ffb..42d4ea61ef5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 997aa9dea28..fe2d4f15a73 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index beee1d847ff..8d25cb7c183 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
 '
 
 test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 4/8] Specify explicitly where we parse timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (2 preceding siblings ...)
  2017-04-20 20:58     ` [PATCH v3 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
@ 2017-04-20 20:58     ` Johannes Schindelin
  2017-04-20 20:58     ` [PATCH v3 5/8] Introduce a new "printf format" for " Johannes Schindelin
                       ` (5 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using a more appropriate data type, let's
introduce a symbol `parse_timestamp` (currently being defined to
`strtoul`) where appropriate, so that we can later easily switch to,
say, use `strtoull()` instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c           | 2 +-
 builtin/receive-pack.c | 4 ++--
 bundle.c               | 2 +-
 commit.c               | 6 +++---
 date.c                 | 6 +++---
 fsck.c                 | 2 +-
 git-compat-util.h      | 2 ++
 pretty.c               | 2 +-
 ref-filter.c           | 2 +-
 refs/files-backend.c   | 2 +-
 t/helper/test-date.c   | 2 +-
 tag.c                  | 4 ++--
 upload-pack.c          | 2 +-
 13 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index f7a7a971fbe..2c93adc69c3 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -882,7 +882,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 3cba3fd278f..9a4c2a7ade4 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
 		retval = NONCE_BAD;
 		goto leave;
 	}
-	stamp = strtoul(nonce, &bohmac, 10);
+	stamp = parse_timestamp(nonce, &bohmac, 10);
 	if (bohmac == nonce || bohmac[0] != '-') {
 		retval = NONCE_BAD;
 		goto leave;
@@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
 	 * would mean it was issued by another server with its clock
 	 * skewed in the future.
 	 */
-	ostamp = strtoul(push_cert_nonce, NULL, 10);
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
 	nonce_stamp_slop = (long)ostamp - (long)stamp;
 
 	if (nonce_stamp_slop_limit &&
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 73c78c2b80c..0d2d0fa1984 100644
--- a/commit.c
+++ b/commit.c
@@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..495c207c64f 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	unsigned long number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index e6152e4e6d4..d589341cddf 100644
--- a/fsck.c
+++ b/fsck.c
@@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index 8a4a3f85e7b..fc1b5fe1a6c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index d0f86f5d85c..24fb0c79062 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 92871266001..c7836ae07bd 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 4d705b4037e..dae0522673b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3247,7 +3247,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 	    !(email_end = strchr(p, '>')) ||
 	    email_end[1] != ' ' ||
-	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
 	    !message || message[0] != ' ' ||
 	    (message[1] != '+' && message[1] != '-') ||
 	    !isdigit(message[2]) || !isdigit(message[3]) ||
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ac7c66c733b..52d1fc34454 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..9b6725e02c9 100644
--- a/tag.c
+++ b/tag.c
@@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d6231..f17f4dd1233 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 5/8] Introduce a new "printf format" for timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (3 preceding siblings ...)
  2017-04-20 20:58     ` [PATCH v3 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-20 20:58     ` " Johannes Schindelin
  2017-04-20 20:58     ` [PATCH v3 6/8] Introduce a new data type " Johannes Schindelin
                       ` (4 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
away from the ill-specified `unsigned long` data type.

So let's introduce the pseudo format "PRItime" (currently simply being
defined to "lu") to make it easier to change the data type used for
timestamps.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 builtin/rev-list.c            |  2 +-
 builtin/rev-parse.c           |  2 +-
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 refs/files-backend.c          |  2 +-
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 14 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 07506a3e457..e4b3c7b0ebf 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f76e4163abb..af7b985c6eb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index b3b10cc1edb..f93ef6c7100 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -910,7 +910,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 9a4c2a7ade4..c49333c9b66 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a2..3b292c99bda 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
 	}
 
 	if (info->show_timestamp)
-		printf("%lu ", commit->date);
+		printf("%"PRItime" ", commit->date);
 	if (info->header_prefix)
 		fputs(info->header_prefix, stdout);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 05133309106..b4509002435 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr)
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+	buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
 	show(buffer);
 	free(buffer);
 }
diff --git a/date.c b/date.c
index 495c207c64f..5c33dfd8ee7 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index 42969353d67..a1b8ea59e8d 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -393,7 +393,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index fc1b5fe1a6c..cd522903eda 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/refs/files-backend.c b/refs/files-backend.c
index dae0522673b..ecbb13dc4b8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4131,7 +4131,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			printf("prune %s", message);
 	} else {
 		if (cb->newlog) {
-			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
 				oid_to_hex(ooid), oid_to_hex(noid),
 				email, timestamp, tz, message);
 			oidcpy(&cb->last_kept_oid, noid);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 52d1fc34454..269040f028f 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/upload-pack.c b/upload-pack.c
index f17f4dd1233..4966f0b8a09 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -863,7 +863,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 6/8] Introduce a new data type for timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (4 preceding siblings ...)
  2017-04-20 20:58     ` [PATCH v3 5/8] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-04-20 20:58     ` " Johannes Schindelin
  2017-04-20 20:58     ` [PATCH v3 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
                       ` (3 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

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>
---
 Documentation/technical/api-parse-options.txt |  8 ++--
 archive-tar.c                                 |  5 +-
 archive-zip.c                                 | 11 ++++-
 archive.h                                     |  2 +-
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++--
 builtin/fsck.c                                |  4 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +--
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        |  6 +--
 builtin/reflog.c                              | 24 +++++-----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 +++---
 commit.c                                      | 12 ++---
 commit.h                                      |  2 +-
 config.c                                      |  2 +-
 credential-cache--daemon.c                    | 12 ++---
 date.c                                        | 66 +++++++++++++--------------
 fetch-pack.c                                  |  6 +--
 git-compat-util.h                             |  2 +
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  2 +-
 reachable.c                                   |  9 ++--
 reachable.h                                   |  4 +-
 ref-filter.c                                  |  4 +-
 reflog-walk.c                                 |  8 ++--
 refs.c                                        | 14 +++---
 refs.h                                        |  8 ++--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +--
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +--
 t/helper/test-date.c                          |  8 ++--
 t/helper/test-parse-options.c                 |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +-
 vcs-svn/fast_export.c                         |  4 +-
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 48 files changed, 167 insertions(+), 156 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e1..829b5581105 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..695339a2369 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
 #define USTAR_MAX_SIZE 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974a..bb117a74d45 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -545,9 +545,16 @@ static void write_zip_trailer(const unsigned char *sha1)
 		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 {
-	struct tm *t = localtime(time);
+	time_t time;
+	struct tm *t;
+
+	if (date_overflows(*timestamp))
+		die("Timestamp too large for this system: %"PRItime, time);
+	time = (time_t)*timestamp;
+	t = localtime(&time);
+	*timestamp = time;
 
 	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 	            (t->tm_year + 1900 - 1980) * 512;
diff --git a/archive.h b/archive.h
index 415e0152e2c..62d1d82c1af 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
 	struct tree *tree;
 	const unsigned char *commit_sha1;
 	const struct commit *commit;
-	time_t time;
+	timestamp_t time;
 	struct pathspec pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
diff --git a/builtin/am.c b/builtin/am.c
index 2c93adc69c3..89914ed8757 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index e4b3c7b0ebf..f00eda16378 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	timestamp_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	timestamp_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1578,7 +1578,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	timestamp_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index af7b985c6eb..ea3a9f8a70a 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 }
 
 static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index 2daede78205..cb1e20aae4a 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/log.c b/builtin/log.c
index f93ef6c7100..fd3d10ec217 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -911,7 +911,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (timestamp_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f85..8ed96391c1d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, timestamp_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 92a5d8a5d26..44374750170 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	timestamp_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, timestamp_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	timestamp_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 0fe35d1b5ae..9b4ba8a80d7 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct oid_array recent_objects;
 
 static int loosened_object_can_be_discarded(const struct object_id *oid,
-					    unsigned long mtime)
+					    timestamp_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e6..8dcfecde0f3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c49333c9b66..b4e22f44497 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 74727757785..4228d9ff4db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	timestamp_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, timestamp_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	timestamp_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d57..8860f429b06 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				timestamp_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9993ded41aa..74f9b18d40c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..05e014fc5ab 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	timestamp_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index ba27595d54d..f3a02993b20 100644
--- a/cache.h
+++ b/cache.h
@@ -1476,18 +1476,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 0d2d0fa1984..99a62b90ee2 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	timestamp_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	timestamp_t a_date = *(author_date_slab_at(author_date, a));
+	timestamp_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 7b1986d5c8a..c9d887b5e53 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	timestamp_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.c b/config.c
index 1a4d85537b3..3247bfaa020 100644
--- a/config.c
+++ b/config.c
@@ -1926,7 +1926,7 @@ int git_config_get_expiry(const char *key, const char **output)
 	if (ret)
 		return ret;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		timestamp_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..f3814cc47a0 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	timestamp_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static timestamp_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	timestamp_t now = time(NULL);
+	timestamp_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	timestamp_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 5c33dfd8ee7..92ab31aa441 100644
--- a/date.c
+++ b/date.c
@@ -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;
 
 	/*
diff --git a/fetch-pack.c b/fetch-pack.c
index a1b8ea59e8d..fbe52f02346 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -392,7 +392,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		timestamp_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -581,7 +581,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 timestamp_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -668,7 +668,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	timestamp_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index cd522903eda..72c12173a14 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+typedef unsigned long timestamp_t;
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..d6ea6075339 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 7419780a9b6..a6810f295cb 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(timestamp_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (timestamp_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 24fb0c79062..587d48371b0 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	timestamp_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index a8a979bd4fc..682418f5d23 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 };
 
 static void add_recent_object(const struct object_id *oid,
-			      unsigned long mtime,
+			      timestamp_t mtime,
 			      struct recent_data *data)
 {
 	struct object *obj;
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   timestamp_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    timestamp_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..3c00fa0526c 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  timestamp_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index c7836ae07bd..1fc5e9970db 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
@@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
diff --git a/reflog-walk.c b/reflog-walk.c
index 99679f58255..3ca5ed8415a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
-		unsigned long timestamp;
+		timestamp_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	timestamp_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index d1e1b4399b3..6841bd9f712 100644
--- a/refs.c
+++ b/refs.c
@@ -712,7 +712,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	timestamp_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -721,15 +721,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	timestamp_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	timestamp_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -776,7 +776,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp,
+				  const char *email, timestamp_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -796,9 +796,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 49e97d7d5fd..2b80d37fd34 100644
--- a/refs.h
+++ b/refs.h
@@ -317,9 +317,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@ -356,7 +356,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		struct object_id *old_oid, struct object_id *new_oid,
-		const char *committer, unsigned long timestamp,
+		const char *committer, timestamp_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@ -616,7 +616,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  timestamp_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index ecbb13dc4b8..6646dcfc838 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3237,7 +3237,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	struct object_id ooid, noid;
 	char *email_end, *message;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	int tz;
 	const char *p = sb->buf;
 
@@ -4114,7 +4114,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index 7ff61ff5f73..8a8c1789c7b 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	timestamp_t date = TIME_MAX;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 }
 
 static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(ooid, cb_data);
diff --git a/revision.h b/revision.h
index 14886ec92b4..0d9e68b36e9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	timestamp_t max_age;
+	timestamp_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index 8eec9f7c1bb..35c1e2a9e32 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		timestamp_t at_time;
+		timestamp_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, timestamp_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 269040f028f..f414a3ac670 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
 	parse_date_format(format, &mode);
 	for (; *argv; argv++) {
 		char *arg;
-		time_t t;
+		timestamp_t t;
 		int tz;
 
 		/*
@@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		timestamp_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 static void parse_approxidate(const char **argv, struct timeval *now)
 {
 	for (; *argv; argv++) {
-		time_t t;
+		timestamp_t t;
 		t = approxidate_relative(*argv, now);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
@@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(timestamp_t) == 8 ? 0 : 1;
 	else if (!strcmp(*argv, "time_t-is64bit"))
 		return sizeof(time_t) == 8 ? 0 : 1;
 	else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..75fe883aac1 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/tag.c b/tag.c
index 9b6725e02c9..d71b67e8d83 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..2abb3726fb5 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	timestamp_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index 4966f0b8a09..97da13e6a54 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	timestamp_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5a89db30e3f 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, timestamp_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			timestamp_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..b9a3b71c99f 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, timestamp_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			timestamp_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..1846685a21a 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index 03754849626..1b1d644b85f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1387,7 +1387,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, timestamp_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 7/8] Abort if the system time cannot handle one of our timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (5 preceding siblings ...)
  2017-04-20 20:58     ` [PATCH v3 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-04-20 20:58     ` " Johannes Schindelin
  2017-04-20 20:59     ` [PATCH v3 8/8] Use uintmax_t for " Johannes Schindelin
                       ` (2 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

We are about to switch to a new data type for time stamps that is
definitely not smaller or equal, but larger or equal to time_t.

So before using the system functions to process or format timestamps,
let's make extra certain that they can handle what we feed them.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 date.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/date.c b/date.c
index 92ab31aa441..db3435df3e4 100644
--- a/date.c
+++ b/date.c
@@ -43,10 +43,13 @@ static time_t gm_time_t(timestamp_t time, int tz)
 {
 	int minutes;
 
+	if (date_overflows(time))
+		die("Timestamp too large for this system: %"PRItime, time);
+
 	minutes = tz < 0 ? -tz : tz;
 	minutes = (minutes / 100)*60 + (minutes % 100);
 	minutes = tz < 0 ? -minutes : minutes;
-	return time + minutes * 60;
+	return (time_t)time + minutes * 60;
 }
 
 /*
@@ -56,7 +59,12 @@ static time_t gm_time_t(timestamp_t time, int tz)
  */
 static struct tm *time_to_tm(timestamp_t time, int tz)
 {
-	time_t t = gm_time_t(time, tz);
+	time_t t;
+
+	if (date_overflows(time))
+		die("Timestamp too large for this system: %"PRItime, time);
+
+	t = gm_time_t((time_t)time, tz);
 	return gmtime(&t);
 }
 
@@ -70,7 +78,10 @@ static int local_tzoffset(timestamp_t time)
 	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);
 
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v3 8/8] Use uintmax_t for timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (6 preceding siblings ...)
  2017-04-20 20:58     ` [PATCH v3 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
@ 2017-04-20 20:59     ` " Johannes Schindelin
  2017-04-21  6:05     ` [PATCH v3 0/8] Introduce timestamp_t " Junio C Hamano
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-20 20:59 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Previously, we used `unsigned long` for timestamps. This was only a good
choice on Linux, where we know implicitly that `unsigned long` is what is
used for `time_t`.

However, we want to use a different data type for timestamps for two
reasons:

- there is nothing that says that `unsigned long` should be the same data
  type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
  `unsigned long` is 32-bit but `time_t` is 64-bit.

- even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
  32-bit, we *want* to be able to encode timestamps in Git that are
  currently absurdly far in the future, *even if* the system library is
  not able to format those timestamps into date strings.

So let's just switch to the maximal integer type available, which should
be at least 64-bit for all practical purposes these days. It certainly
cannot be worse than `unsigned long`, so...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 git-compat-util.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 72c12173a14..c678ca94b8f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,10 +319,14 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
-typedef unsigned long timestamp_t;
-#define PRItime "lu"
-#define parse_timestamp strtoul
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#ifdef ULLONG_MAX
+#define TIME_MAX ULLONG_MAX
+#else
 #define TIME_MAX ULONG_MAX
+#endif
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
-- 
2.12.2.windows.2.406.gd14a8f8640f

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v3 0/8] Introduce timestamp_t for timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (7 preceding siblings ...)
  2017-04-20 20:59     ` [PATCH v3 8/8] Use uintmax_t for " Johannes Schindelin
@ 2017-04-21  6:05     ` " Junio C Hamano
  2017-04-21 10:44       ` Johannes Schindelin
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
  9 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-04-21  6:05 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> Note: while the `time_t` data type exists and is meant to be used for
> timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
> used `time_t` for that reason, but it came with a few serious downsides:
> as `time_t` can be signed (and indeed, on Windows it is an int64_t),
> Git's expectation that 0 is the minimal value does no longer hold true,
> introducing its own set of interesting challenges. Besides, if we *can*
> handle far in the future timestamps (except for formatting them using
> the system libraries), it is more consistent to do so.

I somehow had an impression that the list consensus during the
discussion on an earlier round of this series was that time_t is
more appropriate, as platforms with time_t with inadequent range
will be updated before it gets too late at around 2038 (or they will
die off).  After all, at some point we need to interact with the
platform functions that expect time_t as their interface and they do
not take our own timestamp_t without casting.

But that is provided if not introducing timestamp_t and using time_t
results in a simpler code.  I do not know if that is the case.

For timestamps in the distant past, even though time_t could be
unsigned, I do not think anybody came up with a concrete example of
a platform with such a problem during the previous discussions,
while I do recall people wanting to use Git to store historical
documents with timestamps before 1970.  We do expect 0 can be used
as a sentinel, which needs to be updated once we seriously start
supporting such use cases.  I think that (i.e. should the timestamp
be signed?) is more or less unrelated to the focus of the discussion
that stemed from this topic, which was "ulong that is often 32-bit
does not necessarily fit the notion of time on a platform, which is
time_t, and we need to widen it", which all involved in the discussion
agreed.

In any case, when merged to 'pu', this had a slight conflict with
topics in flight in builtin/name-rev.c and I think I resolved it
correctly, but please double check.  I will have to revamp the
resolution rerere remembered when the next version of this series
starts using time_t instead of timestamp_t anyway, but it is better
to always get conflict resolution right.  That is what maintainers
do and what they are for ;-)

Thanks.


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v3 0/8] Introduce timestamp_t for timestamps
  2017-04-21  6:05     ` [PATCH v3 0/8] Introduce timestamp_t " Junio C Hamano
@ 2017-04-21 10:44       ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen

Hi Junio,

On Thu, 20 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > Note: while the `time_t` data type exists and is meant to be used for
> > timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
> > used `time_t` for that reason, but it came with a few serious
> > downsides: as `time_t` can be signed (and indeed, on Windows it is an
> > int64_t), Git's expectation that 0 is the minimal value does no longer
> > hold true, introducing its own set of interesting challenges. Besides,
> > if we *can* handle far in the future timestamps (except for formatting
> > them using the system libraries), it is more consistent to do so.
> 
> I somehow had an impression that the list consensus during the
> discussion on an earlier round of this series was that time_t is
> more appropriate, as platforms with time_t with inadequent range
> will be updated before it gets too late at around 2038 (or they will
> die off).

There was this sentiment, but that would require a change in Git's source
code where it can handle unsigned *and* signed data types for timestamps,
and that was too much of a change to bring in that late in the patch
series.

Besides, supporting a signed timestamp data type is a separate issue from
trying to support dates that are insanely far in the future.

> After all, at some point we need to interact with the
> platform functions that expect time_t as their interface and they do
> not take our own timestamp_t without casting.

No, not necessarily. When we generate a .zip archive, for example, we may
never need to parse or format the timestamps. When we log with a format
that does not require the system routines to format the date (e.g. Unix
epoch). And when we do not parse nor format the timestamps to begin with,
e.g. fetching and pushing or committing.

It is *only* when we try to parse or format timestamps, *and* the system's
time_t is not large enough, that we run into trouble.

This is an improvement from before, where we would run into trouble
whenever time_t was larger than unsigned long, *or* whenever time_t is too
small for those timestamps.

Your comment, and my reaction, reminded me that I had planned to look into
all calls to date_overflow() and remove those that are now unnecessary, or
at least move them to the place where they are absolutely necessary.

> But that is provided if not introducing timestamp_t and using time_t
> results in a simpler code.  I do not know if that is the case.

As I had pointed out in my reply to Peff: removing the assumption that 0
is the minimal timestamp is something I am unwilling to tackle, it looks
way too fragile/dangerous to me to do this in the time I could allocate
for that.

> For timestamps in the distant past, even though time_t could be
> unsigned, I do not think anybody came up with a concrete example of a
> platform with such a problem during the previous discussions, while I do
> recall people wanting to use Git to store historical documents with
> timestamps before 1970.  We do expect 0 can be used as a sentinel, which
> needs to be updated once we seriously start supporting such use cases.
> I think that (i.e. should the timestamp be signed?) is more or less
> unrelated to the focus of the discussion that stemed from this topic,
> which was "ulong that is often 32-bit does not necessarily fit the
> notion of time on a platform, which is time_t, and we need to widen it",
> which all involved in the discussion agreed.

If anybody feels strongly enough about representing timestamps earlier
than the Jan 1 1970, they should feel very welcome to work on this.

My patch series would make that task slightly easier, even.

> In any case, when merged to 'pu', this had a slight conflict with
> topics in flight in builtin/name-rev.c and I think I resolved it
> correctly, but please double check.

I double-checked, there were just two conversions from `unsigned long
taggerdate` to `timestamp_t taggerdate`. I repeated the merge with my
latest iteration and resolved them the same way as you did.

Apart from the date_overflows() changes I mentioned above, I also fixed
the problems identified by Travis CI and VSTS Build.

v4 is coming,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
                       ` (8 preceding siblings ...)
  2017-04-21  6:05     ` [PATCH v3 0/8] Introduce timestamp_t " Junio C Hamano
@ 2017-04-21 10:45     ` " Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 1/9] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                         ` (10 more replies)
  9 siblings, 11 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as we already make use of
64-bit data types internally. There is no good reason why we should not
use the same for timestamps. Hence, let's use uintmax_t for timestamps.

Note: while the `time_t` data type exists and is meant to be used for
timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
used `time_t` for that reason, but it came with a few serious downsides:
as `time_t` can be signed (and indeed, on Windows it is an int64_t),
Git's expectation that 0 is the minimal value does no longer hold true,
introducing its own set of interesting challenges. Besides, if we *can*
handle far in the future timestamps (except for formatting them using
the system libraries), it is more consistent to do so.

The upside of using `uintmax_t` for timestamps is that we do a much
better job to support far in the future timestamps across all platforms,
including 32-bit ones. The downside is that those platforms that use a
32-bit `time_t` will barf when parsing or formatting those timestamps.

This iteration has two fixes for issues reported by Travis CI (thank
deity for Continuous Testing, eh?), one by the MacOSX nodes (but not by
the Linux nor the Windows nodes), the other by the windows node (which
reminds me to run tests after rebasing to `master` not only in a Linux
VM but also on Windows, even if it takes ages there due to the heavy
shell scripting).

After reading Junio's comments on the cover letter of v2, I had another
look at the usage of date_overflows() and was able to relax two of them.

Changes since v3:

- fixed the fix in archive-zip.c that tried to report a too large
  timestamp (and would have reported the uninitialized time_t instead)

- adjusted the so-far forgotten each_reflog() function (that was
  introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
  test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
  too

- removed the date_overflows() check from time_to_tm(), as it calls
  gm_time_t() which already performs that check

- the date_overflows() check in show_ident_date() was removed, as we do
  not know at that point yet whether we use the system functions to
  render the date or not (and there would not be a problem in the latter
  case)


Johannes Schindelin (9):
  ref-filter: avoid using `unsigned long` for catch-all data type
  t0006 & t5000: prepare for 64-bit timestamps
  t0006 & t5000: skip "far in the future" test when time_t is too
    limited
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Introduce a new data type for timestamps
  Abort if the system time cannot handle one of our timestamps
  Use uintmax_t for timestamps
  show_date_ident(): defer date overflow check

 Documentation/technical/api-parse-options.txt |   8 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |  12 ++-
 archive.h                                     |   2 +-
 builtin/am.c                                  |   4 +-
 builtin/blame.c                               |  14 ++--
 builtin/fsck.c                                |   6 +-
 builtin/gc.c                                  |   2 +-
 builtin/log.c                                 |   4 +-
 builtin/merge-base.c                          |   2 +-
 builtin/name-rev.c                            |   6 +-
 builtin/pack-objects.c                        |   4 +-
 builtin/prune.c                               |   4 +-
 builtin/receive-pack.c                        |  14 ++--
 builtin/reflog.c                              |  24 +++---
 builtin/rev-list.c                            |   2 +-
 builtin/rev-parse.c                           |   2 +-
 builtin/show-branch.c                         |   4 +-
 builtin/worktree.c                            |   4 +-
 bundle.c                                      |   4 +-
 cache.h                                       |  14 ++--
 commit.c                                      |  18 ++---
 commit.h                                      |   2 +-
 config.c                                      |   2 +-
 credential-cache--daemon.c                    |  12 +--
 date.c                                        | 106 ++++++++++++++------------
 fetch-pack.c                                  |   8 +-
 fsck.c                                        |   2 +-
 git-compat-util.h                             |   9 +++
 http-backend.c                                |   4 +-
 parse-options-cb.c                            |   4 +-
 pretty.c                                      |  16 ++--
 reachable.c                                   |   9 +--
 reachable.h                                   |   4 +-
 ref-filter.c                                  |  22 +++---
 reflog-walk.c                                 |   8 +-
 refs.c                                        |  14 ++--
 refs.h                                        |   8 +-
 refs/files-backend.c                          |   8 +-
 revision.c                                    |   6 +-
 revision.h                                    |   4 +-
 sha1_name.c                                   |   6 +-
 t/helper/test-date.c                          |  18 +++--
 t/helper/test-parse-options.c                 |   4 +-
 t/helper/test-ref-store.c                     |   4 +-
 t/t0006-date.sh                               |   4 +-
 t/t5000-tar-tree.sh                           |   6 +-
 t/test-lib.sh                                 |   3 +
 tag.c                                         |   6 +-
 tag.h                                         |   2 +-
 upload-pack.c                                 |   8 +-
 vcs-svn/fast_export.c                         |   8 +-
 vcs-svn/fast_export.h                         |   4 +-
 vcs-svn/svndump.c                             |   2 +-
 wt-status.c                                   |   2 +-
 55 files changed, 257 insertions(+), 227 deletions(-)


base-commit: 6a2c2f8d34fa1e8f3bb85d159d354810ed63692e
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v4
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v4

Interdiff vs v3:

 diff --git a/archive-zip.c b/archive-zip.c
 index bb117a74d45..68df3d64402 100644
 --- a/archive-zip.c
 +++ b/archive-zip.c
 @@ -551,7 +551,8 @@ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
  	struct tm *t;
  
  	if (date_overflows(*timestamp))
 -		die("Timestamp too large for this system: %"PRItime, time);
 +		die("timestamp too large for this system: %"PRItime,
 +		    *timestamp);
  	time = (time_t)*timestamp;
  	t = localtime(&time);
  	*timestamp = time;
 diff --git a/date.c b/date.c
 index db3435df3e4..75f6335cd09 100644
 --- a/date.c
 +++ b/date.c
 @@ -43,12 +43,12 @@ static time_t gm_time_t(timestamp_t time, int tz)
  {
  	int minutes;
  
 -	if (date_overflows(time))
 -		die("Timestamp too large for this system: %"PRItime, time);
 -
  	minutes = tz < 0 ? -tz : tz;
  	minutes = (minutes / 100)*60 + (minutes % 100);
  	minutes = tz < 0 ? -minutes : minutes;
 +
 +	if (date_overflows(time + minutes * 60))
 +		die("Timestamp too large for this system: %"PRItime, time);
  	return (time_t)time + minutes * 60;
  }
  
 @@ -59,12 +59,7 @@ static time_t gm_time_t(timestamp_t time, int tz)
   */
  static struct tm *time_to_tm(timestamp_t time, int tz)
  {
 -	time_t t;
 -
 -	if (date_overflows(time))
 -		die("Timestamp too large for this system: %"PRItime, time);
 -
 -	t = gm_time_t((time_t)time, tz);
 +	time_t t = gm_time_t(time, tz);
  	return gmtime(&t);
  }
  
 diff --git a/pretty.c b/pretty.c
 index 587d48371b0..35fd290096a 100644
 --- a/pretty.c
 +++ b/pretty.c
 @@ -410,14 +410,10 @@ const char *show_ident_date(const struct ident_split *ident,
  
  	if (ident->date_begin && ident->date_end)
  		date = parse_timestamp(ident->date_begin, NULL, 10);
 -	if (date_overflows(date))
 -		date = 0;
 -	else {
 -		if (ident->tz_begin && ident->tz_end)
 -			tz = strtol(ident->tz_begin, NULL, 10);
 -		if (tz >= INT_MAX || tz <= INT_MIN)
 -			tz = 0;
 -	}
 +	if (ident->tz_begin && ident->tz_end)
 +		tz = strtol(ident->tz_begin, NULL, 10);
 +	if (tz >= INT_MAX || tz <= INT_MIN)
 +		tz = 0;
  	return show_date(date, tz, mode);
  }
  
 diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
 index 2d84c45ffe9..9077ec2c330 100644
 --- a/t/helper/test-ref-store.c
 +++ b/t/helper/test-ref-store.c
 @@ -138,10 +138,10 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
  }
  
  static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
 -		       const char *committer, unsigned long timestamp,
 +		       const char *committer, timestamp_t timestamp,
  		       int tz, const char *msg, void *cb_data)
  {
 -	printf("%s %s %s %lu %d %s\n",
 +	printf("%s %s %s %"PRItime" %d %s\n",
  	       oid_to_hex(old_oid), oid_to_hex(new_oid),
  	       committer, timestamp, tz, msg);
  	return 0;

-- 
2.12.2.windows.2.406.gd14a8f8640f


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 1/9] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
@ 2017-04-21 10:45       ` Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 2/9] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
                         ` (9 subsequent siblings)
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply change that field to use `uintmax_t` instead.

This patch is a bit larger than the mere change of the data type
because the field's name was tied to its data type, which has been fixed
at the same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 3a640448fd8..92871266001 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -351,7 +351,7 @@ struct ref_formatting_state {
 struct atom_value {
 	const char *s;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 	struct used_atom *atom;
 };
 
@@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1941,9 +1941,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 2/9] t0006 & t5000: prepare for 64-bit timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 1/9] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-21 10:45       ` Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 3/9] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
                         ` (8 subsequent siblings)
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply uses an incorrect data type for timestamps, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) papered over this
issue simply by skipping the respective test cases on platforms where
they would fail due to the data type in use.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`,
conflating the two issues.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, after we
switch to a larger data type, we can flip that prerequisite to test
`time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822d..beee1d847ff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1164,3 +1164,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 3/9] t0006 & t5000: skip "far in the future" test when time_t is too limited
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 1/9] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 2/9] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
@ 2017-04-21 10:45       ` Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 4/9] Specify explicitly where we parse timestamps Johannes Schindelin
                         ` (7 subsequent siblings)
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned long, which is
ill-defined, as there is no guarantee about the number of bits that
data type has.

In preparation of switching to another data type that is large enough
to hold "far in the future" dates, we need to prepare the t0006-date.sh
script for the case where we *still* cannot format those dates if the
system library uses 32-bit time_t.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 2 +-
 t/test-lib.sh        | 1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..ac7c66c733b 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
-"  test-date is64bit\n";
+"  test-date is64bit\n"
+"  test-date time_t-is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv)
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
 		return sizeof(unsigned long) == 8 ? 0 : 1;
+	else if (!strcmp(*argv, "time_t-is64bit"))
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 9539b425ffb..42d4ea61ef5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 997aa9dea28..fe2d4f15a73 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index beee1d847ff..8d25cb7c183 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
 '
 
 test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 4/9] Specify explicitly where we parse timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (2 preceding siblings ...)
  2017-04-21 10:45       ` [PATCH v4 3/9] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
@ 2017-04-21 10:45       ` Johannes Schindelin
  2017-04-24  3:19         ` Junio C Hamano
  2017-04-21 10:45       ` [PATCH v4 5/9] Introduce a new "printf format" for " Johannes Schindelin
                         ` (6 subsequent siblings)
  10 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using a more appropriate data type, let's
introduce a symbol `parse_timestamp` (currently being defined to
`strtoul`) where appropriate, so that we can later easily switch to,
say, use `strtoull()` instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c           | 2 +-
 builtin/receive-pack.c | 4 ++--
 bundle.c               | 2 +-
 commit.c               | 6 +++---
 date.c                 | 6 +++---
 fsck.c                 | 2 +-
 git-compat-util.h      | 2 ++
 pretty.c               | 2 +-
 ref-filter.c           | 2 +-
 refs/files-backend.c   | 2 +-
 t/helper/test-date.c   | 2 +-
 tag.c                  | 4 ++--
 upload-pack.c          | 2 +-
 13 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index f7a7a971fbe..2c93adc69c3 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -882,7 +882,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 3cba3fd278f..9a4c2a7ade4 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
 		retval = NONCE_BAD;
 		goto leave;
 	}
-	stamp = strtoul(nonce, &bohmac, 10);
+	stamp = parse_timestamp(nonce, &bohmac, 10);
 	if (bohmac == nonce || bohmac[0] != '-') {
 		retval = NONCE_BAD;
 		goto leave;
@@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
 	 * would mean it was issued by another server with its clock
 	 * skewed in the future.
 	 */
-	ostamp = strtoul(push_cert_nonce, NULL, 10);
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
 	nonce_stamp_slop = (long)ostamp - (long)stamp;
 
 	if (nonce_stamp_slop_limit &&
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 73c78c2b80c..0d2d0fa1984 100644
--- a/commit.c
+++ b/commit.c
@@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..495c207c64f 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	unsigned long number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index e6152e4e6d4..d589341cddf 100644
--- a/fsck.c
+++ b/fsck.c
@@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index 8a4a3f85e7b..fc1b5fe1a6c 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index d0f86f5d85c..24fb0c79062 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 92871266001..c7836ae07bd 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 4d705b4037e..dae0522673b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3247,7 +3247,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 	    !(email_end = strchr(p, '>')) ||
 	    email_end[1] != ' ' ||
-	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
 	    !message || message[0] != ' ' ||
 	    (message[1] != '+' && message[1] != '-') ||
 	    !isdigit(message[2]) || !isdigit(message[3]) ||
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ac7c66c733b..52d1fc34454 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..9b6725e02c9 100644
--- a/tag.c
+++ b/tag.c
@@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d6231..f17f4dd1233 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 5/9] Introduce a new "printf format" for timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (3 preceding siblings ...)
  2017-04-21 10:45       ` [PATCH v4 4/9] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-21 10:45       ` " Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 6/9] Introduce a new data type " Johannes Schindelin
                         ` (5 subsequent siblings)
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
away from the ill-specified `unsigned long` data type.

So let's introduce the pseudo format "PRItime" (currently simply being
defined to "lu") to make it easier to change the data type used for
timestamps.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 builtin/rev-list.c            |  2 +-
 builtin/rev-parse.c           |  2 +-
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 refs/files-backend.c          |  2 +-
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 t/helper/test-ref-store.c     |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 15 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 07506a3e457..e4b3c7b0ebf 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f76e4163abb..af7b985c6eb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index b3b10cc1edb..f93ef6c7100 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -910,7 +910,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 9a4c2a7ade4..c49333c9b66 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a2..3b292c99bda 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
 	}
 
 	if (info->show_timestamp)
-		printf("%lu ", commit->date);
+		printf("%"PRItime" ", commit->date);
 	if (info->header_prefix)
 		fputs(info->header_prefix, stdout);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 05133309106..b4509002435 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr)
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+	buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
 	show(buffer);
 	free(buffer);
 }
diff --git a/date.c b/date.c
index 495c207c64f..5c33dfd8ee7 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index 42969353d67..a1b8ea59e8d 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -393,7 +393,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index fc1b5fe1a6c..cd522903eda 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/refs/files-backend.c b/refs/files-backend.c
index dae0522673b..ecbb13dc4b8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4131,7 +4131,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			printf("prune %s", message);
 	} else {
 		if (cb->newlog) {
-			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
 				oid_to_hex(ooid), oid_to_hex(noid),
 				email, timestamp, tz, message);
 			oidcpy(&cb->last_kept_oid, noid);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 52d1fc34454..269040f028f 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 2d84c45ffe9..a436bfdb053 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -141,7 +141,7 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
 		       const char *committer, unsigned long timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
-	printf("%s %s %s %lu %d %s\n",
+	printf("%s %s %s %"PRItime" %d %s\n",
 	       oid_to_hex(old_oid), oid_to_hex(new_oid),
 	       committer, timestamp, tz, msg);
 	return 0;
diff --git a/upload-pack.c b/upload-pack.c
index f17f4dd1233..4966f0b8a09 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -863,7 +863,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 6/9] Introduce a new data type for timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (4 preceding siblings ...)
  2017-04-21 10:45       ` [PATCH v4 5/9] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-04-21 10:45       ` " Johannes Schindelin
  2017-04-21 10:45       ` [PATCH v4 7/9] Abort if the system time cannot handle one of our " Johannes Schindelin
                         ` (4 subsequent siblings)
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

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>
---
 Documentation/technical/api-parse-options.txt |  8 ++--
 archive-tar.c                                 |  5 +-
 archive-zip.c                                 | 12 ++++-
 archive.h                                     |  2 +-
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++--
 builtin/fsck.c                                |  4 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +--
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        |  6 +--
 builtin/reflog.c                              | 24 +++++-----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 +++---
 commit.c                                      | 12 ++---
 commit.h                                      |  2 +-
 config.c                                      |  2 +-
 credential-cache--daemon.c                    | 12 ++---
 date.c                                        | 66 +++++++++++++--------------
 fetch-pack.c                                  |  6 +--
 git-compat-util.h                             |  2 +
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  2 +-
 reachable.c                                   |  9 ++--
 reachable.h                                   |  4 +-
 ref-filter.c                                  |  4 +-
 reflog-walk.c                                 |  8 ++--
 refs.c                                        | 14 +++---
 refs.h                                        |  8 ++--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +--
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +--
 t/helper/test-date.c                          |  8 ++--
 t/helper/test-parse-options.c                 |  2 +-
 t/helper/test-ref-store.c                     |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +-
 vcs-svn/fast_export.c                         |  4 +-
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 49 files changed, 169 insertions(+), 157 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e1..829b5581105 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..695339a2369 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
 #define USTAR_MAX_SIZE 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974a..68df3d64402 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -545,9 +545,17 @@ static void write_zip_trailer(const unsigned char *sha1)
 		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 {
-	struct tm *t = localtime(time);
+	time_t time;
+	struct tm *t;
+
+	if (date_overflows(*timestamp))
+		die("timestamp too large for this system: %"PRItime,
+		    *timestamp);
+	time = (time_t)*timestamp;
+	t = localtime(&time);
+	*timestamp = time;
 
 	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 	            (t->tm_year + 1900 - 1980) * 512;
diff --git a/archive.h b/archive.h
index 415e0152e2c..62d1d82c1af 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
 	struct tree *tree;
 	const unsigned char *commit_sha1;
 	const struct commit *commit;
-	time_t time;
+	timestamp_t time;
 	struct pathspec pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
diff --git a/builtin/am.c b/builtin/am.c
index 2c93adc69c3..89914ed8757 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -877,7 +877,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index e4b3c7b0ebf..f00eda16378 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	timestamp_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	timestamp_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1578,7 +1578,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	timestamp_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index af7b985c6eb..ea3a9f8a70a 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 }
 
 static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index 2daede78205..cb1e20aae4a 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/log.c b/builtin/log.c
index f93ef6c7100..fd3d10ec217 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -911,7 +911,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (timestamp_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f85..8ed96391c1d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, timestamp_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 92a5d8a5d26..44374750170 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	timestamp_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, timestamp_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	timestamp_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 0fe35d1b5ae..9b4ba8a80d7 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct oid_array recent_objects;
 
 static int loosened_object_can_be_discarded(const struct object_id *oid,
-					    unsigned long mtime)
+					    timestamp_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e6..8dcfecde0f3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index c49333c9b66..b4e22f44497 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 74727757785..4228d9ff4db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	timestamp_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, timestamp_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	timestamp_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d57..8860f429b06 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				timestamp_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9993ded41aa..74f9b18d40c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..05e014fc5ab 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	timestamp_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index ba27595d54d..f3a02993b20 100644
--- a/cache.h
+++ b/cache.h
@@ -1476,18 +1476,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 0d2d0fa1984..99a62b90ee2 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	timestamp_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	timestamp_t a_date = *(author_date_slab_at(author_date, a));
+	timestamp_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 7b1986d5c8a..c9d887b5e53 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	timestamp_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.c b/config.c
index 1a4d85537b3..3247bfaa020 100644
--- a/config.c
+++ b/config.c
@@ -1926,7 +1926,7 @@ int git_config_get_expiry(const char *key, const char **output)
 	if (ret)
 		return ret;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		timestamp_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..f3814cc47a0 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	timestamp_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static timestamp_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	timestamp_t now = time(NULL);
+	timestamp_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	timestamp_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 5c33dfd8ee7..92ab31aa441 100644
--- a/date.c
+++ b/date.c
@@ -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;
 
 	/*
diff --git a/fetch-pack.c b/fetch-pack.c
index a1b8ea59e8d..fbe52f02346 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -392,7 +392,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		timestamp_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -581,7 +581,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 timestamp_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -668,7 +668,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	timestamp_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index cd522903eda..72c12173a14 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+typedef unsigned long timestamp_t;
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..d6ea6075339 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 7419780a9b6..a6810f295cb 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(timestamp_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (timestamp_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 24fb0c79062..587d48371b0 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	timestamp_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index a8a979bd4fc..682418f5d23 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 };
 
 static void add_recent_object(const struct object_id *oid,
-			      unsigned long mtime,
+			      timestamp_t mtime,
 			      struct recent_data *data)
 {
 	struct object *obj;
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   timestamp_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    timestamp_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..3c00fa0526c 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  timestamp_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index c7836ae07bd..1fc5e9970db 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
@@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
diff --git a/reflog-walk.c b/reflog-walk.c
index 99679f58255..3ca5ed8415a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
-		unsigned long timestamp;
+		timestamp_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	timestamp_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index d1e1b4399b3..6841bd9f712 100644
--- a/refs.c
+++ b/refs.c
@@ -712,7 +712,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	timestamp_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -721,15 +721,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	timestamp_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	timestamp_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -776,7 +776,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp,
+				  const char *email, timestamp_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -796,9 +796,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 49e97d7d5fd..2b80d37fd34 100644
--- a/refs.h
+++ b/refs.h
@@ -317,9 +317,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@ -356,7 +356,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		struct object_id *old_oid, struct object_id *new_oid,
-		const char *committer, unsigned long timestamp,
+		const char *committer, timestamp_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@ -616,7 +616,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  timestamp_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index ecbb13dc4b8..6646dcfc838 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3237,7 +3237,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	struct object_id ooid, noid;
 	char *email_end, *message;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	int tz;
 	const char *p = sb->buf;
 
@@ -4114,7 +4114,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index 7ff61ff5f73..8a8c1789c7b 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	timestamp_t date = TIME_MAX;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 }
 
 static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(ooid, cb_data);
diff --git a/revision.h b/revision.h
index 14886ec92b4..0d9e68b36e9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	timestamp_t max_age;
+	timestamp_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index 8eec9f7c1bb..35c1e2a9e32 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		timestamp_t at_time;
+		timestamp_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, timestamp_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 269040f028f..f414a3ac670 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
 	parse_date_format(format, &mode);
 	for (; *argv; argv++) {
 		char *arg;
-		time_t t;
+		timestamp_t t;
 		int tz;
 
 		/*
@@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		timestamp_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 static void parse_approxidate(const char **argv, struct timeval *now)
 {
 	for (; *argv; argv++) {
-		time_t t;
+		timestamp_t t;
 		t = approxidate_relative(*argv, now);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
@@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(timestamp_t) == 8 ? 0 : 1;
 	else if (!strcmp(*argv, "time_t-is64bit"))
 		return sizeof(time_t) == 8 ? 0 : 1;
 	else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..75fe883aac1 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index a436bfdb053..9077ec2c330 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -138,7 +138,7 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
 }
 
 static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-		       const char *committer, unsigned long timestamp,
+		       const char *committer, timestamp_t timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
 	printf("%s %s %s %"PRItime" %d %s\n",
diff --git a/tag.c b/tag.c
index 9b6725e02c9..d71b67e8d83 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..2abb3726fb5 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	timestamp_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index 4966f0b8a09..97da13e6a54 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	timestamp_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5a89db30e3f 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, timestamp_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			timestamp_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..b9a3b71c99f 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, timestamp_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			timestamp_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..1846685a21a 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index 03754849626..1b1d644b85f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1387,7 +1387,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, timestamp_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 7/9] Abort if the system time cannot handle one of our timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (5 preceding siblings ...)
  2017-04-21 10:45       ` [PATCH v4 6/9] Introduce a new data type " Johannes Schindelin
@ 2017-04-21 10:45       ` " Johannes Schindelin
  2017-04-24  3:16         ` Junio C Hamano
  2017-04-21 10:46       ` [PATCH v4 8/9] Use uintmax_t for " Johannes Schindelin
                         ` (3 subsequent siblings)
  10 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:45 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

We are about to switch to a new data type for time stamps that is
definitely not smaller or equal, but larger or equal to time_t.

So before using the system functions to process or format timestamps,
let's make extra certain that they can handle what we feed them.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 date.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/date.c b/date.c
index 92ab31aa441..75f6335cd09 100644
--- a/date.c
+++ b/date.c
@@ -46,7 +46,10 @@ static time_t gm_time_t(timestamp_t time, int tz)
 	minutes = tz < 0 ? -tz : tz;
 	minutes = (minutes / 100)*60 + (minutes % 100);
 	minutes = tz < 0 ? -minutes : minutes;
-	return time + minutes * 60;
+
+	if (date_overflows(time + minutes * 60))
+		die("Timestamp too large for this system: %"PRItime, time);
+	return (time_t)time + minutes * 60;
 }
 
 /*
@@ -70,7 +73,10 @@ static int local_tzoffset(timestamp_t time)
 	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);
 
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (6 preceding siblings ...)
  2017-04-21 10:45       ` [PATCH v4 7/9] Abort if the system time cannot handle one of our " Johannes Schindelin
@ 2017-04-21 10:46       ` " Johannes Schindelin
  2017-04-24  3:24         ` Junio C Hamano
  2017-04-21 10:46       ` [PATCH v4 9/9] show_date_ident(): defer date overflow check Johannes Schindelin
                         ` (2 subsequent siblings)
  10 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Previously, we used `unsigned long` for timestamps. This was only a good
choice on Linux, where we know implicitly that `unsigned long` is what is
used for `time_t`.

However, we want to use a different data type for timestamps for two
reasons:

- there is nothing that says that `unsigned long` should be the same data
  type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
  `unsigned long` is 32-bit but `time_t` is 64-bit.

- even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
  32-bit, we *want* to be able to encode timestamps in Git that are
  currently absurdly far in the future, *even if* the system library is
  not able to format those timestamps into date strings.

So let's just switch to the maximal integer type available, which should
be at least 64-bit for all practical purposes these days. It certainly
cannot be worse than `unsigned long`, so...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 git-compat-util.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 72c12173a14..c678ca94b8f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,10 +319,14 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
-typedef unsigned long timestamp_t;
-#define PRItime "lu"
-#define parse_timestamp strtoul
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#ifdef ULLONG_MAX
+#define TIME_MAX ULLONG_MAX
+#else
 #define TIME_MAX ULONG_MAX
+#endif
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v4 9/9] show_date_ident(): defer date overflow check
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (7 preceding siblings ...)
  2017-04-21 10:46       ` [PATCH v4 8/9] Use uintmax_t for " Johannes Schindelin
@ 2017-04-21 10:46       ` Johannes Schindelin
  2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
  10 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-21 10:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Now that we use uintmax_t for timestamps, we can represent timestamps
that would not fit inside the time_t data type. As long as we do not
have to use the system functions, we can even display them, e.g. as Unix
epoch.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 pretty.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/pretty.c b/pretty.c
index 587d48371b0..35fd290096a 100644
--- a/pretty.c
+++ b/pretty.c
@@ -410,14 +410,10 @@ const char *show_ident_date(const struct ident_split *ident,
 
 	if (ident->date_begin && ident->date_end)
 		date = parse_timestamp(ident->date_begin, NULL, 10);
-	if (date_overflows(date))
-		date = 0;
-	else {
-		if (ident->tz_begin && ident->tz_end)
-			tz = strtol(ident->tz_begin, NULL, 10);
-		if (tz >= INT_MAX || tz <= INT_MIN)
-			tz = 0;
-	}
+	if (ident->tz_begin && ident->tz_end)
+		tz = strtol(ident->tz_begin, NULL, 10);
+	if (tz >= INT_MAX || tz <= INT_MIN)
+		tz = 0;
 	return show_date(date, tz, mode);
 }
 
-- 
2.12.2.windows.2.406.gd14a8f8640f

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 7/9] Abort if the system time cannot handle one of our timestamps
  2017-04-21 10:45       ` [PATCH v4 7/9] Abort if the system time cannot handle one of our " Johannes Schindelin
@ 2017-04-24  3:16         ` Junio C Hamano
  2017-04-24 13:57           ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-04-24  3:16 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> We are about to switch to a new data type for time stamps that is
> definitely not smaller or equal, but larger or equal to time_t.
>
> So before using the system functions to process or format timestamps,
> let's make extra certain that they can handle what we feed them.
>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>  date.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/date.c b/date.c
> index 92ab31aa441..75f6335cd09 100644
> --- a/date.c
> +++ b/date.c
> @@ -46,7 +46,10 @@ static time_t gm_time_t(timestamp_t time, int tz)
>  	minutes = tz < 0 ? -tz : tz;
>  	minutes = (minutes / 100)*60 + (minutes % 100);
>  	minutes = tz < 0 ? -minutes : minutes;
> -	return time + minutes * 60;
> +
> +	if (date_overflows(time + minutes * 60))
> +		die("Timestamp too large for this system: %"PRItime, time);
> +	return (time_t)time + minutes * 60;
>  }

All the other calls to date_overflows() take a variable that holds
timestamp_t and presumably they are checking for integer wraparound
when the values are computed, but this one is not.  Perhaps we want
to make it a bit more careful here?  I wonder if something like this 
is a good approach:

    #define date_overflows(time) date_overflows_add(time, 0)

    int date_overflows_add(timestamp_t base, timestamp_t minutes)
    {
	timestamp_t t;
	if (unsigned_add_overflows(base, minutes))
	    return 1;
	t = base + minutes;
	if ((uintmax_t) t >= TIME_MAX)
            return 1;
	... what you have in date_overflows() ...
    }


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 4/9] Specify explicitly where we parse timestamps
  2017-04-21 10:45       ` [PATCH v4 4/9] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-24  3:19         ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-24  3:19 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> Currently, Git's source code represents all timestamps as `unsigned
> long`. In preparation for using a more appropriate data type, let's
> introduce a symbol `parse_timestamp` (currently being defined to
> `strtoul`) where appropriate, so that we can later easily switch to,
> say, use `strtoull()` instead.

Apparently a very good first step in this series.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-21 10:46       ` [PATCH v4 8/9] Use uintmax_t for " Johannes Schindelin
@ 2017-04-24  3:24         ` Junio C Hamano
  2017-04-24 10:28           ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-04-24  3:24 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> Previously, we used `unsigned long` for timestamps. This was only a good
> choice on Linux, where we know implicitly that `unsigned long` is what is
> used for `time_t`.
>
> However, we want to use a different data type for timestamps for two
> reasons:
>
> - there is nothing that says that `unsigned long` should be the same data
>   type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
>   `unsigned long` is 32-bit but `time_t` is 64-bit.
>
> - even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
>   32-bit, we *want* to be able to encode timestamps in Git that are
>   currently absurdly far in the future, *even if* the system library is
>   not able to format those timestamps into date strings.
>
> So let's just switch to the maximal integer type available, which should
> be at least 64-bit for all practical purposes these days. It certainly
> cannot be worse than `unsigned long`, so...

Should we at least clamp in date_overflows() so that large values
representable with timestamp_t that will become unrepresentable when
we start allowing negative timestamps would be rejected?  That way
we won't have to hear complaints from the people who used timestamps
far in the future that we regressed the implementation for them by
halving the possible timestamp range.



> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>  git-compat-util.h | 10 +++++++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 72c12173a14..c678ca94b8f 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -319,10 +319,14 @@ extern char *gitdirname(char *);
>  #define PRIo32 "o"
>  #endif
>  
> -typedef unsigned long timestamp_t;
> -#define PRItime "lu"
> -#define parse_timestamp strtoul
> +typedef uintmax_t timestamp_t;
> +#define PRItime PRIuMAX
> +#define parse_timestamp strtoumax
> +#ifdef ULLONG_MAX
> +#define TIME_MAX ULLONG_MAX
> +#else
>  #define TIME_MAX ULONG_MAX
> +#endif
>  
>  #ifndef PATH_SEP
>  #define PATH_SEP ':'

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (8 preceding siblings ...)
  2017-04-21 10:46       ` [PATCH v4 9/9] show_date_ident(): defer date overflow check Johannes Schindelin
@ 2017-04-24  3:29       ` Junio C Hamano
  2017-04-24  6:15         ` Jacob Keller
                           ` (2 more replies)
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
  10 siblings, 3 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-24  3:29 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen, Jeff King, René Scharfe

Johannes Schindelin <johannes.schindelin@gmx.de> writes:

> Changes since v3:
>
> - fixed the fix in archive-zip.c that tried to report a too large
>   timestamp (and would have reported the uninitialized time_t instead)
>
> - adjusted the so-far forgotten each_reflog() function (that was
>   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
>   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
>   too
>
> - removed the date_overflows() check from time_to_tm(), as it calls
>   gm_time_t() which already performs that check
>
> - the date_overflows() check in show_ident_date() was removed, as we do
>   not know at that point yet whether we use the system functions to
>   render the date or not (and there would not be a problem in the latter
>   case)

Assuming that the list consensus is to go with a separate
timestamp_t (for that added Cc for those whose comments I saw in an
earlier round), the patches looked mostly good (I didn't read with
fine toothed comb the largest one 6/8 to see if there were
inadvertent or missed conversions from ulong to timestamp_t,
though), modulo a few minor "huh?" comments I sent separately.

Will queue; thanks.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
@ 2017-04-24  6:15         ` Jacob Keller
  2017-04-24 14:02           ` Johannes Schindelin
  2017-04-24 11:37         ` Jeff King
  2017-04-24 14:00         ` Johannes Schindelin
  2 siblings, 1 reply; 113+ messages in thread
From: Jacob Keller @ 2017-04-24  6:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, Git mailing list, Torsten Bögershausen, Jeff King, René Scharfe

On Sun, Apr 23, 2017 at 8:29 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
>
>> Changes since v3:
>>
>> - fixed the fix in archive-zip.c that tried to report a too large
>>   timestamp (and would have reported the uninitialized time_t instead)
>>
>> - adjusted the so-far forgotten each_reflog() function (that was
>>   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
>>   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
>>   too
>>
>> - removed the date_overflows() check from time_to_tm(), as it calls
>>   gm_time_t() which already performs that check
>>
>> - the date_overflows() check in show_ident_date() was removed, as we do
>>   not know at that point yet whether we use the system functions to
>>   render the date or not (and there would not be a problem in the latter
>>   case)
>
> Assuming that the list consensus is to go with a separate
> timestamp_t (for that added Cc for those whose comments I saw in an
> earlier round), the patches looked mostly good (I didn't read with
> fine toothed comb the largest one 6/8 to see if there were
> inadvertent or missed conversions from ulong to timestamp_t,
> though), modulo a few minor "huh?" comments I sent separately.
>
> Will queue; thanks.

I think that this timestamp_t makes sense. I didn't get a chance to
review the code to make sure nothing was forgotten, but I think the
direction makes sense to resolve the problems with current time_t and
ulong assumptions.

Thanks,
Jake

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-24  3:24         ` Junio C Hamano
@ 2017-04-24 10:28           ` Johannes Schindelin
  2017-04-25  3:59             ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 10:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen

Hi Junio,

On Sun, 23 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > Previously, we used `unsigned long` for timestamps. This was only a
> > good choice on Linux, where we know implicitly that `unsigned long` is
> > what is used for `time_t`.
> >
> > However, we want to use a different data type for timestamps for two
> > reasons:
> >
> > - there is nothing that says that `unsigned long` should be the same data
> >   type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
> >   `unsigned long` is 32-bit but `time_t` is 64-bit.
> >
> > - even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
> >   32-bit, we *want* to be able to encode timestamps in Git that are
> >   currently absurdly far in the future, *even if* the system library is
> >   not able to format those timestamps into date strings.
> >
> > So let's just switch to the maximal integer type available, which should
> > be at least 64-bit for all practical purposes these days. It certainly
> > cannot be worse than `unsigned long`, so...
> 
> Should we at least clamp in date_overflows() so that large values
> representable with timestamp_t that will become unrepresentable when
> we start allowing negative timestamps would be rejected?  That way
> we won't have to hear complaints from the people who used timestamps
> far in the future that we regressed the implementation for them by
> halving the possible timestamp range.

Please note that the date_overflows() command only tests when we are about
to call system functions. I do not think that it does what you think it
does (namely, validate timestamps when they enter Git).

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
  2017-04-24  6:15         ` Jacob Keller
@ 2017-04-24 11:37         ` Jeff King
  2017-04-25 20:13           ` Johannes Schindelin
  2017-04-24 14:00         ` Johannes Schindelin
  2 siblings, 1 reply; 113+ messages in thread
From: Jeff King @ 2017-04-24 11:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git, Torsten Bögershausen, René Scharfe

On Sun, Apr 23, 2017 at 08:29:11PM -0700, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > Changes since v3:
> >
> > - fixed the fix in archive-zip.c that tried to report a too large
> >   timestamp (and would have reported the uninitialized time_t instead)
> >
> > - adjusted the so-far forgotten each_reflog() function (that was
> >   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
> >   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
> >   too
> >
> > - removed the date_overflows() check from time_to_tm(), as it calls
> >   gm_time_t() which already performs that check
> >
> > - the date_overflows() check in show_ident_date() was removed, as we do
> >   not know at that point yet whether we use the system functions to
> >   render the date or not (and there would not be a problem in the latter
> >   case)
> 
> Assuming that the list consensus is to go with a separate
> timestamp_t (for that added Cc for those whose comments I saw in an
> earlier round), the patches looked mostly good (I didn't read with
> fine toothed comb the largest one 6/8 to see if there were
> inadvertent or missed conversions from ulong to timestamp_t,
> though), modulo a few minor "huh?" comments I sent separately.
> 
> Will queue; thanks.

Sorry, I haven't read the series carefully yet (but from a skim I'm
happy with the overall direction). It does seem to cause failures in
t4212, though. For example:

  expecting success: 
  	commit=$(munge_author_date HEAD 18446744073709551617) &&
  	echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
  	git log -1 --format=%ad $commit >actual &&
  	test_cmp expect actual
  
  fatal: Timestamp too large for this system: 18446744073709551615
  not ok 7 - date parser recognizes integer overflow

We used to convert overflows into a sentinel time, but now we die. I
originally chose the sentinel approach because it lets you use the tools
to examine and recover from the broken state. I could be convinced that
dying is better, but clearly we'd need to at least update the tests.

-Peff

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 7/9] Abort if the system time cannot handle one of our timestamps
  2017-04-24  3:16         ` Junio C Hamano
@ 2017-04-24 13:57           ` Johannes Schindelin
  2017-04-25  2:37             ` Junio C Hamano
  2017-04-25  3:56             ` Junio C Hamano
  0 siblings, 2 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen

Hi Junio,

On Sun, 23 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > diff --git a/date.c b/date.c
> > index 92ab31aa441..75f6335cd09 100644
> > --- a/date.c
> > +++ b/date.c
> > @@ -46,7 +46,10 @@ static time_t gm_time_t(timestamp_t time, int tz)
> >  	minutes = tz < 0 ? -tz : tz;
> >  	minutes = (minutes / 100)*60 + (minutes % 100);
> >  	minutes = tz < 0 ? -minutes : minutes;
> > -	return time + minutes * 60;
> > +
> > +	if (date_overflows(time + minutes * 60))
> > +		die("Timestamp too large for this system: %"PRItime, time);
> > +	return (time_t)time + minutes * 60;
> >  }
> 
> All the other calls to date_overflows() take a variable that holds
> timestamp_t and presumably they are checking for integer wraparound
> when the values are computed, but this one is not.

I was debating whether this extra check is necessary and had decided
against it. Apparently I was wrong to do so.

> Perhaps we want to make it a bit more careful here?  I wonder if
> something like this is a good approach:
> 
>     #define date_overflows(time) date_overflows_add(time, 0)
> 
>     int date_overflows_add(timestamp_t base, timestamp_t minutes)
>     {
> 	timestamp_t t;
> 	if (unsigned_add_overflows(base, minutes))
> 	    return 1;
> 	t = base + minutes;
> 	if ((uintmax_t) t >= TIME_MAX)
>             return 1;
> 	... what you have in date_overflows() ...
>     }

That sounds like uglifying the common case for a single user. Let's not.

The code would also be incorrect, as the `minutes` variable can be
negative, which the `unsigned_add_overflows()` macro cannot handle (it
would report very, very false positives, and it would hurt you more than
me because I live East of Greenwich).

Apart from that, the signature should use seconds, not minutes, or
alternatively multiply by 60.

I'll add a fixed extra check, to the single location that needs it.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 0/8] Introduce timestamp_t for timestamps
  2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
                         ` (9 preceding siblings ...)
  2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
@ 2017-04-24 13:57       ` " Johannes Schindelin
  2017-04-24 13:57         ` [PATCH v5 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                           ` (9 more replies)
  10 siblings, 10 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as we already make use of
64-bit data types internally. There is no good reason why we should not
use the same for timestamps. Hence, let's use uintmax_t for timestamps.

Note: while the `time_t` data type exists and is meant to be used for
timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
used `time_t` for that reason, but it came with a few serious downsides:
as `time_t` can be signed (and indeed, on Windows it is an int64_t),
Git's expectation that 0 is the minimal value does no longer hold true,
introducing its own set of interesting challenges. Besides, if we *can*
handle far in the future timestamps (except for formatting them using
the system libraries), it is more consistent to do so.

The upside of using `uintmax_t` for timestamps is that we do a much
better job to support far in the future timestamps across all platforms,
including 32-bit ones. The downside is that those platforms that use a
32-bit `time_t` will barf when parsing or formatting those timestamps.

This iteration makes the date_overflows() check more stringent again.

It is arguably a bug to paper over too-large author/committer dates and
to replace them with Jan 1 1970 without even telling the user that we do
that, but this is the behavior that t4212 verifies, so I reinstated that
behavior. The change in behavior was missed because of the missing
unsigned_add_overflows() test.

Changes since v4:

- in gm_time_t(), we now test specifically that the timezone adjustment
  neither underflows nor overflows.

- the patch introduced in v4 that tried to defer the date_overflows()
  check to gm_time_t() rather than replacing the ident timestamp by a 0
  without any warning was dropped again: it broke t4212.


Johannes Schindelin (8):
  ref-filter: avoid using `unsigned long` for catch-all data type
  t0006 & t5000: prepare for 64-bit timestamps
  t0006 & t5000: skip "far in the future" test when time_t is too
    limited
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Introduce a new data type for timestamps
  Abort if the system time cannot handle one of our timestamps
  Use uintmax_t for timestamps

 Documentation/technical/api-parse-options.txt |   8 +-
 archive-tar.c                                 |   5 +-
 archive-zip.c                                 |  12 ++-
 archive.h                                     |   2 +-
 builtin/am.c                                  |   4 +-
 builtin/blame.c                               |  14 ++--
 builtin/fsck.c                                |   6 +-
 builtin/gc.c                                  |   2 +-
 builtin/log.c                                 |   4 +-
 builtin/merge-base.c                          |   2 +-
 builtin/name-rev.c                            |   6 +-
 builtin/pack-objects.c                        |   4 +-
 builtin/prune.c                               |   4 +-
 builtin/receive-pack.c                        |  14 ++--
 builtin/reflog.c                              |  24 +++---
 builtin/rev-list.c                            |   2 +-
 builtin/rev-parse.c                           |   2 +-
 builtin/show-branch.c                         |   4 +-
 builtin/worktree.c                            |   4 +-
 bundle.c                                      |   4 +-
 cache.h                                       |  14 ++--
 commit.c                                      |  18 ++--
 commit.h                                      |   2 +-
 config.c                                      |   2 +-
 credential-cache--daemon.c                    |  12 +--
 date.c                                        | 113 ++++++++++++++------------
 fetch-pack.c                                  |   8 +-
 fsck.c                                        |   2 +-
 git-compat-util.h                             |   9 ++
 http-backend.c                                |   4 +-
 parse-options-cb.c                            |   4 +-
 pretty.c                                      |   4 +-
 reachable.c                                   |   9 +-
 reachable.h                                   |   4 +-
 ref-filter.c                                  |  22 ++---
 reflog-walk.c                                 |   8 +-
 refs.c                                        |  14 ++--
 refs.h                                        |   8 +-
 refs/files-backend.c                          |   8 +-
 revision.c                                    |   6 +-
 revision.h                                    |   4 +-
 sha1_name.c                                   |   6 +-
 t/helper/test-date.c                          |  18 ++--
 t/helper/test-parse-options.c                 |   4 +-
 t/helper/test-ref-store.c                     |   4 +-
 t/t0006-date.sh                               |   4 +-
 t/t5000-tar-tree.sh                           |   6 +-
 t/test-lib.sh                                 |   3 +
 tag.c                                         |   6 +-
 tag.h                                         |   2 +-
 upload-pack.c                                 |   8 +-
 vcs-svn/fast_export.c                         |   8 +-
 vcs-svn/fast_export.h                         |   4 +-
 vcs-svn/svndump.c                             |   2 +-
 wt-status.c                                   |   2 +-
 55 files changed, 260 insertions(+), 219 deletions(-)


base-commit: e2cb6ab84c94f147f1259260961513b40c36108a
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v5
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v5

Interdiff vs v4:

 diff --git a/date.c b/date.c
 index 75f6335cd09..63fa99685e2 100644
 --- a/date.c
 +++ b/date.c
 @@ -47,9 +47,16 @@ static time_t gm_time_t(timestamp_t time, int tz)
  	minutes = (minutes / 100)*60 + (minutes % 100);
  	minutes = tz < 0 ? -minutes : minutes;
  
 -	if (date_overflows(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 + minutes * 60;
 +	return (time_t)time;
  }
  
  /*
 diff --git a/pretty.c b/pretty.c
 index 35fd290096a..587d48371b0 100644
 --- a/pretty.c
 +++ b/pretty.c
 @@ -410,10 +410,14 @@ const char *show_ident_date(const struct ident_split *ident,
  
  	if (ident->date_begin && ident->date_end)
  		date = parse_timestamp(ident->date_begin, NULL, 10);
 -	if (ident->tz_begin && ident->tz_end)
 -		tz = strtol(ident->tz_begin, NULL, 10);
 -	if (tz >= INT_MAX || tz <= INT_MIN)
 -		tz = 0;
 +	if (date_overflows(date))
 +		date = 0;
 +	else {
 +		if (ident->tz_begin && ident->tz_end)
 +			tz = strtol(ident->tz_begin, NULL, 10);
 +		if (tz >= INT_MAX || tz <= INT_MIN)
 +			tz = 0;
 +	}
  	return show_date(date, tz, mode);
  }
  

-- 
2.12.2.windows.2.406.gd14a8f8640f


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
@ 2017-04-24 13:57         ` Johannes Schindelin
  2017-04-24 13:57         ` [PATCH v5 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
                           ` (8 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply change that field to use `uintmax_t` instead.

This patch is a bit larger than the mere change of the data type
because the field's name was tied to its data type, which has been fixed
at the same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 3a640448fd8..92871266001 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -351,7 +351,7 @@ struct ref_formatting_state {
 struct atom_value {
 	const char *s;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 	struct used_atom *atom;
 };
 
@@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1941,9 +1941,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 2/8] t0006 & t5000: prepare for 64-bit timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
  2017-04-24 13:57         ` [PATCH v5 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-24 13:57         ` Johannes Schindelin
  2017-04-24 13:58         ` [PATCH v5 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
                           ` (7 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:57 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply uses an incorrect data type for timestamps, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) papered over this
issue simply by skipping the respective test cases on platforms where
they would fail due to the data type in use.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`,
conflating the two issues.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, after we
switch to a larger data type, we can flip that prerequisite to test
`time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822d..beee1d847ff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1164,3 +1164,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
  2017-04-24 13:57         ` [PATCH v5 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
  2017-04-24 13:57         ` [PATCH v5 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
@ 2017-04-24 13:58         ` Johannes Schindelin
  2017-04-24 13:58         ` [PATCH v5 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
                           ` (6 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Git's source code refers to timestamps as unsigned long, which is
ill-defined, as there is no guarantee about the number of bits that
data type has.

In preparation of switching to another data type that is large enough
to hold "far in the future" dates, we need to prepare the t0006-date.sh
script for the case where we *still* cannot format those dates if the
system library uses 32-bit time_t.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 2 +-
 t/test-lib.sh        | 1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..ac7c66c733b 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
-"  test-date is64bit\n";
+"  test-date is64bit\n"
+"  test-date time_t-is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv)
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
 		return sizeof(unsigned long) == 8 ? 0 : 1;
+	else if (!strcmp(*argv, "time_t-is64bit"))
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 9539b425ffb..42d4ea61ef5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 997aa9dea28..fe2d4f15a73 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index beee1d847ff..8d25cb7c183 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
 '
 
 test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 4/8] Specify explicitly where we parse timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (2 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
@ 2017-04-24 13:58         ` Johannes Schindelin
  2017-04-25  5:59           ` Junio C Hamano
  2017-04-24 13:58         ` [PATCH v5 5/8] Introduce a new "printf format" for " Johannes Schindelin
                           ` (5 subsequent siblings)
  9 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using a more appropriate data type, let's
introduce a symbol `parse_timestamp` (currently being defined to
`strtoul`) where appropriate, so that we can later easily switch to,
say, use `strtoull()` instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c           | 2 +-
 builtin/receive-pack.c | 4 ++--
 bundle.c               | 2 +-
 commit.c               | 6 +++---
 date.c                 | 6 +++---
 fsck.c                 | 2 +-
 git-compat-util.h      | 2 ++
 pretty.c               | 2 +-
 ref-filter.c           | 2 +-
 refs/files-backend.c   | 2 +-
 t/helper/test-date.c   | 2 +-
 tag.c                  | 4 ++--
 upload-pack.c          | 2 +-
 13 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 805f56cec2f..ffb7a6355fb 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -886,7 +886,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f96834f42c9..d25a57931f8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
 		retval = NONCE_BAD;
 		goto leave;
 	}
-	stamp = strtoul(nonce, &bohmac, 10);
+	stamp = parse_timestamp(nonce, &bohmac, 10);
 	if (bohmac == nonce || bohmac[0] != '-') {
 		retval = NONCE_BAD;
 		goto leave;
@@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
 	 * would mean it was issued by another server with its clock
 	 * skewed in the future.
 	 */
-	ostamp = strtoul(push_cert_nonce, NULL, 10);
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
 	nonce_stamp_slop = (long)ostamp - (long)stamp;
 
 	if (nonce_stamp_slop_limit &&
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 73c78c2b80c..0d2d0fa1984 100644
--- a/commit.c
+++ b/commit.c
@@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..495c207c64f 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	unsigned long number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index e6152e4e6d4..d589341cddf 100644
--- a/fsck.c
+++ b/fsck.c
@@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index bd04564a69a..d78cc36dd1a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index d0f86f5d85c..24fb0c79062 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 92871266001..c7836ae07bd 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index c9d900fd128..801d26468e0 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3247,7 +3247,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 	    !(email_end = strchr(p, '>')) ||
 	    email_end[1] != ' ' ||
-	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
 	    !message || message[0] != ' ' ||
 	    (message[1] != '+' && message[1] != '-') ||
 	    !isdigit(message[2]) || !isdigit(message[3]) ||
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ac7c66c733b..52d1fc34454 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..9b6725e02c9 100644
--- a/tag.c
+++ b/tag.c
@@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d6231..f17f4dd1233 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 5/8] Introduce a new "printf format" for timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (3 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-24 13:58         ` " Johannes Schindelin
  2017-04-24 13:58         ` [PATCH v5 6/8] Introduce a new data type " Johannes Schindelin
                           ` (4 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
away from the ill-specified `unsigned long` data type.

So let's introduce the pseudo format "PRItime" (currently simply being
defined to "lu") to make it easier to change the data type used for
timestamps.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 builtin/rev-list.c            |  2 +-
 builtin/rev-parse.c           |  2 +-
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 refs/files-backend.c          |  2 +-
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 t/helper/test-ref-store.c     |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 15 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 07506a3e457..e4b3c7b0ebf 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b5e13a45560..c233eda21c8 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index b3b10cc1edb..f93ef6c7100 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -910,7 +910,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d25a57931f8..ab718f4402c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a2..3b292c99bda 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
 	}
 
 	if (info->show_timestamp)
-		printf("%lu ", commit->date);
+		printf("%"PRItime" ", commit->date);
 	if (info->header_prefix)
 		fputs(info->header_prefix, stdout);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 05133309106..b4509002435 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr)
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+	buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
 	show(buffer);
 	free(buffer);
 }
diff --git a/date.c b/date.c
index 495c207c64f..5c33dfd8ee7 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index afb8b050248..6004cea60d4 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -395,7 +395,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index d78cc36dd1a..b2451a12d54 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 801d26468e0..4e9e3628716 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4135,7 +4135,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			printf("prune %s", message);
 	} else {
 		if (cb->newlog) {
-			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
 				oid_to_hex(ooid), oid_to_hex(noid),
 				email, timestamp, tz, message);
 			oidcpy(&cb->last_kept_oid, noid);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 52d1fc34454..269040f028f 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 2d84c45ffe9..a436bfdb053 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -141,7 +141,7 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
 		       const char *committer, unsigned long timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
-	printf("%s %s %s %lu %d %s\n",
+	printf("%s %s %s %"PRItime" %d %s\n",
 	       oid_to_hex(old_oid), oid_to_hex(new_oid),
 	       committer, timestamp, tz, msg);
 	return 0;
diff --git a/upload-pack.c b/upload-pack.c
index f17f4dd1233..4966f0b8a09 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -863,7 +863,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 6/8] Introduce a new data type for timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (4 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 5/8] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-04-24 13:58         ` " Johannes Schindelin
  2017-04-26 16:43           ` Johannes Sixt
  2017-04-26 22:32           ` René Scharfe
  2017-04-24 13:58         ` [PATCH v5 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
                           ` (3 subsequent siblings)
  9 siblings, 2 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

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>
---
 Documentation/technical/api-parse-options.txt |  8 ++--
 archive-tar.c                                 |  5 +-
 archive-zip.c                                 | 12 ++++-
 archive.h                                     |  2 +-
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++--
 builtin/fsck.c                                |  4 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +--
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        |  6 +--
 builtin/reflog.c                              | 24 +++++-----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 +++---
 commit.c                                      | 12 ++---
 commit.h                                      |  2 +-
 config.c                                      |  2 +-
 credential-cache--daemon.c                    | 12 ++---
 date.c                                        | 66 +++++++++++++--------------
 fetch-pack.c                                  |  6 +--
 git-compat-util.h                             |  2 +
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  2 +-
 reachable.c                                   |  9 ++--
 reachable.h                                   |  4 +-
 ref-filter.c                                  |  4 +-
 reflog-walk.c                                 |  8 ++--
 refs.c                                        | 14 +++---
 refs.h                                        |  8 ++--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +--
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +--
 t/helper/test-date.c                          |  8 ++--
 t/helper/test-parse-options.c                 |  2 +-
 t/helper/test-ref-store.c                     |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +-
 vcs-svn/fast_export.c                         |  4 +-
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 49 files changed, 169 insertions(+), 157 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e1..829b5581105 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..695339a2369 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
 #define USTAR_MAX_SIZE 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974a..68df3d64402 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -545,9 +545,17 @@ static void write_zip_trailer(const unsigned char *sha1)
 		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 {
-	struct tm *t = localtime(time);
+	time_t time;
+	struct tm *t;
+
+	if (date_overflows(*timestamp))
+		die("timestamp too large for this system: %"PRItime,
+		    *timestamp);
+	time = (time_t)*timestamp;
+	t = localtime(&time);
+	*timestamp = time;
 
 	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 	            (t->tm_year + 1900 - 1980) * 512;
diff --git a/archive.h b/archive.h
index 415e0152e2c..62d1d82c1af 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
 	struct tree *tree;
 	const unsigned char *commit_sha1;
 	const struct commit *commit;
-	time_t time;
+	timestamp_t time;
 	struct pathspec pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
diff --git a/builtin/am.c b/builtin/am.c
index ffb7a6355fb..8bdbd53b618 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -881,7 +881,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index e4b3c7b0ebf..f00eda16378 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	timestamp_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	timestamp_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1578,7 +1578,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	timestamp_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index c233eda21c8..32a32e55c85 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 }
 
 static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index 91f7696a85e..f484eda43ca 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/log.c b/builtin/log.c
index f93ef6c7100..fd3d10ec217 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -911,7 +911,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (timestamp_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f85..8ed96391c1d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, timestamp_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 92a5d8a5d26..44374750170 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	timestamp_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, timestamp_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	timestamp_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 0fe35d1b5ae..9b4ba8a80d7 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct oid_array recent_objects;
 
 static int loosened_object_can_be_discarded(const struct object_id *oid,
-					    unsigned long mtime)
+					    timestamp_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e6..8dcfecde0f3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ab718f4402c..0bb36d584d7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 74727757785..4228d9ff4db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	timestamp_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, timestamp_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	timestamp_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d57..8860f429b06 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				timestamp_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9993ded41aa..74f9b18d40c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..05e014fc5ab 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	timestamp_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index ef0fe43a9df..e2482a510f7 100644
--- a/cache.h
+++ b/cache.h
@@ -1478,18 +1478,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 0d2d0fa1984..99a62b90ee2 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	timestamp_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	timestamp_t a_date = *(author_date_slab_at(author_date, a));
+	timestamp_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 7b1986d5c8a..c9d887b5e53 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	timestamp_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.c b/config.c
index 0daaed338ea..4cf644b9fbf 100644
--- a/config.c
+++ b/config.c
@@ -1943,7 +1943,7 @@ int git_config_get_expiry(const char *key, const char **output)
 	if (ret)
 		return ret;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		timestamp_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..f3814cc47a0 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	timestamp_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static timestamp_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	timestamp_t now = time(NULL);
+	timestamp_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	timestamp_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 5c33dfd8ee7..92ab31aa441 100644
--- a/date.c
+++ b/date.c
@@ -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;
 
 	/*
diff --git a/fetch-pack.c b/fetch-pack.c
index 6004cea60d4..5f15dd2c390 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -394,7 +394,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		timestamp_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -583,7 +583,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 timestamp_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -670,7 +670,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	timestamp_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index b2451a12d54..594100e7652 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+typedef unsigned long timestamp_t;
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..d6ea6075339 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 7419780a9b6..a6810f295cb 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(timestamp_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (timestamp_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 24fb0c79062..587d48371b0 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	timestamp_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index a8a979bd4fc..682418f5d23 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 };
 
 static void add_recent_object(const struct object_id *oid,
-			      unsigned long mtime,
+			      timestamp_t mtime,
 			      struct recent_data *data)
 {
 	struct object *obj;
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   timestamp_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    timestamp_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..3c00fa0526c 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  timestamp_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index c7836ae07bd..1fc5e9970db 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
@@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
diff --git a/reflog-walk.c b/reflog-walk.c
index 99679f58255..3ca5ed8415a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
-		unsigned long timestamp;
+		timestamp_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	timestamp_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index a3d5f42e373..f4944a658c7 100644
--- a/refs.c
+++ b/refs.c
@@ -712,7 +712,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	timestamp_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -721,15 +721,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	timestamp_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	timestamp_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -776,7 +776,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp,
+				  const char *email, timestamp_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -796,9 +796,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 49e97d7d5fd..2b80d37fd34 100644
--- a/refs.h
+++ b/refs.h
@@ -317,9 +317,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@ -356,7 +356,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		struct object_id *old_oid, struct object_id *new_oid,
-		const char *committer, unsigned long timestamp,
+		const char *committer, timestamp_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@ -616,7 +616,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  timestamp_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 4e9e3628716..4d9d1144569 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3237,7 +3237,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	struct object_id ooid, noid;
 	char *email_end, *message;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	int tz;
 	const char *p = sb->buf;
 
@@ -4118,7 +4118,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index 7ff61ff5f73..8a8c1789c7b 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	timestamp_t date = TIME_MAX;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 }
 
 static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(ooid, cb_data);
diff --git a/revision.h b/revision.h
index 14886ec92b4..0d9e68b36e9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	timestamp_t max_age;
+	timestamp_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index 8eec9f7c1bb..35c1e2a9e32 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		timestamp_t at_time;
+		timestamp_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, timestamp_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 269040f028f..f414a3ac670 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
 	parse_date_format(format, &mode);
 	for (; *argv; argv++) {
 		char *arg;
-		time_t t;
+		timestamp_t t;
 		int tz;
 
 		/*
@@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		timestamp_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 static void parse_approxidate(const char **argv, struct timeval *now)
 {
 	for (; *argv; argv++) {
-		time_t t;
+		timestamp_t t;
 		t = approxidate_relative(*argv, now);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
@@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(timestamp_t) == 8 ? 0 : 1;
 	else if (!strcmp(*argv, "time_t-is64bit"))
 		return sizeof(time_t) == 8 ? 0 : 1;
 	else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..75fe883aac1 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index a436bfdb053..9077ec2c330 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -138,7 +138,7 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
 }
 
 static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-		       const char *committer, unsigned long timestamp,
+		       const char *committer, timestamp_t timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
 	printf("%s %s %s %"PRItime" %d %s\n",
diff --git a/tag.c b/tag.c
index 9b6725e02c9..d71b67e8d83 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..2abb3726fb5 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	timestamp_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index 4966f0b8a09..97da13e6a54 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	timestamp_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5a89db30e3f 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, timestamp_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			timestamp_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..b9a3b71c99f 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, timestamp_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			timestamp_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..1846685a21a 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index 03754849626..1b1d644b85f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1387,7 +1387,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, timestamp_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 7/8] Abort if the system time cannot handle one of our timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (5 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-04-24 13:58         ` " Johannes Schindelin
  2017-04-24 13:58         ` [PATCH v5 8/8] Use uintmax_t for " Johannes Schindelin
                           ` (2 subsequent siblings)
  9 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

We are about to switch to a new data type for time stamps that is
definitely not smaller or equal, but larger or equal to time_t.

So before using the system functions to process or format timestamps,
let's make extra certain that they can handle what we feed them.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 date.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/date.c b/date.c
index 92ab31aa441..63fa99685e2 100644
--- a/date.c
+++ b/date.c
@@ -46,7 +46,17 @@ static time_t gm_time_t(timestamp_t time, int tz)
 	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;
 }
 
 /*
@@ -70,7 +80,10 @@ static int local_tzoffset(timestamp_t time)
 	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);
 
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v5 8/8] Use uintmax_t for timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (6 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
@ 2017-04-24 13:58         ` " Johannes Schindelin
  2017-04-26 16:36           ` Johannes Sixt
  2017-04-25 21:54         ` [PATCH v5 0/8] Introduce timestamp_t " René Scharfe
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
  9 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 13:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen

Previously, we used `unsigned long` for timestamps. This was only a good
choice on Linux, where we know implicitly that `unsigned long` is what is
used for `time_t`.

However, we want to use a different data type for timestamps for two
reasons:

- there is nothing that says that `unsigned long` should be the same data
  type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
  `unsigned long` is 32-bit but `time_t` is 64-bit.

- even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
  32-bit, we *want* to be able to encode timestamps in Git that are
  currently absurdly far in the future, *even if* the system library is
  not able to format those timestamps into date strings.

So let's just switch to the maximal integer type available, which should
be at least 64-bit for all practical purposes these days. It certainly
cannot be worse than `unsigned long`, so...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 git-compat-util.h | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 594100e7652..b66995685af 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,10 +319,14 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
-typedef unsigned long timestamp_t;
-#define PRItime "lu"
-#define parse_timestamp strtoul
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#ifdef ULLONG_MAX
+#define TIME_MAX ULLONG_MAX
+#else
 #define TIME_MAX ULONG_MAX
+#endif
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
-- 
2.12.2.windows.2.406.gd14a8f8640f

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
  2017-04-24  6:15         ` Jacob Keller
  2017-04-24 11:37         ` Jeff King
@ 2017-04-24 14:00         ` Johannes Schindelin
  2 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 14:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen, Jeff King, René Scharfe

[-- Attachment #1: Type: text/plain, Size: 1416 bytes --]

Hi Junio,

On Sun, 23 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> 
> > Changes since v3:
> >
> > - fixed the fix in archive-zip.c that tried to report a too large
> >   timestamp (and would have reported the uninitialized time_t instead)
> >
> > - adjusted the so-far forgotten each_reflog() function (that was
> >   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
> >   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
> >   too
> >
> > - removed the date_overflows() check from time_to_tm(), as it calls
> >   gm_time_t() which already performs that check
> >
> > - the date_overflows() check in show_ident_date() was removed, as we do
> >   not know at that point yet whether we use the system functions to
> >   render the date or not (and there would not be a problem in the latter
> >   case)
> 
> Assuming that the list consensus is to go with a separate
> timestamp_t (for that added Cc for those whose comments I saw in an
> earlier round), the patches looked mostly good (I didn't read with
> fine toothed comb the largest one 6/8 to see if there were
> inadvertent or missed conversions from ulong to timestamp_t,
> though), modulo a few minor "huh?" comments I sent separately.

Dang, I forgot to Cc: Peff and René... And I sent out v5 before adding
them, sorry!

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-24  6:15         ` Jacob Keller
@ 2017-04-24 14:02           ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-24 14:02 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Junio C Hamano, Git mailing list, Torsten Bögershausen, Jeff King, René Scharfe

Hi Jacob,

On Sun, 23 Apr 2017, Jacob Keller wrote:

> On Sun, Apr 23, 2017 at 8:29 PM, Junio C Hamano <gitster@pobox.com> wrote:
> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> >
> >> Changes since v3:
> >>
> >> - fixed the fix in archive-zip.c that tried to report a too large
> >>   timestamp (and would have reported the uninitialized time_t instead)
> >>
> >> - adjusted the so-far forgotten each_reflog() function (that was
> >>   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
> >>   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
> >>   too
> >>
> >> - removed the date_overflows() check from time_to_tm(), as it calls
> >>   gm_time_t() which already performs that check
> >>
> >> - the date_overflows() check in show_ident_date() was removed, as we do
> >>   not know at that point yet whether we use the system functions to
> >>   render the date or not (and there would not be a problem in the latter
> >>   case)
> >
> > Assuming that the list consensus is to go with a separate
> > timestamp_t (for that added Cc for those whose comments I saw in an
> > earlier round), the patches looked mostly good (I didn't read with
> > fine toothed comb the largest one 6/8 to see if there were
> > inadvertent or missed conversions from ulong to timestamp_t,
> > though), modulo a few minor "huh?" comments I sent separately.
> >
> > Will queue; thanks.
> 
> I think that this timestamp_t makes sense. I didn't get a chance to
> review the code to make sure nothing was forgotten, but I think the
> direction makes sense to resolve the problems with current time_t and
> ulong assumptions.

TBH I rely a bit on the combination of compiling on Windows and on 32-bit
Linux to make sure that all the callers are converted. Originally, I was
more diligent, of course, but it took a while to get this patch series
into `pu` and there are so many possible new callers...

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 7/9] Abort if the system time cannot handle one of our timestamps
  2017-04-24 13:57           ` Johannes Schindelin
@ 2017-04-25  2:37             ` Junio C Hamano
  2017-04-25  3:56             ` Junio C Hamano
  1 sibling, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-25  2:37 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hi Junio,
>
> On Sun, 23 Apr 2017, Junio C Hamano wrote:
>
>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
>> 
>> > diff --git a/date.c b/date.c
>> > index 92ab31aa441..75f6335cd09 100644
>> > --- a/date.c
>> > +++ b/date.c
>> > @@ -46,7 +46,10 @@ static time_t gm_time_t(timestamp_t time, int tz)
>> >  	minutes = tz < 0 ? -tz : tz;
>> >  	minutes = (minutes / 100)*60 + (minutes % 100);
>> >  	minutes = tz < 0 ? -minutes : minutes;
>> > -	return time + minutes * 60;
>> > +
>> > +	if (date_overflows(time + minutes * 60))
>> > +		die("Timestamp too large for this system: %"PRItime, time);
>> > +	return (time_t)time + minutes * 60;
>> >  }
>> 
>> All the other calls to date_overflows() take a variable that holds
>> timestamp_t and presumably they are checking for integer wraparound
>> when the values are computed, but this one is not.
>
> I was debating whether this extra check is necessary and had decided
> against it. Apparently I was wrong to do so.

Not so fast.  Apparently you had thought about it and I didn't think
about it as long as you did, hence I asked ...

>
>> Perhaps we want to make it a bit more careful here?  I wonder if
>> something like this is a good approach:

as a question, not a request to change things, and not a "do not
come back until you rewrite it this way" ;-).  Getting a question is
not a sign that you were wrong at all.

If you thought it was not needed, and if you can clearly explain to
future readers of "git log" why it is the case, then I am prefectly
fine without an extra check.

In a response to another message, you seem to indicate that existing
code does not do any range checking to make sure ulong (the current
type the existing code uses) does not wrap around and you kept that
"property" of the code in this series (i.e.  date_overflows() range
check was merely for downcasting timestamp_t to time_t before giving
the result to the system functions).  If that is the case, then NOT
checking for integer wraparound in "+ minutes*60" computation IS the
consistent thing to do within your series, I would think.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 7/9] Abort if the system time cannot handle one of our timestamps
  2017-04-24 13:57           ` Johannes Schindelin
  2017-04-25  2:37             ` Junio C Hamano
@ 2017-04-25  3:56             ` Junio C Hamano
  1 sibling, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-25  3:56 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> The code would also be incorrect, as the `minutes` variable can be
> negative, which the `unsigned_add_overflows()` macro cannot handle (it
> would report very, very false positives, and it would hurt you more than
> me because I live East of Greenwich).

Yes and no.  If we were to care about integer wraparound to do this
check, we'd still have to worry about negative offset applied to a
time very close to the epoch that turns into a timestamp far far in
the future (unsigned_sub_underflows, that is).


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-24 10:28           ` Johannes Schindelin
@ 2017-04-25  3:59             ` Junio C Hamano
  2017-04-25 20:10               ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-04-25  3:59 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> Should we at least clamp in date_overflows() so that large values
>> representable with timestamp_t that will become unrepresentable when
>> we start allowing negative timestamps would be rejected?  That way
>> we won't have to hear complaints from the people who used timestamps
>> far in the future that we regressed the implementation for them by
>> halving the possible timestamp range.
>
> Please note that the date_overflows() command only tests when we are about
> to call system functions. I do not think that it does what you think it
> does (namely, validate timestamps when they enter Git).

OK, then please read my question without date_overflows(), that is,
"should we at least clamp the values with some new mechanism to
leave the door open for supporting times before the epoch, even if
(and especially if) we leave the use of signed integral type for
timestamps out of the scope of this series?"


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 4/8] Specify explicitly where we parse timestamps
  2017-04-24 13:58         ` [PATCH v5 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-25  5:59           ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-25  5:59 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

oJohannes Schindelin <johannes.schindelin@gmx.de> writes:

> diff --git a/pretty.c b/pretty.c
> index d0f86f5d85c..24fb0c79062 100644
> --- a/pretty.c
> +++ b/pretty.c
> @@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
>  	long tz = 0;
>  
>  	if (ident->date_begin && ident->date_end)
> -		date = strtoul(ident->date_begin, NULL, 10);
> +		date = parse_timestamp(ident->date_begin, NULL, 10);
>  	if (date_overflows(date))
>  		date = 0;
>  	else {

Good.  We'd need to choose a different sentinel when we allow signed
timestamps, but that is outside the scope of this series.  

It would not hurt if we used a symbolic constant instead of 0 here
to save duplicated effort for future developers, though, and it is
in line with the spirit of this exact patch where it uses a more
specific function name to differentiate strtoul() used for times
from other uses of the same function.

In any case, I think another change to this section of the code made
later in another patch was where t4121 was broken in the previous
round, and it is good to see the breakage go.  As Peff was hinting,
we might want to revisit "should we substitute with a sentinel, or
make this a die?" decision, but discussing it is totally outside the
scope of this series.


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-25  3:59             ` Junio C Hamano
@ 2017-04-25 20:10               ` Johannes Schindelin
  2017-04-26  1:52                 ` Junio C Hamano
  0 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-25 20:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen

[-- Attachment #1: Type: text/plain, Size: 1422 bytes --]

Hi Junio,

On Mon, 24 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> Should we at least clamp in date_overflows() so that large values
> >> representable with timestamp_t that will become unrepresentable when
> >> we start allowing negative timestamps would be rejected?  That way we
> >> won't have to hear complaints from the people who used timestamps far
> >> in the future that we regressed the implementation for them by
> >> halving the possible timestamp range.
> >
> > Please note that the date_overflows() command only tests when we are
> > about to call system functions. I do not think that it does what you
> > think it does (namely, validate timestamps when they enter Git).
> 
> OK, then please read my question without date_overflows(), that is,
> "should we at least clamp the values with some new mechanism to leave
> the door open for supporting times before the epoch, even if (and
> especially if) we leave the use of signed integral type for timestamps
> out of the scope of this series?"

You are asking the wrong person because I do not care about timestamps
dating before the dawn of Unix.

In any case, it is a question unrelated to the work I performed in this
patch series: the raison d'être of these patches is to allow timestamps to
refer to dates that are currently insanely far in the future.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 0/9] Introduce timestamp_t for timestamps
  2017-04-24 11:37         ` Jeff King
@ 2017-04-25 20:13           ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-25 20:13 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, Torsten Bögershausen, René Scharfe

Hi Peff,

On Mon, 24 Apr 2017, Jeff King wrote:

> On Sun, Apr 23, 2017 at 08:29:11PM -0700, Junio C Hamano wrote:
> 
> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> > 
> > > Changes since v3:
> > >
> > > - fixed the fix in archive-zip.c that tried to report a too large
> > >   timestamp (and would have reported the uninitialized time_t instead)
> > >
> > > - adjusted the so-far forgotten each_reflog() function (that was
> > >   introduced after v1, in 80f2a6097c4 (t/helper: add test-ref-store to
> > >   test ref-store functions, 2017-03-26)) to use timestamp_t and PRItime,
> > >   too
> > >
> > > - removed the date_overflows() check from time_to_tm(), as it calls
> > >   gm_time_t() which already performs that check
> > >
> > > - the date_overflows() check in show_ident_date() was removed, as we do
> > >   not know at that point yet whether we use the system functions to
> > >   render the date or not (and there would not be a problem in the latter
> > >   case)
> > 
> > Assuming that the list consensus is to go with a separate
> > timestamp_t (for that added Cc for those whose comments I saw in an
> > earlier round), the patches looked mostly good (I didn't read with
> > fine toothed comb the largest one 6/8 to see if there were
> > inadvertent or missed conversions from ulong to timestamp_t,
> > though), modulo a few minor "huh?" comments I sent separately.
> > 
> > Will queue; thanks.
> 
> Sorry, I haven't read the series carefully yet (but from a skim I'm
> happy with the overall direction). It does seem to cause failures in
> t4212, though. For example:
> 
>   expecting success: 
>   	commit=$(munge_author_date HEAD 18446744073709551617) &&
>   	echo "Thu Jan 1 00:00:00 1970 +0000" >expect &&
>   	git log -1 --format=%ad $commit >actual &&
>   	test_cmp expect actual
>   
>   fatal: Timestamp too large for this system: 18446744073709551615
>   not ok 7 - date parser recognizes integer overflow
> 
> We used to convert overflows into a sentinel time, but now we die. I
> originally chose the sentinel approach because it lets you use the tools
> to examine and recover from the broken state. I could be convinced that
> dying is better, but clearly we'd need to at least update the tests.

Sorry, I dropped this patch in v5 because I did not want to fight this
battle.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 0/8] Introduce timestamp_t for timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (7 preceding siblings ...)
  2017-04-24 13:58         ` [PATCH v5 8/8] Use uintmax_t for " Johannes Schindelin
@ 2017-04-25 21:54         ` " René Scharfe
  2017-04-25 22:22           ` Johannes Schindelin
  2017-04-26  1:56           ` Junio C Hamano
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
  9 siblings, 2 replies; 113+ messages in thread
From: René Scharfe @ 2017-04-25 21:54 UTC (permalink / raw)
  To: Johannes Schindelin, git; +Cc: Junio C Hamano, Torsten Bögershausen

Am 24.04.2017 um 15:57 schrieb Johannes Schindelin:
> Git v2.9.2 was released in a hurry to accomodate for platforms like
> Windows, where the `unsigned long` data type is 32-bit even for 64-bit
> setups.
> 
> The quick fix was to simply disable all the testing with "absurd" future
> dates.
> 
> However, we can do much better than that, as we already make use of
> 64-bit data types internally. There is no good reason why we should not
> use the same for timestamps. Hence, let's use uintmax_t for timestamps.
> 
> Note: while the `time_t` data type exists and is meant to be used for
> timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
> used `time_t` for that reason, but it came with a few serious downsides:
> as `time_t` can be signed (and indeed, on Windows it is an int64_t),
> Git's expectation that 0 is the minimal value does no longer hold true,
> introducing its own set of interesting challenges. Besides, if we *can*
> handle far in the future timestamps (except for formatting them using
> the system libraries), it is more consistent to do so.

time_t is signed on Linux and BSDs as well.

Using an unsigned type gives us the ability to represent times beyond
the 292 billion years in the future that int64_t would give us, but
prevents recording events that occurred before the Epoch.  That doesn't
sound like a good deal to me -- storing historical works (e.g. law
texts) with real time stamps is probably more interesting than fixing
the year 292277026596 problem within this decade.

> The upside of using `uintmax_t` for timestamps is that we do a much
> better job to support far in the future timestamps across all platforms,
> including 32-bit ones. The downside is that those platforms that use a
> 32-bit `time_t` will barf when parsing or formatting those timestamps.

IIUC this series has two aims: solving the year 2038 problem on 32-bit
Linux by replacing time_t (int32_t), and solving the year 2106 problem
on Windows by replacing unsigned long (uint32_t), right?  The latter one
sounds more interesting, because 32-bit platforms would still be unable
to fully use bigger time values as you wrote above.

Can we leave time_t alone and just do the part where you replace
unsigned long with timestamp_t defined as uint64_t?  That should already
help on Windows, correct?  When/if timestamp_t is later changed to a
signed type then we could easily convert the time_t cases to timestamp_t
as well, or the other way around.

> This iteration makes the date_overflows() check more stringent again.
> 
> It is arguably a bug to paper over too-large author/committer dates and
> to replace them with Jan 1 1970 without even telling the user that we do
> that, but this is the behavior that t4212 verifies, so I reinstated that
> behavior. The change in behavior was missed because of the missing
> unsigned_add_overflows() test.

I can't think of many ways to get future time stamps (broken clock,
broken CMOS battery, bit rot, time travel), so I wouldn't expect a
change towards better error reporting to affect a lot of users.  (Not
necessarily as part of this series, of course.)

> Changes since v4:
> 
> - in gm_time_t(), we now test specifically that the timezone adjustment
>    neither underflows nor overflows.
> 
> - the patch introduced in v4 that tried to defer the date_overflows()
>    check to gm_time_t() rather than replacing the ident timestamp by a 0
>    without any warning was dropped again: it broke t4212.
> 
> 
> Johannes Schindelin (8):
>    ref-filter: avoid using `unsigned long` for catch-all data type
>    t0006 & t5000: prepare for 64-bit timestamps
>    t0006 & t5000: skip "far in the future" test when time_t is too
>      limited
>    Specify explicitly where we parse timestamps
>    Introduce a new "printf format" for timestamps
>    Introduce a new data type for timestamps
>    Abort if the system time cannot handle one of our timestamps
>    Use uintmax_t for timestamps
> 
>   Documentation/technical/api-parse-options.txt |   8 +-
>   archive-tar.c                                 |   5 +-
>   archive-zip.c                                 |  12 ++-
>   archive.h                                     |   2 +-
>   builtin/am.c                                  |   4 +-
>   builtin/blame.c                               |  14 ++--
>   builtin/fsck.c                                |   6 +-
>   builtin/gc.c                                  |   2 +-
>   builtin/log.c                                 |   4 +-
>   builtin/merge-base.c                          |   2 +-
>   builtin/name-rev.c                            |   6 +-
>   builtin/pack-objects.c                        |   4 +-
>   builtin/prune.c                               |   4 +-
>   builtin/receive-pack.c                        |  14 ++--
>   builtin/reflog.c                              |  24 +++---
>   builtin/rev-list.c                            |   2 +-
>   builtin/rev-parse.c                           |   2 +-
>   builtin/show-branch.c                         |   4 +-
>   builtin/worktree.c                            |   4 +-
>   bundle.c                                      |   4 +-
>   cache.h                                       |  14 ++--
>   commit.c                                      |  18 ++--
>   commit.h                                      |   2 +-
>   config.c                                      |   2 +-
>   credential-cache--daemon.c                    |  12 +--
>   date.c                                        | 113 ++++++++++++++------------
>   fetch-pack.c                                  |   8 +-
>   fsck.c                                        |   2 +-
>   git-compat-util.h                             |   9 ++
>   http-backend.c                                |   4 +-
>   parse-options-cb.c                            |   4 +-
>   pretty.c                                      |   4 +-
>   reachable.c                                   |   9 +-
>   reachable.h                                   |   4 +-
>   ref-filter.c                                  |  22 ++---
>   reflog-walk.c                                 |   8 +-
>   refs.c                                        |  14 ++--
>   refs.h                                        |   8 +-
>   refs/files-backend.c                          |   8 +-
>   revision.c                                    |   6 +-
>   revision.h                                    |   4 +-
>   sha1_name.c                                   |   6 +-
>   t/helper/test-date.c                          |  18 ++--
>   t/helper/test-parse-options.c                 |   4 +-
>   t/helper/test-ref-store.c                     |   4 +-
>   t/t0006-date.sh                               |   4 +-
>   t/t5000-tar-tree.sh                           |   6 +-
>   t/test-lib.sh                                 |   3 +
>   tag.c                                         |   6 +-
>   tag.h                                         |   2 +-
>   upload-pack.c                                 |   8 +-
>   vcs-svn/fast_export.c                         |   8 +-
>   vcs-svn/fast_export.h                         |   4 +-
>   vcs-svn/svndump.c                             |   2 +-
>   wt-status.c                                   |   2 +-
>   55 files changed, 260 insertions(+), 219 deletions(-)

How did you find all the pieces of code that need to be touched?  Is
there a regex or something that can be used to spot new such places
that sneak in, e.g. through in-flight merges?

> base-commit: e2cb6ab84c94f147f1259260961513b40c36108a
> Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v5
> Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v5
> 
> Interdiff vs v4:
> 
>   diff --git a/date.c b/date.c
>   index 75f6335cd09..63fa99685e2 100644
>   --- a/date.c
>   +++ b/date.c
>   @@ -47,9 +47,16 @@ static time_t gm_time_t(timestamp_t time, int tz)
>    	minutes = (minutes / 100)*60 + (minutes % 100);
>    	minutes = tz < 0 ? -minutes : minutes;
>    
>   -	if (date_overflows(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 + minutes * 60;
>   +	return (time_t)time;
>    }
>    
>    /*
>   diff --git a/pretty.c b/pretty.c
>   index 35fd290096a..587d48371b0 100644
>   --- a/pretty.c
>   +++ b/pretty.c
>   @@ -410,10 +410,14 @@ const char *show_ident_date(const struct ident_split *ident,
>    
>    	if (ident->date_begin && ident->date_end)
>    		date = parse_timestamp(ident->date_begin, NULL, 10);
>   -	if (ident->tz_begin && ident->tz_end)
>   -		tz = strtol(ident->tz_begin, NULL, 10);
>   -	if (tz >= INT_MAX || tz <= INT_MIN)
>   -		tz = 0;
>   +	if (date_overflows(date))
>   +		date = 0;
>   +	else {
>   +		if (ident->tz_begin && ident->tz_end)
>   +			tz = strtol(ident->tz_begin, NULL, 10);
>   +		if (tz >= INT_MAX || tz <= INT_MIN)
>   +			tz = 0;
>   +	}
>    	return show_date(date, tz, mode);
>    }
>    
> 

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 0/8] Introduce timestamp_t for timestamps
  2017-04-25 21:54         ` [PATCH v5 0/8] Introduce timestamp_t " René Scharfe
@ 2017-04-25 22:22           ` Johannes Schindelin
  2017-04-26 22:09             ` René Scharfe
  2017-04-26  1:56           ` Junio C Hamano
  1 sibling, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-25 22:22 UTC (permalink / raw)
  To: René Scharfe; +Cc: git, Junio C Hamano, Torsten Bögershausen

[-- Attachment #1: Type: text/plain, Size: 9771 bytes --]

Hi René,

On Tue, 25 Apr 2017, René Scharfe wrote:

> Am 24.04.2017 um 15:57 schrieb Johannes Schindelin:
> > Git v2.9.2 was released in a hurry to accomodate for platforms like
> > Windows, where the `unsigned long` data type is 32-bit even for 64-bit
> > setups.
> > 
> > The quick fix was to simply disable all the testing with "absurd"
> > future dates.
> > 
> > However, we can do much better than that, as we already make use of
> > 64-bit data types internally. There is no good reason why we should
> > not use the same for timestamps. Hence, let's use uintmax_t for
> > timestamps.
> > 
> > Note: while the `time_t` data type exists and is meant to be used for
> > timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
> > used `time_t` for that reason, but it came with a few serious
> > downsides: as `time_t` can be signed (and indeed, on Windows it is an
> > int64_t), Git's expectation that 0 is the minimal value does no longer
> > hold true, introducing its own set of interesting challenges. Besides,
> > if we *can* handle far in the future timestamps (except for formatting
> > them using the system libraries), it is more consistent to do so.
> 
> time_t is signed on Linux and BSDs as well.

s/is/happens to be/.

The point is: we must not rely on time_t to be signed just because it
*happens* to be the case on the setups to which we have access. We want
Git to be portable, not only "portable to our own setups".

> Using an unsigned type gives us the ability to represent times beyond
> the 292 billion years in the future that int64_t would give us, but
> prevents recording events that occurred before the Epoch.  That doesn't
> sound like a good deal to me -- storing historical works (e.g. law
> texts) with real time stamps is probably more interesting than fixing
> the year 292277026596 problem within this decade.

It sounds like a good deal to me, if the alternative is that *I* have to
patch Git's source code to support signed timestamps, when *I* am probably
the only one in this entire thread who does not even want them.

So could y'all please just stop talking about signed timestamps to me? I
feel that they are really, really start to irritate the hell out of me.

> > The upside of using `uintmax_t` for timestamps is that we do a much
> > better job to support far in the future timestamps across all
> > platforms, including 32-bit ones. The downside is that those platforms
> > that use a 32-bit `time_t` will barf when parsing or formatting those
> > timestamps.
> 
> IIUC this series has two aims: solving the year 2038 problem on 32-bit
> Linux by replacing time_t (int32_t), and solving the year 2106 problem
> on Windows by replacing unsigned long (uint32_t), right?

No. The series has one aim: to stop using `unsigned long` (which is
ill-defined to begin with) for timestamps.

> The latter one sounds more interesting, because 32-bit platforms would
> still be unable to fully use bigger time values as you wrote above.
> 
> Can we leave time_t alone and just do the part where you replace
> unsigned long with timestamp_t defined as uint64_t?  That should already
> help on Windows, correct?  When/if timestamp_t is later changed to a
> signed type then we could easily convert the time_t cases to timestamp_t
> as well, or the other way around.

This patch series leaves time_t alone already, so your wish has been
fulfilled preemptively.

> > This iteration makes the date_overflows() check more stringent again.
> > 
> > It is arguably a bug to paper over too-large author/committer dates and
> > to replace them with Jan 1 1970 without even telling the user that we do
> > that, but this is the behavior that t4212 verifies, so I reinstated that
> > behavior. The change in behavior was missed because of the missing
> > unsigned_add_overflows() test.
> 
> I can't think of many ways to get future time stamps (broken clock,
> broken CMOS battery, bit rot, time travel), so I wouldn't expect a
> change towards better error reporting to affect a lot of users.  (Not
> necessarily as part of this series, of course.)

If you want to suggest that we should stop verifying overflows when a
complex reasoning can prove that the overflow is not happening in a
billion years, I disagree. Not only is it unnecessarily time-consuming to
ask readers to perform the complex reasoning, and not only is there enough
room for bugs to hide in plain sight (because of the complexity), it also
makes the same code harder to reuse in other software where a different
timestamp data type was chosen (or inherited from previous Git versions).

I'd much rather have easy-to-reason code that does not cause head
scratching (like the "why do we ignore a too large timestamp?" triggering
`if (date_overflows(date)) date = 0;`) than pretending to be smart and
clever and make everybody else feel stupid by forcing them through hoops
of thinking bubbles until they also reached the conclusion that this
actually won't happen. Unless there is a bug in the code.

> >   Documentation/technical/api-parse-options.txt |   8 +-
> >   archive-tar.c                                 |   5 +-
> >   archive-zip.c                                 |  12 ++-
> >   archive.h                                     |   2 +-
> >   builtin/am.c                                  |   4 +-
> >   builtin/blame.c                               |  14 ++--
> >   builtin/fsck.c                                |   6 +-
> >   builtin/gc.c                                  |   2 +-
> >   builtin/log.c                                 |   4 +-
> >   builtin/merge-base.c                          |   2 +-
> >   builtin/name-rev.c                            |   6 +-
> >   builtin/pack-objects.c                        |   4 +-
> >   builtin/prune.c                               |   4 +-
> >   builtin/receive-pack.c                        |  14 ++--
> >   builtin/reflog.c                              |  24 +++---
> >   builtin/rev-list.c                            |   2 +-
> >   builtin/rev-parse.c                           |   2 +-
> >   builtin/show-branch.c                         |   4 +-
> >   builtin/worktree.c                            |   4 +-
> >   bundle.c                                      |   4 +-
> >   cache.h                                       |  14 ++--
> >   commit.c                                      |  18 ++--
> >   commit.h                                      |   2 +-
> >   config.c                                      |   2 +-
> >   credential-cache--daemon.c                    |  12 +--
> >   date.c                                        | 113
> >   ++++++++++++++------------
> >   fetch-pack.c                                  |   8 +-
> >   fsck.c                                        |   2 +-
> >   git-compat-util.h                             |   9 ++
> >   http-backend.c                                |   4 +-
> >   parse-options-cb.c                            |   4 +-
> >   pretty.c                                      |   4 +-
> >   reachable.c                                   |   9 +-
> >   reachable.h                                   |   4 +-
> >   ref-filter.c                                  |  22 ++---
> >   reflog-walk.c                                 |   8 +-
> >   refs.c                                        |  14 ++--
> >   refs.h                                        |   8 +-
> >   refs/files-backend.c                          |   8 +-
> >   revision.c                                    |   6 +-
> >   revision.h                                    |   4 +-
> >   sha1_name.c                                   |   6 +-
> >   t/helper/test-date.c                          |  18 ++--
> >   t/helper/test-parse-options.c                 |   4 +-
> >   t/helper/test-ref-store.c                     |   4 +-
> >   t/t0006-date.sh                               |   4 +-
> >   t/t5000-tar-tree.sh                           |   6 +-
> >   t/test-lib.sh                                 |   3 +
> >   tag.c                                         |   6 +-
> >   tag.h                                         |   2 +-
> >   upload-pack.c                                 |   8 +-
> >   vcs-svn/fast_export.c                         |   8 +-
> >   vcs-svn/fast_export.h                         |   4 +-
> >   vcs-svn/svndump.c                             |   2 +-
> >   wt-status.c                                   |   2 +-
> >   55 files changed, 260 insertions(+), 219 deletions(-)
> 
> How did you find all the pieces of code that need to be touched?

Pain and suffering.

Seriosly, for v1 of this patch series, I went painstakingly through `git
grep -Ovi "unsigned long"`. I determined for every single use of `unsigned
long` whether it referred to a timestamp and changed it accordingly.

In subsequent iterations, I went the cheaper route of compiling with
DEVELOPER=1 on Windows and once I even went through replacing the 64-bit
libraries and compiler/linker in my Linux VM with 32-bit ones to imitate
the 32-bit Linux Travis coordinate (because I failed to get the Docker
setup to run in the VM). These build runs identified new callers of
functions whose signature I had to change to avoid `unsigned long` for
timestamps.

This was no fun.

> Is there a regex or something that can be used to spot new such places
> that sneak in, e.g. through in-flight merges?

No, a regex would only work if we already had converted all `unsigned
long` uses to semantically meaningful data types.

Ciao,
Dscho

P.S.: Please remove the quoted interdiff when there is no reason to keep
it around.

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-25 20:10               ` Johannes Schindelin
@ 2017-04-26  1:52                 ` Junio C Hamano
  2017-04-26  3:45                   ` Junio C Hamano
  2017-04-26  9:32                   ` Johannes Schindelin
  0 siblings, 2 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-26  1:52 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> In any case, it is a question unrelated to the work I performed in this
> patch series: the raison d'être of these patches is to allow timestamps to
> refer to dates that are currently insanely far in the future.

Yes, but the job of the maintainer is to prevent narrow-focused
individual contributors from throwing us into a hole we cannot dig
out of by closing the door for plausible future enhancements.


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 0/8] Introduce timestamp_t for timestamps
  2017-04-25 21:54         ` [PATCH v5 0/8] Introduce timestamp_t " René Scharfe
  2017-04-25 22:22           ` Johannes Schindelin
@ 2017-04-26  1:56           ` Junio C Hamano
  1 sibling, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-26  1:56 UTC (permalink / raw)
  To: René Scharfe; +Cc: Johannes Schindelin, git, Torsten Bögershausen

René Scharfe <l.s.r@web.de> writes:

> I can't think of many ways to get future time stamps (broken clock,
> broken CMOS battery, bit rot, time travel), so I wouldn't expect a
> change towards better error reporting to affect a lot of users.  (Not
> necessarily as part of this series, of course.)

Better error reporting is one thing, but we do not want to kill "git
log" in the middle by calling die().  

Dropping the patch v4 9/9 that caused us to call die() was a good
thing to do within the scope of this series, I would think.


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-26  1:52                 ` Junio C Hamano
@ 2017-04-26  3:45                   ` Junio C Hamano
  2017-04-26  9:32                   ` Johannes Schindelin
  1 sibling, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-26  3:45 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Torsten Bögershausen

Junio C Hamano <gitster@pobox.com> writes:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
>> In any case, it is a question unrelated to the work I performed in this
>> patch series: the raison d'être of these patches is to allow timestamps to
>> refer to dates that are currently insanely far in the future.
>
> Yes, but the job of the maintainer is to prevent narrow-focused
> individual contributors from throwing us into a hole we cannot dig
> out of by closing the door for plausible future enhancements.

Having said that, IIRC, this series does not tighten the existing
code to specifically check for integer wrap-around anyway, so in a
sense, users who use a timestamp that is in an insanely distant
future is already accepting the risk of getting broken in the
future, so my answer to the question I asked is "it would be extra
nice to future-proof people's data, but not doing anything is
probably OK---at least we is not making things worse."


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-26  1:52                 ` Junio C Hamano
  2017-04-26  3:45                   ` Junio C Hamano
@ 2017-04-26  9:32                   ` Johannes Schindelin
  2017-04-26 13:18                     ` Junio C Hamano
  1 sibling, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26  9:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Torsten Bögershausen

[-- Attachment #1: Type: text/plain, Size: 758 bytes --]

Hi Junio,

On Tue, 25 Apr 2017, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > In any case, it is a question unrelated to the work I performed in
> > this patch series: the raison d'être of these patches is to allow
> > timestamps to refer to dates that are currently insanely far in the
> > future.
> 
> Yes, but the job of the maintainer is to prevent narrow-focused
> individual contributors from throwing us into a hole we cannot dig out
> of by closing the door for plausible future enhancements.

You make it sound as if I made the code stricter in any way, or even
introduced a check that was not there before.

As I did no such thing, you may want to reword your statement?

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v4 8/9] Use uintmax_t for timestamps
  2017-04-26  9:32                   ` Johannes Schindelin
@ 2017-04-26 13:18                     ` Junio C Hamano
  0 siblings, 0 replies; 113+ messages in thread
From: Junio C Hamano @ 2017-04-26 13:18 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Torsten Bögershausen

(in gmail so pardon top posting)

As I said, this series does *not* tighten the existing code anyway, so
it is not like something that used to be accepted are now getting rejected.

Happy?

What I was worried about is actually the other way around, though.


On Wed, Apr 26, 2017 at 6:32 PM, Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> Hi Junio,
>
> On Tue, 25 Apr 2017, Junio C Hamano wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>>
>> > In any case, it is a question unrelated to the work I performed in
>> > this patch series: the raison d'être of these patches is to allow
>> > timestamps to refer to dates that are currently insanely far in the
>> > future.
>>
>> Yes, but the job of the maintainer is to prevent narrow-focused
>> individual contributors from throwing us into a hole we cannot dig out
>> of by closing the door for plausible future enhancements.
>
> You make it sound as if I made the code stricter in any way, or even
> introduced a check that was not there before.
>
> As I did no such thing, you may want to reword your statement?
>
> Ciao,
> Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 8/8] Use uintmax_t for timestamps
  2017-04-24 13:58         ` [PATCH v5 8/8] Use uintmax_t for " Johannes Schindelin
@ 2017-04-26 16:36           ` Johannes Sixt
  2017-04-26 19:09             ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Johannes Sixt @ 2017-04-26 16:36 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano, Torsten Bögershausen

Am 24.04.2017 um 15:58 schrieb Johannes Schindelin:
>  #define PRIo32 "o"
>  #endif
>  
> -typedef unsigned long timestamp_t;
> -#define PRItime "lu"
> -#define parse_timestamp strtoul
> +typedef uintmax_t timestamp_t;
> +#define PRItime PRIuMAX
> +#define parse_timestamp strtoumax
> +#ifdef ULLONG_MAX
> +#define TIME_MAX ULLONG_MAX
> +#else
>  #define TIME_MAX ULONG_MAX
> +#endif
>  
>  #ifndef PATH_SEP
>  #define PATH_SEP ':'
> 

I think you should squash in this:

diff --git a/git-compat-util.h b/git-compat-util.h
index 26d2643667..b5f4a7bb2f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -322,11 +322,7 @@ extern char *gitdirname(char *);
 typedef uintmax_t timestamp_t;
 #define PRItime PRIuMAX
 #define parse_timestamp strtoumax
-#ifdef ULLONG_MAX
-#define TIME_MAX ULLONG_MAX
-#else
-#define TIME_MAX ULONG_MAX
-#endif
+#define TIME_MAX UINTMAX_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'

UINTMAX_MAX is already used git-compat-util.h

-- Hannes


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 6/8] Introduce a new data type for timestamps
  2017-04-24 13:58         ` [PATCH v5 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-04-26 16:43           ` Johannes Sixt
  2017-04-26 19:18             ` Johannes Schindelin
  2017-04-26 22:32           ` René Scharfe
  1 sibling, 1 reply; 113+ messages in thread
From: Johannes Sixt @ 2017-04-26 16:43 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano, Torsten Bögershausen

Am 24.04.2017 um 15:58 schrieb Johannes Schindelin:
> diff --git a/archive-tar.c b/archive-tar.c
> index 380e3aedd23..695339a2369 100644
> --- a/archive-tar.c
> +++ b/archive-tar.c
> @@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
>   */
>  #if ULONG_MAX == 0xFFFFFFFF
>  #define USTAR_MAX_SIZE ULONG_MAX
> -#define USTAR_MAX_MTIME ULONG_MAX
>  #else
>  #define USTAR_MAX_SIZE 077777777777UL
> +#endif

This part of the hunk is fine: if ULONG_MAX is not 2^32-1, then 2^33-1
will fit in a long.

> +#if TIME_MAX == 0xFFFFFFFF
> +#define USTAR_MAX_MTIME TIME_MAX
> +#else
>  #define USTAR_MAX_MTIME 077777777777UL
>  #endif

But this is not: just because TIME_MAX is not 32 bits, does not mean that
long is also more than 32 bits. We need this:

diff --git a/archive-tar.c b/archive-tar.c
index aadd5865f6..b5d6ce27d3 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -33,7 +33,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
 #if TIME_MAX == 0xFFFFFFFF
 #define USTAR_MAX_MTIME TIME_MAX
 #else
-#define USTAR_MAX_MTIME 077777777777UL
+#define USTAR_MAX_MTIME 077777777777ULL
 #endif
 
 /* writes out the whole block, but only if it is full */


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 8/8] Use uintmax_t for timestamps
  2017-04-26 16:36           ` Johannes Sixt
@ 2017-04-26 19:09             ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:09 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Torsten Bögershausen

Hi Hannes,

On Wed, 26 Apr 2017, Johannes Sixt wrote:

> Am 24.04.2017 um 15:58 schrieb Johannes Schindelin:
> >  #define PRIo32 "o"
> >  #endif
> >  
> > -typedef unsigned long timestamp_t;
> > -#define PRItime "lu"
> > -#define parse_timestamp strtoul
> > +typedef uintmax_t timestamp_t;
> > +#define PRItime PRIuMAX
> > +#define parse_timestamp strtoumax
> > +#ifdef ULLONG_MAX
> > +#define TIME_MAX ULLONG_MAX
> > +#else
> >  #define TIME_MAX ULONG_MAX
> > +#endif
> >  
> >  #ifndef PATH_SEP
> >  #define PATH_SEP ':'
> > 
> 
> I think you should squash in this:
> 
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 26d2643667..b5f4a7bb2f 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -322,11 +322,7 @@ extern char *gitdirname(char *);
>  typedef uintmax_t timestamp_t;
>  #define PRItime PRIuMAX
>  #define parse_timestamp strtoumax
> -#ifdef ULLONG_MAX
> -#define TIME_MAX ULLONG_MAX
> -#else
> -#define TIME_MAX ULONG_MAX
> -#endif
> +#define TIME_MAX UINTMAX_MAX
>  
>  #ifndef PATH_SEP
>  #define PATH_SEP ':'
> 
> UINTMAX_MAX is already used git-compat-util.h

Good point. I very much appreciate your fresh eyes here, as I simply
overlooked this in the humongous task to convert v1 to use uintmax_t
instead of time_t.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 6/8] Introduce a new data type for timestamps
  2017-04-26 16:43           ` Johannes Sixt
@ 2017-04-26 19:18             ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:18 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Torsten Bögershausen

Hi Hannes,

On Wed, 26 Apr 2017, Johannes Sixt wrote:

> Am 24.04.2017 um 15:58 schrieb Johannes Schindelin:
> > diff --git a/archive-tar.c b/archive-tar.c
> > index 380e3aedd23..695339a2369 100644
> > --- a/archive-tar.c
> > +++ b/archive-tar.c
> > @@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
> >   */
> >  #if ULONG_MAX == 0xFFFFFFFF
> >  #define USTAR_MAX_SIZE ULONG_MAX
> > -#define USTAR_MAX_MTIME ULONG_MAX
> >  #else
> >  #define USTAR_MAX_SIZE 077777777777UL
> > +#endif
> 
> This part of the hunk is fine: if ULONG_MAX is not 2^32-1, then 2^33-1
> will fit in a long.
> 
> > +#if TIME_MAX == 0xFFFFFFFF
> > +#define USTAR_MAX_MTIME TIME_MAX
> > +#else
> >  #define USTAR_MAX_MTIME 077777777777UL
> >  #endif
> 
> But this is not: just because TIME_MAX is not 32 bits, does not mean that
> long is also more than 32 bits. We need this:
> 
> diff --git a/archive-tar.c b/archive-tar.c
> index aadd5865f6..b5d6ce27d3 100644
> --- a/archive-tar.c
> +++ b/archive-tar.c
> @@ -33,7 +33,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
>  #if TIME_MAX == 0xFFFFFFFF
>  #define USTAR_MAX_MTIME TIME_MAX
>  #else
> -#define USTAR_MAX_MTIME 077777777777UL
> +#define USTAR_MAX_MTIME 077777777777ULL
>  #endif
>  
>  /* writes out the whole block, but only if it is full */

Right! Thank you, v6 coming up,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 0/8] Introduce timestamp_t for timestamps
  2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
                           ` (8 preceding siblings ...)
  2017-04-25 21:54         ` [PATCH v5 0/8] Introduce timestamp_t " René Scharfe
@ 2017-04-26 19:20         ` " Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
                             ` (7 more replies)
  9 siblings, 8 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:20 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Git v2.9.2 was released in a hurry to accomodate for platforms like
Windows, where the `unsigned long` data type is 32-bit even for 64-bit
setups.

The quick fix was to simply disable all the testing with "absurd" future
dates.

However, we can do much better than that, as we already make use of
64-bit data types internally. There is no good reason why we should not
use the same for timestamps. Hence, let's use uintmax_t for timestamps.

Note: while the `time_t` data type exists and is meant to be used for
timestamps, on 32-bit Linux it is *still* 32-bit. An earlier iteration
used `time_t` for that reason, but it came with a few serious downsides:
as `time_t` can be signed (and indeed, on Windows it is an int64_t),
Git's expectation that 0 is the minimal value would no longer hold true,
introducing its own set of interesting challenges. Besides, if we *can*
handle far in the future timestamps (except for formatting them using
the system libraries), it is more consistent to do so.

The upside of using `uintmax_t` for timestamps is that we do a much
better job to support far in the future timestamps across all platforms,
including 32-bit ones. The downside is that those platforms that use a
32-bit `time_t` will barf when parsing or formatting those timestamps.

Hannes Sixt found two more improvements which I incorporated into this
iteration.

Changes since v5:

- use UINTMAX_MAX as TIME_MAX instead of rolling funny ULLONG_MAX games
  (thanks Hannes Sixt)

- mark the large constant in archive-tar.c as unsigned long long, as we
  cannot know that unsigned long is large enough (again, thanks Hannes
  Sixt)


Johannes Schindelin (8):
  ref-filter: avoid using `unsigned long` for catch-all data type
  t0006 & t5000: prepare for 64-bit timestamps
  t0006 & t5000: skip "far in the future" test when time_t is too
    limited
  Specify explicitly where we parse timestamps
  Introduce a new "printf format" for timestamps
  Introduce a new data type for timestamps
  Abort if the system time cannot handle one of our timestamps
  Use uintmax_t for timestamps

 Documentation/technical/api-parse-options.txt |   8 +-
 archive-tar.c                                 |   7 +-
 archive-zip.c                                 |  12 ++-
 archive.h                                     |   2 +-
 builtin/am.c                                  |   4 +-
 builtin/blame.c                               |  14 ++--
 builtin/fsck.c                                |   6 +-
 builtin/gc.c                                  |   2 +-
 builtin/log.c                                 |   4 +-
 builtin/merge-base.c                          |   2 +-
 builtin/name-rev.c                            |   6 +-
 builtin/pack-objects.c                        |   4 +-
 builtin/prune.c                               |   4 +-
 builtin/receive-pack.c                        |  14 ++--
 builtin/reflog.c                              |  24 +++---
 builtin/rev-list.c                            |   2 +-
 builtin/rev-parse.c                           |   2 +-
 builtin/show-branch.c                         |   4 +-
 builtin/worktree.c                            |   4 +-
 bundle.c                                      |   4 +-
 cache.h                                       |  14 ++--
 commit.c                                      |  18 ++--
 commit.h                                      |   2 +-
 config.c                                      |   2 +-
 credential-cache--daemon.c                    |  12 +--
 date.c                                        | 113 ++++++++++++++------------
 fetch-pack.c                                  |   8 +-
 fsck.c                                        |   2 +-
 git-compat-util.h                             |   5 ++
 http-backend.c                                |   4 +-
 parse-options-cb.c                            |   4 +-
 pretty.c                                      |   4 +-
 reachable.c                                   |   9 +-
 reachable.h                                   |   4 +-
 ref-filter.c                                  |  22 ++---
 reflog-walk.c                                 |   8 +-
 refs.c                                        |  14 ++--
 refs.h                                        |   8 +-
 refs/files-backend.c                          |   8 +-
 revision.c                                    |   6 +-
 revision.h                                    |   4 +-
 sha1_name.c                                   |   6 +-
 t/helper/test-date.c                          |  18 ++--
 t/helper/test-parse-options.c                 |   4 +-
 t/helper/test-ref-store.c                     |   4 +-
 t/t0006-date.sh                               |   4 +-
 t/t5000-tar-tree.sh                           |   6 +-
 t/test-lib.sh                                 |   3 +
 tag.c                                         |   6 +-
 tag.h                                         |   2 +-
 upload-pack.c                                 |   8 +-
 vcs-svn/fast_export.c                         |   8 +-
 vcs-svn/fast_export.h                         |   4 +-
 vcs-svn/svndump.c                             |   2 +-
 wt-status.c                                   |   2 +-
 55 files changed, 257 insertions(+), 220 deletions(-)


base-commit: e2cb6ab84c94f147f1259260961513b40c36108a
Published-As: https://github.com/dscho/git/releases/tag/time_t-may-be-int64-v6
Fetch-It-Via: git fetch https://github.com/dscho/git time_t-may-be-int64-v6

Interdiff vs v5:

 diff --git a/archive-tar.c b/archive-tar.c
 index 695339a2369..319a5b1c7cd 100644
 --- a/archive-tar.c
 +++ b/archive-tar.c
 @@ -28,7 +28,7 @@ static int write_tar_filter_archive(const struct archiver *ar,
  #if ULONG_MAX == 0xFFFFFFFF
  #define USTAR_MAX_SIZE ULONG_MAX
  #else
 -#define USTAR_MAX_SIZE 077777777777UL
 +#define USTAR_MAX_SIZE 077777777777ULL
  #endif
  #if TIME_MAX == 0xFFFFFFFF
  #define USTAR_MAX_MTIME TIME_MAX
 diff --git a/git-compat-util.h b/git-compat-util.h
 index b66995685af..f366180f4b9 100644
 --- a/git-compat-util.h
 +++ b/git-compat-util.h
 @@ -322,11 +322,7 @@ extern char *gitdirname(char *);
  typedef uintmax_t timestamp_t;
  #define PRItime PRIuMAX
  #define parse_timestamp strtoumax
 -#ifdef ULLONG_MAX
 -#define TIME_MAX ULLONG_MAX
 -#else
 -#define TIME_MAX ULONG_MAX
 -#endif
 +#define TIME_MAX UINTMAX_MAX
  
  #ifndef PATH_SEP
  #define PATH_SEP ':'

-- 
2.12.2.windows.2.406.gd14a8f8640f


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 1/8] ref-filter: avoid using `unsigned long` for catch-all data type
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
@ 2017-04-26 19:26           ` Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
                             ` (6 subsequent siblings)
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:26 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

In its `atom_value` struct, the ref-filter source code wants to store
different values in a field called `ul` (for `unsigned long`), e.g.
timestamps.

However, as we are about to switch the data type of timestamps away from
`unsigned long` (because it may be 32-bit even when `time_t` is 64-bit),
that data type is not large enough.

Simply change that field to use `uintmax_t` instead.

This patch is a bit larger than the mere change of the data type
because the field's name was tied to its data type, which has been fixed
at the same time.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 ref-filter.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 3a640448fd8..92871266001 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -351,7 +351,7 @@ struct ref_formatting_state {
 struct atom_value {
 	const char *s;
 	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
-	unsigned long ul; /* used for sorting when not FIELD_STR */
+	uintmax_t value; /* used for sorting when not FIELD_STR */
 	struct used_atom *atom;
 };
 
@@ -723,7 +723,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
 		if (!strcmp(name, "objecttype"))
 			v->s = typename(obj->type);
 		else if (!strcmp(name, "objectsize")) {
-			v->ul = sz;
+			v->value = sz;
 			v->s = xstrfmt("%lu", sz);
 		}
 		else if (deref)
@@ -770,8 +770,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
 			v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
 		}
 		else if (!strcmp(name, "numparent")) {
-			v->ul = commit_list_count(commit->parents);
-			v->s = xstrfmt("%lu", v->ul);
+			v->value = commit_list_count(commit->parents);
+			v->s = xstrfmt("%lu", (unsigned long)v->value);
 		}
 		else if (!strcmp(name, "parent")) {
 			struct commit_list *parents;
@@ -875,11 +875,11 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
 		goto bad;
 	v->s = xstrdup(show_date(timestamp, tz, &date_mode));
-	v->ul = timestamp;
+	v->value = timestamp;
 	return;
  bad:
 	v->s = "";
-	v->ul = 0;
+	v->value = 0;
 }
 
 /* See grab_values */
@@ -1941,9 +1941,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 	else if (cmp_type == FIELD_STR)
 		cmp = cmp_fn(va->s, vb->s);
 	else {
-		if (va->ul < vb->ul)
+		if (va->value < vb->value)
 			cmp = -1;
-		else if (va->ul == vb->ul)
+		else if (va->value == vb->value)
 			cmp = cmp_fn(a->refname, b->refname);
 		else
 			cmp = 1;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 2/8] t0006 & t5000: prepare for 64-bit timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
@ 2017-04-26 19:26           ` Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
                             ` (5 subsequent siblings)
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:26 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Git's source code refers to timestamps as unsigned longs. On 32-bit
platforms, as well as on Windows, unsigned long is not large enough to
capture dates that are "absurdly far in the future".

It is perfectly valid by the C standard, of course, for the `long` data
type to refer to 32-bit integers. That is why the `time_t` data type
exists: so that it can be 64-bit even if `long` is 32-bit. Git's source
code simply uses an incorrect data type for timestamps, is all.

The earlier quick fix 6b9c38e14cd (t0006: skip "far in the future" test
when unsigned long is not long enough, 2016-07-11) papered over this
issue simply by skipping the respective test cases on platforms where
they would fail due to the data type in use.

This quick fix, however, tests for *long* to be 64-bit or not. What we
need, though, is a test that says whether *whatever data type we use for
timestamps* is 64-bit or not.

The same quick fix was used to handle the similar problem where Git's
source code uses `unsigned long` to represent size, instead of `size_t`,
conflating the two issues.

So let's just add another prerequisite to test specifically whether
timestamps are represented by a 64-bit data type or not. Later, after we
switch to a larger data type, we can flip that prerequisite to test
`time_t` instead of `long`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 6 +++---
 t/test-lib.sh        | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5d..4727bea255c 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,8 @@ static const char *usage_msg = "\n"
 "  test-date relative [time_t]...\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
-"  test-date approxidate [date]...\n";
+"  test-date approxidate [date]...\n"
+"  test-date is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -93,6 +94,8 @@ int cmd_main(int argc, const char **argv)
 		parse_dates(argv+1, &now);
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
+	else if (!strcmp(*argv, "is64bit"))
+		return sizeof(unsigned long) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d7..9539b425ffb 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e40..997aa9dea28 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
 	test_cmp expect actual
 '
 
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
 	rm -f .git/index &&
 	echo content >file &&
 	git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
 		git commit -m "tempori parendum"
 '
 
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822d..beee1d847ff 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1164,3 +1164,5 @@ build_option () {
 test_lazy_prereq LONG_IS_64BIT '
 	test 8 -le "$(build_option sizeof-long)"
 '
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
@ 2017-04-26 19:26           ` Johannes Schindelin
  2017-04-26 19:26           ` [PATCH v6 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
                             ` (4 subsequent siblings)
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:26 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Git's source code refers to timestamps as unsigned long, which is
ill-defined, as there is no guarantee about the number of bits that
data type has.

In preparation of switching to another data type that is large enough
to hold "far in the future" dates, we need to prepare the t0006-date.sh
script for the case where we *still* cannot format those dates if the
system library uses 32-bit time_t.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 t/helper/test-date.c | 5 ++++-
 t/t0006-date.sh      | 4 ++--
 t/t5000-tar-tree.sh  | 2 +-
 t/test-lib.sh        | 1 +
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 4727bea255c..ac7c66c733b 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -5,7 +5,8 @@ static const char *usage_msg = "\n"
 "  test-date show:<format> [time_t]...\n"
 "  test-date parse [date]...\n"
 "  test-date approxidate [date]...\n"
-"  test-date is64bit\n";
+"  test-date is64bit\n"
+"  test-date time_t-is64bit\n";
 
 static void show_relative_dates(const char **argv, struct timeval *now)
 {
@@ -96,6 +97,8 @@ int cmd_main(int argc, const char **argv)
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
 		return sizeof(unsigned long) == 8 ? 0 : 1;
+	else if (!strcmp(*argv, "time_t-is64bit"))
+		return sizeof(time_t) == 8 ? 0 : 1;
 	else
 		usage(usage_msg);
 	return 0;
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index 9539b425ffb..42d4ea61ef5 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
 
 # arbitrary time absurdly far in the future
 FUTURE="5758122296 -0400"
-check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT
+check_show iso       "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
 
 check_parse() {
 	echo "$1 -> $2" >expect
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 997aa9dea28..fe2d4f15a73 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -402,7 +402,7 @@ test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
 	git archive HEAD >future.tar
 '
 
-test_expect_success TAR_HUGE,TIME_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
 	echo 4147 >expect &&
 	tar_info future.tar | cut -d" " -f2 >actual &&
 	test_cmp expect actual
diff --git a/t/test-lib.sh b/t/test-lib.sh
index beee1d847ff..8d25cb7c183 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1166,3 +1166,4 @@ test_lazy_prereq LONG_IS_64BIT '
 '
 
 test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 4/8] Specify explicitly where we parse timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
                             ` (2 preceding siblings ...)
  2017-04-26 19:26           ` [PATCH v6 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
@ 2017-04-26 19:26           ` Johannes Schindelin
  2017-04-26 19:29           ` [PATCH v6 5/8] Introduce a new "printf format" for " Johannes Schindelin
                             ` (3 subsequent siblings)
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:26 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Currently, Git's source code represents all timestamps as `unsigned
long`. In preparation for using a more appropriate data type, let's
introduce a symbol `parse_timestamp` (currently being defined to
`strtoul`) where appropriate, so that we can later easily switch to,
say, use `strtoull()` instead.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/am.c           | 2 +-
 builtin/receive-pack.c | 4 ++--
 bundle.c               | 2 +-
 commit.c               | 6 +++---
 date.c                 | 6 +++---
 fsck.c                 | 2 +-
 git-compat-util.h      | 2 ++
 pretty.c               | 2 +-
 ref-filter.c           | 2 +-
 refs/files-backend.c   | 2 +-
 t/helper/test-date.c   | 2 +-
 tag.c                  | 4 ++--
 upload-pack.c          | 2 +-
 13 files changed, 20 insertions(+), 18 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 805f56cec2f..ffb7a6355fb 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -886,7 +886,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 			char *end;
 
 			errno = 0;
-			timestamp = strtoul(str, &end, 10);
+			timestamp = parse_timestamp(str, &end, 10);
 			if (errno)
 				return error(_("invalid timestamp"));
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index f96834f42c9..d25a57931f8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -534,7 +534,7 @@ static const char *check_nonce(const char *buf, size_t len)
 		retval = NONCE_BAD;
 		goto leave;
 	}
-	stamp = strtoul(nonce, &bohmac, 10);
+	stamp = parse_timestamp(nonce, &bohmac, 10);
 	if (bohmac == nonce || bohmac[0] != '-') {
 		retval = NONCE_BAD;
 		goto leave;
@@ -552,7 +552,7 @@ static const char *check_nonce(const char *buf, size_t len)
 	 * would mean it was issued by another server with its clock
 	 * skewed in the future.
 	 */
-	ostamp = strtoul(push_cert_nonce, NULL, 10);
+	ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
 	nonce_stamp_slop = (long)ostamp - (long)stamp;
 
 	if (nonce_stamp_slop_limit &&
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0a..f43bfcf5ff3 100644
--- a/bundle.c
+++ b/bundle.c
@@ -227,7 +227,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 	if (!line++)
 		goto out;
-	date = strtoul(line, NULL, 10);
+	date = parse_timestamp(line, NULL, 10);
 	result = (revs->max_age == -1 || revs->max_age < date) &&
 		(revs->min_age == -1 || revs->min_age > date);
 out:
diff --git a/commit.c b/commit.c
index 73c78c2b80c..0d2d0fa1984 100644
--- a/commit.c
+++ b/commit.c
@@ -89,8 +89,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -607,7 +607,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	    !ident.date_begin || !ident.date_end)
 		goto fail_exit; /* malformed "author" line */
 
-	date = strtoul(ident.date_begin, &date_end, 10);
+	date = parse_timestamp(ident.date_begin, &date_end, 10);
 	if (date_end != ident.date_end)
 		goto fail_exit; /* malformed date */
 	*(author_date_slab_at(author_date, commit)) = date;
diff --git a/date.c b/date.c
index a996331f5b3..495c207c64f 100644
--- a/date.c
+++ b/date.c
@@ -510,7 +510,7 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
 	char *end;
 	unsigned long num;
 
-	num = strtoul(date, &end, 10);
+	num = parse_timestamp(date, &end, 10);
 
 	/*
 	 * Seconds since 1970? We trigger on that for any numbers with
@@ -658,7 +658,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
 
 	if (*date < '0' || '9' < *date)
 		return -1;
-	stamp = strtoul(date, &end, 10);
+	stamp = parse_timestamp(date, &end, 10);
 	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
 		return -1;
 	date = end + 2;
@@ -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 = strtoul(date, &end, 10);
+	unsigned long number = parse_timestamp(date, &end, 10);
 
 	switch (*end) {
 	case ':':
diff --git a/fsck.c b/fsck.c
index e6152e4e6d4..d589341cddf 100644
--- a/fsck.c
+++ b/fsck.c
@@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
 	p++;
 	if (*p == '0' && p[1] != ' ')
 		return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
-	if (date_overflows(strtoul(p, &end, 10)))
+	if (date_overflows(parse_timestamp(p, &end, 10)))
 		return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
 	if ((end == p || *end != ' '))
 		return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-compat-util.h b/git-compat-util.h
index bd04564a69a..d78cc36dd1a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,8 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define parse_timestamp strtoul
+
 #ifndef PATH_SEP
 #define PATH_SEP ':'
 #endif
diff --git a/pretty.c b/pretty.c
index d0f86f5d85c..24fb0c79062 100644
--- a/pretty.c
+++ b/pretty.c
@@ -409,7 +409,7 @@ const char *show_ident_date(const struct ident_split *ident,
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
-		date = strtoul(ident->date_begin, NULL, 10);
+		date = parse_timestamp(ident->date_begin, NULL, 10);
 	if (date_overflows(date))
 		date = 0;
 	else {
diff --git a/ref-filter.c b/ref-filter.c
index 92871266001..c7836ae07bd 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -868,7 +868,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 
 	if (!eoemail)
 		goto bad;
-	timestamp = strtoul(eoemail + 2, &zone, 10);
+	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
 	if (timestamp == ULONG_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index c9d900fd128..801d26468e0 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3247,7 +3247,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 	    parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 	    !(email_end = strchr(p, '>')) ||
 	    email_end[1] != ' ' ||
-	    !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+	    !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
 	    !message || message[0] != ' ' ||
 	    (message[1] != '+' && message[1] != '-') ||
 	    !isdigit(message[2]) || !isdigit(message[3]) ||
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index ac7c66c733b..52d1fc34454 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -34,7 +34,7 @@ static void show_dates(const char **argv, const char *format)
 		 * Do not use our normal timestamp parsing here, as the point
 		 * is to test the formatting code in isolation.
 		 */
-		t = strtol(*argv, &arg, 10);
+		t = parse_timestamp(*argv, &arg, 10);
 		while (*arg == ' ')
 			arg++;
 		tz = atoi(arg);
diff --git a/tag.c b/tag.c
index 243d1fdbbcb..9b6725e02c9 100644
--- a/tag.c
+++ b/tag.c
@@ -110,8 +110,8 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
 		/* nada */;
 	if (buf >= tail)
 		return 0;
-	/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-	return strtoul(dateptr, NULL, 10);
+	/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+	return parse_timestamp(dateptr, NULL, 10);
 }
 
 int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d6231..f17f4dd1233 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -775,7 +775,7 @@ static void receive_needs(void)
 		}
 		if (skip_prefix(line, "deepen-since ", &arg)) {
 			char *end = NULL;
-			deepen_since = strtoul(arg, &end, 0);
+			deepen_since = parse_timestamp(arg, &end, 0);
 			if (!end || *end || !deepen_since ||
 			    /* revisions.c's max_age -1 is special */
 			    deepen_since == -1)
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 5/8] Introduce a new "printf format" for timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
                             ` (3 preceding siblings ...)
  2017-04-26 19:26           ` [PATCH v6 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
@ 2017-04-26 19:29           ` " Johannes Schindelin
  2017-04-26 19:29           ` [PATCH v6 6/8] Introduce a new data type " Johannes Schindelin
                             ` (2 subsequent siblings)
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Currently, Git's source code treats all timestamps as if they were
unsigned longs. Therefore, it is okay to write "%lu" when printing them.

There is a substantial problem with that, though: at least on Windows,
time_t is *larger* than unsigned long, and hence we will want to switch
away from the ill-specified `unsigned long` data type.

So let's introduce the pseudo format "PRItime" (currently simply being
defined to "lu") to make it easier to change the data type used for
timestamps.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/blame.c               |  6 +++---
 builtin/fsck.c                |  2 +-
 builtin/log.c                 |  2 +-
 builtin/receive-pack.c        |  4 ++--
 builtin/rev-list.c            |  2 +-
 builtin/rev-parse.c           |  2 +-
 date.c                        | 26 +++++++++++++-------------
 fetch-pack.c                  |  2 +-
 git-compat-util.h             |  1 +
 refs/files-backend.c          |  2 +-
 t/helper/test-date.c          |  2 +-
 t/helper/test-parse-options.c |  2 +-
 t/helper/test-ref-store.c     |  2 +-
 upload-pack.c                 |  2 +-
 vcs-svn/fast_export.c         |  4 ++--
 15 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 07506a3e457..e4b3c7b0ebf 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1727,11 +1727,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
 	get_commit_info(suspect->commit, &ci, 1);
 	printf("author %s\n", ci.author.buf);
 	printf("author-mail %s\n", ci.author_mail.buf);
-	printf("author-time %lu\n", ci.author_time);
+	printf("author-time %"PRItime"\n", ci.author_time);
 	printf("author-tz %s\n", ci.author_tz.buf);
 	printf("committer %s\n", ci.committer.buf);
 	printf("committer-mail %s\n", ci.committer_mail.buf);
-	printf("committer-time %lu\n", ci.committer_time);
+	printf("committer-time %"PRItime"\n", ci.committer_time);
 	printf("committer-tz %s\n", ci.committer_tz.buf);
 	printf("summary %s\n", ci.summary.buf);
 	if (suspect->commit->object.flags & UNINTERESTING)
@@ -1844,7 +1844,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
 
 	strbuf_reset(&time_buf);
 	if (show_raw_time) {
-		strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+		strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
 	}
 	else {
 		const char *time_str;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b5e13a45560..c233eda21c8 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 			if (timestamp && name_objects)
 				add_decoration(fsck_walk_options.object_names,
 					obj,
-					xstrfmt("%s@{%ld}", refname, timestamp));
+					xstrfmt("%s@{%"PRItime"}", refname, timestamp));
 			obj->used = 1;
 			mark_object_reachable(obj);
 		} else {
diff --git a/builtin/log.c b/builtin/log.c
index b3b10cc1edb..f93ef6c7100 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -910,7 +910,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
-	strbuf_addf(&buf, "%s.%lu.git.%s", base,
+	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
 		    (unsigned long) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d25a57931f8..ab718f4402c 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -459,12 +459,12 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	strbuf_addf(&buf, "%s:%lu", path, stamp);
+	strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
 	hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
 	strbuf_release(&buf);
 
 	/* RFC 2104 5. HMAC-SHA1-80 */
-	strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+	strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
 	return strbuf_detach(&buf, NULL);
 }
 
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a2..3b292c99bda 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
 	}
 
 	if (info->show_timestamp)
-		printf("%lu ", commit->date);
+		printf("%"PRItime" ", commit->date);
 	if (info->header_prefix)
 		fputs(info->header_prefix, stdout);
 
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 05133309106..b4509002435 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr)
 	/* date handling requires both flags and revs */
 	if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
 		return;
-	buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+	buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
 	show(buffer);
 	free(buffer);
 }
diff --git a/date.c b/date.c
index 495c207c64f..5c33dfd8ee7 100644
--- a/date.c
+++ b/date.c
@@ -100,41 +100,41 @@ 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;
 	}
@@ -145,20 +145,20 @@ void show_date_relative(unsigned long time, int tz,
 		unsigned long 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);
 }
 
@@ -179,7 +179,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
 
 	if (mode->type == DATE_UNIX) {
 		strbuf_reset(&timebuf);
-		strbuf_addf(&timebuf, "%lu", time);
+		strbuf_addf(&timebuf, "%"PRItime, time);
 		return timebuf.buf;
 	}
 
@@ -188,7 +188,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;
 	}
 
@@ -643,7 +643,7 @@ 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);
 }
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index afb8b050248..6004cea60d4 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -395,7 +395,7 @@ static int find_common(struct fetch_pack_args *args,
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
 		unsigned long max_age = approxidate(args->deepen_since);
-		packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
 		int i;
diff --git a/git-compat-util.h b/git-compat-util.h
index d78cc36dd1a..b2451a12d54 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,7 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+#define PRItime "lu"
 #define parse_timestamp strtoul
 
 #ifndef PATH_SEP
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 801d26468e0..4e9e3628716 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4135,7 +4135,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
 			printf("prune %s", message);
 	} else {
 		if (cb->newlog) {
-			fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+			fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
 				oid_to_hex(ooid), oid_to_hex(noid),
 				email, timestamp, tz, message);
 			oidcpy(&cb->last_kept_oid, noid);
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 52d1fc34454..269040f028f 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -53,7 +53,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 
 		strbuf_reset(&result);
 		parse_date(*argv, &result);
-		if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+		if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
 			printf("%s -> %s\n",
 			       *argv, show_date(t, tz, DATE_MODE(ISO8601)));
 		else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24bd..7d93627e454 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
 	show(&expect, &ret, "magnitude: %lu", magnitude);
-	show(&expect, &ret, "timestamp: %lu", timestamp);
+	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
 	show(&expect, &ret, "verbose: %d", verbose);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 2d84c45ffe9..a436bfdb053 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -141,7 +141,7 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
 		       const char *committer, unsigned long timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
-	printf("%s %s %s %lu %d %s\n",
+	printf("%s %s %s %"PRItime" %d %s\n",
 	       oid_to_hex(old_oid), oid_to_hex(new_oid),
 	       committer, timestamp, tz, msg);
 	return 0;
diff --git a/upload-pack.c b/upload-pack.c
index f17f4dd1233..4966f0b8a09 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -863,7 +863,7 @@ static void receive_needs(void)
 
 		argv_array_push(&av, "rev-list");
 		if (deepen_since)
-			argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+			argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
 		if (deepen_not.nr) {
 			argv_array_push(&av, "--not");
 			for (i = 0; i < deepen_not.nr; i++) {
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf5..6c9f2866d8b 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -73,7 +73,7 @@ void fast_export_begin_note(uint32_t revision, const char *author,
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
 	printf("commit %s\n", note_ref);
-	printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+	printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
 	printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
 	fwrite(log, loglen, 1, stdout);
 	if (firstnote) {
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
 	}
 	printf("commit %s\n", local_ref);
 	printf("mark :%"PRIu32"\n", revision);
-	printf("committer %s <%s@%s> %lu +0000\n",
+	printf("committer %s <%s@%s> %"PRItime" +0000\n",
 		   *author ? author : "nobody",
 		   *author ? author : "nobody",
 		   *uuid ? uuid : "local", timestamp);
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 6/8] Introduce a new data type for timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
                             ` (4 preceding siblings ...)
  2017-04-26 19:29           ` [PATCH v6 5/8] Introduce a new "printf format" for " Johannes Schindelin
@ 2017-04-26 19:29           ` " Johannes Schindelin
  2017-05-20  5:47             ` [PATCH] name-rev: change a "long" variable to timestamp_t Junio C Hamano
  2017-04-26 19:29           ` [PATCH v6 7/8] Abort if the system time cannot handle one of our timestamps Johannes Schindelin
  2017-04-26 19:29           ` [PATCH v6 8/8] Use uintmax_t for " Johannes Schindelin
  7 siblings, 1 reply; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

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>
---
 Documentation/technical/api-parse-options.txt |  8 ++--
 archive-tar.c                                 |  7 ++-
 archive-zip.c                                 | 12 ++++-
 archive.h                                     |  2 +-
 builtin/am.c                                  |  2 +-
 builtin/blame.c                               |  8 ++--
 builtin/fsck.c                                |  4 +-
 builtin/gc.c                                  |  2 +-
 builtin/log.c                                 |  2 +-
 builtin/merge-base.c                          |  2 +-
 builtin/name-rev.c                            |  6 +--
 builtin/pack-objects.c                        |  4 +-
 builtin/prune.c                               |  4 +-
 builtin/receive-pack.c                        |  6 +--
 builtin/reflog.c                              | 24 +++++-----
 builtin/show-branch.c                         |  4 +-
 builtin/worktree.c                            |  4 +-
 bundle.c                                      |  2 +-
 cache.h                                       | 14 +++---
 commit.c                                      | 12 ++---
 commit.h                                      |  2 +-
 config.c                                      |  2 +-
 credential-cache--daemon.c                    | 12 ++---
 date.c                                        | 66 +++++++++++++--------------
 fetch-pack.c                                  |  6 +--
 git-compat-util.h                             |  2 +
 http-backend.c                                |  4 +-
 parse-options-cb.c                            |  4 +-
 pretty.c                                      |  2 +-
 reachable.c                                   |  9 ++--
 reachable.h                                   |  4 +-
 ref-filter.c                                  |  4 +-
 reflog-walk.c                                 |  8 ++--
 refs.c                                        | 14 +++---
 refs.h                                        |  8 ++--
 refs/files-backend.c                          |  4 +-
 revision.c                                    |  6 +--
 revision.h                                    |  4 +-
 sha1_name.c                                   |  6 +--
 t/helper/test-date.c                          |  8 ++--
 t/helper/test-parse-options.c                 |  2 +-
 t/helper/test-ref-store.c                     |  2 +-
 tag.c                                         |  2 +-
 tag.h                                         |  2 +-
 upload-pack.c                                 |  4 +-
 vcs-svn/fast_export.c                         |  4 +-
 vcs-svn/fast_export.h                         |  4 +-
 vcs-svn/svndump.c                             |  2 +-
 wt-status.c                                   |  2 +-
 49 files changed, 170 insertions(+), 158 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e1..829b5581105 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
 	scale the provided value by 1024, 1024^2 or 1024^3 respectively.
 	The scaled value is put into `unsigned_long_var`.
 
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
 	Introduce an option with expiry date argument, see `parse_expiry_date()`.
-	The timestamp is put into `int_var`.
+	The timestamp is put into `timestamp_t_var`.
 
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
 	Introduce an option with argument.
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd23..319a5b1c7cd 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
  */
 #if ULONG_MAX == 0xFFFFFFFF
 #define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
 #else
-#define USTAR_MAX_SIZE 077777777777UL
+#define USTAR_MAX_SIZE 077777777777ULL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
 #define USTAR_MAX_MTIME 077777777777UL
 #endif
 
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974a..68df3d64402 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -545,9 +545,17 @@ static void write_zip_trailer(const unsigned char *sha1)
 		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
 }
 
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
 {
-	struct tm *t = localtime(time);
+	time_t time;
+	struct tm *t;
+
+	if (date_overflows(*timestamp))
+		die("timestamp too large for this system: %"PRItime,
+		    *timestamp);
+	time = (time_t)*timestamp;
+	t = localtime(&time);
+	*timestamp = time;
 
 	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
 	            (t->tm_year + 1900 - 1980) * 512;
diff --git a/archive.h b/archive.h
index 415e0152e2c..62d1d82c1af 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
 	struct tree *tree;
 	const unsigned char *commit_sha1;
 	const struct commit *commit;
-	time_t time;
+	timestamp_t time;
 	struct pathspec pathspec;
 	unsigned int verbose : 1;
 	unsigned int worktree_attributes : 1;
diff --git a/builtin/am.c b/builtin/am.c
index ffb7a6355fb..8bdbd53b618 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -881,7 +881,7 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
 		if (skip_prefix(sb.buf, "# User ", &str))
 			fprintf(out, "From: %s\n", str);
 		else if (skip_prefix(sb.buf, "# Date ", &str)) {
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			long tz, tz2;
 			char *end;
 
diff --git a/builtin/blame.c b/builtin/blame.c
index e4b3c7b0ebf..f00eda16378 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1561,13 +1561,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 struct commit_info {
 	struct strbuf author;
 	struct strbuf author_mail;
-	unsigned long author_time;
+	timestamp_t author_time;
 	struct strbuf author_tz;
 
 	/* filled only when asked for details */
 	struct strbuf committer;
 	struct strbuf committer_mail;
-	unsigned long committer_time;
+	timestamp_t committer_time;
 	struct strbuf committer_tz;
 
 	struct strbuf summary;
@@ -1578,7 +1578,7 @@ struct commit_info {
  */
 static void get_ac_line(const char *inbuf, const char *what,
 	struct strbuf *name, struct strbuf *mail,
-	unsigned long *time, struct strbuf *tz)
+	timestamp_t *time, struct strbuf *tz)
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
@@ -1837,7 +1837,7 @@ static void assign_blame(struct scoreboard *sb, int opt)
 	stop_progress(&pi.progress);
 }
 
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
 			       int show_raw_time)
 {
 	static struct strbuf time_buf = STRBUF_INIT;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index c233eda21c8..32a32e55c85 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 static int default_refs;
 
 static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	struct object *obj;
 
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 }
 
 static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	const char *refname = cb_data;
diff --git a/builtin/gc.c b/builtin/gc.c
index 91f7696a85e..f484eda43ca 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/log.c b/builtin/log.c
index f93ef6c7100..fd3d10ec217 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -911,7 +911,7 @@ static void gen_message_id(struct rev_info *info, char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
-		    (unsigned long) time(NULL),
+		    (timestamp_t) time(NULL),
 		    git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
 	info->message_id = strbuf_detach(&buf, NULL);
 }
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f85..8ed96391c1d 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 }
 
 static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-				  const char *ident, unsigned long timestamp,
+				  const char *ident, timestamp_t timestamp,
 				  int tz, const char *message, void *cbdata)
 {
 	struct rev_collect *revs = cbdata;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 92a5d8a5d26..44374750170 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,7 +10,7 @@
 
 typedef struct rev_name {
 	const char *tip_name;
-	unsigned long taggerdate;
+	timestamp_t taggerdate;
 	int generation;
 	int distance;
 } rev_name;
@@ -21,7 +21,7 @@ static long cutoff = LONG_MAX;
 #define MERGE_TRAVERSAL_WEIGHT 65535
 
 static void name_rev(struct commit *commit,
-		const char *tip_name, unsigned long taggerdate,
+		const char *tip_name, timestamp_t taggerdate,
 		int generation, int distance,
 		int deref)
 {
@@ -146,7 +146,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
 	struct name_ref_data *data = cb_data;
 	int can_abbreviate_output = data->tags_only && data->name_only;
 	int deref = 0;
-	unsigned long taggerdate = ULONG_MAX;
+	timestamp_t taggerdate = TIME_MAX;
 
 	if (data->tags_only && !starts_with(path, "refs/tags/"))
 		return 0;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 0fe35d1b5ae..9b4ba8a80d7 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
 static int pack_loose_unreachable;
 static int local;
 static int have_non_local_packs;
@@ -2675,7 +2675,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
 static struct oid_array recent_objects;
 
 static int loosened_object_can_be_discarded(const struct object_id *oid,
-					    unsigned long mtime)
+					    timestamp_t mtime)
 {
 	if (!unpack_unreachable_expiration)
 		return 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e6..8dcfecde0f3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 };
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 static int show_progress = -1;
 
 static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	};
 	char *s;
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	save_commit_buffer = 0;
 	check_replace_refs = 0;
 	ref_paranoia = 1;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ab718f4402c..0bb36d584d7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
 static const char *NONCE_SLOP = "SLOP";
 static const char *nonce_status;
 static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
 static struct ref_transaction *transaction;
 
 static enum {
@@ -454,7 +454,7 @@ static void hmac_sha1(unsigned char *out,
 	git_SHA1_Final(out, &ctx);
 }
 
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
 {
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
@@ -496,7 +496,7 @@ static char *find_header(const char *msg, size_t len, const char *key)
 static const char *check_nonce(const char *buf, size_t len)
 {
 	char *nonce = find_header(buf, len, "nonce");
-	unsigned long stamp, ostamp;
+	timestamp_t stamp, ostamp;
 	char *bohmac, *expect = NULL;
 	const char *retval = NONCE_BAD;
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 74727757785..4228d9ff4db 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
 static const char reflog_exists_usage[] =
 "git reflog exists <ref>";
 
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
 
 struct cmd_reflog_expire_cb {
 	struct rev_info revs;
 	int stalefix;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	int recno;
 };
 
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 static void mark_reachable(struct expire_reflog_policy_cb *cb)
 {
 	struct commit_list *pending;
-	unsigned long expire_limit = cb->mark_limit;
+	timestamp_t expire_limit = cb->mark_limit;
 	struct commit_list *leftover = NULL;
 
 	for (pending = cb->mark_list; pending; pending = pending->next)
@@ -284,7 +284,7 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
  * Return true iff the specified reflog entry should be expired.
  */
 static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-				    const char *email, unsigned long timestamp, int tz,
+				    const char *email, timestamp_t timestamp, int tz,
 				    const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
 
 static struct reflog_expire_cfg {
 	struct reflog_expire_cfg *next;
-	unsigned long expire_total;
-	unsigned long expire_unreachable;
+	timestamp_t expire_total;
+	timestamp_t expire_unreachable;
 	char pattern[FLEX_ARRAY];
 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
 
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
 	return ent;
 }
 
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
 {
 	if (!value)
 		return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
 {
 	const char *pattern, *key;
 	int pattern_len;
-	unsigned long expire;
+	timestamp_t expire;
 	int slot;
 	struct reflog_expire_cfg *ent;
 
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 {
 	struct expire_reflog_policy_cb cb;
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	int i, status, do_all;
 	int explicit_expiry = 0;
 	unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 }
 
 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d57..8860f429b06 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			base = strtoul(reflog_base, &ep, 10);
 			if (*ep) {
 				/* Ah, that is a date spec... */
-				unsigned long at;
+				timestamp_t at;
 				at = approxidate(reflog_base);
 				read_ref_at(ref, flags, at, -1, oid.hash, NULL,
 					    NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			char *logmsg;
 			char *nth_desc;
 			const char *msg;
-			unsigned long timestamp;
+			timestamp_t timestamp;
 			int tz;
 
 			if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 9993ded41aa..74f9b18d40c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -30,7 +30,7 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
 
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	expire = ULONG_MAX;
+	expire = TIME_MAX;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 	if (ac)
 		usage_with_options(worktree_usage, options);
diff --git a/bundle.c b/bundle.c
index f43bfcf5ff3..05e014fc5ab 100644
--- a/bundle.c
+++ b/bundle.c
@@ -211,7 +211,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 	unsigned long size;
 	enum object_type type;
 	char *buf = NULL, *line, *lineend;
-	unsigned long date;
+	timestamp_t date;
 	int result = 1;
 
 	if (revs->max_age == -1 && revs->min_age == -1)
diff --git a/cache.h b/cache.h
index ef0fe43a9df..e2482a510f7 100644
--- a/cache.h
+++ b/cache.h
@@ -1478,18 +1478,18 @@ struct date_mode {
 #define DATE_MODE(t) date_mode_from_type(DATE_##t)
 struct date_mode *date_mode_from_type(enum date_mode_type type);
 
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
 			struct strbuf *timebuf);
 int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
 void datestamp(struct strbuf *out);
 #define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
 void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
 
 #define IDENT_STRICT	       1
 #define IDENT_NO_DATE	       2
diff --git a/commit.c b/commit.c
index 0d2d0fa1984..99a62b90ee2 100644
--- a/commit.c
+++ b/commit.c
@@ -66,7 +66,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 	return commit;
 }
 
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
@@ -473,8 +473,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
 
 static int commit_list_compare_by_date(const void *a, const void *b)
 {
-	unsigned long a_date = ((const struct commit_list *)a)->item->date;
-	unsigned long b_date = ((const struct commit_list *)b)->item->date;
+	timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+	timestamp_t b_date = ((const struct commit_list *)b)->item->date;
 	if (a_date < b_date)
 		return 1;
 	if (a_date > b_date)
@@ -598,7 +598,7 @@ static void record_author_date(struct author_date_slab *author_date,
 	const char *ident_line;
 	size_t ident_len;
 	char *date_end;
-	unsigned long date;
+	timestamp_t date;
 
 	ident_line = find_commit_header(buffer, "author", &ident_len);
 	if (!ident_line)
@@ -621,8 +621,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
 {
 	const struct commit *a = a_, *b = b_;
 	struct author_date_slab *author_date = cb_data;
-	unsigned long a_date = *(author_date_slab_at(author_date, a));
-	unsigned long b_date = *(author_date_slab_at(author_date, b));
+	timestamp_t a_date = *(author_date_slab_at(author_date, a));
+	timestamp_t b_date = *(author_date_slab_at(author_date, b));
 
 	/* newer commits with larger date first */
 	if (a_date < b_date)
diff --git a/commit.h b/commit.h
index 7b1986d5c8a..c9d887b5e53 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
 	struct object object;
 	void *util;
 	unsigned int index;
-	unsigned long date;
+	timestamp_t date;
 	struct commit_list *parents;
 	struct tree *tree;
 };
diff --git a/config.c b/config.c
index 0daaed338ea..4cf644b9fbf 100644
--- a/config.c
+++ b/config.c
@@ -1943,7 +1943,7 @@ int git_config_get_expiry(const char *key, const char **output)
 	if (ret)
 		return ret;
 	if (strcmp(*output, "now")) {
-		unsigned long now = approxidate("now");
+		timestamp_t now = approxidate("now");
 		if (approxidate(*output) >= now)
 			git_die_config(key, _("Invalid %s: '%s'"), key, *output);
 	}
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526a..f3814cc47a0 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
 
 struct credential_cache_entry {
 	struct credential item;
-	unsigned long expiration;
+	timestamp_t expiration;
 };
 static struct credential_cache_entry *entries;
 static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
 		e->expiration = 0;
 }
 
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
 {
-	static unsigned long wait_for_entry_until;
+	static timestamp_t wait_for_entry_until;
 	int i = 0;
-	unsigned long now = time(NULL);
-	unsigned long next = (unsigned long)-1;
+	timestamp_t now = time(NULL);
+	timestamp_t next = TIME_MAX;
 
 	/*
 	 * Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
 static int serve_cache_loop(int fd)
 {
 	struct pollfd pfd;
-	unsigned long wakeup;
+	timestamp_t wakeup;
 
 	wakeup = check_expirations();
 	if (!wakeup)
diff --git a/date.c b/date.c
index 5c33dfd8ee7..92ab31aa441 100644
--- a/date.c
+++ b/date.c
@@ -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;
 
 	/*
diff --git a/fetch-pack.c b/fetch-pack.c
index 6004cea60d4..5f15dd2c390 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -394,7 +394,7 @@ static int find_common(struct fetch_pack_args *args,
 	if (args->depth > 0)
 		packet_buf_write(&req_buf, "deepen %d", args->depth);
 	if (args->deepen_since) {
-		unsigned long max_age = approxidate(args->deepen_since);
+		timestamp_t max_age = approxidate(args->deepen_since);
 		packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
 	}
 	if (args->deepen_not) {
@@ -583,7 +583,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-					 unsigned long cutoff)
+					 timestamp_t cutoff)
 {
 	while (complete && cutoff <= complete->item->date) {
 		print_verbose(args, _("Marking %s as complete"),
@@ -670,7 +670,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
 	struct ref *ref;
 	int retval;
-	unsigned long cutoff = 0;
+	timestamp_t cutoff = 0;
 
 	save_commit_buffer = 0;
 
diff --git a/git-compat-util.h b/git-compat-util.h
index b2451a12d54..594100e7652 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,8 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
+typedef unsigned long timestamp_t;
 #define PRItime "lu"
 #define parse_timestamp strtoul
+#define TIME_MAX ULONG_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4f..d6ea6075339 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
 	strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
 }
 
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
 {
 	const char *value = show_date(when, 0, DATE_MODE(RFC2822));
 	hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
 
 static void hdr_cache_forever(struct strbuf *hdr)
 {
-	unsigned long now = time(NULL);
+	timestamp_t now = time(NULL);
 	hdr_date(hdr, "Date", now);
 	hdr_date(hdr, "Expires", now + 31536000);
 	hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 7419780a9b6..a6810f295cb 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
 int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	*(unsigned long *)(opt->value) = approxidate(arg);
+	*(timestamp_t *)(opt->value) = approxidate(arg);
 	return 0;
 }
 
 int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
 			     int unset)
 {
-	return parse_expiry_date(arg, (unsigned long *)opt->value);
+	return parse_expiry_date(arg, (timestamp_t *)opt->value);
 }
 
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
diff --git a/pretty.c b/pretty.c
index 24fb0c79062..587d48371b0 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,7 +405,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
 const char *show_ident_date(const struct ident_split *ident,
 			    const struct date_mode *mode)
 {
-	unsigned long date = 0;
+	timestamp_t date = 0;
 	long tz = 0;
 
 	if (ident->date_begin && ident->date_end)
diff --git a/reachable.c b/reachable.c
index a8a979bd4fc..682418f5d23 100644
--- a/reachable.c
+++ b/reachable.c
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
 
 struct recent_data {
 	struct rev_info *revs;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 };
 
 static void add_recent_object(const struct object_id *oid,
-			      unsigned long mtime,
+			      timestamp_t mtime,
 			      struct recent_data *data)
 {
 	struct object *obj;
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
 }
 
 int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-					   unsigned long timestamp)
+					   timestamp_t timestamp)
 {
 	struct recent_data data;
 	int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 }
 
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-			    unsigned long mark_recent,
-			    struct progress *progress)
+			    timestamp_t mark_recent, struct progress *progress)
 {
 	struct connectivity_progress cp;
 
diff --git a/reachable.h b/reachable.h
index d23efc36ec5..3c00fa0526c 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
 
 struct progress;
 extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
-						  unsigned long timestamp);
+						  timestamp_t timestamp);
 extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
-				   unsigned long mark_recent, struct progress *);
+				   timestamp_t mark_recent, struct progress *);
 
 #endif
diff --git a/ref-filter.c b/ref-filter.c
index c7836ae07bd..1fc5e9970db 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -849,7 +849,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 {
 	const char *eoemail = strstr(buf, "> ");
 	char *zone;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	long tz;
 	struct date_mode date_mode = { DATE_NORMAL };
 	const char *formatp;
@@ -869,7 +869,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
 	if (!eoemail)
 		goto bad;
 	timestamp = parse_timestamp(eoemail + 2, &zone, 10);
-	if (timestamp == ULONG_MAX)
+	if (timestamp == TIME_MAX)
 		goto bad;
 	tz = strtol(zone, NULL, 10);
 	if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
diff --git a/reflog-walk.c b/reflog-walk.c
index 99679f58255..3ca5ed8415a 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
-		unsigned long timestamp;
+		timestamp_t timestamp;
 		int tz;
 		char *message;
 	} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
 };
 
 static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 }
 
 static int get_reflog_recno_by_time(struct complete_reflogs *array,
-	unsigned long timestamp)
+	timestamp_t timestamp)
 {
 	int i;
 	for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
-	unsigned long timestamp = 0;
+	timestamp_t timestamp = 0;
 	int recno = -1;
 	struct string_list_item *item;
 	struct complete_reflogs *reflogs;
diff --git a/refs.c b/refs.c
index a3d5f42e373..f4944a658c7 100644
--- a/refs.c
+++ b/refs.c
@@ -712,7 +712,7 @@ int is_branch(const char *refname)
 
 struct read_ref_at_cb {
 	const char *refname;
-	unsigned long at_time;
+	timestamp_t at_time;
 	int cnt;
 	int reccnt;
 	unsigned char *sha1;
@@ -721,15 +721,15 @@ struct read_ref_at_cb {
 	unsigned char osha1[20];
 	unsigned char nsha1[20];
 	int tz;
-	unsigned long date;
+	timestamp_t date;
 	char **msg;
-	unsigned long *cutoff_time;
+	timestamp_t *cutoff_time;
 	int *cutoff_tz;
 	int *cutoff_cnt;
 };
 
 static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -776,7 +776,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
 }
 
 static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp,
+				  const char *email, timestamp_t timestamp,
 				  int tz, const char *message, void *cb_data)
 {
 	struct read_ref_at_cb *cb = cb_data;
@@ -796,9 +796,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 	return 1;
 }
 
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
 {
 	struct read_ref_at_cb cb;
 
diff --git a/refs.h b/refs.h
index 49e97d7d5fd..2b80d37fd34 100644
--- a/refs.h
+++ b/refs.h
@@ -317,9 +317,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
 
 /** Reads log for the value of ref during at_time. **/
 int read_ref_at(const char *refname, unsigned int flags,
-		unsigned long at_time, int cnt,
+		timestamp_t at_time, int cnt,
 		unsigned char *sha1, char **msg,
-		unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+		timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
 
 /** Check if a particular reflog exists */
 int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@ -356,7 +356,7 @@ int delete_reflog(const char *refname);
 /* iterate over reflog entries */
 typedef int each_reflog_ent_fn(
 		struct object_id *old_oid, struct object_id *new_oid,
-		const char *committer, unsigned long timestamp,
+		const char *committer, timestamp_t timestamp,
 		int tz, const char *msg, void *cb_data);
 
 int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@ -616,7 +616,7 @@ typedef void reflog_expiry_prepare_fn(const char *refname,
 typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
 					  unsigned char *nsha1,
 					  const char *email,
-					  unsigned long timestamp, int tz,
+					  timestamp_t timestamp, int tz,
 					  const char *message, void *cb_data);
 typedef void reflog_expiry_cleanup_fn(void *cb_data);
 
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 4e9e3628716..4d9d1144569 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3237,7 +3237,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
 {
 	struct object_id ooid, noid;
 	char *email_end, *message;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	int tz;
 	const char *p = sb->buf;
 
@@ -4118,7 +4118,7 @@ struct expire_reflog_cb {
 };
 
 static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
-			     const char *email, unsigned long timestamp, int tz,
+			     const char *email, timestamp_t timestamp, int tz,
 			     const char *message, void *cb_data)
 {
 	struct expire_reflog_cb *cb = cb_data;
diff --git a/revision.c b/revision.c
index 7ff61ff5f73..8a8c1789c7b 100644
--- a/revision.c
+++ b/revision.c
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
 /* How many extra uninteresting commits we want to see.. */
 #define SLOP 5
 
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
 			     struct commit **interesting_cache)
 {
 	/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 static int limit_list(struct rev_info *revs)
 {
 	int slop = SLOP;
-	unsigned long date = ~0ul;
+	timestamp_t date = TIME_MAX;
 	struct commit_list *list = revs->commits;
 	struct commit_list *newlist = NULL;
 	struct commit_list **p = &newlist;
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 }
 
 static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
-		const char *email, unsigned long timestamp, int tz,
+		const char *email, timestamp_t timestamp, int tz,
 		const char *message, void *cb_data)
 {
 	handle_one_reflog_commit(ooid, cb_data);
diff --git a/revision.h b/revision.h
index 14886ec92b4..0d9e68b36e9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
 	/* special limits */
 	int skip_count;
 	int max_count;
-	unsigned long max_age;
-	unsigned long min_age;
+	timestamp_t max_age;
+	timestamp_t min_age;
 	int min_parents;
 	int max_parents;
 	int (*include_check)(struct commit *, void *);
diff --git a/sha1_name.c b/sha1_name.c
index 8eec9f7c1bb..35c1e2a9e32 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 
 	if (reflog_len) {
 		int nth, i;
-		unsigned long at_time;
-		unsigned long co_time;
+		timestamp_t at_time;
+		timestamp_t co_time;
 		int co_tz, co_cnt;
 
 		/* Is it asking for N-th entry, or approxidate? */
@@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata {
 };
 
 static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
-				  const char *email, unsigned long timestamp, int tz,
+				  const char *email, timestamp_t timestamp, int tz,
 				  const char *message, void *cb_data)
 {
 	struct grab_nth_branch_switch_cbdata *cb = cb_data;
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 269040f028f..f414a3ac670 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -27,7 +27,7 @@ static void show_dates(const char **argv, const char *format)
 	parse_date_format(format, &mode);
 	for (; *argv; argv++) {
 		char *arg;
-		time_t t;
+		timestamp_t t;
 		int tz;
 
 		/*
@@ -48,7 +48,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 	struct strbuf result = STRBUF_INIT;
 
 	for (; *argv; argv++) {
-		unsigned long t;
+		timestamp_t t;
 		int tz;
 
 		strbuf_reset(&result);
@@ -65,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
 static void parse_approxidate(const char **argv, struct timeval *now)
 {
 	for (; *argv; argv++) {
-		time_t t;
+		timestamp_t t;
 		t = approxidate_relative(*argv, now);
 		printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
 	}
@@ -96,7 +96,7 @@ int cmd_main(int argc, const char **argv)
 	else if (!strcmp(*argv, "approxidate"))
 		parse_approxidate(argv+1, &now);
 	else if (!strcmp(*argv, "is64bit"))
-		return sizeof(unsigned long) == 8 ? 0 : 1;
+		return sizeof(timestamp_t) == 8 ? 0 : 1;
 	else if (!strcmp(*argv, "time_t-is64bit"))
 		return sizeof(time_t) == 8 ? 0 : 1;
 	else
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 7d93627e454..75fe883aac1 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
 static int boolean = 0;
 static int integer = 0;
 static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
 static int abbrev = 7;
 static int verbose = -1; /* unspecified */
 static int dry_run = 0, quiet = 0;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index a436bfdb053..9077ec2c330 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -138,7 +138,7 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
 }
 
 static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
-		       const char *committer, unsigned long timestamp,
+		       const char *committer, timestamp_t timestamp,
 		       int tz, const char *msg, void *cb_data)
 {
 	printf("%s %s %s %"PRItime" %d %s\n",
diff --git a/tag.c b/tag.c
index 9b6725e02c9..d71b67e8d83 100644
--- a/tag.c
+++ b/tag.c
@@ -97,7 +97,7 @@ struct tag *lookup_tag(const unsigned char *sha1)
 	return object_as_type(obj, OBJ_TAG, 0);
 }
 
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
 {
 	const char *dateptr;
 
diff --git a/tag.h b/tag.h
index a5721b6731e..2abb3726fb5 100644
--- a/tag.h
+++ b/tag.h
@@ -9,7 +9,7 @@ struct tag {
 	struct object object;
 	struct object *tagged;
 	char *tag;
-	unsigned long date;
+	timestamp_t date;
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
diff --git a/upload-pack.c b/upload-pack.c
index 4966f0b8a09..97da13e6a54 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
 #define CLIENT_SHALLOW	(1u << 18)
 #define HIDDEN_REF	(1u << 19)
 
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
 
 static int deepen_relative;
 static int multi_ack;
@@ -735,7 +735,7 @@ static void receive_needs(void)
 	struct string_list deepen_not = STRING_LIST_INIT_DUP;
 	int depth = 0;
 	int has_non_tip = 0;
-	unsigned long deepen_since = 0;
+	timestamp_t deepen_since = 0;
 	int deepen_rev_list = 0;
 
 	shallow_nr = 0;
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 6c9f2866d8b..5a89db30e3f 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,7 +68,7 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
 }
 
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref)
+		const char *log, timestamp_t timestamp, const char *note_ref)
 {
 	static int firstnote = 1;
 	size_t loglen = strlen(log);
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log,
 			const char *uuid, const char *url,
-			unsigned long timestamp, const char *local_ref)
+			timestamp_t timestamp, const char *local_ref)
 {
 	static const struct strbuf empty = STRBUF_INIT;
 	if (!log)
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811c..b9a3b71c99f 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
 void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
 void fast_export_note(const char *committish, const char *dataref);
 void fast_export_begin_note(uint32_t revision, const char *author,
-		const char *log, unsigned long timestamp, const char *note_ref);
+		const char *log, timestamp_t timestamp, const char *note_ref);
 void fast_export_begin_commit(uint32_t revision, const char *author,
 			const struct strbuf *log, const char *uuid,const char *url,
-			unsigned long timestamp, const char *local_ref);
+			timestamp_t timestamp, const char *local_ref);
 void fast_export_end_commit(uint32_t revision);
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
 void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b9..1846685a21a 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
 	uint32_t revision;
-	unsigned long timestamp;
+	timestamp_t timestamp;
 	struct strbuf log, author, note;
 } rev_ctx;
 
diff --git a/wt-status.c b/wt-status.c
index 03754849626..1b1d644b85f 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1387,7 +1387,7 @@ struct grab_1st_switch_cbdata {
 };
 
 static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
-			   const char *email, unsigned long timestamp, int tz,
+			   const char *email, timestamp_t timestamp, int tz,
 			   const char *message, void *cb_data)
 {
 	struct grab_1st_switch_cbdata *cb = cb_data;
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 7/8] Abort if the system time cannot handle one of our timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
                             ` (5 preceding siblings ...)
  2017-04-26 19:29           ` [PATCH v6 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-04-26 19:29           ` Johannes Schindelin
  2017-04-26 19:29           ` [PATCH v6 8/8] Use uintmax_t for " Johannes Schindelin
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

We are about to switch to a new data type for time stamps that is
definitely not smaller or equal, but larger or equal to time_t.

So before using the system functions to process or format timestamps,
let's make extra certain that they can handle what we feed them.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 date.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/date.c b/date.c
index 92ab31aa441..63fa99685e2 100644
--- a/date.c
+++ b/date.c
@@ -46,7 +46,17 @@ static time_t gm_time_t(timestamp_t time, int tz)
 	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;
 }
 
 /*
@@ -70,7 +80,10 @@ static int local_tzoffset(timestamp_t time)
 	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);
 
-- 
2.12.2.windows.2.406.gd14a8f8640f



^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH v6 8/8] Use uintmax_t for timestamps
  2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
                             ` (6 preceding siblings ...)
  2017-04-26 19:29           ` [PATCH v6 7/8] Abort if the system time cannot handle one of our timestamps Johannes Schindelin
@ 2017-04-26 19:29           ` " Johannes Schindelin
  7 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-04-26 19:29 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Torsten Bögershausen, Jeff King, René Scharfe, Jacob Keller, Johannes Sixt

Previously, we used `unsigned long` for timestamps. This was only a good
choice on Linux, where we know implicitly that `unsigned long` is what is
used for `time_t`.

However, we want to use a different data type for timestamps for two
reasons:

- there is nothing that says that `unsigned long` should be the same data
  type as `time_t`, and indeed, on 64-bit Windows for example, it is not:
  `unsigned long` is 32-bit but `time_t` is 64-bit.

- even on 32-bit Linux, where `unsigned long` (and thereby `time_t`) is
  32-bit, we *want* to be able to encode timestamps in Git that are
  currently absurdly far in the future, *even if* the system library is
  not able to format those timestamps into date strings.

So let's just switch to the maximal integer type available, which should
be at least 64-bit for all practical purposes these days. It certainly
cannot be worse than `unsigned long`, so...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 git-compat-util.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 594100e7652..f366180f4b9 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,10 +319,10 @@ extern char *gitdirname(char *);
 #define PRIo32 "o"
 #endif
 
-typedef unsigned long timestamp_t;
-#define PRItime "lu"
-#define parse_timestamp strtoul
-#define TIME_MAX ULONG_MAX
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#define TIME_MAX UINTMAX_MAX
 
 #ifndef PATH_SEP
 #define PATH_SEP ':'
-- 
2.12.2.windows.2.406.gd14a8f8640f

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 0/8] Introduce timestamp_t for timestamps
  2017-04-25 22:22           ` Johannes Schindelin
@ 2017-04-26 22:09             ` René Scharfe
  0 siblings, 0 replies; 113+ messages in thread
From: René Scharfe @ 2017-04-26 22:09 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Junio C Hamano, Torsten Bögershausen

Hi Dscho,

Am 26.04.2017 um 00:22 schrieb Johannes Schindelin:
> On Tue, 25 Apr 2017, René Scharfe wrote:
>> Am 24.04.2017 um 15:57 schrieb Johannes Schindelin:
>> Can we leave time_t alone and just do the part where you replace
>> unsigned long with timestamp_t defined as uint64_t?  That should already
>> help on Windows, correct?  When/if timestamp_t is later changed to a
>> signed type then we could easily convert the time_t cases to timestamp_t
>> as well, or the other way around.
> 
> This patch series leaves time_t alone already, so your wish has been
> fulfilled preemptively.

Sounds good!  It does contain conversions from time_t to timestamp_t in
archive-zip.c, though.  I'll comment in reply to the relevant patch.

>>> It is arguably a bug to paper over too-large author/committer dates and
>>> to replace them with Jan 1 1970 without even telling the user that we do
>>> that, but this is the behavior that t4212 verifies, so I reinstated that
>>> behavior. The change in behavior was missed because of the missing
>>> unsigned_add_overflows() test.
>>
>> I can't think of many ways to get future time stamps (broken clock,
>> broken CMOS battery, bit rot, time travel), so I wouldn't expect a
>> change towards better error reporting to affect a lot of users.  (Not
>> necessarily as part of this series, of course.)
> 
> If you want to suggest that we should stop verifying overflows when a
> complex reasoning can prove that the overflow is not happening in a
> billion years, I disagree. Not only is it unnecessarily time-consuming to
> ask readers to perform the complex reasoning, and not only is there enough
> room for bugs to hide in plain sight (because of the complexity), it also
> makes the same code harder to reuse in other software where a different
> timestamp data type was chosen (or inherited from previous Git versions).
> 
> I'd much rather have easy-to-reason code that does not cause head
> scratching (like the "why do we ignore a too large timestamp?" triggering
> `if (date_overflows(date)) date = 0;`) than pretending to be smart and
> clever and make everybody else feel stupid by forcing them through hoops
> of thinking bubbles until they also reached the conclusion that this
> actually won't happen. Unless there is a bug in the code.

No, I meant that the presence of tests does not necessarily cast an
undesirable behavior into stone, especially if it's hard to trigger
in real life.  And that improvements thereof can be done later.  In
other words: It's OK to maintain the same behavior in this series,
but cheer up, we may be able to fix the issue eventually.

René

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH v5 6/8] Introduce a new data type for timestamps
  2017-04-24 13:58         ` [PATCH v5 6/8] Introduce a new data type " Johannes Schindelin
  2017-04-26 16:43           ` Johannes Sixt
@ 2017-04-26 22:32           ` René Scharfe
  1 sibling, 0 replies; 113+ messages in thread
From: René Scharfe @ 2017-04-26 22:32 UTC (permalink / raw)
  To: Johannes Schindelin, git; +Cc: Junio C Hamano, Torsten Bögershausen

Am 24.04.2017 um 15:58 schrieb Johannes Schindelin:
> diff --git a/archive-tar.c b/archive-tar.c
> index 380e3aedd23..695339a2369 100644
> --- a/archive-tar.c
> +++ b/archive-tar.c
> @@ -27,9 +27,12 @@ static int write_tar_filter_archive(const struct archiver *ar,
>    */
>   #if ULONG_MAX == 0xFFFFFFFF
>   #define USTAR_MAX_SIZE ULONG_MAX
> -#define USTAR_MAX_MTIME ULONG_MAX
>   #else
>   #define USTAR_MAX_SIZE 077777777777UL
> +#endif
> +#if TIME_MAX == 0xFFFFFFFF
> +#define USTAR_MAX_MTIME TIME_MAX
> +#else
>   #define USTAR_MAX_MTIME 077777777777UL
>   #endif
>   
> diff --git a/archive-zip.c b/archive-zip.c
> index b429a8d974a..68df3d64402 100644
> --- a/archive-zip.c
> +++ b/archive-zip.c
> @@ -545,9 +545,17 @@ static void write_zip_trailer(const unsigned char *sha1)
>   		write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
>   }
>   
> -static void dos_time(time_t *time, int *dos_date, int *dos_time)
> +static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
>   {
> -	struct tm *t = localtime(time);
> +	time_t time;
> +	struct tm *t;
> +
> +	if (date_overflows(*timestamp))
> +		die("timestamp too large for this system: %"PRItime,
> +		    *timestamp);
> +	time = (time_t)*timestamp;
> +	t = localtime(&time);
> +	*timestamp = time;
>   
>   	*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
>   	            (t->tm_year + 1900 - 1980) * 512;
> diff --git a/archive.h b/archive.h
> index 415e0152e2c..62d1d82c1af 100644
> --- a/archive.h
> +++ b/archive.h
> @@ -9,7 +9,7 @@ struct archiver_args {
>   	struct tree *tree;
>   	const unsigned char *commit_sha1;
>   	const struct commit *commit;
> -	time_t time;
> +	timestamp_t time;
>   	struct pathspec pathspec;
>   	unsigned int verbose : 1;
>   	unsigned int worktree_attributes : 1;

time_t is converted to timestamp_t here.  Hmm.

Truncation can already occur in archive.c::parse_treeish_arg() when
assigning commit->date (of type timestamp_t) to archive_time (of type
time_t).  The overflow check should either be moved there or the type of
the latter variable should be changed, right?

Compilation and tests are still successful on Debian x86 and MinGW64
even after removing the changes above.  On the former the test "generate
tar with future mtime" in t5000 writes Epoch time stamps in both cases
(and doesn't notice because tar is unable to handle times after 2038, so
that check is skipped), on the latter it emits the correct future dates.

So what are the benefits of these changes?

Unless I'm missing something (very possible at this time of day) I'd say
drop these hunks and let me handle the fallout of the series in archive.
I was planning on improving its date handling code anyway (eventually)..

René

^ permalink raw reply	[flat|threaded] 113+ messages in thread

* [PATCH] name-rev: change a "long" variable to timestamp_t
  2017-04-26 19:29           ` [PATCH v6 6/8] Introduce a new data type " Johannes Schindelin
@ 2017-05-20  5:47             ` Junio C Hamano
  2017-05-22 13:39               ` Johannes Schindelin
  0 siblings, 1 reply; 113+ messages in thread
From: Junio C Hamano @ 2017-05-20  5:47 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin

Earlier dddbad72 ("timestamp_t: a new data type for timestamps",
2017-04-26) updated all in-core variables, fields and function
return values that are used to store "seconds since epoch" to a new
type timestamp_t.  Unfortunately one variable "cutoff", which is
used to keep track of the oldest timestamp of commit we saw on the
command line, was "long", and left behind.

Update it to timestamp_t as well.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * Noticed while merging the topic 'mg/name-rev-debug' to 'pu'.

 builtin/name-rev.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 4437475017..1f6fcae121 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -15,7 +15,7 @@ typedef struct rev_name {
 	int distance;
 } rev_name;
 
-static long cutoff = LONG_MAX;
+static timestamp_t cutoff = TIME_MAX;
 
 /* How many generations are maximally preferred over _one_ merge traversal? */
 #define MERGE_TRAVERSAL_WEIGHT 65535
-- 
2.13.0-439-gd4feae47b3


^ permalink raw reply	[flat|threaded] 113+ messages in thread

* Re: [PATCH] name-rev: change a "long" variable to timestamp_t
  2017-05-20  5:47             ` [PATCH] name-rev: change a "long" variable to timestamp_t Junio C Hamano
@ 2017-05-22 13:39               ` Johannes Schindelin
  0 siblings, 0 replies; 113+ messages in thread
From: Johannes Schindelin @ 2017-05-22 13:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi Junio,

On Sat, 20 May 2017, Junio C Hamano wrote:

> Earlier dddbad72 ("timestamp_t: a new data type for timestamps",
> 2017-04-26) updated all in-core variables, fields and function
> return values that are used to store "seconds since epoch" to a new
> type timestamp_t.  Unfortunately one variable "cutoff", which is
> used to keep track of the oldest timestamp of commit we saw on the
> command line, was "long", and left behind.
> 
> Update it to timestamp_t as well.
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Obviously correct. Thanks.

Ciao,
Dscho

^ permalink raw reply	[flat|threaded] 113+ messages in thread

end of thread, back to index

Thread overview: 113+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-27 21:30 [PATCH 0/6] Use time_t Johannes Schindelin
2017-02-27 21:30 ` [PATCH 1/6] t0006 & t5000: prepare for 64-bit " Johannes Schindelin
2017-02-27 22:55   ` Junio C Hamano
2017-02-27 21:30 ` [PATCH 2/6] Specify explicitly where we parse timestamps Johannes Schindelin
2017-02-27 22:37   ` Junio C Hamano
2017-02-27 22:51     ` Junio C Hamano
2017-02-28 10:49       ` Johannes Schindelin
2017-02-27 21:31 ` [PATCH 3/6] Introduce a new "printf format" for " Johannes Schindelin
2017-03-01 18:20   ` Junio C Hamano
2017-03-01 19:53     ` Junio C Hamano
2017-02-27 21:31 ` [PATCH 4/6] Prepare for timestamps to use 64-bit signed types Johannes Schindelin
2017-02-27 21:31 ` [PATCH 5/6] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-02-27 21:31 ` [PATCH 6/6] Use time_t where appropriate Johannes Schindelin
2017-02-27 22:48 ` [PATCH 0/6] Use time_t Junio C Hamano
2017-02-28 11:32   ` Johannes Schindelin
2017-02-28 14:28 ` Jeff King
2017-02-28 15:01   ` Johannes Schindelin
2017-02-28 16:38   ` René Scharfe
2017-02-28 18:55     ` Junio C Hamano
2017-02-28 20:04       ` Jeff King
2017-02-28 20:54       ` Johannes Schindelin
2017-02-28 21:31         ` Jeff King
2017-02-28 21:31         ` René Scharfe
2017-02-28 23:10           ` Johannes Schindelin
2017-03-01  0:59             ` René Scharfe
2017-02-28 17:26   ` Junio C Hamano
2017-02-28 20:01     ` Jeff King
2017-02-28 22:27       ` Junio C Hamano
2017-02-28 22:33         ` Jeff King
2017-03-01 17:23           ` Junio C Hamano
2017-04-02 19:06 ` [PATCH v2 0/8] Introduce timestamp_t for timestamps Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-04-03  4:22     ` Torsten Bögershausen
2017-04-03 22:47       ` Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
2017-04-03  4:26     ` Torsten Bögershausen
2017-04-03 22:50       ` Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 5/8] Introduce a new "printf format" for " Johannes Schindelin
2017-04-02 19:06   ` [PATCH v2 6/8] Introduce a new data type " Johannes Schindelin
2017-04-02 19:07   ` [PATCH v2 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
2017-04-02 19:07   ` [PATCH v2 8/8] Use uintmax_t for " Johannes Schindelin
2017-04-20 20:52   ` [PATCH v3 0/8] Introduce timestamp_t " Johannes Schindelin
2017-04-20 20:52     ` [PATCH v3 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-04-20 20:52     ` [PATCH v3 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
2017-04-20 20:58     ` [PATCH v3 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
2017-04-20 20:58     ` [PATCH v3 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
2017-04-20 20:58     ` [PATCH v3 5/8] Introduce a new "printf format" for " Johannes Schindelin
2017-04-20 20:58     ` [PATCH v3 6/8] Introduce a new data type " Johannes Schindelin
2017-04-20 20:58     ` [PATCH v3 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
2017-04-20 20:59     ` [PATCH v3 8/8] Use uintmax_t for " Johannes Schindelin
2017-04-21  6:05     ` [PATCH v3 0/8] Introduce timestamp_t " Junio C Hamano
2017-04-21 10:44       ` Johannes Schindelin
2017-04-21 10:45     ` [PATCH v4 0/9] " Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 1/9] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 2/9] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 3/9] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 4/9] Specify explicitly where we parse timestamps Johannes Schindelin
2017-04-24  3:19         ` Junio C Hamano
2017-04-21 10:45       ` [PATCH v4 5/9] Introduce a new "printf format" for " Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 6/9] Introduce a new data type " Johannes Schindelin
2017-04-21 10:45       ` [PATCH v4 7/9] Abort if the system time cannot handle one of our " Johannes Schindelin
2017-04-24  3:16         ` Junio C Hamano
2017-04-24 13:57           ` Johannes Schindelin
2017-04-25  2:37             ` Junio C Hamano
2017-04-25  3:56             ` Junio C Hamano
2017-04-21 10:46       ` [PATCH v4 8/9] Use uintmax_t for " Johannes Schindelin
2017-04-24  3:24         ` Junio C Hamano
2017-04-24 10:28           ` Johannes Schindelin
2017-04-25  3:59             ` Junio C Hamano
2017-04-25 20:10               ` Johannes Schindelin
2017-04-26  1:52                 ` Junio C Hamano
2017-04-26  3:45                   ` Junio C Hamano
2017-04-26  9:32                   ` Johannes Schindelin
2017-04-26 13:18                     ` Junio C Hamano
2017-04-21 10:46       ` [PATCH v4 9/9] show_date_ident(): defer date overflow check Johannes Schindelin
2017-04-24  3:29       ` [PATCH v4 0/9] Introduce timestamp_t for timestamps Junio C Hamano
2017-04-24  6:15         ` Jacob Keller
2017-04-24 14:02           ` Johannes Schindelin
2017-04-24 11:37         ` Jeff King
2017-04-25 20:13           ` Johannes Schindelin
2017-04-24 14:00         ` Johannes Schindelin
2017-04-24 13:57       ` [PATCH v5 0/8] " Johannes Schindelin
2017-04-24 13:57         ` [PATCH v5 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-04-24 13:57         ` [PATCH v5 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
2017-04-24 13:58         ` [PATCH v5 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
2017-04-24 13:58         ` [PATCH v5 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
2017-04-25  5:59           ` Junio C Hamano
2017-04-24 13:58         ` [PATCH v5 5/8] Introduce a new "printf format" for " Johannes Schindelin
2017-04-24 13:58         ` [PATCH v5 6/8] Introduce a new data type " Johannes Schindelin
2017-04-26 16:43           ` Johannes Sixt
2017-04-26 19:18             ` Johannes Schindelin
2017-04-26 22:32           ` René Scharfe
2017-04-24 13:58         ` [PATCH v5 7/8] Abort if the system time cannot handle one of our " Johannes Schindelin
2017-04-24 13:58         ` [PATCH v5 8/8] Use uintmax_t for " Johannes Schindelin
2017-04-26 16:36           ` Johannes Sixt
2017-04-26 19:09             ` Johannes Schindelin
2017-04-25 21:54         ` [PATCH v5 0/8] Introduce timestamp_t " René Scharfe
2017-04-25 22:22           ` Johannes Schindelin
2017-04-26 22:09             ` René Scharfe
2017-04-26  1:56           ` Junio C Hamano
2017-04-26 19:20         ` [PATCH v6 " Johannes Schindelin
2017-04-26 19:26           ` [PATCH v6 1/8] ref-filter: avoid using `unsigned long` for catch-all data type Johannes Schindelin
2017-04-26 19:26           ` [PATCH v6 2/8] t0006 & t5000: prepare for 64-bit timestamps Johannes Schindelin
2017-04-26 19:26           ` [PATCH v6 3/8] t0006 & t5000: skip "far in the future" test when time_t is too limited Johannes Schindelin
2017-04-26 19:26           ` [PATCH v6 4/8] Specify explicitly where we parse timestamps Johannes Schindelin
2017-04-26 19:29           ` [PATCH v6 5/8] Introduce a new "printf format" for " Johannes Schindelin
2017-04-26 19:29           ` [PATCH v6 6/8] Introduce a new data type " Johannes Schindelin
2017-05-20  5:47             ` [PATCH] name-rev: change a "long" variable to timestamp_t Junio C Hamano
2017-05-22 13:39               ` Johannes Schindelin
2017-04-26 19:29           ` [PATCH v6 7/8] Abort if the system time cannot handle one of our timestamps Johannes Schindelin
2017-04-26 19:29           ` [PATCH v6 8/8] Use uintmax_t for " Johannes Schindelin

git@vger.kernel.org mailing list mirror (one of many)

Archives are clonable:
	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.org/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/
       or Tor2web: https://www.tor2web.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox