##// END OF EJS Templates
phabricator: use .arcconfig for the callsign if not set locally (issue6243)...
phabricator: use .arcconfig for the callsign if not set locally (issue6243) This makes things easier for people working with more than one repository because this file can be committed to each repository. The bug report asks to read <repo>/.arcrc, but AFAICT, that file lives in ~/ and holds the credentials. And we already track an .arcconfig file. Any callsign set globally is still used if that is all that is present, but .arcconfig will override it if available. The idea behind letting the local hgrc override .arcconfig is that the developer may need to do testing against another server, and not dirty the working directory. Originally I was going to just try to read the callsign in `getrepophid()` if it wasn't present in the hg config. That works fine, but I think it also makes sense to read the URL from this file too. That would have worked less well because `readurltoken()` doesn't have access to the repo object to know where to find the file. Supplimenting the config mechanism is less magical because it reports the source and value of the properties used, and it doesn't need to read the file twice. Invalid hgrc files generally cause the program to abort. I only flagged it as a warning here because it's not our config file, not crucial to the whole program operating, and really shouldn't be corrupt in the typical case where it is checked into the repo. Differential Revision: https://phab.mercurial-scm.org/D7934

File last commit:

r44170:aef7b91d default
r44586:59b3fe1e default
Show More
dateutil.py
354 lines | 10.7 KiB | text/x-python | PythonLexer
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 # 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 = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
b'%Y-%m-%dT%H:%M', # without seconds
b'%Y-%m-%dT%H%M%S', # another awful but legal variant without :
b'%Y-%m-%dT%H%M', # without seconds
b'%Y-%m-%d %H:%M:%S', # our common legal variant
b'%Y-%m-%d %H:%M', # without seconds
b'%Y-%m-%d %H%M%S', # without :
b'%Y-%m-%d %H%M', # without seconds
b'%Y-%m-%d %I:%M:%S%p',
b'%Y-%m-%d %H:%M',
b'%Y-%m-%d %I:%M%p',
b'%Y-%m-%d',
b'%m-%d',
b'%m/%d',
b'%m/%d/%y',
b'%m/%d/%Y',
b'%a %b %d %H:%M:%S %Y',
b'%a %b %d %I:%M:%S%p %Y',
b'%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
b'%b %d %H:%M:%S %Y',
b'%b %d %I:%M:%S%p %Y',
b'%b %d %H:%M:%S',
b'%b %d %I:%M:%S%p',
b'%b %d %H:%M',
b'%b %d %I:%M%p',
b'%b %d %Y',
b'%b %d',
b'%H:%M:%S',
b'%I:%M:%S%p',
b'%H:%M',
b'%I:%M%p',
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 extendeddateformats = defaultdateformats + (b"%Y", b"%Y-%m", b"%b", b"%b %Y",)
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625
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:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 hint = _(b"check your clock")
raise error.Abort(_(b"negative timestamp: %d") % timestamp, hint=hint)
Augie Fackler
formatting: blacken the codebase...
r43346 delta = datetime.datetime.utcfromtimestamp(
timestamp
) - datetime.datetime.fromtimestamp(timestamp)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 tz = delta.days * 86400 + delta.seconds
return timestamp, tz
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def datestr(date=None, format=b'%a %b %d %H:%M:%S %Y %1%2'):
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 """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()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b"%1" in format or b"%2" in format or b"%z" in format:
sign = (tz > 0) and b"-" or b"+"
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 minutes = abs(tz) // 60
q, r = divmod(minutes, 60)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 format = format.replace(b"%z", b"%1%2")
format = format.replace(b"%1", b"%c%02d" % (sign, q))
format = format.replace(b"%2", b"%02d" % r)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 d = t - tz
Augie Fackler
formatting: blacken the codebase...
r43346 if d > 0x7FFFFFFF:
d = 0x7FFFFFFF
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 def shortdate(date=None):
"""turn (timestamp, tzoff) tuple into iso 8631 date."""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return datestr(date, format=b'%Y-%m-%d')
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 def parsetimezone(s):
"""find a trailing timezone, if any, in string, and return a
(offset, remainder) pair"""
s = pycompat.bytestr(s)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if s.endswith(b"GMT") or s.endswith(b"UTC"):
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return 0, s[:-3].rstrip()
# Unix-style timezones [+-]hhmm
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if len(s) >= 5 and s[-5] in b"+-" and s[-4:].isdigit():
sign = (s[-5] == b"+") and 1 or -1
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 hours = int(s[-4:-2])
minutes = int(s[-2:])
return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
# ISO8601 trailing Z
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if s.endswith(b"Z") and s[-2:-1].isdigit():
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return 0, s[:-1]
# ISO8601-style [+-]hh:mm
Augie Fackler
formatting: blacken the codebase...
r43346 if (
len(s) >= 6
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 and s[-6] in b"+-"
and s[-3] == b":"
Augie Fackler
formatting: blacken the codebase...
r43346 and s[-5:-3].isdigit()
and s[-2:].isdigit()
):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sign = (s[-6] == b"+") and 1 or -1
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 hours = int(s[-5:-3])
minutes = int(s[-2:])
return -sign * (hours * 60 + minutes) * 60, s[:-6]
return None, s
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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
Augie Fackler
formatting: blacken the codebase...
r43346 usenow = False # default to using biased defaults
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for part in (
b"S",
b"M",
b"HI",
b"d",
b"mb",
b"yY",
): # decreasing specificity
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 part = pycompat.bytestr(part)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 found = [True for p in part if (b"%" + p) in format]
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 if not found:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 date += b"@" + defaults[part][usenow]
format += b"@%" + part[0]
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 else:
# We've found a specific time element, less specific time
# elements are relative to today
usenow = True
Augie Fackler
formatting: blacken the codebase...
r43346 timetuple = time.strptime(
encoding.strfromlocal(date), encoding.strfromlocal(format)
)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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
Jun Wu
dateutil: correct default for Ymd in parsedate...
r44220 >>> parsedate(b'2000 UTC', formats=extendeddateformats)
(946684800, 0)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 """
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()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if date == b'now' or date == _(b'now'):
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return makedate()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if date == b'today' or date == _(b'today'):
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 date = datetime.date.today().strftime('%b %d')
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 date = encoding.strtolocal(date)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif date == b'yesterday' or date == _(b'yesterday'):
Augie Fackler
formatting: blacken the codebase...
r43346 date = (datetime.date.today() - datetime.timedelta(days=1)).strftime(
r'%b %d'
)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 date = encoding.strtolocal(date)
try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 when, offset = map(int, date.split(b' '))
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 except ValueError:
# fill out defaults
now = makedate()
defaults = {}
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for part in (b"d", b"mb", b"yY", b"HI", b"M", b"S"):
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 # this piece is for rounding the specific end of unknowns
b = bias.get(part)
if b is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if part[0:1] in b"HMS":
b = b"00"
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 else:
Jun Wu
dateutil: correct default for Ymd in parsedate...
r44220 # year, month, and day start from 1
b = b"1"
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625
# this piece is for matching the generic end to today's date
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 n = datestr(now, b"%" + part[0:1])
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625
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(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'invalid date: %r') % pycompat.bytestr(date)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 # 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
Augie Fackler
formatting: blacken the codebase...
r43346 if when < -0x80000000 or when > 0x7FFFFFFF:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b'date exceeds 32 bits: %d') % when)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 if offset < -50400 or offset > 43200:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b'impossible time zone offset: %d') % offset)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return when, offset
Augie Fackler
formatting: blacken the codebase...
r43346
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 d = {b'mb': b"1", b'd': b"1"}
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return parsedate(date, extendeddateformats, d)[0]
def upper(date):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 d = {b'mb': b"12", b'HI': b"23", b'M': b"59", b'S': b"59"}
for days in (b"31", b"30", b"29"):
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 d[b"d"] = days
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return parsedate(date, extendeddateformats, d)[0]
except error.ParseError:
pass
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 d[b"d"] = b"28"
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return parsedate(date, extendeddateformats, d)[0]
date = date.strip()
if not date:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"dates cannot consist entirely of whitespace"))
Mark Thomas
py3: fix test-parse-date.t...
r40292 elif date[0:1] == b"<":
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 if not date[1:]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"invalid day spec, use '<DATE'"))
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 when = upper(date[1:])
return lambda x: x <= when
Mark Thomas
py3: fix test-parse-date.t...
r40292 elif date[0:1] == b">":
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 if not date[1:]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"invalid day spec, use '>DATE'"))
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 when = lower(date[1:])
return lambda x: x >= when
Mark Thomas
py3: fix test-parse-date.t...
r40292 elif date[0:1] == b"-":
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 try:
days = int(date[1:])
except ValueError:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"invalid day spec: %s") % date[1:])
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 if days < 0:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"%s must be nonnegative (see 'hg help dates')") % date[1:]
Augie Fackler
formatting: blacken the codebase...
r43346 )
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 when = makedate()[0] - days * 3600 * 24
return lambda x: x >= when
Mark Thomas
py3: fix test-parse-date.t...
r40292 elif b" to " in date:
a, b = date.split(b" to ")
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 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