Patchwork [v6] Allow commit date before Unix epoch, clean fix for (issue2513)

login
register
mail settings
Submitter Florent Gallaire
Date April 4, 2016, 2:40 p.m.
Message ID <CAB9F1ccD_2LyxVYSdj0rkf5L+dpj=9Hg1HxSaFPHd8C8GHg8_g@mail.gmail.com>
Download mbox | patch
Permalink /patch/14345/
State Changes Requested
Delegated to: Yuya Nishihara
Headers show

Comments

Florent Gallaire - April 4, 2016, 2:40 p.m.
# HG changeset patch
# User Florent Gallaire <fgallaire@gmail.com>
# Date 1459780256 -7200
#      Mon Apr 04 16:30:56 2016 +0200
# Node ID 0fb1c7daaaf8a5c90ae4dce904a60013b9abe5aa
# Parent  ff0d3b6b287f89594bd8d0308fe2810d2a18ea01
date: reallow negative timestamp, fix for Windows buggy gmtime() (issue2513)

DVCS are very useful to store various texts (as legislation) written before
Unix epoch. Fri, 13 Dec 1901 is a nice gain over Thu, 01 Jan 1970.
Revert dd24f3e7ca9e and e1002cf9fe54, fix c208dcd0f709. Add tests.

Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Yuya Nishihara - April 6, 2016, 2:25 p.m.
On Mon, 4 Apr 2016 16:40:23 +0200, Florent Gallaire wrote:
> # HG changeset patch
> # User Florent Gallaire <fgallaire@gmail.com>
> # Date 1459780256 -7200
> #      Mon Apr 04 16:30:56 2016 +0200
> # Node ID 0fb1c7daaaf8a5c90ae4dce904a60013b9abe5aa
> # Parent  ff0d3b6b287f89594bd8d0308fe2810d2a18ea01
> date: reallow negative timestamp, fix for Windows buggy gmtime() (issue2513)

It appears that the devs agreed to allow negative timestamps, and the code
looks mostly good to me. I wanted to queue this, but I got test failure on
Windows. Can you take a look?

> +    if abs(d) > 0x7fffffff:
> +        d = cmp(d, 0) * 0x7fffffff

Nit: cmp() isn't guaranteed to return -1 or 1, and it's been removed in Python3.
Perhaps it could be replaced by min() and max().

https://docs.python.org/2.7/library/functions.html#cmp
http://stackoverflow.com/a/10635142

> --- a/tests/test-commit.t    Tue Mar 29 12:29:00 2016 -0500
> +++ b/tests/test-commit.t    Mon Apr 04 16:30:56 2016 +0200
> @@ -27,8 +27,57 @@
>    $ hg commit -d '111111111111 0' -m commit-7
>    abort: date exceeds 32 bits: 111111111111
>    [255]
> -  $ hg commit -d '-7654321 3600' -m commit-7
> -  abort: negative date value: -7654321
> +  $ hg commit -d '-111111111111 0' -m commit-7
> +  abort: date exceeds 32 bits: -111111111111
> +  [255]
> +  $ echo foo >> foo
> +  $ hg commit -d '1901-12-13 20:45:53 +0000' -m commit-7-2
> +  $ echo foo >> foo
> +  $ hg commit -d '-2147483647 0' -m commit-7-3
> +  $ hg commit -d '1901-12-13 20:45:52 +0000' -m commit-7

Perhaps we should test datestr(), for example:

  $ hg log -T '{rev} {date|isodatesec}\n' -l2
  3 1901-12-13 20:45:53 +0000
  2 1901-12-13 20:45:53 +0000

> +  abort: date exceeds 32 bits: -2147483648
> +  [255]
> +  $ hg commit -d '-2147483648 0' -m commit-7
> +  abort: date exceeds 32 bits: -2147483648
> +  [255]
> +  $ hg commit -d '1900-01-01 00:00:00 +0000' -m commit-7
> +  abort: date exceeds 32 bits: -2208988800
> +  [255]

I got different error on Windows XP (32bit) probably because parsedate()
doesn't support overflow dates. Because this patch doesn't touch parsing of
date strings, I think we can drop the tests of "1900-01-01" and "1899-12-31".

   $ hg commit -d '1900-01-01 00:00:00' -m commit-7
