From cd53c42e23b209b748d8f3e13fa7a8756dea6164 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 6 Feb 2021 15:20:23 +0800 Subject: [PATCH] nightly commit --- documents/Principle_zh-CN.md | 2 +- src/database.py | 1 + src/dt.py | 98 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/dt.py diff --git a/documents/Principle_zh-CN.md b/documents/Principle_zh-CN.md index 693ff3d..b778c80 100644 --- a/documents/Principle_zh-CN.md +++ b/documents/Principle_zh-CN.md @@ -8,7 +8,7 @@ 本日历系统基于UNIX时间戳进行处理。在设计上使用Int64进行存储以规避2038问题。同时,由于日历设计不需要精确到秒,因此本日历中与时间存储有关的时间戳使用标准UNIX时间戳/60来存储,构成粒度为分钟的时间戳。但是其余的,例如用户登录相关的时间戳,仍然为标准UNIX时间戳 -本日历目前无条件限定最小时间为1950年1月1日,最大时间为2200年12月31日。 +本日历目前无条件限定最小时间为1950年1月1日,最大时间为2199年12月31日。 API在遇到当前接口产生错误时,使用外层结构来进行返回错误,例如token无效,应用程序错误,参数错误等。请注意捕获错误。如果没有错误,则表示data字段为有效数据。 diff --git a/src/database.py b/src/database.py index 7e150c2..bb3e955 100644 --- a/src/database.py +++ b/src/database.py @@ -4,6 +4,7 @@ import json import utils import threading import logging +import dt def SafeDatabaseOperation(func): def wrapper(self, *args, **kwargs): diff --git a/src/dt.py b/src/dt.py new file mode 100644 index 0000000..7e6058a --- /dev/null +++ b/src/dt.py @@ -0,0 +1,98 @@ +import datetime +import time +import re +from functools import reduce + +MIN_TIMESTAMP = int(datetime.datetime(1950, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp() / 60) +MAX_TIMESTAMP = int(datetime.datetime(2200, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp() / 60) +DAY1_SPAN = 60 * 24 +DAY7_SPAN = 7 * DAY1_SPAN +YEAR400_SPAN = DAY1_SPAN * 400 * 365 + +def ResolveLoopStr(strl, starttime, tzoffset): + # check no loop + if strl == '': + return starttime + 1 + + # try compute from loopStop + (loopRules, loopStopRules) = strl.split('-') + cache = precompiledLoopStopRules['infinity'].search(loopStopRules) + if cache is not None: + return MAX_TIMESTAMP + cache = precompiledLoopStopRules['datetime'].search(loopStopRules) + if cache is not None: + return int(cache.group(1)) # group 1 is datetime + cache = precompiledLoopStopRules['times'].search(loopStopRules) + if cache is not None: + loopTimes = cache # for follwing calc + else: + raise Exception('Invalid loopStopRules') # invalid rules + + for rules in precompiledLoopRules: + cache = rules[0].search(loopRules) + if cache is not None: + return rules[1](cache, starttime, loopTimes, tzoffset) + else: + raise Exception('Invalid loopRules') + + +def LoopHandle_Year(searchResult, starttime, times, tzoffset): + pass + +def LoopHandle_Month(searchResult, starttime, times, tzoffset): + pass + +def LoopHandle_Week(searchResult, starttime, times, tzoffset): + weekOccupied = tuple(map(lambda x: x == 'T', searchResult.group(1))) + weekEventCount = reduce(lambda x, y: x + (1 if y else 0), weekOccupied, 0) + if weekEventCount == 0: + raise Exception('Invalid week format') + + weekSpan = int(searchResult.group(2)) + nowDayOfWeek = datetime.datetime.fromtimestamp(starttime, UTCTimezone(tzoffset)).weekday() + if not weekOccupied[nowDayOfWeek]: + times+=1 # if first event is not suit for week loop rules, add one more event to suit it. + fullWeek = times / weekEventCount + remainEvent = times % weekEventCount + + val = DAY7_SPAN * fullWeek * weekSpan + if val > MAX_TIMESTAMP: + return MAX_TIMESTAMP # return now, to reduce calc usage + + while remainEvent != 0: + val += DAY1_SPAN + if weekOccupied[nowDayOfWeek % 7]: + remainEvent -= 1 + nowDayOfWeek += 1 + + return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP + +def LoopHandle_Day(searchResult, starttime, times, tzoffset): + val = starttime + DAY1_SPAN * times * int(searchResult.group(1)) + return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP + +precompiledLoopRules = ( + (re.compile(r'^Y([RF]{1})([1-9]\d*)$'), LoopHandle_Year), + (re.compile(r'^M([RF]{1})([ABCD]{1})([1-9]\d*)$'), LoopHandle_Month), + (re.compile(r'^W([TF]{7})([1-9]\d*)$'), LoopHandle_Week), + (re.compile(r'^D([1-9]\d*)$'), LoopHandle_Day) +) + +precompiledLoopStopRules = { + 'infinity': re.compile(r'F') + 'datetime': re.compile(r'^D([1-9]\d*|0)$') + 'times': re.compile(r'^T([1-9]\d*)$') +} + +class UTCTimezone(datetime.tzinfo): + def __init__(self, offset = 0): + self._offset = offset + + def utcoffset(self, dt): + return datetime.timedelta(minutes=self._offset) + + def tzname(self, dt): + return 'UTC {}'.format(self._offset) + + def dst(self, dt): + return timedelta(0) \ No newline at end of file