Mercurial > public > mercurial-scm > hg-stable
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 ") |