D2055: util: extract parsedate

lothiraldan (Boris Feld) phabricator at mercurial-scm.org
Mon Feb 5 16:26:19 UTC 2018


lothiraldan created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Extract parsedate from util.py to utils/dateutil.py

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2055

AFFECTED FILES
  hgext/convert/convcmd.py
  hgext/convert/cvsps.py
  hgext/convert/subversion.py
  hgext/fetch.py
  hgext/gpg.py
  hgext/keyword.py
  hgext/mq.py
  hgext/patchbomb.py
  mercurial/changelog.py
  mercurial/cmdutil.py
  mercurial/commands.py
  mercurial/context.py
  mercurial/debugcommands.py
  mercurial/obsolete.py
  mercurial/templater.py
  mercurial/ui.py
  mercurial/util.py
  mercurial/utils/dateutil.py
  tests/fakedirstatewritetime.py
  tests/fakepatchtime.py

CHANGE DETAILS

diff --git a/tests/fakepatchtime.py b/tests/fakepatchtime.py
--- a/tests/fakepatchtime.py
+++ b/tests/fakepatchtime.py
@@ -7,8 +7,8 @@
     extensions,
     patch as patchmod,
     registrar,
-    util,
 )
+from mercurial.utils import dateutil
 
 configtable = {}
 configitem = registrar.configitem(configtable)
@@ -30,7 +30,7 @@
     if fakenow:
         # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between
         # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
-        fakenow = util.parsedate(fakenow, ['%Y%m%d%H%M'])[0]
+        fakenow = dateutil.parsedate(fakenow, ['%Y%m%d%H%M'])[0]
         for f in files:
             repo.wvfs.utime(f, (fakenow, fakenow))
 
diff --git a/tests/fakedirstatewritetime.py b/tests/fakedirstatewritetime.py
--- a/tests/fakedirstatewritetime.py
+++ b/tests/fakedirstatewritetime.py
@@ -13,8 +13,8 @@
     extensions,
     policy,
     registrar,
-    util,
 )
+from mercurial.utils import dateutil
 
 configtable = {}
 configitem = registrar.configitem(configtable)
@@ -49,7 +49,7 @@
 
     # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between
     # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
-    fakenow = util.parsedate(fakenow, ['%Y%m%d%H%M'])[0]
+    fakenow = dateutil.parsedate(fakenow, ['%Y%m%d%H%M'])[0]
 
     orig_pack_dirstate = parsers.pack_dirstate
     orig_dirstate_getfsnow = dirstate._getfsnow
diff --git a/mercurial/utils/dateutil.py b/mercurial/utils/dateutil.py
--- a/mercurial/utils/dateutil.py
+++ b/mercurial/utils/dateutil.py
@@ -174,3 +174,82 @@
         unixtime = localunixtime + offset
     return unixtime, offset
 