-  abort: date exceeds 32 bits: -2208988800
+  abort: invalid date: '1900-01-01 00:00:00'
   [255]
   $ hg commit -d '1900-01-01 +0000' -m commit-7
   abort: date exceeds 32 bits: -2208988800
   [255]
   $ hg commit -d '1900-01-01' -m commit-7
-  abort: date exceeds 32 bits: -2208988800
+  abort: invalid date: '1900-01-01'
   [255]
   $ hg commit -d '-2208988800 0' -m commit-7
   abort: date exceeds 32 bits: -2208988800

> +  $ hg commit -d '1900-01-01 00:00:00' -m commit-7
> +  abort: date exceeds 32 bits: -2208988800
> +  [255]
> +  $ hg commit -d '1900-01-01 +0000' -m commit-7
> +  abort: date exceeds 32 bits: -2208988800
> +  [255]
> +  $ hg commit -d '1900-01-01' -m commit-7
> +  abort: date exceeds 32 bits: -2208988800
> +  [255]
> +  $ hg commit -d '-2208988800 0' -m commit-7
> +  abort: date exceeds 32 bits: -2208988800
> +  [255]
> +  $ hg commit -d '1899-12-31 23:59:59 +0000' -m commit-7
> +  abort: date exceeds 32 bits: -2208988801
> +  [255]
> +  $ hg commit -d '1899-12-31 23:59:59' -m commit-7
> +  abort: invalid date: '1899-12-31 23:59:59'
> +  [255]
> +  $ hg commit -d '-2208988801 0' -m commit-7
> +  abort: date exceeds 32 bits: -2208988801
> +  [255]
> +  $ hg commit -d '1899-12-31 00:00:00 +0000' -m commit-7
> +  abort: date exceeds 32 bits: -2209075200
> +  [255]
> +  $ hg commit -d '1899-12-31 00:00:00' -m commit-7
> +  abort: invalid date: '1899-12-31 00:00:00'
> +  [255]
> +  $ hg commit -d '1899-12-31 +0000' -m commit-7
> +  abort: date exceeds 32 bits: -2209075200
> +  [255]
> +  $ hg commit -d '1899-12-31' -m commit-7
> +  abort: invalid date: '1899-12-31'
> +  [255]
> +  $ hg commit -d '-2209075200 0' -m commit-7
> +  abort: date exceeds 32 bits: -2209075200
>    [255]
Florent Gallaire - April 8, 2016, 11:50 a.m.
2016-04-06 16:25 GMT+02:00 Yuya Nishihara <yuya@tcha.org>:

>> +    if abs(d) > 0x7fffffff:
>> +        d = cmp(d, 0) * 0x7fffffff
>
> Nit: cmp() isn't guaranteed to return -1 or 1, and it's been removed in Python3.
> Perhaps it could be replaced by min() and max().
>
> https://docs.python.org/2.7/library/functions.html#cmp
> http://stackoverflow.com/a/10635142
>

Problems seem unlikely but still possible, I will fix that the simpler way.

> Perhaps we should test datestr(), for example:
>
>   $ hg log -T '{rev} {date|isodatesec}\n' -l2
>   3 1901-12-13 20:45:53 +0000
>   2 1901-12-13 20:45:53 +0000

Yes, that's fine.

> I got different error on Windows XP (32bit) probably because parsedate()
> doesn't support overflow dates. Because this patch doesn't touch parsing of
> date strings, I think we can drop the tests of "1900-01-01" and "1899-12-31".

I agree.

Florent

Patch

