Mercurial > hg-stable
changeset 36636:c6061cadb400
util: extract all date-related utils in utils/dateutil module
With this commit, util.py lose 262 lines
Note for extensions author, if this commit breaks your extension, you can pull
the step-by-step split here to help you more easily pinpoint the renaming that
broke your extension:
hg pull https://bitbucket.org/octobus/mercurial-devel/ -r ac1f6453010d
Differential Revision: https://phab.mercurial-scm.org/D2282
line wrap: on
line diff
--- a/contrib/synthrepo.py Thu Feb 08 23:27:24 2018 +0530 +++ b/contrib/synthrepo.py Thu Feb 15 17:18:26 2018 +0100 @@ -59,8 +59,8 @@ patch, registrar, scmutil, - util, ) +from mercurial.utils import dateutil # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -382,7 +382,7 @@ message = 'synthesized wide repo with %d files' % (len(files),) mc = context.memctx(repo, [pctx.node(), nullid], message, files, filectxfn, ui.username(), - '%d %d' % util.makedate()) + '%d %d' % dateutil.makedate()) initnode = mc.commit() if ui.debugflag: hexfn = hex
--- a/hgext/blackbox.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/blackbox.py Thu Feb 15 17:18:26 2018 +0100 @@ -49,6 +49,7 @@ ui as uimod, util, ) +from mercurial.utils import dateutil # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -164,7 +165,7 @@ return ui._bbinlog = True default = self.configdate('devel', 'default-date') - date = util.datestr(default, '%Y/%m/%d %H:%M:%S') + date = dateutil.datestr(default, '%Y/%m/%d %H:%M:%S') user = util.getuser() pid = '%d' % util.getpid() formattedmsg = msg[0] % msg[1:]
--- a/hgext/churn.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/churn.py Thu Feb 15 17:18:26 2018 +0100 @@ -23,8 +23,8 @@ pycompat, registrar, scmutil, - util, ) +from mercurial.utils import dateutil cmdtable = {} command = registrar.command(cmdtable) @@ -65,7 +65,7 @@ rate = {} df = False if opts.get('date'): - df = util.matchdate(opts['date']) + df = dateutil.matchdate(opts['date']) m = scmutil.match(repo[None], pats, opts) def prep(ctx, fns):
--- a/hgext/convert/common.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/common.py Thu Feb 15 17:18:26 2018 +0100 @@ -541,7 +541,7 @@ self.fp = None def makedatetimestamp(t): - """Like util.makedate() but for time t instead of current time""" + """Like dateutil.makedate() but for time t instead of current time""" delta = (datetime.datetime.utcfromtimestamp(t) - datetime.datetime.fromtimestamp(t)) tz = delta.days * 86400 + delta.seconds
--- a/hgext/convert/convcmd.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/convcmd.py Thu Feb 15 17:18:26 2018 +0100 @@ -19,6 +19,7 @@ scmutil, util, ) +from mercurial.utils import dateutil from . import ( bzr, @@ -355,7 +356,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):
--- a/hgext/convert/cvs.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/cvs.py Thu Feb 15 17:18:26 2018 +0100 @@ -18,6 +18,7 @@ pycompat, util, ) +from mercurial.utils import dateutil from . import ( common, @@ -93,7 +94,7 @@ cs.comment = self.recode(cs.comment) if self.ui.configbool('convert', 'localtimezone'): cs.date = makedatetimestamp(cs.date[0]) - date = util.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2') + date = dateutil.datestr(cs.date, '%Y-%m-%d %H:%M:%S %1%2') self.tags.update(dict.fromkeys(cs.tags, id)) files = {}
--- a/hgext/convert/cvsps.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/cvsps.py Thu Feb 15 17:18:26 2018 +0100 @@ -17,6 +17,7 @@ pycompat, util, ) +from mercurial.utils import dateutil pickle = util.pickle @@ -192,7 +193,7 @@ if oldlog: date = oldlog[-1].date # last commit date as a (time,tz) tuple - date = util.datestr(date, '%Y/%m/%d %H:%M:%S %1%2') + date = dateutil.datestr(date, '%Y/%m/%d %H:%M:%S %1%2') # build the CVS commandline cmd = ['cvs', '-q'] @@ -336,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)) @@ -901,7 +902,7 @@ # bug-for-bug compatibility with cvsps. ui.write('---------------------\n') ui.write(('PatchSet %d \n' % cs.id)) - ui.write(('Date: %s\n' % util.datestr(cs.date, + ui.write(('Date: %s\n' % dateutil.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2'))) ui.write(('Author: %s\n' % cs.author)) ui.write(('Branch: %s\n' % (cs.branch or 'HEAD')))
--- a/hgext/convert/darcs.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/darcs.py Thu Feb 15 17:18:26 2018 +0100 @@ -16,6 +16,7 @@ error, util, ) +from mercurial.utils import dateutil from . import common NoRepo = common.NoRepo @@ -148,12 +149,14 @@ def getcommit(self, rev): elt = self.changes[rev] - date = util.strdate(elt.get('local_date'), '%a %b %d %H:%M:%S %Z %Y') + dateformat = '%a %b %d %H:%M:%S %Z %Y' + date = dateutil.strdate(elt.get('local_date'), dateformat) desc = elt.findtext('name') + '\n' + elt.findtext('comment', '') # etree can return unicode objects for name, comment, and author, # so recode() is used to ensure str objects are emitted. + newdateformat = '%Y-%m-%d %H:%M:%S %1%2' return common.commit(author=self.recode(elt.get('author')), - date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), + date=dateutil.datestr(date, newdateformat), desc=self.recode(desc).strip(), parents=self.parents[rev])
--- a/hgext/convert/gnuarch.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/gnuarch.py Thu Feb 15 17:18:26 2018 +0100 @@ -19,6 +19,7 @@ error, util, ) +from mercurial.utils import dateutil from . import common class gnuarch_source(common.converter_source, common.commandline): @@ -280,8 +281,8 @@ catlog = self.catlogparser.parsestr(data) # Commit date - self.changes[rev].date = util.datestr( - util.strdate(catlog['Standard-date'], + self.changes[rev].date = dateutil.datestr( + dateutil.strdate(catlog['Standard-date'], '%Y-%m-%d %H:%M:%S')) # Commit author
--- a/hgext/convert/hg.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/hg.py Thu Feb 15 17:18:26 2018 +0100 @@ -36,6 +36,7 @@ scmutil, util, ) +from mercurial.utils import dateutil stringio = util.stringio from . import common @@ -583,7 +584,7 @@ crev = rev return common.commit(author=ctx.user(), - date=util.datestr(ctx.date(), + date=dateutil.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'), desc=ctx.description(), rev=crev,
--- a/hgext/convert/monotone.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/monotone.py Thu Feb 15 17:18:26 2018 +0100 @@ -14,8 +14,8 @@ from mercurial import ( error, pycompat, - util, ) +from mercurial.utils import dateutil from . import common @@ -310,9 +310,10 @@ certs = self.mtngetcerts(rev) if certs.get('suspend') == certs["branch"]: extra['close'] = 1 + dateformat = "%Y-%m-%dT%H:%M:%S" return common.commit( author=certs["author"], - date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")), + date=dateutil.datestr(dateutil.strdate(certs["date"], dateformat)), desc=certs["changelog"], rev=rev, parents=self.mtnrun("parents", rev).splitlines(),
--- a/hgext/convert/p4.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/p4.py Thu Feb 15 17:18:26 2018 +0100 @@ -14,6 +14,7 @@ error, util, ) +from mercurial.utils import dateutil from . import common @@ -346,7 +347,7 @@ parents = [] return common.commit(author=self.recode(obj["user"]), - date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), + date=dateutil.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), parents=parents, desc=desc, branch=None, rev=obj['change'], extra={"p4": obj['change'], "convert_revision": obj['change']})
--- a/hgext/convert/subversion.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/convert/subversion.py Thu Feb 15 17:18:26 2018 +0100 @@ -16,6 +16,7 @@ util, vfs as vfsmod, ) +from mercurial.utils import dateutil from . import common @@ -891,7 +892,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]) @@ -913,7 +914,7 @@ branch = None cset = commit(author=author, - date=util.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), + date=dateutil.datestr(date, '%Y-%m-%d %H:%M:%S %1%2'), desc=log, parents=parents, branch=branch,
--- a/hgext/fetch.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/fetch.py Thu Feb 15 17:18:26 2018 +0100 @@ -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()
--- a/hgext/gpg.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/gpg.py Thu Feb 15 17:18:26 2018 +0100 @@ -21,6 +21,7 @@ registrar, util, ) +from mercurial.utils import dateutil cmdtable = {} command = registrar.command(cmdtable) @@ -258,7 +259,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]
--- a/hgext/journal.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/journal.py Thu Feb 15 17:18:26 2018 +0100 @@ -35,6 +35,7 @@ registrar, util, ) +from mercurial.utils import dateutil cmdtable = {} command = registrar.command(cmdtable) @@ -326,7 +327,7 @@ newhashes = [newhashes] entry = journalentry( - util.makedate(), self.user, self.command, namespace, name, + dateutil.makedate(), self.user, self.command, namespace, name, oldhashes, newhashes) vfs = self.vfs
--- a/hgext/keyword.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/keyword.py Thu Feb 15 17:18:26 2018 +0100 @@ -111,6 +111,7 @@ templatefilters, util, ) +from mercurial.utils import dateutil cmdtable = {} command = registrar.command(cmdtable) @@ -156,21 +157,23 @@ def utcdate(text): '''Date. Returns a UTC-date in this format: "2009/08/18 11:00:13". ''' - return util.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): '''Date. Returns a date in this format: "2009-08-18 13:00:13 +0200 (Tue, 18 Aug 2009)". ''' - return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') + return dateutil.datestr(text, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') # date like in svn's $Id @templatefilter('svnutcdate') def svnutcdate(text): '''Date. Returns a UTC-date in this format: "2009-08-18 11:00:13Z". ''' - return util.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': ''}
--- a/hgext/mq.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/mq.py Thu Feb 15 17:18:26 2018 +0100 @@ -98,6 +98,7 @@ util, vfs as vfsmod, ) +from mercurial.utils import dateutil release = lockmod.release seriesopts = [('s', 'summary', None, _('print first line of patch header'))] @@ -1201,7 +1202,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) @@ -1644,7 +1645,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: @@ -2596,7 +2597,7 @@ if not opts.get('user') and opts.get('currentuser'): opts['user'] = ui.username() if not opts.get('date') and opts.get('currentdate'): - opts['date'] = "%d %d" % util.makedate() + opts['date'] = "%d %d" % dateutil.makedate() @command("^qnew", [('e', 'edit', None, _('invoke editor on commit messages')),
--- a/hgext/notify.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/notify.py Thu Feb 15 17:18:26 2018 +0100 @@ -149,6 +149,7 @@ registrar, util, ) +from mercurial.utils import dateutil # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -360,7 +361,7 @@ for k, v in headers: msg[k] = v - msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") + msg['Date'] = dateutil.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") # try to make subject line exist and be useful if not subject:
--- a/hgext/patchbomb.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/patchbomb.py Thu Feb 15 17:18:26 2018 +0100 @@ -99,6 +99,7 @@ templater, util, ) +from mercurial.utils import dateutil stringio = util.stringio cmdtable = {} @@ -665,9 +666,9 @@ # start if date: - start_time = util.parsedate(date) + start_time = dateutil.parsedate(date) else: - start_time = util.makedate() + start_time = dateutil.makedate() def genmsgid(id): return '<%s.%d@%s>' % (id[:20], int(start_time[0]),
--- a/hgext/shelve.py Thu Feb 08 23:27:24 2018 +0530 +++ b/hgext/shelve.py Thu Feb 15 17:18:26 2018 +0100 @@ -55,6 +55,7 @@ from . import ( rebase, ) +from mercurial.utils import dateutil cmdtable = {} command = registrar.command(cmdtable) @@ -563,7 +564,8 @@ continue ui.write(' ' * (16 - len(sname))) used = 16 - age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True) + date = dateutil.makedate(mtime) + age = '(%s)' % templatefilters.age(date, abbrev=True) ui.write(age, label='shelve.age') ui.write(' ' * (12 - len(age))) used += 12
--- a/mercurial/changelog.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/changelog.py Thu Feb 15 17:18:26 2018 +0100 @@ -24,6 +24,7 @@ revlog, util, ) +from .utils import dateutil _defaultextra = {'branch': 'default'} @@ -524,9 +525,9 @@ desc = stripdesc(desc) if date: - parseddate = "%d %d" % util.parsedate(date) + parseddate = "%d %d" % dateutil.parsedate(date) else: - parseddate = "%d %d" % util.makedate() + parseddate = "%d %d" % dateutil.makedate() if extra: branch = extra.get("branch") if branch in ("default", ""):
--- a/mercurial/cmdutil.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/cmdutil.py Thu Feb 15 17:18:26 2018 +0100 @@ -47,6 +47,7 @@ util, vfs as vfsmod, ) +from .utils import dateutil stringio = util.stringio # templates of common command options @@ -1530,7 +1531,7 @@ write("# HG changeset patch\n") write("# User %s\n" % ctx.user()) write("# Date %d %d\n" % ctx.date()) - write("# %s\n" % util.datestr(ctx.date())) + write("# %s\n" % dateutil.datestr(ctx.date())) if branch and branch != 'default': write("# Branch %s\n" % branch) write("# Node ID %s\n" % hex(node)) @@ -1629,7 +1630,7 @@ def finddate(ui, repo, date): """Find the tipmost changeset that matches the given date spec""" - df = util.matchdate(date) + df = dateutil.matchdate(date) m = scmutil.matchall(repo) results = {} @@ -1642,7 +1643,7 @@ rev = ctx.rev() if rev in results: ui.status(_("found revision %s from %s\n") % - (rev, util.datestr(results[rev]))) + (rev, dateutil.datestr(results[rev]))) return '%d' % rev raise error.Abort(_("revision matching date not found")) @@ -2261,7 +2262,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) @@ -2326,7 +2327,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
--- a/mercurial/commands.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/commands.py Thu Feb 15 17:18:26 2018 +0100 @@ -61,6 +61,7 @@ util, wireprotoserver, ) +from .utils import dateutil release = lockmod.release @@ -302,9 +303,9 @@ rootfm = ui.formatter('annotate', opts) if ui.quiet: - datefunc = util.shortdate + datefunc = dateutil.shortdate else: - datefunc = util.datestr + datefunc = dateutil.datestr if ctx.rev() is None: def hexfn(node): if node is None: @@ -584,7 +585,7 @@ date = opts.get('date') if date: - opts['date'] = util.parsedate(date) + opts['date'] = dateutil.parsedate(date) cmdutil.checkunfinished(repo) cmdutil.bailifchanged(repo) @@ -2161,7 +2162,7 @@ if not opts.get('user') and opts.get('currentuser'): opts['user'] = ui.username() if not opts.get('date') and opts.get('currentdate'): - opts['date'] = "%d %d" % util.makedate() + opts['date'] = "%d %d" % dateutil.makedate() editor = cmdutil.getcommiteditor(editform='graft', **pycompat.strkwargs(opts)) @@ -3011,7 +3012,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') @@ -5307,7 +5308,7 @@ date = opts.get('date') if date: - date = util.parsedate(date) + date = dateutil.parsedate(date) if opts.get('remove'): editform = 'tag.remove'
--- a/mercurial/context.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/context.py Thu Feb 15 17:18:26 2018 +0100 @@ -49,6 +49,7 @@ subrepoutil, util, ) +from .utils import dateutil propertycache = util.propertycache @@ -1287,7 +1288,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: @@ -1364,7 +1365,7 @@ ui = self._repo.ui date = ui.configdate('devel', 'default-date') if date is None: - date = util.makedate() + date = dateutil.makedate() return date def subrev(self, subpath): @@ -2111,11 +2112,11 @@ if data is None: raise error.ProgrammingError("data must be non-None") self._auditconflicts(path) - self._markdirty(path, exists=True, data=data, date=util.makedate(), + self._markdirty(path, exists=True, data=data, date=dateutil.makedate(), flags=flags) def setflags(self, path, l, x): - self._markdirty(path, exists=True, date=util.makedate(), + self._markdirty(path, exists=True, date=dateutil.makedate(), flags=(l and 'l' or '') + (x and 'x' or '')) def remove(self, path): @@ -2404,7 +2405,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. """ @@ -2619,7 +2620,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):
--- a/mercurial/debugcommands.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/debugcommands.py Thu Feb 15 17:18:26 2018 +0100 @@ -77,6 +77,7 @@ vfs as vfsmod, wireprotoserver, ) +from .utils import dateutil release = lockmod.release @@ -560,13 +561,13 @@ 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: %d %d\n") % d) - ui.write(("standard: %s\n") % util.datestr(d)) + ui.write(("standard: %s\n") % dateutil.datestr(d)) if range: - m = util.matchdate(range) + m = dateutil.matchdate(range) ui.write(("match: %s\n") % m(d[0])) @command('debugdeltachain', @@ -1578,7 +1579,7 @@ try: date = opts.get('date') if date: - date = util.parsedate(date) + date = dateutil.parsedate(date) else: date = None prec = parsenodeid(precursor)
--- a/mercurial/formatter.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/formatter.py Thu Feb 15 17:18:26 2018 +0100 @@ -126,6 +126,7 @@ templater, util, ) +from .utils import dateutil pickle = util.pickle @@ -243,7 +244,7 @@ @staticmethod def formatdate(date, fmt): '''stringify date tuple in the given format''' - return util.datestr(date, fmt) + return dateutil.datestr(date, fmt) @staticmethod def formatdict(data, key, value, fmt, sep): '''stringify key-value pairs separated by sep'''
--- a/mercurial/hgweb/hgwebdir_mod.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/hgweb/hgwebdir_mod.py Thu Feb 15 17:18:26 2018 +0100 @@ -46,6 +46,7 @@ webutil, wsgicgi, ) +from ..utils import dateutil def cleannames(items): return [(util.pconvert(name).strip('/'), path) for name, path in items] @@ -376,7 +377,7 @@ if directory: # get the directory's time information try: - d = (get_mtime(path), util.makedate()[1]) + d = (get_mtime(path), dateutil.makedate()[1]) except OSError: continue @@ -425,7 +426,7 @@ u.warn(_('error accessing repository at %s\n') % path) continue try: - d = (get_mtime(r.spath), util.makedate()[1]) + d = (get_mtime(r.spath), dateutil.makedate()[1]) except OSError: continue
--- a/mercurial/logcmdutil.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/logcmdutil.py Thu Feb 15 17:18:26 2018 +0100 @@ -35,6 +35,7 @@ templater, util, ) +from .utils import dateutil def getlimit(opts): """get the log limit according to option -l/--limit""" @@ -229,7 +230,7 @@ % scmutil.formatrevnode(self.ui, mrev, mnode), label='ui.debug log.manifest') self.ui.write(columns['user'] % ctx.user(), label='log.user') - self.ui.write(columns['date'] % util.datestr(ctx.date()), + self.ui.write(columns['date'] % dateutil.datestr(ctx.date()), label='log.date') if ctx.isunstable():
--- a/mercurial/mdiff.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/mdiff.py Thu Feb 15 17:18:26 2018 +0100 @@ -19,6 +19,7 @@ pycompat, util, ) +from .utils import dateutil _missing_newline_marker = "\\ No newline at end of file\n" @@ -255,7 +256,7 @@ aprefix = 'a/' bprefix = 'b/' - epoch = util.datestr((0, 0)) + epoch = dateutil.datestr((0, 0)) fn1 = util.pconvert(fn1) fn2 = util.pconvert(fn2)
--- a/mercurial/obsolete.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/obsolete.py Thu Feb 15 17:18:26 2018 +0100 @@ -81,6 +81,7 @@ policy, util, ) +from .utils import dateutil parsers = policy.importmod(r'parsers') @@ -601,13 +602,13 @@ 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: - date = util.makedate() + date = dateutil.makedate() else: - date = util.makedate() + date = dateutil.makedate() if len(prec) != 20: raise ValueError(prec) for succ in succs:
--- a/mercurial/obsutil.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/obsutil.py Thu Feb 15 17:18:26 2018 +0100 @@ -15,6 +15,7 @@ phases, util, ) +from .utils import dateutil class marker(object): """Wrap obsolete marker raw data""" @@ -841,11 +842,11 @@ max_date = max(dates) if min_date == max_date: - fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2') + fmtmin_date = dateutil.datestr(min_date, '%Y-%m-%d %H:%M %1%2') line.append(" (at %s)" % fmtmin_date) else: - fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2') - fmtmax_date = util.datestr(max_date, '%Y-%m-%d %H:%M %1%2') + fmtmin_date = dateutil.datestr(min_date, '%Y-%m-%d %H:%M %1%2') + fmtmax_date = dateutil.datestr(max_date, '%Y-%m-%d %H:%M %1%2') line.append(" (between %s and %s)" % (fmtmin_date, fmtmax_date)) return "".join(line)
--- a/mercurial/patch.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/patch.py Thu Feb 15 17:18:26 2018 +0100 @@ -40,6 +40,7 @@ util, vfs as vfsmod, ) +from .utils import dateutil diffhelpers = policy.importmod(r'diffhelpers') stringio = util.stringio @@ -2669,8 +2670,8 @@ def isempty(fctx): return fctx is None or fctx.size() == 0 - date1 = util.datestr(ctx1.date()) - date2 = util.datestr(ctx2.date()) + date1 = dateutil.datestr(ctx1.date()) + date2 = dateutil.datestr(ctx2.date()) gitmode = {'l': '120000', 'x': '100755', '': '100644'}
--- a/mercurial/revset.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/revset.py Thu Feb 15 17:18:26 2018 +0100 @@ -30,6 +30,7 @@ smartset, util, ) +from .utils import dateutil # helpers for processing parsed tree getsymbol = revsetlang.getsymbol @@ -658,7 +659,7 @@ """ # i18n: "date" is a keyword ds = getstring(x, _("date requires a string")) - dm = util.matchdate(ds) + dm = dateutil.matchdate(ds) return subset.filter(lambda x: dm(repo[x].date()[0]), condrepr=('<date %r>', ds))
--- a/mercurial/subrepo.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/subrepo.py Thu Feb 15 17:18:26 2018 +0100 @@ -36,6 +36,7 @@ util, vfs as vfsmod, ) +from .utils import dateutil hg = None reporelpath = subrepoutil.reporelpath @@ -1467,7 +1468,7 @@ if date: # git's date parser silently ignores when seconds < 1e9 # convert to ISO8601 - env['GIT_AUTHOR_DATE'] = util.datestr(date, + env['GIT_AUTHOR_DATE'] = dateutil.datestr(date, '%Y-%m-%dT%H:%M:%S %1%2') self._gitcommand(cmd, env=env) # make sure commit works otherwise HEAD might not exist under certain
--- a/mercurial/templatefilters.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/templatefilters.py Thu Feb 15 17:18:26 2018 +0100 @@ -22,6 +22,7 @@ url, util, ) +from .utils import dateutil urlerr = util.urlerr urlreq = util.urlreq @@ -78,7 +79,7 @@ else: delta = max(1, int(now - then)) if delta > agescales[0][1] * 2: - return util.shortdate(date) + return dateutil.shortdate(date) for t, s, a in agescales: n = delta // s @@ -203,7 +204,7 @@ """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00 +0200". """ - return util.datestr(text, '%Y-%m-%d %H:%M %1%2') + return dateutil.datestr(text, '%Y-%m-%d %H:%M %1%2') @templatefilter('isodatesec') def isodatesec(text): @@ -211,7 +212,7 @@ seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date filter. """ - return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2') + return dateutil.datestr(text, '%Y-%m-%d %H:%M:%S %1%2') def indent(text, prefix): '''indent each non-empty line of text after first with prefix.''' @@ -325,14 +326,14 @@ """Date. Returns a date using the Internet date format specified in RFC 3339: "2009-08-18T13:00:13+02:00". """ - return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2") + return dateutil.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2") @templatefilter('rfc822date') def rfc822date(text): """Date. Returns a date using the same format used in email headers: "Tue, 18 Aug 2009 13:00:13 +0200". """ - return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2") + return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2") @templatefilter('short') def short(text): @@ -353,7 +354,7 @@ @templatefilter('shortdate') def shortdate(text): """Date. Returns a date like "2006-09-18".""" - return util.shortdate(text) + return dateutil.shortdate(text) @templatefilter('slashpath') def slashpath(path):
--- a/mercurial/templater.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/templater.py Thu Feb 15 17:18:26 2018 +0100 @@ -29,6 +29,7 @@ templatekw, util, ) +from .utils import dateutil class ResourceUnavailable(error.Abort): pass @@ -649,9 +650,9 @@ fmt = evalstring(context, mapping, args[1]) try: if fmt is None: - return util.datestr(date) + return dateutil.datestr(date) else: - return util.datestr(date, fmt) + return dateutil.datestr(date, fmt) except (TypeError, ValueError): # i18n: "date" is a keyword raise error.ParseError(_("date expects a date information")) @@ -954,7 +955,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")) @@ -962,7 +963,7 @@ tzoffset = None tz = evalfuncarg(context, mapping, args[1]) if isinstance(tz, bytes): - tzoffset, remainder = util.parsetimezone(tz) + tzoffset, remainder = dateutil.parsetimezone(tz) if remainder: tzoffset = None if tzoffset is None: @@ -972,7 +973,7 @@ # i18n: "localdate" is a keyword raise error.ParseError(_("localdate expects a timezone")) else: - tzoffset = util.makedate()[1] + tzoffset = dateutil.makedate()[1] return (date[0], tzoffset) @templatefunc('max(iterable)')
--- a/mercurial/ui.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/ui.py Thu Feb 15 17:18:26 2018 +0100 @@ -37,6 +37,7 @@ scmutil, util, ) +from .utils import dateutil urlreq = util.urlreq @@ -714,7 +715,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
--- a/mercurial/util.py Thu Feb 08 23:27:24 2018 +0530 +++ b/mercurial/util.py Thu Feb 15 17:18:26 2018 +0100 @@ -17,11 +17,9 @@ import abc import bz2 -import calendar import codecs import collections import contextlib -import datetime import errno import gc import hashlib @@ -55,6 +53,7 @@ pycompat, urllibcompat, ) +from .utils import dateutil base85 = policy.importmod(r'base85') osutil = policy.importmod(r'osutil') @@ -855,48 +854,6 @@ if n == 4: return (vints[0], vints[1], vints[2], extra) -# used by parsedate -defaultdateformats = ( - '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601 - '%Y-%m-%dT%H:%M', # without seconds - '%Y-%m-%dT%H%M%S', # another awful but legal variant without : - '%Y-%m-%dT%H%M', # without seconds - '%Y-%m-%d %H:%M:%S', # our common legal variant - '%Y-%m-%d %H:%M', # without seconds - '%Y-%m-%d %H%M%S', # without : - '%Y-%m-%d %H%M', # without seconds - '%Y-%m-%d %I:%M:%S%p', - '%Y-%m-%d %H:%M', - '%Y-%m-%d %I:%M%p', - '%Y-%m-%d', - '%m-%d', - '%m/%d', - '%m/%d/%y', - '%m/%d/%Y', - '%a %b %d %H:%M:%S %Y', - '%a %b %d %I:%M:%S%p %Y', - '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822" - '%b %d %H:%M:%S %Y', - '%b %d %I:%M:%S%p %Y', - '%b %d %H:%M:%S', - '%b %d %I:%M:%S%p', - '%b %d %H:%M', - '%b %d %I:%M%p', - '%b %d %Y', - '%b %d', - '%H:%M:%S', - '%I:%M:%S%p', - '%H:%M', - '%I:%M%p', -) - -extendeddateformats = defaultdateformats + ( - "%Y", - "%Y-%m", - "%b", - "%b %Y", - ) - def cachefunc(func): '''cache the result of function calls''' # XXX doesn't handle keywords args @@ -2304,277 +2261,6 @@ return data -def makedate(timestamp=None): - '''Return a unix timestamp (or the current time) as a (unixtime, - offset) tuple based off the local timezone.''' - if timestamp is None: - timestamp = time.time() - if timestamp < 0: - hint = _("check your clock") - raise Abort(_("negative timestamp: %d") % timestamp, hint=hint) - delta = (datetime.datetime.utcfromtimestamp(timestamp) - - datetime.datetime.fromtimestamp(timestamp)) - tz = delta.days * 86400 + delta.seconds - return timestamp, tz - -def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'): - """represent a (unixtime, offset) tuple as a localized time. - unixtime is seconds since the epoch, and offset is the time zone's - number of seconds away from UTC. - - >>> datestr((0, 0)) - 'Thu Jan 01 00:00:00 1970 +0000' - >>> datestr((42, 0)) - 'Thu Jan 01 00:00:42 1970 +0000' - >>> datestr((-42, 0)) - 'Wed Dec 31 23:59:18 1969 +0000' - >>> datestr((0x7fffffff, 0)) - 'Tue Jan 19 03:14:07 2038 +0000' - >>> datestr((-0x80000000, 0)) - 'Fri Dec 13 20:45:52 1901 +0000' - """ - t, tz = date or makedate() - if "%1" in format or "%2" in format or "%z" in format: - sign = (tz > 0) and "-" or "+" - minutes = abs(tz) // 60 - q, r = divmod(minutes, 60) - format = format.replace("%z", "%1%2") - format = format.replace("%1", "%c%02d" % (sign, q)) - format = format.replace("%2", "%02d" % r) - d = t - tz - if d > 0x7fffffff: - d = 0x7fffffff - elif d < -0x80000000: - d = -0x80000000 - # 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 = encoding.strtolocal(t.strftime(encoding.strfromlocal(format))) - return s - -def shortdate(date=None): - """turn (timestamp, tzoff) tuple into iso 8631 date.""" - return datestr(date, format='%Y-%m-%d') - -def parsetimezone(s): - """find a trailing timezone, if any, in string, and return a - (offset, remainder) pair""" - s = pycompat.bytestr(s) - - if s.endswith("GMT") or s.endswith("UTC"): - return 0, s[:-3].rstrip() - - # Unix-style timezones [+-]hhmm - if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit(): - sign = (s[-5] == "+") and 1 or -1 - hours = int(s[-4:-2]) - minutes = int(s[-2:]) - return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip() - - # ISO8601 trailing Z - if s.endswith("Z") and s[-2:-1].isdigit(): - return 0, s[:-1] - - # ISO8601-style [+-]hh:mm - if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and - s[-5:-3].isdigit() and s[-2:].isdigit()): - sign = (s[-6] == "+") and 1 or -1 - hours = int(s[-5:-3]) - minutes = int(s[-2:]) - return -sign * (hours * 60 + minutes) * 60, s[:-6] - - return None, s - -def strdate(string, format, defaults=None): - """parse a localized time string and return a (unixtime, offset) tuple. - if the string cannot be parsed, ValueError is raised.""" - if defaults is None: - defaults = {} - - # NOTE: unixtime = localunixtime + offset - offset, date = parsetimezone(string) - - # add missing elements from defaults - usenow = False # default to using biased defaults - for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity - part = pycompat.bytestr(part) - found = [True for p in part if ("%"+p) in format] - if not found: - date += "@" + defaults[part][usenow] - format += "@%" + part[0] - else: - # We've found a specific time element, less specific time - # elements are relative to today - usenow = True - - timetuple = time.strptime(encoding.strfromlocal(date), - encoding.strfromlocal(format)) - localunixtime = int(calendar.timegm(timetuple)) - if offset is None: - # local timezone - unixtime = int(time.mktime(timetuple)) - offset = unixtime - localunixtime - else: - 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') % pycompat.bytestr(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 - - Formats include: - - '{date}' match a given date to the accuracy provided - - '<{date}' on or before a given date - - '>{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") - >>> f = matchdate(b"10:30") - >>> f(p1[0]) - False - >>> f(p2[0]) - True - >>> f(p3[0]) - True - >>> f(p4[0]) - False - >>> f(p5[0]) - False - """ - - def lower(date): - d = {'mb': "1", 'd': "1"} - return 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] - except error.ParseError: - pass - d["d"] = "28" - return parsedate(date, extendeddateformats, d)[0] - - date = date.strip() - - if not date: - raise Abort(_("dates cannot consist entirely of whitespace")) - elif date[0] == "<": - if not date[1:]: - raise Abort(_("invalid day spec, use '<DATE'")) - when = upper(date[1:]) - return lambda x: x <= when - elif date[0] == ">": - if not date[1:]: - raise Abort(_("invalid day spec, use '>DATE'")) - when = lower(date[1:]) - return lambda x: x >= when - elif date[0] == "-": - try: - days = int(date[1:]) - except ValueError: - raise Abort(_("invalid day spec: %s") % date[1:]) - if days < 0: - raise Abort(_("%s must be nonnegative (see 'hg help dates')") - % date[1:]) - when = makedate()[0] - days * 3600 * 24 - return lambda x: x >= when - elif " to " in date: - a, b = date.split(" to ") - start, stop = lower(a), upper(b) - return lambda x: x >= start and x <= stop - else: - start, stop = lower(date), upper(date) - return lambda x: x >= start and x <= stop - def stringmatcher(pattern, casesensitive=True): """ accepts a string, possibly starting with 're:' or 'literal:' prefix. @@ -4303,3 +3989,54 @@ if not (byte & 0x80): return result shift += 7 + +### +# Deprecation warnings for util.py splitting +### + +defaultdateformats = dateutil.defaultdateformats + +extendeddateformats = dateutil.extendeddateformats + +def makedate(*args, **kwargs): + msg = ("'util.makedate' is deprecated, " + "use 'utils.dateutil.makedate'") + nouideprecwarn(msg, "4.6") + return dateutil.makedate(*args, **kwargs) + +def datestr(*args, **kwargs): + msg = ("'util.datestr' is deprecated, " + "use 'utils.dateutil.datestr'") + nouideprecwarn(msg, "4.6") + debugstacktrace() + return dateutil.datestr(*args, **kwargs) + +def shortdate(*args, **kwargs): + msg = ("'util.shortdate' is deprecated, " + "use 'utils.dateutil.shortdate'") + nouideprecwarn(msg, "4.6") + return dateutil.shortdate(*args, **kwargs) + +def parsetimezone(*args, **kwargs): + msg = ("'util.parsetimezone' is deprecated, " + "use 'utils.dateutil.parsetimezone'") + nouideprecwarn(msg, "4.6") + return dateutil.parsetimezone(*args, **kwargs) + +def strdate(*args, **kwargs): + msg = ("'util.strdate' is deprecated, " + "use 'utils.dateutil.strdate'") + 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) + +def matchdate(*args, **kwargs): + msg = ("'util.matchdate' is deprecated, " + "use 'utils.dateutil.matchdate'") + nouideprecwarn(msg, "4.6") + return dateutil.matchdate(*args, **kwargs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/utils/dateutil.py Thu Feb 15 17:18:26 2018 +0100 @@ -0,0 +1,332 @@ +# util.py - Mercurial utility functions relative to dates +# +# Copyright 2018 Boris Feld <boris.feld@octobus.net> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import, print_function + +import calendar +import datetime +import time + +from ..i18n import _ +from .. import ( + encoding, + error, + pycompat, +) + +# used by parsedate +defaultdateformats = ( + '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601 + '%Y-%m-%dT%H:%M', # without seconds + '%Y-%m-%dT%H%M%S', # another awful but legal variant without : + '%Y-%m-%dT%H%M', # without seconds + '%Y-%m-%d %H:%M:%S', # our common legal variant + '%Y-%m-%d %H:%M', # without seconds + '%Y-%m-%d %H%M%S', # without : + '%Y-%m-%d %H%M', # without seconds + '%Y-%m-%d %I:%M:%S%p', + '%Y-%m-%d %H:%M', + '%Y-%m-%d %I:%M%p', + '%Y-%m-%d', + '%m-%d', + '%m/%d', + '%m/%d/%y', + '%m/%d/%Y', + '%a %b %d %H:%M:%S %Y', + '%a %b %d %I:%M:%S%p %Y', + '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822" + '%b %d %H:%M:%S %Y', + '%b %d %I:%M:%S%p %Y', + '%b %d %H:%M:%S', + '%b %d %I:%M:%S%p', + '%b %d %H:%M', + '%b %d %I:%M%p', + '%b %d %Y', + '%b %d', + '%H:%M:%S', + '%I:%M:%S%p', + '%H:%M', + '%I:%M%p', +) + +extendeddateformats = defaultdateformats + ( + "%Y", + "%Y-%m", + "%b", + "%b %Y", +) + +def makedate(timestamp=None): + '''Return a unix timestamp (or the current time) as a (unixtime, + offset) tuple based off the local timezone.''' + if timestamp is None: + timestamp = time.time() + if timestamp < 0: + hint = _("check your clock") + raise error.Abort(_("negative timestamp: %d") % timestamp, hint=hint) + delta = (datetime.datetime.utcfromtimestamp(timestamp) - + datetime.datetime.fromtimestamp(timestamp)) + tz = delta.days * 86400 + delta.seconds + return timestamp, tz + +def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'): + """represent a (unixtime, offset) tuple as a localized time. + unixtime is seconds since the epoch, and offset is the time zone's + number of seconds away from UTC. + + >>> datestr((0, 0)) + 'Thu Jan 01 00:00:00 1970 +0000' + >>> datestr((42, 0)) + 'Thu Jan 01 00:00:42 1970 +0000' + >>> datestr((-42, 0)) + 'Wed Dec 31 23:59:18 1969 +0000' + >>> datestr((0x7fffffff, 0)) + 'Tue Jan 19 03:14:07 2038 +0000' + >>> datestr((-0x80000000, 0)) + 'Fri Dec 13 20:45:52 1901 +0000' + """ + t, tz = date or makedate() + if "%1" in format or "%2" in format or "%z" in format: + sign = (tz > 0) and "-" or "+" + minutes = abs(tz) // 60 + q, r = divmod(minutes, 60) + format = format.replace("%z", "%1%2") + format = format.replace("%1", "%c%02d" % (sign, q)) + format = format.replace("%2", "%02d" % r) + d = t - tz + if d > 0x7fffffff: + d = 0x7fffffff + elif d < -0x80000000: + d = -0x80000000 + # 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 = encoding.strtolocal(t.strftime(encoding.strfromlocal(format))) + return s + +def shortdate(date=None): + """turn (timestamp, tzoff) tuple into iso 8631 date.""" + return datestr(date, format='%Y-%m-%d') + +def parsetimezone(s): + """find a trailing timezone, if any, in string, and return a + (offset, remainder) pair""" + s = pycompat.bytestr(s) + + if s.endswith("GMT") or s.endswith("UTC"): + return 0, s[:-3].rstrip() + + # Unix-style timezones [+-]hhmm + if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit(): + sign = (s[-5] == "+") and 1 or -1 + hours = int(s[-4:-2]) + minutes = int(s[-2:]) + return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip() + + # ISO8601 trailing Z + if s.endswith("Z") and s[-2:-1].isdigit(): + return 0, s[:-1] + + # ISO8601-style [+-]hh:mm + if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and + s[-5:-3].isdigit() and s[-2:].isdigit()): + sign = (s[-6] == "+") and 1 or -1 + hours = int(s[-5:-3]) + minutes = int(s[-2:]) + return -sign * (hours * 60 + minutes) * 60, s[:-6] + + return None, s + +def strdate(string, format, defaults=None): + """parse a localized time string and return a (unixtime, offset) tuple. + if the string cannot be parsed, ValueError is raised.""" + if defaults is None: + defaults = {} + + # NOTE: unixtime = localunixtime + offset + offset, date = parsetimezone(string) + + # add missing elements from defaults + usenow = False # default to using biased defaults + for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity + part = pycompat.bytestr(part) + found = [True for p in part if ("%"+p) in format] + if not found: + date += "@" + defaults[part][usenow] + format += "@%" + part[0] + else: + # We've found a specific time element, less specific time + # elements are relative to today + usenow = True + + timetuple = time.strptime(encoding.strfromlocal(date), + encoding.strfromlocal(format)) + localunixtime = int(calendar.timegm(timetuple)) + if offset is None: + # local timezone + unixtime = int(time.mktime(timetuple)) + offset = unixtime - localunixtime + else: + 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') % pycompat.bytestr(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 + + Formats include: + + '{date}' match a given date to the accuracy provided + + '<{date}' on or before a given date + + '>{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") + >>> f = matchdate(b"10:30") + >>> f(p1[0]) + False + >>> f(p2[0]) + True + >>> f(p3[0]) + True + >>> f(p4[0]) + False + >>> f(p5[0]) + False + """ + + def lower(date): + d = {'mb': "1", 'd': "1"} + return 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] + except error.ParseError: + pass + d["d"] = "28" + return parsedate(date, extendeddateformats, d)[0] + + date = date.strip() + + if not date: + raise error.Abort(_("dates cannot consist entirely of whitespace")) + elif date[0] == "<": + if not date[1:]: + raise error.Abort(_("invalid day spec, use '<DATE'")) + when = upper(date[1:]) + return lambda x: x <= when + elif date[0] == ">": + if not date[1:]: + raise error.Abort(_("invalid day spec, use '>DATE'")) + when = lower(date[1:]) + return lambda x: x >= when + elif date[0] == "-": + try: + days = int(date[1:]) + except ValueError: + raise error.Abort(_("invalid day spec: %s") % date[1:]) + if days < 0: + raise error.Abort(_("%s must be nonnegative (see 'hg help dates')") + % date[1:]) + when = makedate()[0] - days * 3600 * 24 + return lambda x: x >= when + elif " to " in date: + a, b = date.split(" to ") + start, stop = lower(a), upper(b) + return lambda x: x >= start and x <= stop + else: + start, stop = lower(date), upper(date) + return lambda x: x >= start and x <= stop
--- a/setup.py Thu Feb 08 23:27:24 2018 +0530 +++ b/setup.py Thu Feb 15 17:18:26 2018 +0100 @@ -809,6 +809,7 @@ 'mercurial.pure', 'mercurial.thirdparty', 'mercurial.thirdparty.attr', + 'mercurial.utils', 'hgext', 'hgext.convert', 'hgext.fsmonitor', 'hgext.fsmonitor.pywatchman', 'hgext.highlight', 'hgext.largefiles', 'hgext.lfs', 'hgext.narrow',
--- a/tests/fakedirstatewritetime.py Thu Feb 08 23:27:24 2018 +0530 +++ b/tests/fakedirstatewritetime.py Thu Feb 15 17:18:26 2018 +0100 @@ -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, [b'%Y%m%d%H%M'])[0] + fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] orig_pack_dirstate = parsers.pack_dirstate orig_dirstate_getfsnow = dirstate._getfsnow
--- a/tests/fakepatchtime.py Thu Feb 08 23:27:24 2018 +0530 +++ b/tests/fakepatchtime.py Thu Feb 15 17:18:26 2018 +0100 @@ -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, [b'%Y%m%d%H%M'])[0] + fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] for f in files: repo.wvfs.utime(f, (fakenow, fakenow))
--- a/tests/test-journal.t Thu Feb 08 23:27:24 2018 +0530 +++ b/tests/test-journal.t Thu Feb 15 17:18:26 2018 +0100 @@ -4,6 +4,7 @@ > # mock out util.getuser() and util.makedate() to supply testable values > import os > from mercurial import util + > from mercurial.utils import dateutil > def mockgetuser(): > return 'foobar' > @@ -19,7 +20,7 @@ > return (time, 0) > > util.getuser = mockgetuser - > util.makedate = mockmakedate + > dateutil.makedate = mockmakedate > EOF $ cat >> $HGRCPATH << EOF