2301 data = self._fh.read(min(n, self._left)) |
2258 data = self._fh.read(min(n, self._left)) |
2302 self._left -= len(data) |
2259 self._left -= len(data) |
2303 assert self._left >= 0 |
2260 assert self._left >= 0 |
2304 |
2261 |
2305 return data |
2262 return data |
2306 |
|
2307 def makedate(timestamp=None): |
|
2308 '''Return a unix timestamp (or the current time) as a (unixtime, |
|
2309 offset) tuple based off the local timezone.''' |
|
2310 if timestamp is None: |
|
2311 timestamp = time.time() |
|
2312 if timestamp < 0: |
|
2313 hint = _("check your clock") |
|
2314 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint) |
|
2315 delta = (datetime.datetime.utcfromtimestamp(timestamp) - |
|
2316 datetime.datetime.fromtimestamp(timestamp)) |
|
2317 tz = delta.days * 86400 + delta.seconds |
|
2318 return timestamp, tz |
|
2319 |
|
2320 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'): |
|
2321 """represent a (unixtime, offset) tuple as a localized time. |
|
2322 unixtime is seconds since the epoch, and offset is the time zone's |
|
2323 number of seconds away from UTC. |
|
2324 |
|
2325 >>> datestr((0, 0)) |
|
2326 'Thu Jan 01 00:00:00 1970 +0000' |
|
2327 >>> datestr((42, 0)) |
|
2328 'Thu Jan 01 00:00:42 1970 +0000' |
|
2329 >>> datestr((-42, 0)) |
|
2330 'Wed Dec 31 23:59:18 1969 +0000' |
|
2331 >>> datestr((0x7fffffff, 0)) |
|
2332 'Tue Jan 19 03:14:07 2038 +0000' |
|
2333 >>> datestr((-0x80000000, 0)) |
|
2334 'Fri Dec 13 20:45:52 1901 +0000' |
|
2335 """ |
|
2336 t, tz = date or makedate() |
|
2337 if "%1" in format or "%2" in format or "%z" in format: |
|
2338 sign = (tz > 0) and "-" or "+" |
|
2339 minutes = abs(tz) // 60 |
|
2340 q, r = divmod(minutes, 60) |
|
2341 format = format.replace("%z", "%1%2") |
|
2342 format = format.replace("%1", "%c%02d" % (sign, q)) |
|
2343 format = format.replace("%2", "%02d" % r) |
|
2344 d = t - tz |
|
2345 if d > 0x7fffffff: |
|
2346 d = 0x7fffffff |
|
2347 elif d < -0x80000000: |
|
2348 d = -0x80000000 |
|
2349 # Never use time.gmtime() and datetime.datetime.fromtimestamp() |
|
2350 # because they use the gmtime() system call which is buggy on Windows |
|
2351 # for negative values. |
|
2352 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d) |
|
2353 s = encoding.strtolocal(t.strftime(encoding.strfromlocal(format))) |
|
2354 return s |
|
2355 |
|
2356 def shortdate(date=None): |
|
2357 """turn (timestamp, tzoff) tuple into iso 8631 date.""" |
|
2358 return datestr(date, format='%Y-%m-%d') |
|
2359 |
|
2360 def parsetimezone(s): |
|
2361 """find a trailing timezone, if any, in string, and return a |
|
2362 (offset, remainder) pair""" |
|
2363 s = pycompat.bytestr(s) |
|
2364 |
|
2365 if s.endswith("GMT") or s.endswith("UTC"): |
|
2366 return 0, s[:-3].rstrip() |
|
2367 |
|
2368 # Unix-style timezones [+-]hhmm |
|
2369 if len(s) >= 5 and s[-5] in "+-" and s[-4:].isdigit(): |
|
2370 sign = (s[-5] == "+") and 1 or -1 |
|
2371 hours = int(s[-4:-2]) |
|
2372 minutes = int(s[-2:]) |
|
2373 return -sign * (hours * 60 + minutes) * 60, s[:-5].rstrip() |
|
2374 |
|
2375 # ISO8601 trailing Z |
|
2376 if s.endswith("Z") and s[-2:-1].isdigit(): |
|
2377 return 0, s[:-1] |
|
2378 |
|
2379 # ISO8601-style [+-]hh:mm |
|
2380 if (len(s) >= 6 and s[-6] in "+-" and s[-3] == ":" and |
|
2381 s[-5:-3].isdigit() and s[-2:].isdigit()): |
|
2382 sign = (s[-6] == "+") and 1 or -1 |
|
2383 hours = int(s[-5:-3]) |
|
2384 minutes = int(s[-2:]) |
|
2385 return -sign * (hours * 60 + minutes) * 60, s[:-6] |
|
2386 |
|
2387 return None, s |
|
2388 |
|
2389 def strdate(string, format, defaults=None): |
|
2390 """parse a localized time string and return a (unixtime, offset) tuple. |
|
2391 if the string cannot be parsed, ValueError is raised.""" |
|
2392 if defaults is None: |
|
2393 defaults = {} |
|
2394 |
|
2395 # NOTE: unixtime = localunixtime + offset |
|
2396 offset, date = parsetimezone(string) |
|
2397 |
|
2398 # add missing elements from defaults |
|
2399 usenow = False # default to using biased defaults |
|
2400 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity |
|
2401 part = pycompat.bytestr(part) |
|
2402 found = [True for p in part if ("%"+p) in format] |
|
2403 if not found: |
|
2404 date += "@" + defaults[part][usenow] |
|
2405 format += "@%" + part[0] |
|
2406 else: |
|
2407 # We've found a specific time element, less specific time |
|
2408 # elements are relative to today |
|
2409 usenow = True |
|
2410 |
|
2411 timetuple = time.strptime(encoding.strfromlocal(date), |
|
2412 encoding.strfromlocal(format)) |
|
2413 localunixtime = int(calendar.timegm(timetuple)) |
|
2414 if offset is None: |
|
2415 # local timezone |
|
2416 unixtime = int(time.mktime(timetuple)) |
|
2417 offset = unixtime - localunixtime |
|
2418 else: |
|
2419 unixtime = localunixtime + offset |
|
2420 return unixtime, offset |
|
2421 |
|
2422 def parsedate(date, formats=None, bias=None): |
|
2423 """parse a localized date/time and return a (unixtime, offset) tuple. |
|
2424 |
|
2425 The date may be a "unixtime offset" string or in one of the specified |
|
2426 formats. If the date already is a (unixtime, offset) tuple, it is returned. |
|
2427 |
|
2428 >>> parsedate(b' today ') == parsedate( |
|
2429 ... datetime.date.today().strftime('%b %d').encode('ascii')) |
|
2430 True |
|
2431 >>> parsedate(b'yesterday ') == parsedate( |
|
2432 ... (datetime.date.today() - datetime.timedelta(days=1) |
|
2433 ... ).strftime('%b %d').encode('ascii')) |
|
2434 True |
|
2435 >>> now, tz = makedate() |
|
2436 >>> strnow, strtz = parsedate(b'now') |
|
2437 >>> (strnow - now) < 1 |
|
2438 True |
|
2439 >>> tz == strtz |
|
2440 True |
|
2441 """ |
|
2442 if bias is None: |
|
2443 bias = {} |
|
2444 if not date: |
|
2445 return 0, 0 |
|
2446 if isinstance(date, tuple) and len(date) == 2: |
|
2447 return date |
|
2448 if not formats: |
|
2449 formats = defaultdateformats |
|
2450 date = date.strip() |
|
2451 |
|
2452 if date == 'now' or date == _('now'): |
|
2453 return makedate() |
|
2454 if date == 'today' or date == _('today'): |
|
2455 date = datetime.date.today().strftime(r'%b %d') |
|
2456 date = encoding.strtolocal(date) |
|
2457 elif date == 'yesterday' or date == _('yesterday'): |
|
2458 date = (datetime.date.today() - |
|
2459 datetime.timedelta(days=1)).strftime(r'%b %d') |
|
2460 date = encoding.strtolocal(date) |
|
2461 |
|
2462 try: |
|
2463 when, offset = map(int, date.split(' ')) |
|
2464 except ValueError: |
|
2465 # fill out defaults |
|
2466 now = makedate() |
|
2467 defaults = {} |
|
2468 for part in ("d", "mb", "yY", "HI", "M", "S"): |
|
2469 # this piece is for rounding the specific end of unknowns |
|
2470 b = bias.get(part) |
|
2471 if b is None: |
|
2472 if part[0:1] in "HMS": |
|
2473 b = "00" |
|
2474 else: |
|
2475 b = "0" |
|
2476 |
|
2477 # this piece is for matching the generic end to today's date |
|
2478 n = datestr(now, "%" + part[0:1]) |
|
2479 |
|
2480 defaults[part] = (b, n) |
|
2481 |
|
2482 for format in formats: |
|
2483 try: |
|
2484 when, offset = strdate(date, format, defaults) |
|
2485 except (ValueError, OverflowError): |
|
2486 pass |
|
2487 else: |
|
2488 break |
|
2489 else: |
|
2490 raise error.ParseError( |
|
2491 _('invalid date: %r') % pycompat.bytestr(date)) |
|
2492 # validate explicit (probably user-specified) date and |
|
2493 # time zone offset. values must fit in signed 32 bits for |
|
2494 # current 32-bit linux runtimes. timezones go from UTC-12 |
|
2495 # to UTC+14 |
|
2496 if when < -0x80000000 or when > 0x7fffffff: |
|
2497 raise error.ParseError(_('date exceeds 32 bits: %d') % when) |
|
2498 if offset < -50400 or offset > 43200: |
|
2499 raise error.ParseError(_('impossible time zone offset: %d') % offset) |
|
2500 return when, offset |
|
2501 |
|
2502 def matchdate(date): |
|
2503 """Return a function that matches a given date match specifier |
|
2504 |
|
2505 Formats include: |
|
2506 |
|
2507 '{date}' match a given date to the accuracy provided |
|
2508 |
|
2509 '<{date}' on or before a given date |
|
2510 |
|
2511 '>{date}' on or after a given date |
|
2512 |
|
2513 >>> p1 = parsedate(b"10:29:59") |
|
2514 >>> p2 = parsedate(b"10:30:00") |
|
2515 >>> p3 = parsedate(b"10:30:59") |
|
2516 >>> p4 = parsedate(b"10:31:00") |
|
2517 >>> p5 = parsedate(b"Sep 15 10:30:00 1999") |
|
2518 >>> f = matchdate(b"10:30") |
|
2519 >>> f(p1[0]) |
|
2520 False |
|
2521 >>> f(p2[0]) |
|
2522 True |
|
2523 >>> f(p3[0]) |
|
2524 True |
|
2525 >>> f(p4[0]) |
|
2526 False |
|
2527 >>> f(p5[0]) |
|
2528 False |
|
2529 """ |
|
2530 |
|
2531 def lower(date): |
|
2532 d = {'mb': "1", 'd': "1"} |
|
2533 return parsedate(date, extendeddateformats, d)[0] |
|
2534 |
|
2535 def upper(date): |
|
2536 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"} |
|
2537 for days in ("31", "30", "29"): |
|
2538 try: |
|
2539 d["d"] = days |
|
2540 return parsedate(date, extendeddateformats, d)[0] |
|
2541 except error.ParseError: |
|
2542 pass |
|
2543 d["d"] = "28" |
|
2544 return parsedate(date, extendeddateformats, d)[0] |
|
2545 |
|
2546 date = date.strip() |
|
2547 |
|
2548 if not date: |
|
2549 raise Abort(_("dates cannot consist entirely of whitespace")) |
|
2550 elif date[0] == "<": |
|
2551 if not date[1:]: |
|
2552 raise Abort(_("invalid day spec, use '<DATE'")) |
|
2553 when = upper(date[1:]) |
|
2554 return lambda x: x <= when |
|
2555 elif date[0] == ">": |
|
2556 if not date[1:]: |
|
2557 raise Abort(_("invalid day spec, use '>DATE'")) |
|
2558 when = lower(date[1:]) |
|
2559 return lambda x: x >= when |
|
2560 elif date[0] == "-": |
|
2561 try: |
|
2562 days = int(date[1:]) |
|
2563 except ValueError: |
|
2564 raise Abort(_("invalid day spec: %s") % date[1:]) |
|
2565 if days < 0: |
|
2566 raise Abort(_("%s must be nonnegative (see 'hg help dates')") |
|
2567 % date[1:]) |
|
2568 when = makedate()[0] - days * 3600 * 24 |
|
2569 return lambda x: x >= when |
|
2570 elif " to " in date: |
|
2571 a, b = date.split(" to ") |
|
2572 start, stop = lower(a), upper(b) |
|
2573 return lambda x: x >= start and x <= stop |
|
2574 else: |
|
2575 start, stop = lower(date), upper(date) |
|
2576 return lambda x: x >= start and x <= stop |
|
2577 |
2263 |
2578 def stringmatcher(pattern, casesensitive=True): |
2264 def stringmatcher(pattern, casesensitive=True): |
2579 """ |
2265 """ |
2580 accepts a string, possibly starting with 're:' or 'literal:' prefix. |
2266 accepts a string, possibly starting with 're:' or 'literal:' prefix. |
2581 returns the matcher name, pattern, and matcher function. |
2267 returns the matcher name, pattern, and matcher function. |
4301 byte = ord(readexactly(fh, 1)) |
3987 byte = ord(readexactly(fh, 1)) |
4302 result |= ((byte & 0x7f) << shift) |
3988 result |= ((byte & 0x7f) << shift) |
4303 if not (byte & 0x80): |
3989 if not (byte & 0x80): |
4304 return result |
3990 return result |
4305 shift += 7 |
3991 shift += 7 |
|
3992 |
|
3993 ### |
|
3994 # Deprecation warnings for util.py splitting |
|
3995 ### |
|
3996 |
|
3997 defaultdateformats = dateutil.defaultdateformats |
|
3998 |
|
3999 extendeddateformats = dateutil.extendeddateformats |
|
4000 |
|
4001 def makedate(*args, **kwargs): |
|
4002 msg = ("'util.makedate' is deprecated, " |
|
4003 "use 'utils.dateutil.makedate'") |
|
4004 nouideprecwarn(msg, "4.6") |
|
4005 return dateutil.makedate(*args, **kwargs) |
|
4006 |
|
4007 def datestr(*args, **kwargs): |
|
4008 msg = ("'util.datestr' is deprecated, " |
|
4009 "use 'utils.dateutil.datestr'") |
|
4010 nouideprecwarn(msg, "4.6") |
|
4011 debugstacktrace() |
|
4012 return dateutil.datestr(*args, **kwargs) |
|
4013 |
|
4014 def shortdate(*args, **kwargs): |
|
4015 msg = ("'util.shortdate' is deprecated, " |
|
4016 "use 'utils.dateutil.shortdate'") |
|
4017 nouideprecwarn(msg, "4.6") |
|
4018 return dateutil.shortdate(*args, **kwargs) |
|
4019 |
|
4020 def parsetimezone(*args, **kwargs): |
|
4021 msg = ("'util.parsetimezone' is deprecated, " |
|
4022 "use 'utils.dateutil.parsetimezone'") |
|
4023 nouideprecwarn(msg, "4.6") |
|
4024 return dateutil.parsetimezone(*args, **kwargs) |
|
4025 |
|
4026 def strdate(*args, **kwargs): |
|
4027 msg = ("'util.strdate' is deprecated, " |
|
4028 "use 'utils.dateutil.strdate'") |
|
4029 nouideprecwarn(msg, "4.6") |
|
4030 return dateutil.strdate(*args, **kwargs) |
|
4031 |
|
4032 def parsedate(*args, **kwargs): |
|
4033 msg = ("'util.parsedate' is deprecated, " |
|
4034 "use 'utils.dateutil.parsedate'") |
|
4035 nouideprecwarn(msg, "4.6") |
|
4036 return dateutil.parsedate(*args, **kwargs) |
|
4037 |
|
4038 def matchdate(*args, **kwargs): |
|
4039 msg = ("'util.matchdate' is deprecated, " |
|
4040 "use 'utils.dateutil.matchdate'") |
|
4041 nouideprecwarn(msg, "4.6") |
|
4042 return dateutil.matchdate(*args, **kwargs) |