diff -r ff0d3b6b287f -r 0fb1c7daaaf8 mercurial/util.py
--- a/mercurial/util.py    Tue Mar 29 12:29:00 2016 -0500
+++ b/mercurial/util.py    Mon Apr 04 16:30:56 2016 +0200
@@ -1578,9 +1578,6 @@ 
     number of seconds away from UTC. if timezone is false, do not
     append time zone to string."""
     t, tz = date or makedate()
-    if t < 0:
-        t = 0   # time.gmtime(lt) fails on Windows for lt < -43200
-        tz = 0
     if "%1" in format or "%2" in format or "%z" in format:
         sign = (tz > 0) and "-" or "+"
         minutes = abs(tz) // 60
@@ -1588,12 +1585,14 @@ 
         format = format.replace("%z", "%1%2")
         format = format.replace("%1", "%c%02d" % (sign, q))
         format = format.replace("%2", "%02d" % r)
-    try:
-        t = time.gmtime(float(t) - tz)
-    except ValueError:
-        # time was out of range
-        t = time.gmtime(sys.maxint)
-    s = time.strftime(format, t)
+    d = t - tz
+    if abs(d) > 0x7fffffff:
+        d = cmp(d, 0) * 0x7fffffff
+    # Never use time.gmtime() and datetime.datetime.fromtimestamp()
+    # because they use the gmtime() system call which is buggy on Windows
+    # for negative values.
+    t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
+    s = t.strftime(format)
     return s

 def shortdate(date=None):
@@ -1714,8 +1713,6 @@ 
     # to UTC+14
     if abs(when) > 0x7fffffff:
         raise Abort(_('date exceeds 32 bits: %d') % when)
-    if when < 0:
-        raise Abort(_('negative date value: %d') % when)
     if offset < -50400 or offset > 43200:
         raise Abort(_('impossible time zone offset: %d') % offset)
     return when, offset
diff -r ff0d3b6b287f -r 0fb1c7daaaf8 tests/test-commit.t
--- a/tests/test-commit.t    Tue Mar 29 12:29:00 2016 -0500
+++ b/tests/test-commit.t    Mon Apr 04 16:30:56 2016 +0200
@@ -27,8 +27,57 @@ 
   $ hg commit -d '111111111111 0' -m commit-7
   abort: date exceeds 32 bits: 111111111111
   [255]
-  $ hg commit -d '-7654321 3600' -m commit-7
-  abort: negative date value: -7654321
+  $ hg commit -d '-111111111111 0' -m commit-7
+  abort: date exceeds 32 bits: -111111111111
+  [255]
+  $ echo foo >> foo
+  $ hg commit -d '1901-12-13 20:45:53 +0000' -m commit-7-2
+  $ echo foo >> foo
+  $ hg commit -d '-2147483647 0' -m commit-7-3
+  $ hg commit -d '1901-12-13 20:45:52 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2147483648
+  [255]
+  $ hg commit -d '-2147483648 0' -m commit-7
+  abort: date exceeds 32 bits: -2147483648
+  [255]
+  $ hg commit -d '1900-01-01 00:00:00 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2208988800
+  [255]
+  $ hg commit -d '1900-01-01 00:00:00' -m commit-7
+  abort: date exceeds 32 bits: -2208988800
+  [255]
+  $ hg commit -d '1900-01-01 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2208988800
+  [255]
+  $ hg commit -d '1900-01-01' -m commit-7
+  abort: date exceeds 32 bits: -2208988800
+  [255]
+  $ hg commit -d '-2208988800 0' -m commit-7
+  abort: date exceeds 32 bits: -2208988800
+  [255]
+  $ hg commit -d '1899-12-31 23:59:59 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2208988801
+  [255]
+  $ hg commit -d '1899-12-31 23:59:59' -m commit-7
+  abort: invalid date: '1899-12-31 23:59:59'
+  [255]
+  $ hg commit -d '-2208988801 0' -m commit-7
+  abort: date exceeds 32 bits: -2208988801
+  [255]
+  $ hg commit -d '1899-12-31 00:00:00 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2209075200
+  [255]
+  $ hg commit -d '1899-12-31 00:00:00' -m commit-7
+  abort: invalid date: '1899-12-31 00:00:00'
+  [255]
+  $ hg commit -d '1899-12-31 +0000' -m commit-7
+  abort: date exceeds 32 bits: -2209075200
+  [255]
+  $ hg commit -d '1899-12-31' -m commit-7
+  abort: invalid date: '1899-12-31'
+  [255]
+  $ hg commit -d '-2209075200 0' -m commit-7
+  abort: date exceeds 32 bits: -2209075200
   [255]

 commit added file that has been deleted
@@ -54,7 +103,7 @@ 
   dir/file
   committing manifest
   committing changelog
-  committed changeset 2:d2a76177cb42
+  committed changeset 4:76aab26859d7

   $ echo > dir.file
   $ hg add
@@ -78,7 +127,7 @@ 
   dir/file
   committing manifest
   committing changelog
-  committed changeset 3:1cd62a2d8db5
+  committed changeset 5:9a50557f1baf
   $ cd ..

   $ hg commit -m commit-14 does-not-exist
@@ -102,7 +151,7 @@ 
   dir/file
   committing manifest
   committing changelog
-  committed changeset 4:49176991390e
+  committed changeset 6:4b4c75bf422d

 An empty date was interpreted as epoch origin
_______________________________________________