comparison mercurial/utils/dateutil.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children 9f70512ae2cf
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
18 pycompat, 18 pycompat,
19 ) 19 )
20 20
21 # used by parsedate 21 # used by parsedate
22 defaultdateformats = ( 22 defaultdateformats = (
23 '%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601 23 b'%Y-%m-%dT%H:%M:%S', # the 'real' ISO8601
24 '%Y-%m-%dT%H:%M', # without seconds 24 b'%Y-%m-%dT%H:%M', # without seconds
25 '%Y-%m-%dT%H%M%S', # another awful but legal variant without : 25 b'%Y-%m-%dT%H%M%S', # another awful but legal variant without :
26 '%Y-%m-%dT%H%M', # without seconds 26 b'%Y-%m-%dT%H%M', # without seconds
27 '%Y-%m-%d %H:%M:%S', # our common legal variant 27 b'%Y-%m-%d %H:%M:%S', # our common legal variant
28 '%Y-%m-%d %H:%M', # without seconds 28 b'%Y-%m-%d %H:%M', # without seconds
29 '%Y-%m-%d %H%M%S', # without : 29 b'%Y-%m-%d %H%M%S', # without :
30 '%Y-%m-%d %H%M', # without seconds 30 b'%Y-%m-%d %H%M', # without seconds
31 '%Y-%m-%d %I:%M:%S%p', 31 b'%Y-%m-%d %I:%M:%S%p',
32 '%Y-%m-%d %H:%M', 32 b'%Y-%m-%d %H:%M',
33 '%Y-%m-%d %I:%M%p', 33 b'%Y-%m-%d %I:%M%p',
34 '%Y-%m-%d', 34 b'%Y-%m-%d',
35 '%m-%d', 35 b'%m-%d',
36 '%m/%d', 36 b'%m/%d',
37 '%m/%d/%y', 37 b'%m/%d/%y',
38 '%m/%d/%Y', 38 b'%m/%d/%Y',
39 '%a %b %d %H:%M:%S %Y', 39 b'%a %b %d %H:%M:%S %Y',
40 '%a %b %d %I:%M:%S%p %Y', 40 b'%a %b %d %I:%M:%S%p %Y',
41 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822" 41 b'%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
42 '%b %d %H:%M:%S %Y', 42 b'%b %d %H:%M:%S %Y',
43 '%b %d %I:%M:%S%p %Y', 43 b'%b %d %I:%M:%S%p %Y',
44 '%b %d %H:%M:%S', 44 b'%b %d %H:%M:%S',
45 '%b %d %I:%M:%S%p', 45 b'%b %d %I:%M:%S%p',
46 '%b %d %H:%M', 46 b'%b %d %H:%M',
47 '%b %d %I:%M%p', 47 b'%b %d %I:%M%p',
48 '%b %d %Y', 48 b'%b %d %Y',
49 '%b %d', 49 b'%b %d',
50 '%H:%M:%S', 50 b'%H:%M:%S',
51 '%I:%M:%S%p', 51 b'%I:%M:%S%p',
52 '%H:%M', 52 b'%H:%M',
53 '%I:%M%p', 53 b'%I:%M%p',
54 ) 54 )
55 55
56 extendeddateformats = defaultdateformats + ("%Y", "%Y-%m", "%b", "%b %Y",) 56 extendeddateformats = defaultdateformats + (b"%Y", b"%Y-%m", b"%b", b"%b %Y",)
57 57
58 58
59 def makedate(timestamp=None): 59 def makedate(timestamp=None):
60 '''Return a unix timestamp (or the current time) as a (unixtime, 60 '''Return a unix timestamp (or the current time) as a (unixtime,
61 offset) tuple based off the local timezone.''' 61 offset) tuple based off the local timezone.'''
62 if timestamp is None: 62 if timestamp is None:
63 timestamp = time.time() 63 timestamp = time.time()
64 if timestamp < 0: 64 if timestamp < 0:
65 hint = _("check your clock") 65 hint = _(b"check your clock")
66 raise error.Abort(_("negative timestamp: %d") % timestamp, hint=hint) 66 raise error.Abort(_(b"negative timestamp: %d") % timestamp, hint=hint)
67 delta = datetime.datetime.utcfromtimestamp( 67 delta = datetime.datetime.utcfromtimestamp(
68 timestamp 68 timestamp
69 ) - datetime.datetime.fromtimestamp(timestamp) 69 ) - datetime.datetime.fromtimestamp(timestamp)
70 tz = delta.days * 86400 + delta.seconds 70 tz = delta.days * 86400 + delta.seconds
71 return timestamp, tz 71 return timestamp, tz
72 72
73 73
74 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'): 74 def datestr(date=None, format=b'%a %b %d %H:%M:%S %Y %1%2'):
75 """represent a (unixtime, offset) tuple as a localized time. 75 """represent a (unixtime, offset) tuple as a localized time.
76 unixtime is seconds since the epoch, and offset is the time zone's 76 unixtime is seconds since the epoch, and offset is the time zone's
77 number of seconds away from UTC. 77 number of seconds away from UTC.
78 78
79 >>> datestr((0, 0)) 79 >>> datestr((0, 0))
86 'Tue Jan 19 03:14:07 2038 +0000' 86 'Tue Jan 19 03:14:07 2038 +0000'
87 >>> datestr((-0x80000000, 0)) 87 >>> datestr((-0x80000000, 0))
88 'Fri Dec 13 20:45:52 1901 +0000' 88 'Fri Dec 13 20:45:52 1901 +0000'
89 """ 89 """
90 t, tz = date or makedate() 90 t, tz = date or makedate()
91 if "%1" in format or "%2" in format or "%z" in format: 91 if b"%1" in format or b"%2" in format or b"%z" in format:
92 sign = (tz > 0) and "-" or "+" 92 sign = (tz > 0) and b"-" or b"+"
93 minutes = abs(tz) // 60 93 minutes = abs(tz) // 60
94 q, r = divmod(minutes, 60) 94 q, r = divmod(minutes, 60)
95 format = format.replace("%z", "%1%2") 95 format = format.replace(b"%z", b"%1%2")
96 format = format.replace("%1", "%c%02d" % (sign, q)) 96 format = format.replace(b"%1", b"%c%02d" % (sign, q))
97 format = format.replace("%2", "%02d" % r) 97 format = format.replace(b"%2", b"%02d" % r)
98 d = t - tz 98 d = t - tz
99 if d > 0x7FFFFFFF: 99 if d > 0x7FFFFFFF:
100 d = 0x7FFFFFFF 100 d = 0x7FFFFFFF
101 elif d < -0x80000000: 101 elif d < -0x80000000:
102 d = -0x80000000 102 d = -0x80000000
108 return s 108 return s
109 109
110 110
111 def shortdate(date=None): 111 def shortdate(date=None):
112 """turn (timestamp, tzoff) tuple into iso 8631 date.""" 112 """turn (timestamp, tzoff) tuple into iso 8631 date."""
113 return datestr(date, format='%Y-%m-%d') 113 return datestr(date, format=b'%Y-%m-%d')
114 114
115 115
116 def parsetimezone(s): 116 def parsetimezone(s):
117 """find a trailing timezone, if any, in string, and return a 117 """find a trailing timezone, if any, in string, and return a
118 (offset, remainder) pair""" 118 (offset, remainder) pair"""
119 s = pycompat.bytestr(s) 119 s = pycompat.bytestr(s)
120 120
121 if s.endswith("GMT") or s.endswith("UTC"): 121 if s.endswith(b"GMT") or s.endswith(b"UTC"):
122 return 0, s[:-3].rstrip() 122 return 0, s[:-3].rstrip()
123 123
124 # Unix-style timezones [+-]hhmm 124 # Unix-style timezones [+-]hhmm
125 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit(): 125 if len(s) >= 5 and s[-5] in b"+-" and s[-4:].isdigit():
126 sign = (s[-5] == "+") and 1 or -1 126 sign = (s[-5] == b"+") and 1 or -1
127 hours = int(s[-4:-2]) 127 hours = int(s[-4:-2])
128 minutes = int(s[-2:]) 128 minutes = int(s[-2:])
129 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip() 129 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip()
130 130
131 # ISO8601 trailing Z 131 # ISO8601 trailing Z
132 if s.endswith("Z") and s[-2:-1].isdigit(): 132 if s.endswith(b"Z") and s[-2:-1].isdigit():
133 return 0, s[:-1] 133 return 0, s[:-1]
134 134
135 # ISO8601-style [+-]hh:mm 135 # ISO8601-style [+-]hh:mm
136 if ( 136 if (
137 len(s) >= 6 137 len(s) >= 6
138 and s[-6] in "+-" 138 and s[-6] in b"+-"
139 and s[-3] == ":" 139 and s[-3] == b":"
140 and s[-5:-3].isdigit() 140 and s[-5:-3].isdigit()
141 and s[-2:].isdigit() 141 and s[-2:].isdigit()
142 ): 142 ):
143 sign = (s[-6] == "+") and 1 or -1 143 sign = (s[-6] == b"+") and 1 or -1
144 hours = int(s[-5:-3]) 144 hours = int(s[-5:-3])
145 minutes = int(s[-2:]) 145 minutes = int(s[-2:])
146 return -sign * (hours * 60 + minutes) * 60, s[:-6] 146 return -sign * (hours * 60 + minutes) * 60, s[:-6]
147 147
148 return None, s 148 return None, s
157 # NOTE: unixtime = localunixtime + offset 157 # NOTE: unixtime = localunixtime + offset
158 offset, date = parsetimezone(string) 158 offset, date = parsetimezone(string)
159 159
160 # add missing elements from defaults 160 # add missing elements from defaults
161 usenow = False # default to using biased defaults 161 usenow = False # default to using biased defaults
162 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity 162 for part in (
163 b"S",
164 b"M",
165 b"HI",
166 b"d",
167 b"mb",
168 b"yY",
169 ): # decreasing specificity
163 part = pycompat.bytestr(part) 170 part = pycompat.bytestr(part)
164 found = [True for p in part if ("%" + p) in format] 171 found = [True for p in part if (b"%" + p) in format]
165 if not found: 172 if not found:
166 date += "@" + defaults[part][usenow] 173 date += b"@" + defaults[part][usenow]
167 format += "@%" + part[0] 174 format += b"@%" + part[0]
168 else: 175 else:
169 # We've found a specific time element, less specific time 176 # We've found a specific time element, less specific time
170 # elements are relative to today 177 # elements are relative to today
171 usenow = True 178 usenow = True
172 179
211 return date 218 return date
212 if not formats: 219 if not formats:
213 formats = defaultdateformats 220 formats = defaultdateformats
214 date = date.strip() 221 date = date.strip()
215 222
216 if date == 'now' or date == _('now'): 223 if date == b'now' or date == _(b'now'):
217 return makedate() 224 return makedate()
218 if date == 'today' or date == _('today'): 225 if date == b'today' or date == _(b'today'):
219 date = datetime.date.today().strftime(r'%b %d') 226 date = datetime.date.today().strftime(r'%b %d')
220 date = encoding.strtolocal(date) 227 date = encoding.strtolocal(date)
221 elif date == 'yesterday' or date == _('yesterday'): 228 elif date == b'yesterday' or date == _(b'yesterday'):
222 date = (datetime.date.today() - datetime.timedelta(days=1)).strftime( 229 date = (datetime.date.today() - datetime.timedelta(days=1)).strftime(
223 r'%b %d' 230 r'%b %d'
224 ) 231 )
225 date = encoding.strtolocal(date) 232 date = encoding.strtolocal(date)
226 233
227 try: 234 try:
228 when, offset = map(int, date.split(' ')) 235 when, offset = map(int, date.split(b' '))
229 except ValueError: 236 except ValueError:
230 # fill out defaults 237 # fill out defaults
231 now = makedate() 238 now = makedate()
232 defaults = {} 239 defaults = {}
233 for part in ("d", "mb", "yY", "HI", "M", "S"): 240 for part in (b"d", b"mb", b"yY", b"HI", b"M", b"S"):
234 # this piece is for rounding the specific end of unknowns 241 # this piece is for rounding the specific end of unknowns
235 b = bias.get(part) 242 b = bias.get(part)
236 if b is None: 243 if b is None:
237 if part[0:1] in "HMS": 244 if part[0:1] in b"HMS":
238 b = "00" 245 b = b"00"
239 else: 246 else:
240 b = "0" 247 b = b"0"
241 248
242 # this piece is for matching the generic end to today's date 249 # this piece is for matching the generic end to today's date
243 n = datestr(now, "%" + part[0:1]) 250 n = datestr(now, b"%" + part[0:1])
244 251
245 defaults[part] = (b, n) 252 defaults[part] = (b, n)
246 253
247 for format in formats: 254 for format in formats:
248 try: 255 try:
251 pass 258 pass
252 else: 259 else:
253 break 260 break
254 else: 261 else:
255 raise error.ParseError( 262 raise error.ParseError(
256 _('invalid date: %r') % pycompat.bytestr(date) 263 _(b'invalid date: %r') % pycompat.bytestr(date)
257 ) 264 )
258 # validate explicit (probably user-specified) date and 265 # validate explicit (probably user-specified) date and
259 # time zone offset. values must fit in signed 32 bits for 266 # time zone offset. values must fit in signed 32 bits for
260 # current 32-bit linux runtimes. timezones go from UTC-12 267 # current 32-bit linux runtimes. timezones go from UTC-12
261 # to UTC+14 268 # to UTC+14
262 if when < -0x80000000 or when > 0x7FFFFFFF: 269 if when < -0x80000000 or when > 0x7FFFFFFF:
263 raise error.ParseError(_('date exceeds 32 bits: %d') % when) 270 raise error.ParseError(_(b'date exceeds 32 bits: %d') % when)
264 if offset < -50400 or offset > 43200: 271 if offset < -50400 or offset > 43200:
265 raise error.ParseError(_('impossible time zone offset: %d') % offset) 272 raise error.ParseError(_(b'impossible time zone offset: %d') % offset)
266 return when, offset 273 return when, offset
267 274
268 275
269 def matchdate(date): 276 def matchdate(date):
270 """Return a function that matches a given date match specifier 277 """Return a function that matches a given date match specifier
294 >>> f(p5[0]) 301 >>> f(p5[0])
295 False 302 False
296 """ 303 """
297 304
298 def lower(date): 305 def lower(date):
299 d = {'mb': "1", 'd': "1"} 306 d = {b'mb': b"1", b'd': b"1"}
300 return parsedate(date, extendeddateformats, d)[0] 307 return parsedate(date, extendeddateformats, d)[0]
301 308
302 def upper(date): 309 def upper(date):
303 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"} 310 d = {b'mb': b"12", b'HI': b"23", b'M': b"59", b'S': b"59"}
304 for days in ("31", "30", "29"): 311 for days in (b"31", b"30", b"29"):
305 try: 312 try:
306 d["d"] = days 313 d[b"d"] = days
307 return parsedate(date, extendeddateformats, d)[0] 314 return parsedate(date, extendeddateformats, d)[0]
308 except error.ParseError: 315 except error.ParseError:
309 pass 316 pass
310 d["d"] = "28" 317 d[b"d"] = b"28"
311 return parsedate(date, extendeddateformats, d)[0] 318 return parsedate(date, extendeddateformats, d)[0]
312 319
313 date = date.strip() 320 date = date.strip()
314 321
315 if not date: 322 if not date:
316 raise error.Abort(_("dates cannot consist entirely of whitespace")) 323 raise error.Abort(_(b"dates cannot consist entirely of whitespace"))
317 elif date[0:1] == b"<": 324 elif date[0:1] == b"<":
318 if not date[1:]: 325 if not date[1:]:
319 raise error.Abort(_("invalid day spec, use '<DATE'")) 326 raise error.Abort(_(b"invalid day spec, use '<DATE'"))
320 when = upper(date[1:]) 327 when = upper(date[1:])
321 return lambda x: x <= when 328 return lambda x: x <= when
322 elif date[0:1] == b">": 329 elif date[0:1] == b">":
323 if not date[1:]: 330 if not date[1:]:
324 raise error.Abort(_("invalid day spec, use '>DATE'")) 331 raise error.Abort(_(b"invalid day spec, use '>DATE'"))
325 when = lower(date[1:]) 332 when = lower(date[1:])
326 return lambda x: x >= when 333 return lambda x: x >= when
327 elif date[0:1] == b"-": 334 elif date[0:1] == b"-":
328 try: 335 try:
329 days = int(date[1:]) 336 days = int(date[1:])
330 except ValueError: 337 except ValueError:
331 raise error.Abort(_("invalid day spec: %s") % date[1:]) 338 raise error.Abort(_(b"invalid day spec: %s") % date[1:])
332 if days < 0: 339 if days < 0:
333 raise error.Abort( 340 raise error.Abort(
334 _("%s must be nonnegative (see 'hg help dates')") % date[1:] 341 _(b"%s must be nonnegative (see 'hg help dates')") % date[1:]
335 ) 342 )
336 when = makedate()[0] - days * 3600 * 24 343 when = makedate()[0] - days * 3600 * 24
337 return lambda x: x >= when 344 return lambda x: x >= when
338 elif b" to " in date: 345 elif b" to " in date:
339 a, b = date.split(b" to ") 346 a, b = date.split(b" to ")