Coverage for control/typ/datetime.py : 64%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""The datetime type."""
3import re
4from datetime import datetime as dt
6from config import Config as C, Names as N
7from control.utils import now, E
8from control.html import HtmlElements as H
9from control.typ.base import TypeBase
12CW = C.web
14QQ = H.icon(CW.unknown[N.generic])
15Qq = H.icon(CW.unknown[N.generic], asChar=True)
17dtTrim = re.compile(r"""[^0-9 T/:.-]+""")
18dtSep = re.compile(r"""[ T/:.-]+""")
21def getDefaultDate():
22 today = now()
23 return (
24 today.year,
25 today.month,
26 today.day,
27 today.hour,
28 today.minute,
29 today.second,
30 )
33DATETIME_FORMAT = """{:>04}-{:>02}-{:>02} {:>02}:{:>02}:{:>02}"""
36def genDatetimePattern():
37 s = """[ /:.-]"""
38 t = """[T /:.-]"""
39 yr = """([12][0-9][0-9][0-9])"""
40 mth = """((0[1-9])|(1[0-2])|[1-9])"""
41 d = """((0[1-9])|([12][0-9])|(3[01])|[1-9])"""
42 hr = """(([0-5][0-9])|[0-9])"""
43 m = hr
44 sec = hr
45 return (
46 """^"""
47 f"""{yr}?({s}{mth})?({s}{d})?({t}{hr})?({s}{m})?({s}{sec})?"""
48 """Z?"""
49 """$"""
50 )
53DATETIME_PATTERN = genDatetimePattern()
56class Datetime(TypeBase):
57 """Type class for date-time values"""
58 rawType = dt
59 widgetType = N.text
60 pattern = DATETIME_PATTERN
62 def partition(self, strVal):
63 """Split a datetime string into 3 date components and 3 time components.
65 !!! note
66 The fraction part of seconds is ignored.
68 !!! warning
69 If there are missing components, they will be taken from the default date,
70 which is `now`.
72 Parameters
73 ----------
74 strVal: string
75 """
76 normalVal = dtTrim.sub(E, strVal)
77 if not normalVal: 77 ↛ 78line 77 didn't jump to line 78, because the condition on line 77 was never true
78 return None
80 normalParts = [int(p) for p in dtSep.split(normalVal)]
81 if len(normalParts) == 0: 81 ↛ 82line 81 didn't jump to line 82, because the condition on line 81 was never true
82 return None
84 if not 1900 <= normalParts[0] <= 2100: 84 ↛ 85line 84 didn't jump to line 85, because the condition on line 84 was never true
85 return None
87 defaultDate = getDefaultDate()
88 if len(normalParts) > 6: 88 ↛ 90line 88 didn't jump to line 90, because the condition on line 88 was never false
89 normalParts = normalParts[0:6]
90 if len(normalParts) < 6: 90 ↛ 91line 90 didn't jump to line 91, because the condition on line 90 was never true
91 normalParts = [
92 normalParts[i] if i < len(normalParts) else defaultDate[i]
93 for i in range(6)
94 ]
95 try:
96 dt(*normalParts) # only for checking
97 except Exception:
98 normalParts = defaultDate
99 return normalParts
101 def normalize(self, strVal):
102 normalParts = self.partition(strVal)
103 if normalParts is None: 103 ↛ 104line 103 didn't jump to line 104, because the condition on line 103 was never true
104 return E
105 return DATETIME_FORMAT.format(*normalParts)
107 def fromStr(self, editVal):
108 if not editVal:
109 return None
110 if editVal == N.now:
111 return now()
112 normalParts = self.partition(editVal)
113 if normalParts is None:
114 return None
115 cast = self.rawType
116 return cast(*normalParts)
118 def toDisplay(self, val, markup=True):
119 if val is None:
120 return None if markup is None else QQ if markup else Qq
121 valBare = self.normalize(val.isoformat())
122 return H.span(valBare) if markup else valBare
124 def toEdit(self, val):
125 return E if val is None else self.normalize(val.isoformat())
127 def toOrig(self, val):
128 if val is None:
129 return None
130 return self.normalize(val.isoformat())