1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
from __future__ import absolute_import, print_function, unicode_literals
import re
import datetime
class DeleteMarker:
pass
class JSONTemplateError(Exception):
def __init__(self, message):
super(JSONTemplateError, self).__init__(message)
self.location = []
def add_location(self, loc):
self.location.insert(0, loc)
def __str__(self):
location = ' at template' + ''.join(self.location)
return "{}{}: {}".format(
self.__class__.__name__,
location if self.location else '',
self.args[0])
class TemplateError(JSONTemplateError):
pass
class InterpreterError(JSONTemplateError):
pass
# Regular expression matching: X days Y hours Z minutes
# todo: support hr, wk, yr
FROMNOW_RE = re.compile(''.join([
r'^(\s*(?P<years>\d+)\s*y(ears?)?)?',
r'(\s*(?P<months>\d+)\s*mo(nths?)?)?',
r'(\s*(?P<weeks>\d+)\s*w(eeks?)?)?',
r'(\s*(?P<days>\d+)\s*d(ays?)?)?',
r'(\s*(?P<hours>\d+)\s*h(ours?)?)?',
r'(\s*(?P<minutes>\d+)\s*m(in(utes?)?)?)?\s*',
r'(\s*(?P<seconds>\d+)\s*s(ec(onds?)?)?)?\s*$',
]))
def fromNow(offset, reference):
# copied from taskcluster-client.py
# We want to handle past dates as well as future
future = True
offset = offset.lstrip()
if offset.startswith('-'):
future = False
offset = offset[1:].lstrip()
if offset.startswith('+'):
offset = offset[1:].lstrip()
# Parse offset
m = FROMNOW_RE.match(offset)
if m is None:
raise ValueError("offset string: '%s' does not parse" % offset)
# In order to calculate years and months we need to calculate how many days
# to offset the offset by, since timedelta only goes as high as weeks
days = 0
hours = 0
minutes = 0
seconds = 0
if m.group('years'):
# forget leap years, a year is 365 days
years = int(m.group('years'))
days += 365 * years
if m.group('months'):
# assume "month" means 30 days
months = int(m.group('months'))
days += 30 * months
days += int(m.group('days') or 0)
hours += int(m.group('hours') or 0)
minutes += int(m.group('minutes') or 0)
seconds += int(m.group('seconds') or 0)
# Offset datetime from utc
delta = datetime.timedelta(
weeks=int(m.group('weeks') or 0),
days=days,
hours=hours,
minutes=minutes,
seconds=seconds,
)
if isinstance(reference, string):
reference = datetime.datetime.strptime(
reference, '%Y-%m-%dT%H:%M:%S.%fZ')
elif reference is None:
reference = datetime.datetime.utcnow()
return stringDate(reference + delta if future else reference - delta)
datefmt_re = re.compile(r'(\.[0-9]{3})[0-9]*(\+00:00)?')
def to_str(v):
if isinstance(v, bool):
return {True: 'true', False: 'false'}[v]
elif isinstance(v, list):
return ','.join(to_str(e) for e in v)
elif v is None:
return 'null'
elif isinstance(v, string):
return v
else:
return str(v)
def stringDate(date):
# Convert to isoFormat
try:
string = date.isoformat(timespec='microseconds')
# py2.7 to py3.5 does not have timespec
except TypeError as e:
string = date.isoformat()
if string.find('.') == -1:
string += '.000'
string = datefmt_re.sub(r'\1Z', string)
return string
# the base class for strings, regardless of python version
try:
string = basestring
except NameError:
string = str
|