finish server - nodebug
This commit is contained in:
@@ -580,7 +580,7 @@ Admin类的操作不涉及任何客户端存储,因此不需要lastChange来
|
|||||||
|
|
||||||
#### 按年
|
#### 按年
|
||||||
|
|
||||||
格式:`Y[R|F][span]`
|
格式:`Y[S|R][span]`
|
||||||
|
|
||||||
每间隔`[span]`年在同样的月份和日期进行循环。`[S|R]`则表示在严格模式(Strict mode)和粗略模式(Rough mode)中的选择。
|
每间隔`[span]`年在同样的月份和日期进行循环。`[S|R]`则表示在严格模式(Strict mode)和粗略模式(Rough mode)中的选择。
|
||||||
假设在某个闰年,在2月29日设置3年循环一次,若选择严格模式,则实际上是12年循环一次(不考虑400年非闰),也就是不存在的日子则无视。而选择粗略模式,则将会在不存在的日子将事件设置在2月28日。
|
假设在某个闰年,在2月29日设置3年循环一次,若选择严格模式,则实际上是12年循环一次(不考虑400年非闰),也就是不存在的日子则无视。而选择粗略模式,则将会在不存在的日子将事件设置在2月28日。
|
||||||
@@ -589,10 +589,10 @@ Admin类的操作不涉及任何客户端存储,因此不需要lastChange来
|
|||||||
|
|
||||||
按月有4种格式
|
按月有4种格式
|
||||||
|
|
||||||
* 每月第`x`天:`M[R|F]A[span]`
|
* 每月第`x`天:`M[S|R]A[span]`
|
||||||
* 每月倒数第`x`天:`M[R|F]B[span]`
|
* 每月倒数第`x`天:`M[S|R]B[span]`
|
||||||
* 每月第`x`个星期`y`:`M[R|F]C[span]`
|
* 每月第`x`个星期`y`:`M[S|R]C[span]`
|
||||||
* 每月倒数第`x`个星期`y`:`M[R|F]D[span]`
|
* 每月倒数第`x`个星期`y`:`M[S|R]D[span]`
|
||||||
|
|
||||||
`[span]`表示每隔多少个月处理一次此类事件。 需要注意相关数字的钳制,此种类型的事件循环也是算力消耗最大的。
|
`[span]`表示每隔多少个月处理一次此类事件。 需要注意相关数字的钳制,此种类型的事件循环也是算力消耗最大的。
|
||||||
`[S|R]`则表示在严格模式(Strict mode)和粗略模式(Rough mode)中的选择。
|
`[S|R]`则表示在严格模式(Strict mode)和粗略模式(Rough mode)中的选择。
|
||||||
|
|||||||
@@ -278,8 +278,15 @@ class CalendarDatabase(object):
|
|||||||
analyseData[7] = cache
|
analyseData[7] = cache
|
||||||
|
|
||||||
if reAnalyseLoop:
|
if reAnalyseLoop:
|
||||||
pass
|
# re-compute loop data and upload it into list
|
||||||
# todo: finish this, re-compute loop data and upload it into list
|
sqlList.append('[ccn_loopDateTimeStart] = ?')
|
||||||
|
argumentsList.append(analyseData[5])
|
||||||
|
sqlList.append('[ccn_loopDateTimeEnd] = ?')
|
||||||
|
argumentsList.append(dt.ResolveLoopStr(
|
||||||
|
analyseData[8],
|
||||||
|
analyseData[5],
|
||||||
|
analyseData[7]
|
||||||
|
))
|
||||||
|
|
||||||
# execute
|
# execute
|
||||||
argumentsList.append(uuid)
|
argumentsList.append(uuid)
|
||||||
@@ -296,9 +303,9 @@ class CalendarDatabase(object):
|
|||||||
newuuid = utils.GenerateUUID()
|
newuuid = utils.GenerateUUID()
|
||||||
lastupdate = utils.GenerateUUID()
|
lastupdate = utils.GenerateUUID()
|
||||||
|
|
||||||
# todo: analyse loopRules and output following 2 fileds.
|
# analyse loopRules and output following 2 fileds.
|
||||||
loopDateTimeStart = eventDateTimeStart
|
loopDateTimeStart = eventDateTimeStart
|
||||||
loopDateTimeEnd = eventDateTimeEnd
|
loopDateTimeEnd = dt.ResolveLoopStr(loopRules, eventDateTimeStart, timezoneOffset)
|
||||||
|
|
||||||
self.cursor.execute('INSERT INTO calendar VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
|
self.cursor.execute('INSERT INTO calendar VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);',
|
||||||
(newuuid,
|
(newuuid,
|
||||||
|
|||||||
193
src/dt.py
193
src/dt.py
@@ -2,9 +2,14 @@ import datetime
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
import utils
|
||||||
|
|
||||||
MIN_TIMESTAMP = int(datetime.datetime(1950, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp() / 60)
|
MonthDayCount = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||||
MAX_TIMESTAMP = int(datetime.datetime(2200, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc).timestamp() / 60)
|
|
||||||
|
MIN_DATETIME = datetime.datetime(1950, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
|
MAX_DATETIME = datetime.datetime(2200, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
|
MIN_TIMESTAMP = int(MIN_DATETIME.timestamp() / 60)
|
||||||
|
MAX_TIMESTAMP = int(MAX_DATETIME.timestamp() / 60)
|
||||||
DAY1_SPAN = 60 * 24
|
DAY1_SPAN = 60 * 24
|
||||||
DAY7_SPAN = 7 * DAY1_SPAN
|
DAY7_SPAN = 7 * DAY1_SPAN
|
||||||
YEAR400_SPAN = DAY1_SPAN * 400 * 365
|
YEAR400_SPAN = DAY1_SPAN * 400 * 365
|
||||||
@@ -12,7 +17,7 @@ YEAR400_SPAN = DAY1_SPAN * 400 * 365
|
|||||||
def ResolveLoopStr(strl, starttime, tzoffset):
|
def ResolveLoopStr(strl, starttime, tzoffset):
|
||||||
# check no loop
|
# check no loop
|
||||||
if strl == '':
|
if strl == '':
|
||||||
return starttime + 1
|
return starttime
|
||||||
|
|
||||||
# try compute from loopStop
|
# try compute from loopStop
|
||||||
(loopRules, loopStopRules) = strl.split('-')
|
(loopRules, loopStopRules) = strl.split('-')
|
||||||
@@ -37,10 +42,96 @@ def ResolveLoopStr(strl, starttime, tzoffset):
|
|||||||
|
|
||||||
|
|
||||||
def LoopHandle_Year(searchResult, starttime, times, tzoffset):
|
def LoopHandle_Year(searchResult, starttime, times, tzoffset):
|
||||||
pass
|
clientDate = datetime.datetime.fromtimestamp(starttime, UTCTimezone(tzoffset))
|
||||||
|
isStrict = searchResult.group(1) == 'S'
|
||||||
|
yearSpan = int(searchResult.group(2))
|
||||||
|
|
||||||
|
newYear = clientYear = clientDate.year
|
||||||
|
newMonth = clientMonth = clientDate.month
|
||||||
|
newDay = clientDay = clientDate.day
|
||||||
|
if clientMonth == 2 and clientDay == 29:
|
||||||
|
if isStrict:
|
||||||
|
realSpan = utils.LCM(yearSpan, 4)
|
||||||
|
valCache = starttime
|
||||||
|
timesCache = times - 1
|
||||||
|
while valCache < MAX_TIMESTAMP and timesCache > 0:
|
||||||
|
newYear += realSpan
|
||||||
|
if not IsLeapYear(newYear):
|
||||||
|
continue
|
||||||
|
valCache = starttime + DAY1_SPAN * (DaysCount(newYear, newMonth, newDay) - DaysCount(clientYear, clientMonth, clientDay))
|
||||||
|
timesCache -= 1
|
||||||
|
else:
|
||||||
|
newYear = 0 if times == 1 else (times * yearSpan)
|
||||||
|
if not IsLeapYear(newYear):
|
||||||
|
newDay = 28 # migrate to 28
|
||||||
|
else:
|
||||||
|
# if times == 1, no extra datetime need to be added
|
||||||
|
newYear += 0 if times == 1 else (times * yearSpan)
|
||||||
|
|
||||||
|
val = starttime + DAY1_SPAN * (DaysCount(newYear, newMonth, newDay) - DaysCount(clientYear, clientMonth, clientDay))
|
||||||
|
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
||||||
|
|
||||||
def LoopHandle_Month(searchResult, starttime, times, tzoffset):
|
def LoopHandle_Month(searchResult, starttime, times, tzoffset):
|
||||||
pass
|
isStrict = searchResult.group(1) == 'S'
|
||||||
|
loopType = searchResult.group(2)
|
||||||
|
monthSpan = int(searchResult.group(3))
|
||||||
|
|
||||||
|
# we should get original data in each method
|
||||||
|
times -= 1
|
||||||
|
clientDate = datetime.datetime.fromtimestamp(starttime, UTCTimezone(tzoffset))
|
||||||
|
newYear = clientYear = clientDate.year
|
||||||
|
newMonth = clientMonth = clientDate.month
|
||||||
|
newDay = clientDay = clientDate.day
|
||||||
|
# data struct
|
||||||
|
# dayStatistics =
|
||||||
|
# (dayForwards, dayBackwards, weeksForward, dayOfWeekForward, weeksBackwards, dayOfWeekBackward)
|
||||||
|
dayStatistics = GetDayInMonth(clientYear, clientMonth, clientDay)
|
||||||
|
|
||||||
|
if isStrict:
|
||||||
|
if loopType == 'A':
|
||||||
|
while times > 0:
|
||||||
|
newMonth += 1
|
||||||
|
if newMonth > 12
|
||||||
|
newMonth = 1
|
||||||
|
newYear += 1
|
||||||
|
maxDays = MonthDayCount[newMonth - 1] + (1 if newMonth == 2 and IsLeapYear(newYear) else 0)
|
||||||
|
if dayStatistics[0] <= maxDays:
|
||||||
|
times -= 1
|
||||||
|
elif loopType == 'B':
|
||||||
|
while times > 0:
|
||||||
|
newMonth += 1
|
||||||
|
if newMonth > 12
|
||||||
|
newMonth = 1
|
||||||
|
newYear += 1
|
||||||
|
maxDays = MonthDayCount[newMonth - 1] + (1 if newMonth == 2 and IsLeapYear(newYear) else 0)
|
||||||
|
if dayStatistics[1] <= maxDays:
|
||||||
|
times -= 1
|
||||||
|
elif loopType == 'C':
|
||||||
|
while times > 0:
|
||||||
|
newMonth += 1
|
||||||
|
if newMonth > 12
|
||||||
|
newMonth = 1
|
||||||
|
newYear += 1
|
||||||
|
monthStatistics = GetMonthWeekStatistics(newYear, newMonth)
|
||||||
|
if dayStatistics[2] <= monthStatistics[dayStatistics[3]]:
|
||||||
|
times -= 1
|
||||||
|
elif loopType == 'D':
|
||||||
|
while times > 0:
|
||||||
|
newMonth += 1
|
||||||
|
if newMonth > 12
|
||||||
|
newMonth = 1
|
||||||
|
newYear += 1
|
||||||
|
monthStatistics = GetMonthWeekStatistics(newYear, newMonth)
|
||||||
|
if dayStatistics[4] <= monthStatistics[dayStatistics[5]]:
|
||||||
|
times -= 1
|
||||||
|
else:
|
||||||
|
newMonth += times * monthSpan
|
||||||
|
newYear += int(newMonth - 1 / 12)
|
||||||
|
newMonth = (newMonth % 12) + 1
|
||||||
|
newDay = MonthDayCount[newMonth - 1] + (1 if newMonth == 2 and IsLeapYear(newYear) else 0)
|
||||||
|
|
||||||
|
val = starttime + DAY1_SPAN * (DaysCount(newYear, newMonth, newDay) - DaysCount(clientYear, clientMonth, clientDay))
|
||||||
|
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
||||||
|
|
||||||
def LoopHandle_Week(searchResult, starttime, times, tzoffset):
|
def LoopHandle_Week(searchResult, starttime, times, tzoffset):
|
||||||
weekOccupied = tuple(map(lambda x: x == 'T', searchResult.group(1)))
|
weekOccupied = tuple(map(lambda x: x == 'T', searchResult.group(1)))
|
||||||
@@ -65,15 +156,17 @@ def LoopHandle_Week(searchResult, starttime, times, tzoffset):
|
|||||||
remainEvent -= 1
|
remainEvent -= 1
|
||||||
nowDayOfWeek += 1
|
nowDayOfWeek += 1
|
||||||
|
|
||||||
|
val -= 1
|
||||||
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
||||||
|
|
||||||
def LoopHandle_Day(searchResult, starttime, times, tzoffset):
|
def LoopHandle_Day(searchResult, starttime, times, tzoffset):
|
||||||
val = starttime + DAY1_SPAN * times * int(searchResult.group(1))
|
val = starttime + DAY1_SPAN * times * int(searchResult.group(1))
|
||||||
|
val -= 1
|
||||||
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
return val if val < MAX_TIMESTAMP else MAX_TIMESTAMP
|
||||||
|
|
||||||
precompiledLoopRules = (
|
precompiledLoopRules = (
|
||||||
(re.compile(r'^Y([RF]{1})([1-9]\d*)$'), LoopHandle_Year),
|
(re.compile(r'^Y([SR]{1})([1-9]\d*)$'), LoopHandle_Year),
|
||||||
(re.compile(r'^M([RF]{1})([ABCD]{1})([1-9]\d*)$'), LoopHandle_Month),
|
(re.compile(r'^M([SR]{1})([ABCD]{1})([1-9]\d*)$'), LoopHandle_Month),
|
||||||
(re.compile(r'^W([TF]{7})([1-9]\d*)$'), LoopHandle_Week),
|
(re.compile(r'^W([TF]{7})([1-9]\d*)$'), LoopHandle_Week),
|
||||||
(re.compile(r'^D([1-9]\d*)$'), LoopHandle_Day)
|
(re.compile(r'^D([1-9]\d*)$'), LoopHandle_Day)
|
||||||
)
|
)
|
||||||
@@ -84,6 +177,92 @@ precompiledLoopStopRules = {
|
|||||||
'times': re.compile(r'^T([1-9]\d*)$')
|
'times': re.compile(r'^T([1-9]\d*)$')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def LeapYearCountEx(endYear, includeThis = False, baseYear = 1, includeBase = True):
|
||||||
|
if not includeThis:
|
||||||
|
endYear -= 1
|
||||||
|
if includeBase:
|
||||||
|
baseYear -= 1
|
||||||
|
|
||||||
|
endly = int(endYear / 4)
|
||||||
|
endly -= int(endYear / 100)
|
||||||
|
endly += int(endYear / 400)
|
||||||
|
|
||||||
|
basely = int(baseYear / 4)
|
||||||
|
basely -= int(baseYear / 100)
|
||||||
|
basely += int(baseYear / 400)
|
||||||
|
|
||||||
|
return (endly - basely)
|
||||||
|
|
||||||
|
def LeapYearCount(year):
|
||||||
|
return LeapYearCountEx(year, False, 1, True)
|
||||||
|
|
||||||
|
def IsLeapYear(year):
|
||||||
|
isLeap = False
|
||||||
|
if year % 4 == 0:
|
||||||
|
isLeap = True
|
||||||
|
if year % 100 == 0:
|
||||||
|
isLeap = False
|
||||||
|
if year % 400 == 0:
|
||||||
|
isLeap = True
|
||||||
|
return isLeap
|
||||||
|
|
||||||
|
def DaysCount(year, month, day):
|
||||||
|
ly = LeapYearCountEx(year, False, 1, True)
|
||||||
|
days = 365 * (year - 1)
|
||||||
|
days += ly
|
||||||
|
|
||||||
|
for index in range(1, month, 1):
|
||||||
|
days += MonthDayCount[index - 1]
|
||||||
|
|
||||||
|
if (month > 2) and IsLeapYear(year):
|
||||||
|
days += 1
|
||||||
|
|
||||||
|
days += day - 1
|
||||||
|
return days
|
||||||
|
|
||||||
|
def DayOfWeek(year, month, day):
|
||||||
|
# as we know, 1/1/1900 is Monday.
|
||||||
|
# via this method, we can got 1/1/1 is Monday
|
||||||
|
# compute day span
|
||||||
|
days=DaysCount(year, month, day)
|
||||||
|
|
||||||
|
# return day of week (from 0 - 6, corresponding with python)
|
||||||
|
return days % 7
|
||||||
|
|
||||||
|
def GetDayInMonth(year, month, day):
|
||||||
|
days = MonthDayCount[month - 1] + (1 if (month == 2 and IsLeapYear(year)) else 0)
|
||||||
|
firstDayOfWeek = DayOfWeek(year, month, 1)
|
||||||
|
lastDayOfWeek = (firstDayOfWeek + days - 1) % 7
|
||||||
|
dayOfWeek = (firstDayOfWeek + day - 1) % 7
|
||||||
|
|
||||||
|
dayForwards = day
|
||||||
|
dayBackwards = days - day + 1
|
||||||
|
|
||||||
|
weeksForward = (dayForwards - 1) / 7
|
||||||
|
weeksBackwards = (dayBackwards - 1) / 7
|
||||||
|
|
||||||
|
dayOfWeekForward = (firstDayOfWeek + ((dayForwards - 1) % 7)) % 7
|
||||||
|
# 7 don't change week
|
||||||
|
# # just keep this is the positive number and prevent pretential minus number calc problem
|
||||||
|
dayOfWeekBackward = (7 + lastDayOfWeek - ((dayBackwards - 1) % 7)) % 7
|
||||||
|
|
||||||
|
return (dayForwards, dayBackwards, weeksForward, dayOfWeekForward, weeksBackwards, dayOfWeekBackward)
|
||||||
|
|
||||||
|
def GetMonthWeekStatistics(year, month):
|
||||||
|
days = MonthDayCount[month - 1] + (1 if (month == 2 and IsLeapYear(year)) else 0)
|
||||||
|
firstDayOfWeek = DayOfWeek(year, month, 1)
|
||||||
|
lastDayOfWeek = (firstDayOfWeek + days - 1) % 7
|
||||||
|
|
||||||
|
result = [4, 4, 4, 4, 4, 4, 4]
|
||||||
|
remain = (days - 1) % 7
|
||||||
|
week = firstDayOfWeek
|
||||||
|
while remain > 0:
|
||||||
|
result[week % 7] += 1
|
||||||
|
week += 1
|
||||||
|
remain -= 1
|
||||||
|
|
||||||
|
return tuple(result)
|
||||||
|
|
||||||
class UTCTimezone(datetime.tzinfo):
|
class UTCTimezone(datetime.tzinfo):
|
||||||
def __init__(self, offset = 0):
|
def __init__(self, offset = 0):
|
||||||
self._offset = offset
|
self._offset = offset
|
||||||
|
|||||||
@@ -43,3 +43,9 @@ def GetTokenExpireOn():
|
|||||||
def Str2Bool(strl):
|
def Str2Bool(strl):
|
||||||
return strl.lower() == 'true'
|
return strl.lower() == 'true'
|
||||||
|
|
||||||
|
def GCD(a, b):
|
||||||
|
return math.gcd(a, b)
|
||||||
|
|
||||||
|
def LCM(a, b):
|
||||||
|
return a * b / GCD(a, b)
|
||||||
|
|
||||||
Reference in New Issue
Block a user