Last active
August 28, 2024 17:55
-
-
Save apg/bc793d42f38d73a7d84ffa0fc309419f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This is a reference implementation for the proposed datetime RFC | |
for the Cedar Policy Language. The RFC is currently being discussed | |
at https://github.com/cedar-policy/rfcs/pull/80 | |
>>> duration("1h") | |
3600000ms | |
>>> datetime("1970-01-01T00:00:00Z") | |
0ms | |
>>> datetime("1969-12-31T23:59:59Z") | |
-1000ms | |
>>> datetime("1970-01-01T00:00:00Z").offset(duration("1m")) | |
60000ms | |
>>> datetime("1970-01-01T00:00:00Z").offset(duration("-1m")) | |
-60000ms | |
>>> datetime("1980-01-01T00:00:00Z") > datetime("1970-01-01T00:00:00Z") | |
True | |
>>> datetime("1980-01-01T00:00:00Z") >= datetime("1970-01-01T00:00:00Z") | |
True | |
>>> datetime("1970-01-01T00:00:00Z") < datetime("1980-01-01T00:00:00Z") | |
True | |
>>> datetime("1969-12-31T23:59:59Z") < datetime("1970-01-01T00:00:00Z") | |
True | |
>>> datetime("1969-12-31T23:59:58Z") < datetime("1969-12-31T23:59:59Z") | |
True | |
>>> datetime("1970-01-01T00:00:01Z") < datetime("1969-12-31T23:59:59Z") | |
False | |
>>> datetime("1970-01-01T00:00:00Z") <= datetime("1980-01-01T00:00:00Z") | |
True | |
>>> datetime("1970-01-01T00:00:00Z") == datetime("1970-01-01T00:00:00Z") | |
True | |
>>> datetime("1970-01-01T00:00:00Z") != datetime("1980-01-01T00:00:00Z") | |
True | |
>>> datetime("1970-01-01T12:00:00Z").toDate() == datetime("1970-01-01T00:00:00Z") | |
True | |
>>> datetime("1970-01-01T12:00:00Z").toTime() == duration("12h") | |
True | |
>>> datetime("1970-01-01T00:00:00Z").durationSince(datetime("1970-01-01T00:00:00Z")) | |
0ms | |
>>> datetime("1970-01-01T00:00:00Z").durationSince(datetime("1970-01-01T00:00:01Z")) | |
-1000ms | |
>>> datetime("1969-12-31T23:59:59Z").durationSince(datetime("1970-01-01T00:00:00Z")) | |
-1000ms | |
>>> datetime("1969-12-31T23:59:58Z").durationSince(datetime("1969-12-31T23:59:59Z")) | |
-1000ms | |
>>> datetime("1970-01-01T00:00:01Z").durationSince(datetime("1969-12-31T23:59:59Z")) | |
2000ms | |
>>> datetime("1970-01-01T00:00:01Z").offset(datetime("1969-12-31T23:59:58Z").durationSince(datetime("1970-01-01T00:00:01Z"))) == datetime("1969-12-31T23:59:58Z") | |
True | |
>>> datetime("1970-01-01T00:00:01Z").offset(datetime("1970-01-01T00:00:03Z").durationSince(datetime("1970-01-01T00:00:01Z"))) == datetime("1970-01-01Z00:00:03Z") | |
True | |
>>> duration("1d") == duration("86400000ms") | |
True | |
>>> duration("1d") != duration("1d1ms") | |
True | |
>>> duration("2d") > duration("86400000ms") | |
True | |
>>> duration("2d") >= duration("86400000ms") | |
True | |
>>> duration("1d") < duration("2d") | |
True | |
>>> duration("1d") <= duration("2d") | |
True | |
>>> duration("1d").toMilliseconds() | |
86400000 | |
>>> duration("1d").toSeconds() | |
86400 | |
>>> duration("1d").toMinutes() | |
1440 | |
>>> duration("1d").toHours() | |
24 | |
>>> duration("1d").toDays() | |
1 | |
""" | |
import datetime as rdt | |
def parseDuration(s): | |
""" | |
Nu, where N is positive or negative integer, and u is one of: | |
d, h, m, s, ms | |
These are combined into a single string, and returned as milliseconds | |
""" | |
totalMS = 0 | |
quantity = 0 | |
negative = False | |
unit = "" | |
i = 0 | |
while i < len(s): | |
if s[i] == '-' and quantity == 0 and not negative: | |
negative = True | |
i += 1 | |
elif s[i].isdigit(): | |
quantity = (quantity * 10) + int(s[i]) | |
i += 1 | |
elif s[i] in "dhms": | |
if s[i] == "m" and i+1 < len(s) and s[i+1] == "s": | |
unit = "ms" | |
i += 2 | |
else: | |
unit = s[i] | |
i += 1 | |
negate = -1 if negative else 1 | |
if unit == "ms": | |
totalMS = totalMS + (quantity * negate) | |
elif unit == "s": | |
totalMS = totalMS + (quantity * 1000 * negate) | |
elif unit == "m": | |
totalMS = totalMS + (quantity * 60 * 1000 * negate) | |
elif unit == "h": | |
totalMS = totalMS + (quantity * 60 * 60 * 1000 * negate) | |
elif unit == "d": | |
totalMS = totalMS + (quantity * 24 * 60 * 60 * 1000 * negate) | |
# reset | |
quantity = 0 | |
negative = False | |
unit = "" | |
else: | |
raise TypeError("not a valid duration string") | |
return totalMS | |
class duration: | |
def __init__(self, s): | |
self.ms = parseDuration(s) | |
def __gt__(self, other): | |
return self.ms > other.ms | |
def __ge__(self, other): | |
return self.ms >= other.ms | |
def __lt__(self, other): | |
return self.ms < other.ms | |
def __le__(self, other): | |
return self.ms <= other.ms | |
def __eq__(self, other): | |
return self.ms == other.ms | |
def __ne__(self, other): | |
return self.ms != other.ms | |
def toMilliseconds(self): | |
return self.ms | |
def toSeconds(self): | |
return self.ms // 1000 | |
def toMinutes(self): | |
return self.ms // (1000 * 60) | |
def toHours(self): | |
return self.ms // (1000 * 60 * 60) | |
def toDays(self): | |
return self.ms // (1000 * 60 * 60 * 24) | |
def __repr__(self): | |
return str(self.ms) + "ms" | |
class datetime: | |
def __init__(self, s): | |
self.ts = int(rdt.datetime.fromisoformat(s).timestamp() * 1000) | |
def __gt__(self, other): | |
return self.ts > other.ts | |
def __ge__(self, other): | |
return self.ts >= other.ts | |
def __lt__(self, other): | |
return self.ts < other.ts | |
def __le__(self, other): | |
return self.ts <= other.ts | |
def __eq__(self, other): | |
return self.ts == other.ts | |
def __ne__(self, other): | |
return self.ts != other.ts | |
def durationSince(self, other): | |
d = duration("0ms") | |
d.ms = self.ts - other.ts | |
return d | |
def offset(self, dur): | |
d = zerodate() | |
d.ts = self.ts + dur.ms | |
return d | |
def toDate(self): | |
d = zerodate() | |
d.ts = self.ts - (self.ts % 86400000) | |
return d | |
def toTime(self): | |
return self.durationSince(self.toDate()) | |
def __repr__(self): | |
return str(self.ts) + "ms" | |
def zerodate(): | |
return datetime("1970-01-01T00:00:00Z") | |
if __name__ == "__main__": | |
import doctest | |
doctest.testmod() | |
zero = datetime("1970-01-01T00:00:00Z") | |
plus3 = datetime("1970-01-01T00:00:03Z") | |
plus5 = datetime("1970-01-01T00:00:05Z") | |
minus3 = datetime("1969-12-31T23:59:57Z") | |
minus5 = datetime("1969-12-31T23:59:55Z") | |
xs = [zero, plus3, plus5, minus3, minus5] | |
ys = [zero, plus3, plus5, minus3, minus5] | |
for x in xs: | |
for y in ys: | |
dur = x.durationSince(y) | |
print(x, y, "durationSince:", dur, y.offset(dur) == x) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment