Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/isodate/isostrf.py: 51%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

84 statements  

1"""This module provides an alternative strftime method. 

2 

3The strftime method in this module allows only a subset of Python's strftime 

4format codes, plus a few additional. It supports the full range of date values 

5possible with standard Python date/time objects. Furthermore there are several 

6pr-defined format strings in this module to make ease producing of ISO 8601 

7conforming strings. 

8""" 

9 

10import re 

11from datetime import date, time, timedelta 

12from typing import Callable, Union 

13 

14from isodate.duration import Duration 

15from isodate.isotzinfo import tz_isoformat 

16 

17# Date specific format strings 

18DATE_BAS_COMPLETE = "%Y%m%d" 

19DATE_EXT_COMPLETE = "%Y-%m-%d" 

20DATE_BAS_WEEK_COMPLETE = "%YW%W%w" 

21DATE_EXT_WEEK_COMPLETE = "%Y-W%W-%w" 

22DATE_BAS_ORD_COMPLETE = "%Y%j" 

23DATE_EXT_ORD_COMPLETE = "%Y-%j" 

24DATE_BAS_WEEK = "%YW%W" 

25DATE_EXT_WEEK = "%Y-W%W" 

26DATE_BAS_MONTH = "%Y%m" 

27DATE_EXT_MONTH = "%Y-%m" 

28DATE_YEAR = "%Y" 

29DATE_CENTURY = "%C" 

30 

31# Time specific format strings 

32TIME_BAS_COMPLETE = "%H%M%S" 

33TIME_EXT_COMPLETE = "%H:%M:%S" 

34TIME_BAS_MINUTE = "%H%M" 

35TIME_EXT_MINUTE = "%H:%M" 

36TIME_HOUR = "%H" 

37 

38# Time zone formats 

39TZ_BAS = "%z" 

40TZ_EXT = "%Z" 

41TZ_HOUR = "%h" 

42 

43# DateTime formats 

44DT_EXT_COMPLETE = DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT 

45DT_BAS_COMPLETE = DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS 

46DT_EXT_ORD_COMPLETE = DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT 

47DT_BAS_ORD_COMPLETE = DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS 

48DT_EXT_WEEK_COMPLETE = DATE_EXT_WEEK_COMPLETE + "T" + TIME_EXT_COMPLETE + TZ_EXT 

49DT_BAS_WEEK_COMPLETE = DATE_BAS_WEEK_COMPLETE + "T" + TIME_BAS_COMPLETE + TZ_BAS 

50 

51# Duration formts 

52D_DEFAULT = "P%P" 

53D_WEEK = "P%p" 

54D_ALT_EXT = "P" + DATE_EXT_COMPLETE + "T" + TIME_EXT_COMPLETE 

55D_ALT_BAS = "P" + DATE_BAS_COMPLETE + "T" + TIME_BAS_COMPLETE 

56D_ALT_EXT_ORD = "P" + DATE_EXT_ORD_COMPLETE + "T" + TIME_EXT_COMPLETE 

57D_ALT_BAS_ORD = "P" + DATE_BAS_ORD_COMPLETE + "T" + TIME_BAS_COMPLETE 

58 

59STRF_DT_MAP: dict[str, Callable[[Union[time, date], int], str]] = { 

60 "%d": lambda tdt, yds: "%02d" % tdt.day, # type: ignore [union-attr] 

61 "%f": lambda tdt, yds: "%06d" % tdt.microsecond, # type: ignore [union-attr] 

62 "%H": lambda tdt, yds: "%02d" % tdt.hour, # type: ignore [union-attr] 

63 "%j": lambda tdt, yds: "%03d" % (tdt.toordinal() - date(tdt.year, 1, 1).toordinal() + 1), # type: ignore [union-attr, operator] # noqa: E501 

64 "%m": lambda tdt, yds: "%02d" % tdt.month, # type: ignore [union-attr] 

65 "%M": lambda tdt, yds: "%02d" % tdt.minute, # type: ignore [union-attr] 

66 "%S": lambda tdt, yds: "%02d" % tdt.second, # type: ignore [union-attr] 

67 "%w": lambda tdt, yds: "%1d" % tdt.isoweekday(), # type: ignore [union-attr] 

68 "%W": lambda tdt, yds: "%02d" % tdt.isocalendar()[1], # type: ignore [union-attr] 

69 "%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.year), # type: ignore [union-attr] # noqa: E501 

70 "%C": lambda tdt, yds: (((yds != 4) and "+") or "") # type: ignore [union-attr] 

71 + (("%%0%dd" % (yds - 2)) % (tdt.year / 100)), # type: ignore [union-attr] 

72 "%h": lambda tdt, yds: tz_isoformat(tdt, "%h"), # type: ignore [arg-type] 

73 "%Z": lambda tdt, yds: tz_isoformat(tdt, "%Z"), # type: ignore [arg-type] 

74 "%z": lambda tdt, yds: tz_isoformat(tdt, "%z"), # type: ignore [arg-type] 

75 "%%": lambda tdt, yds: "%", 

76} 

77 