+def parsedate(date, formats=None, bias=None):
+    """parse a localized date/time and return a (unixtime, offset) tuple.
+
+    The date may be a "unixtime offset" string or in one of the specified
+    formats. If the date already is a (unixtime, offset) tuple, it is returned.
+
+    >>> parsedate(b' today ') == parsedate(
+    ...     datetime.date.today().strftime('%b %d').encode('ascii'))
+    True
+    >>> parsedate(b'yesterday ') == parsedate(
+    ...     (datetime.date.today() - datetime.timedelta(days=1)
+    ...      ).strftime('%b %d').encode('ascii'))
+    True
+    >>> now, tz = makedate()
+    >>> strnow, strtz = parsedate(b'now')
+    >>> (strnow - now) < 1
+    True
+    >>> tz == strtz
+    True
+    """
+    if bias is None:
+        bias = {}
+    if not date:
+        return 0, 0
+    if isinstance(date, tuple) and len(date) == 2:
+        return date
+    if not formats:
+        formats = defaultdateformats
+    date = date.strip()
+
+    if date == 'now' or date == _('now'):
+        return makedate()
+    if date == 'today' or date == _('today'):
+        date = datetime.date.today().strftime(r'%b %d')
+        date = encoding.strtolocal(date)
+    elif date == 'yesterday' or date == _('yesterday'):
+        date = (datetime.date.today() -
+                datetime.timedelta(days=1)).strftime(r'%b %d')
+        date = encoding.strtolocal(date)
+
+    try:
+        when, offset = map(int, date.split(' '))
+    except ValueError:
+        # fill out defaults
+        now = makedate()
+        defaults = {}
+        for part in ("d", "mb", "yY", "HI", "M", "S"):
+            # this piece is for rounding the specific end of unknowns
+            b = bias.get(part)
+            if b is None:
+                if part[0:1] in "HMS":
+                    b = "00"
+                else:
+                    b = "0"
+
+            # this piece is for matching the generic end to today's date
+            n = datestr(now, "%" + part[0:1])
+
+            defaults[part] = (b, n)
+
+        for format in formats:
+            try:
+                when, offset = strdate(date, format, defaults)
+            except (ValueError, OverflowError):
+                pass
+            else:
+                break
+        else:
+            raise error.ParseError(_('invalid date: %r') % date)
+    # validate explicit (probably user-specified) date and
+    # time zone offset. values must fit in signed 32 bits for
+    # current 32-bit linux runtimes. timezones go from UTC-12
+    # to UTC+14
+    if when < -0x80000000 or when > 0x7fffffff:
+        raise error.ParseError(_('date exceeds 32 bits: %d') % when)
+    if offset < -50400 or offset > 43200:
+        raise error.ParseError(_('impossible time zone offset: %d') % offset)
+    return when, offset
+
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -20,7 +20,6 @@
 import codecs
 import collections
 import contextlib
-import datetime
 import errno
 import gc
 import hashlib
@@ -1905,85 +1904,6 @@
             limit -= len(s)
         yield s
 
