diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -788,6 +788,12 @@ def debugdata(ui, file_, rev): except KeyError: raise util.Abort(_('invalid revision identifier %s') % rev) +def debugdate(ui, date): + """parse and display a date""" + d = util.parsedate(date) + ui.write("internal: %s %s\n" % d) + ui.write("standard: %s\n" % util.datestr(d)) + def debugindex(ui, file_): """dump the contents of an index file""" r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0) @@ -2477,6 +2483,7 @@ table = { "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')), "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')), "debugstate": (debugstate, [], _('debugstate')), + "debugdate": (debugdate, [], _('debugdata DATE')), "debugdata": (debugdata, [], _('debugdata FILE REV')), "debugindex": (debugindex, [], _('debugindex FILE')), "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')), @@ -2727,7 +2734,7 @@ table = { } norepo = ("clone init version help debugancestor debugcomplete debugdata" - " debugindex debugindexdot") + " debugindex debugindexdot debugdate") optionalrepo = ("paths serve showconfig") def findpossible(ui, cmd): diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -9,37 +9,31 @@ helptable = { "dates|Date Formats": r''' Some commands (backout, commit, tag) allow the user to specify a date. - Possible formats for dates are: - -YYYY-mm-dd \HH:MM[:SS] [(+|-)NNNN]:: - This is a subset of ISO 8601, allowing just the recommended notations - for date and time. The last part represents the timezone; if omitted, - local time is assumed. Examples: - - "2005-08-22 03:27 -0700" - - "2006-04-19 21:39:51" + Many date formats are acceptible. Here are some examples: -aaa bbb dd HH:MM:SS YYYY [(+|-)NNNN]:: - This is the date format used by the C library. Here, aaa stands for - abbreviated weekday name and bbb for abbreviated month name. The last - part represents the timezone; if omitted, local time is assumed. - Examples: + "Wed Dec 6 13:18:29 2006" (local timezone assumed) + "Dec 6 13:18 -0600" (year assumed, time offset provided) + "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000) + "Dec 6" (midnight) + "13:18" (today assumed) + "3:39" (3:39AM assumed) + "3:39pm" (15:39) + "2006-12-6 13:18:29" (ISO 8601 format) + "2006-12-6 13:18" + "2006-12-6" + "12-6" + "12/6" + "12/6/6" (Dec 6 2006) + "" (Jan 1 00:00:00 1970 UTC) - "Mon Aug 22 03:27:00 2005 -0700" + Lastly, there is Mercurial's internal format: - "Wed Apr 19 21:39:51 2006" + "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC) -unixtime offset:: This is the internal representation format for dates. unixtime is the number of seconds since the epoch (1970-01-01 00:00 UTC). offset is the offset of the local timezone, in seconds west of UTC (negative if the timezone is east of UTC). - Examples: - - "1124706420 25200" (2005-08-22 03:27:00 -0700) - - "1145475591 -7200" (2006-04-19 21:39:51 +0200) ''', 'environment|env|Environment Variables': diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -72,8 +72,29 @@ def localsub(s, a, b=None): raise Abort("decoding near '%s': %s!\n" % (sub, inst)) # used by parsedate -defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', - '%a %b %d %H:%M:%S %Y') +defaultdateformats = ( + '%Y-%m-%d %H:%M:%S', + '%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', + '%b %d %H:%M:%S %Y', + '%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:%SP', + '%H:%M', + '%I:%M%p', +) class SignalInterrupt(Exception): """Exception raised on SIGTERM and SIGHUP.""" @@ -1040,18 +1061,32 @@ def datestr(date=None, format='%a %b %d def strdate(string, format='%a %b %d %H:%M:%S %Y'): """parse a localized time string and return a (unixtime, offset) tuple. if the string cannot be parsed, ValueError is raised.""" - def hastimezone(string): - return (string[-4:].isdigit() and - (string[-5] == '+' or string[-5] == '-') and - string[-6].isspace()) + def timezone(string): + tz = string.split()[-1] + if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): + tz = int(tz) + offset = - 3600 * (tz / 100) - 60 * (tz % 100) + return offset + if tz == "GMT" or tz == "UTC": + return 0 + return None # NOTE: unixtime = localunixtime + offset - if hastimezone(string): - date, tz = string[:-6], string[-5:] - tz = int(tz) - offset = - 3600 * (tz / 100) - 60 * (tz % 100) - else: - date, offset = string, None + offset, date = timezone(string), string + if offset != None: + date = " ".join(string.split()[:-1]) + + # add missing elements + if '%y' not in format.lower(): + date += "@" + datestr(makedate(), "%Y", False) + format += "@%Y" + if '%m' not in format and '%b' not in format: + date += "@" + datestr(makedate(), "%m", False) + format += "@%m" + if '%d' not in format: + date += "@" + datestr(makedate(), "%d", False) + format += "@%d" + timetuple = time.strptime(date, format) localunixtime = int(calendar.timegm(timetuple)) if offset is None: @@ -1066,8 +1101,11 @@ def parsedate(string, formats=None): """parse a localized time string and return a (unixtime, offset) tuple. The date may be a "unixtime offset" string or in one of the specified formats.""" + if not string: + return 0, 0 if not formats: formats = defaultdateformats + string = string.strip() try: when, offset = map(int, string.split(' ')) except ValueError: @@ -1079,17 +1117,15 @@ def parsedate(string, formats=None): else: break else: - raise ValueError(_('invalid date: %r ' - 'see hg(1) manual page for details') - % string) + raise Abort(_('invalid date: %r ') % string) # 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 abs(when) > 0x7fffffff: - raise ValueError(_('date exceeds 32 bits: %d') % when) + raise Abort(_('date exceeds 32 bits: %d') % when) if offset < -50400 or offset > 43200: - raise ValueError(_('impossible time zone offset: %d') % offset) + raise Abort(_('impossible time zone offset: %d') % offset) return when, offset def shortuser(user): diff --git a/tests/test-commit.out b/tests/test-commit.out --- a/tests/test-commit.out +++ b/tests/test-commit.out @@ -2,18 +2,13 @@ abort: impossible time zone offset: 4444444 transaction abort! rollback completed -abort: invalid date: '1\t15.1' see hg(1) manual page for details -transaction abort! -rollback completed -abort: invalid date: 'foo bar' see hg(1) manual page for details +abort: invalid date: '1\t15.1' transaction abort! rollback completed -abort: invalid date: ' 1 4444' see hg(1) manual page for details +abort: invalid date: 'foo bar' transaction abort! rollback completed -abort: date exceeds 32 bits: 111111111111 -transaction abort! -rollback completed +nothing changed % partial commit test trouble committing bar! abort: No such file or directory: .../test/bar diff --git a/tests/test-debugcomplete.out b/tests/test-debugcomplete.out --- a/tests/test-debugcomplete.out +++ b/tests/test-debugcomplete.out @@ -61,6 +61,7 @@ debugcheckstate debugcomplete debugconfig debugdata +debugdate debugindex debugindexdot debugrawcommit diff --git a/tests/test-parse-date.out b/tests/test-parse-date.out --- a/tests/test-parse-date.out +++ b/tests/test-parse-date.out @@ -3,7 +3,7 @@ changeset 3:107ce1ee2b43 backs out chang merging with changeset 2:e6c3abc120e7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) -abort: invalid date: 'should fail' see hg(1) manual page for details +abort: invalid date: 'should fail' transaction abort! rollback completed abort: date exceeds 32 bits: 100000000000000000