78STRF_D_MAP: dict[str, Callable[[Union[timedelta, Duration], int], str]] = { 

79 "%d": lambda tdt, yds: "%02d" % tdt.days, 

80 "%f": lambda tdt, yds: "%06d" % tdt.microseconds, 

81 "%H": lambda tdt, yds: "%02d" % (tdt.seconds / 60 / 60), 

82 "%m": lambda tdt, yds: "%02d" % tdt.months, # type: ignore [union-attr] 

83 "%M": lambda tdt, yds: "%02d" % ((tdt.seconds / 60) % 60), 

84 "%S": lambda tdt, yds: "%02d" % (tdt.seconds % 60), 

85 "%W": lambda tdt, yds: "%02d" % (abs(tdt.days / 7)), 

86 "%Y": lambda tdt, yds: (((yds != 4) and "+") or "") + (("%%0%dd" % yds) % tdt.years), # type: ignore [union-attr] # noqa: E501 

87 "%C": lambda tdt, yds: (((yds != 4) and "+") or "") 

88 + (("%%0%dd" % (yds - 2)) % (tdt.years / 100)), # type: ignore [union-attr] 

89 "%%": lambda tdt, yds: "%", 

90} 

91 

92 

93def _strfduration(tdt: Union[timedelta, Duration], format: str, yeardigits: int = 4) -> str: 

94 """This is the work method for timedelta and Duration instances. 

95 

96 See strftime for more details. 

97 """ 

98 

99 def repl(match: re.Match[str]) -> str: 

100 """Lookup format command and return corresponding replacement.""" 

101 if match.group(0) in STRF_D_MAP: 

102 return STRF_D_MAP[match.group(0)](tdt, yeardigits) 

103 elif match.group(0) == "%P": 

104 ret: list[str] = [] 

105 if isinstance(tdt, Duration): 

106 if tdt.years: 

107 ret.append("%sY" % abs(tdt.years)) 

108 if tdt.months: 

109 ret.append("%sM" % abs(tdt.months)) 

110 usecs = abs((tdt.days * 24 * 60 * 60 + tdt.seconds) * 1000000 + tdt.microseconds) 

111 seconds, usecs = divmod(usecs, 1000000) 

112 minutes, seconds = divmod(seconds, 60) 

113 hours, minutes = divmod(minutes, 60) 

114 days, hours = divmod(hours, 24) 

115 if days: 

116 ret.append("%sD" % days) 

117 if hours or minutes or seconds or usecs: 

118 ret.append("T") 

119 if hours: 

120 ret.append("%sH" % hours) 

121 if minutes: 

122 ret.append("%sM" % minutes) 

123 if seconds or usecs: 

124 if usecs: 

125 ret.append(("%d.%06d" % (seconds, usecs)).rstrip("0")) 

126 else: 

127 ret.append("%d" % seconds) 

128 ret.append("S") 

129 # at least one component has to be there. 

130 return "".join(ret) if ret else "0D" 

131 elif match.group(0) == "%p": 

132 return str(abs(tdt.days // 7)) + "W" 

133 return match.group(0) 

134 

135 return re.sub("%d|%f|%H|%m|%M|%S|%W|%Y|%C|%%|%P|%p", repl, format) 

136 

137 

138def _strfdt(tdt: Union[time, date], format: str, yeardigits: int = 4) -> str: 

139 """This is the work method for time and date instances. 

140 

141 See strftime for more details. 

142 """ 

143 

144 def repl(match: re.Match[str]) -> str: 

145 """Lookup format command and return corresponding replacement.""" 

146 if match.group(0) in STRF_DT_MAP: 

147 return STRF_DT_MAP[match.group(0)](tdt, yeardigits) 

148 return match.group(0) 

149 

150 return re.sub("%d|%f|%H|%j|%m|%M|%S|%w|%W|%Y|%C|%z|%Z|%h|%%", repl, format) 

151 

152 

153def strftime(tdt: Union[timedelta, Duration, time, date], format: str, yeardigits: int = 4) -> str: 

154 """Directive Meaning Notes. 

155 

156 %d Day of the month as a decimal number [01,31]. 

157 %f Microsecond as a decimal number [0,999999], zero-padded 

158 on the left (1) 

159 %H Hour (24-hour clock) as a decimal number [00,23]. 

160 %j Day of the year as a decimal number [001,366]. 

161 %m Month as a decimal number [01,12]. 

162 %M Minute as a decimal number [00,59]. 

163 %S Second as a decimal number [00,61]. (3) 

164 %w Weekday as a decimal number [0(Monday),6]. 

165 %W Week number of the year (Monday as the first day of the week) 

166 as a decimal number [00,53]. All days in a new year preceding the 

167 first Monday are considered to be in week 0. (4) 

168 %Y Year with century as a decimal number. [0000,9999] 

169 %C Century as a decimal number. [00,99] 

170 %z UTC offset in the form +HHMM or -HHMM (empty string if the 

171 object is naive). (5) 

172 %Z Time zone name (empty string if the object is naive). 

173 %P ISO8601 duration format. 

174 %p ISO8601 duration format in weeks. 

175 %% A literal '%' character. 

176 

177 """ 

178 if isinstance(tdt, (timedelta, Duration)): 

179 return _strfduration(tdt, format, yeardigits) 

180 return _strfdt(tdt, format, yeardigits)