-def parsedate(date, formats=None, bias=None):
-    """parse a localized date/time and return a (unixtime, offset) tuple.
-
-    The date may be a "unixtime offset" string or in one of the specified
-    formats. If the date already is a (unixtime, offset) tuple, it is returned.
-
-    >>> parsedate(b' today ') == parsedate(
-    ...     datetime.date.today().strftime('%b %d').encode('ascii'))
-    True
-    >>> parsedate(b'yesterday ') == parsedate(
-    ...     (datetime.date.today() - datetime.timedelta(days=1)
-    ...      ).strftime('%b %d').encode('ascii'))
-    True
-    >>> now, tz = dateutil.makedate()
-    >>> strnow, strtz = parsedate(b'now')
-    >>> (strnow - now) < 1
-    True
-    >>> tz == strtz
-    True
-    """
-    if bias is None:
-        bias = {}
-    if not date:
-        return 0, 0
-    if isinstance(date, tuple) and len(date) == 2:
-        return date
-    if not formats:
-        formats = defaultdateformats
-    date = date.strip()
-
-    if date == 'now' or date == _('now'):
-        return dateutil.makedate()
-    if date == 'today' or date == _('today'):
-        date = datetime.date.today().strftime(r'%b %d')
-        date = encoding.strtolocal(date)
-    elif date == 'yesterday' or date == _('yesterday'):
-        date = (datetime.date.today() -
-                datetime.timedelta(days=1)).strftime(r'%b %d')
-        date = encoding.strtolocal(date)
-
-    try:
-        when, offset = map(int, date.split(' '))
-    except ValueError:
-        # fill out defaults
-        now = dateutil.makedate()
-        defaults = {}
-        for part in ("d", "mb", "yY", "HI", "M", "S"):
-            # this piece is for rounding the specific end of unknowns
-            b = bias.get(part)
-            if b is None:
-                if part[0:1] in "HMS":
-                    b = "00"
-                else:
-                    b = "0"
-
-            # this piece is for matching the generic end to today's date
-            n = dateutil.datestr(now, "%" + part[0:1])
-
-            defaults[part] = (b, n)
-
-        for format in formats:
-            try:
-                when, offset = dateutil.strdate(date, format, defaults)
-            except (ValueError, OverflowError):
-                pass
-            else:
-                break
-        else:
-            raise error.ParseError(_('invalid date: %r') % date)
-    # validate explicit (probably user-specified) date and
-    # time zone offset. values must fit in signed 32 bits for
-    # current 32-bit linux runtimes. timezones go from UTC-12
-    # to UTC+14
-    if when < -0x80000000 or when > 0x7fffffff:
-        raise error.ParseError(_('date exceeds 32 bits: %d') % when)
-    if offset < -50400 or offset > 43200:
-        raise error.ParseError(_('impossible time zone offset: %d') % offset)
-    return when, offset
-
 def matchdate(date):
     """Return a function that matches a given date match specifier
 
@@ -1995,11 +1915,11 @@
 
     '>{date}' on or after a given date
 
-    >>> p1 = parsedate(b"10:29:59")
-    >>> p2 = parsedate(b"10:30:00")
-    >>> p3 = parsedate(b"10:30:59")
-    >>> p4 = parsedate(b"10:31:00")
-    >>> p5 = parsedate(b"Sep 15 10:30:00 1999")
+    >>> p1 = dateutil.parsedate(b"10:29:59")
+    >>> p2 = dateutil.parsedate(b"10:30:00")
+    >>> p3 = dateutil.parsedate(b"10:30:59")
+    >>> p4 = dateutil.parsedate(b"10:31:00")
+    >>> p5 = dateutil.parsedate(b"Sep 15 10:30:00 1999")
     >>> f = matchdate(b"10:30")
     >>> f(p1[0])
     False
@@ -2015,18 +1935,18 @@
 
     def lower(date):
         d = {'mb': "1", 'd': "1"}
-        return parsedate(date, extendeddateformats, d)[0]
+        return dateutil.parsedate(date, extendeddateformats, d)[0]
 
     def upper(date):
         d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
         for days in ("31", "30", "29"):
             try:
                 d["d"] = days
-                return parsedate(date, extendeddateformats, d)[0]
+                return dateutil.parsedate(date, extendeddateformats, d)[0]
             except Abort:
                 pass
         d["d"] = "28"
-        return parsedate(date, extendeddateformats, d)[0]
+        return dateutil.parsedate(date, extendeddateformats, d)[0]
 
     date = date.strip()
 
@@ -3827,3 +3747,9 @@
     nouideprecwarn(msg, "4.6")
     return dateutil.strdate(*args, **kwargs)
 
+def parsedate(*args, **kwargs):
+    msg = ("'util.parsedate' is deprecated, "
+           "use 'utils.dateutil.parsedate'")
+    nouideprecwarn(msg, "4.6")
+    return dateutil.parsedate(*args, **kwargs)
+
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -37,6 +37,7 @@
     scmutil,
     util,
 )
+from .utils import dateutil
 
 urlreq = util.urlreq
 
@@ -722,7 +723,7 @@
         (0, 0)
         """
         if self.config(section, name, default, untrusted):
