Hide keyboard shortcuts

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.""" 

2 

3import re 

4from datetime import datetime as dt 

5 

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 

10 

11 

12CW = C.web 

13 

14QQ = H.icon(CW.unknown[N.generic]) 

15Qq = H.icon(CW.unknown[N.generic], asChar=True) 

16 

17dtTrim = re.compile(r"""[^0-9 T/:.-]+""") 

18dtSep = re.compile(r"""[ T/:.-]+""") 

19 

20 

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 ) 

31 

32 

33DATETIME_FORMAT = """{:>04}-{:>02}-{:>02} {:>02}:{:>02}:{:>02}""" 

34 

35 

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 ) 

51 

52 

53DATETIME_PATTERN = genDatetimePattern() 

54 

55 

56class Datetime(TypeBase): 

57 """Type class for date-time values""" 

58 rawType = dt 

59 widgetType = N.text 

60 pattern = DATETIME_PATTERN 

61 

62 def partition(self, strVal): 

63 """Split a datetime string into 3 date components and 3 time components. 

64 

65 !!! note 

66 The fraction part of seconds is ignored. 

67 

68 !!! warning 

69 If there are missing components, they will be taken from the default date, 

70 which is `now`. 

71 

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 

79 

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 

83 

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 

86 

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 

100 

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) 

106 

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) 

117 

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 

123 

124 def toEdit(self, val): 

125 return E if val is None else self.normalize(val.isoformat()) 

126 

127 def toOrig(self, val): 

128 if val is None: 

129 return None 

130 return self.normalize(val.isoformat())