-            return self.configwith(util.parsedate, section, name, default,
+            return self.configwith(dateutil.parsedate, section, name, default,
                                    'date', untrusted)
         if default is _unset:
             return None
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -905,7 +905,7 @@
 
     date = evalfuncarg(context, mapping, args[0])
     try:
-        date = util.parsedate(date)
+        date = dateutil.parsedate(date)
     except AttributeError:  # not str nor date tuple
         # i18n: "localdate" is a keyword
         raise error.ParseError(_("localdate expects a date information"))
diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -609,7 +609,7 @@
         if date is None:
             if 'date' in metadata:
                 # as a courtesy for out-of-tree extensions
-                date = util.parsedate(metadata.pop('date'))
+                date = dateutil.parsedate(metadata.pop('date'))
             elif ui is not None:
                 date = ui.configdate('devel', 'default-date')
                 if date is None:
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -557,9 +557,9 @@
 def debugdate(ui, date, range=None, **opts):
     """parse and display a date"""
     if opts[r"extended"]:
-        d = util.parsedate(date, util.extendeddateformats)
+        d = dateutil.parsedate(date, util.extendeddateformats)
     else:
-        d = util.parsedate(date)
+        d = dateutil.parsedate(date)
     ui.write(("internal: %s %s\n") % d)
     ui.write(("standard: %s\n") % dateutil.datestr(d))
     if range:
@@ -1574,7 +1574,7 @@
             try:
                 date = opts.get('date')
                 if date:
-                    date = util.parsedate(date)
+                    date = dateutil.parsedate(date)
                 else:
                     date = None
                 prec = parsenodeid(precursor)
diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1331,7 +1331,7 @@
         self._node = None
         self._text = text
         if date:
-            self._date = util.parsedate(date)
+            self._date = dateutil.parsedate(date)
         if user:
             self._user = user
         if changes:
@@ -2448,7 +2448,7 @@
 
     user receives the committer name and defaults to current
     repository username, date is the commit date in any format
-    supported by util.parsedate() and defaults to current date, extra
+    supported by dateutil.parsedate() and defaults to current date, extra
     is a dictionary of metadata or is left empty.
     """
 
@@ -2663,7 +2663,7 @@
 
     user receives the committer name and defaults to current repository
     username, date is the commit date in any format supported by
-    util.parsedate() and defaults to current date, extra is a dictionary of
+    dateutil.parsedate() and defaults to current date, extra is a dictionary of
     metadata or is left empty.
     """
     def __new__(cls, repo, originalctx, *args, **kwargs):
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -584,7 +584,7 @@
 
     date = opts.get('date')
     if date:
-        opts['date'] = util.parsedate(date)
+        opts['date'] = dateutil.parsedate(date)
 
     cmdutil.checkunfinished(repo)
     cmdutil.bailifchanged(repo)
@@ -3004,7 +3004,7 @@
 
     date = opts.get('date')
     if date:
-        opts['date'] = util.parsedate(date)
+        opts['date'] = dateutil.parsedate(date)
 
     exact = opts.get('exact')
     update = not opts.get('bypass')
@@ -5330,7 +5330,7 @@
 
         date = opts.get('date')
         if date:
-            date = util.parsedate(date)
+            date = dateutil.parsedate(date)
 
         if opts.get('remove'):
             editform = 'tag.remove'
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -3118,7 +3118,7 @@
     '''commit the specified files or all outstanding changes'''
     date = opts.get('date')
     if date:
-        opts['date'] = util.parsedate(date)
+        opts['date'] = dateutil.parsedate(date)
     message = logmessage(ui, opts)
     matcher = scmutil.match(repo[None], pats, opts)
 
@@ -3183,7 +3183,7 @@
         date = opts.get('date') or old.date()
 
         # Parse the date to allow comparison between date and old.date()
-        date = util.parsedate(date)
+        date = dateutil.parsedate(date)
 
         if len(old.parents()) > 1:
             # ctx.files() isn't reliable for merges, so fall back to the
diff --git a/mercurial/changelog.py b/mercurial/changelog.py
--- a/mercurial/changelog.py
+++ b/mercurial/changelog.py
@@ -512,7 +512,7 @@
         desc = stripdesc(desc)
 
         if date:
-            parseddate = "%d %d" % util.parsedate(date)
+            parseddate = "%d %d" % dateutil.parsedate(date)
         else:
             parseddate = "%d %d" % dateutil.makedate()
         if extra:
diff --git a/hgext/patchbomb.py b/hgext/patchbomb.py
--- a/hgext/patchbomb.py
+++ b/hgext/patchbomb.py
@@ -663,7 +663,7 @@
 
     # start
     if date:
-        start_time = util.parsedate(date)
+        start_time = dateutil.parsedate(date)
     else:
         start_time = dateutil.makedate()
 
diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -1200,7 +1200,7 @@
         user = opts.get('user')
         date = opts.get('date')
         if date:
-            date = util.parsedate(date)
+            date = dateutil.parsedate(date)
         diffopts = self.diffopts({'git': opts.get('git')}, plain=True)
         if opts.get('checkname', True):
             self.checkpatchname(patchfn)
@@ -1646,7 +1646,7 @@
         newuser = opts.get('user')
         newdate = opts.get('date')
         if newdate:
-            newdate = '%d %d' % util.parsedate(newdate)
+            newdate = '%d %d' % dateutil.parsedate(newdate)
         wlock = repo.wlock()
 
         try:
diff --git a/hgext/keyword.py b/hgext/keyword.py
--- a/hgext/keyword.py
+++ b/hgext/keyword.py
@@ -156,7 +156,8 @@
 def utcdate(text):
     '''Date. Returns a UTC-date in this format: "2009/08/18 11:00:13".
     '''
-    return dateutil.datestr((util.parsedate(text)[0], 0), '%Y/%m/%d %H:%M:%S')
+    dateformat = '%Y/%m/%d %H:%M:%S'
+    return dateutil.datestr((dateutil.parsedate(text)[0], 0), dateformat)
 # date like in svn's $Date
 @templatefilter('svnisodate')
 def svnisodate(text):
@@ -170,7 +171,8 @@
     '''Date. Returns a UTC-date in this format: "2009-08-18
     11:00:13Z".
     '''
-    return dateutil.datestr((util.parsedate(text)[0], 0), '%Y-%m-%d %H:%M:%SZ')
+    dateformat = '%Y-%m-%d %H:%M:%SZ'
+    return dateutil.datestr((dateutil.parsedate(text)[0], 0), dateformat)
 
 # make keyword tools accessible
 kwtools = {'hgcmd': ''}
diff --git a/hgext/gpg.py b/hgext/gpg.py
--- a/hgext/gpg.py
+++ b/hgext/gpg.py
@@ -21,6 +21,7 @@
     registrar,
     util,
 )
+from mercurial.utils import dateutil
 
 cmdtable = {}
 command = registrar.command(cmdtable)
@@ -259,7 +260,7 @@
 
     date = opts.get('date')
     if date:
-        opts['date'] = util.parsedate(date)
+        opts['date'] = dateutil.parsedate(date)
 
     if revs:
         nodes = [repo.lookup(n) for n in revs]
diff --git a/hgext/fetch.py b/hgext/fetch.py
--- a/hgext/fetch.py
+++ b/hgext/fetch.py
@@ -23,6 +23,7 @@
     registrar,
     util,
 )
+from mercurial.utils import dateutil
 
 release = lock.release
 cmdtable = {}
@@ -64,7 +65,7 @@
     opts = pycompat.byteskwargs(opts)
     date = opts.get('date')
     if date:
-        opts['date'] = util.parsedate(date)
+        opts['date'] = dateutil.parsedate(date)
 
     parent, _p2 = repo.dirstate.parents()
     branch = repo.dirstate.branch()
diff --git a/hgext/convert/subversion.py b/hgext/convert/subversion.py
--- a/hgext/convert/subversion.py
+++ b/hgext/convert/subversion.py
@@ -891,7 +891,7 @@
             # Example SVN datetime. Includes microseconds.
             # ISO-8601 conformant
             # '2007-01-04T17:35:00.902377Z'
-            date = util.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
+            date = dateutil.parsedate(date[:19] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
             if self.ui.configbool('convert', 'localtimezone'):
                 date = makedatetimestamp(date[0])
 
diff --git a/hgext/convert/cvsps.py b/hgext/convert/cvsps.py
--- a/hgext/convert/cvsps.py
+++ b/hgext/convert/cvsps.py
@@ -337,7 +337,7 @@
             if len(d.split()) != 3:
                 # cvs log dates always in GMT
                 d = d + ' UTC'
-            e.date = util.parsedate(d, ['%y/%m/%d %H:%M:%S',
+            e.date = dateutil.parsedate(d, ['%y/%m/%d %H:%M:%S',
                                         '%Y/%m/%d %H:%M:%S',
                                         '%Y-%m-%d %H:%M:%S'])
             e.author = scache(match.group(2))
diff --git a/hgext/convert/convcmd.py b/hgext/convert/convcmd.py
--- a/hgext/convert/convcmd.py
+++ b/hgext/convert/convcmd.py
@@ -19,6 +19,7 @@
     scmutil,
     util,
 )
+from mercurial.utils import dateutil
 
 from . import (
     bzr,
@@ -356,7 +357,7 @@
             dates = {}
             def getdate(n):
                 if n not in dates:
-                    dates[n] = util.parsedate(self.commitcache[n].date)
+                    dates[n] = dateutil.parsedate(self.commitcache[n].date)
                 return dates[n]
 
             def picknext(nodes):



To: lothiraldan, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list