Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/utils/dateformat.py: 34%

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

163 statements  

1""" 

2PHP date() style date formatting 

3See https://www.php.net/date for format strings 

4 

5Usage: 

6>>> from datetime import datetime 

7>>> d = datetime.now() 

8>>> df = DateFormat(d) 

9>>> print(df.format('jS F Y H:i')) 

107th October 2003 11:39 

11>>> 

12""" 

13 

14import calendar 

15from datetime import date, datetime, time 

16from email.utils import format_datetime as format_datetime_rfc5322 

17 

18from django.utils.dates import ( 

19 MONTHS, 

20 MONTHS_3, 

21 MONTHS_ALT, 

22 MONTHS_AP, 

23 WEEKDAYS, 

24 WEEKDAYS_ABBR, 

25) 

26from django.utils.regex_helper import _lazy_re_compile 

27from django.utils.timezone import ( 

28 _datetime_ambiguous_or_imaginary, 

29 get_default_timezone, 

30 is_naive, 

31 make_aware, 

32) 

33from django.utils.translation import gettext as _ 

34 

35re_formatchars = _lazy_re_compile(r"(?<!\\)([aAbcdDeEfFgGhHiIjlLmMnNoOPrsStTUuwWyYzZ])") 

36re_escaped = _lazy_re_compile(r"\\(.)") 

37 

38 

39class Formatter: 

40 def format(self, formatstr): 

41 pieces = [] 

42 for i, piece in enumerate(re_formatchars.split(str(formatstr))): 

43 if i % 2: 

44 if type(self.data) is date and hasattr(TimeFormat, piece): 

45 raise TypeError( 

46 "The format for date objects may not contain " 

47 "time-related format specifiers (found '%s')." % piece 

48 ) 

49 pieces.append(str(getattr(self, piece)())) 

50 elif piece: 

51 pieces.append(re_escaped.sub(r"\1", piece)) 

52 return "".join(pieces) 

53 

54 

55class TimeFormat(Formatter): 

56 def __init__(self, obj): 

57 self.data = obj 

58 self.timezone = None 

59 

60 if isinstance(obj, datetime): 

61 # Timezone is only supported when formatting datetime objects, not 

62 # date objects (timezone information not appropriate), or time 

63 # objects (against established django policy). 

64 if is_naive(obj): 

65 timezone = get_default_timezone() 

66 else: 

67 timezone = obj.tzinfo 

68 if not _datetime_ambiguous_or_imaginary(obj, timezone): 

69 self.timezone = timezone 

70 

71 def a(self): 

72 "'a.m.' or 'p.m.'" 

73 if self.data.hour > 11: 

74 return _("p.m.") 

75 return _("a.m.") 

76 

77 def A(self): 

78 "'AM' or 'PM'" 

79 if self.data.hour > 11: 

80 return _("PM") 

81 return _("AM") 

82 

83 def e(self): 

84 """ 

85 Timezone name. 

86 

87 If timezone information is not available, return an empty string. 

88 """ 

89 if not self.timezone: 

90 return "" 

91 

92 try: 

93 if getattr(self.data, "tzinfo", None): 

94 return self.data.tzname() or "" 

95 except NotImplementedError: 

96 pass 

97 return "" 

98 

99 def f(self): 

100 """ 

101 Time, in 12-hour hours and minutes, with minutes left off if they're 

102 zero. 

103 Examples: '1', '1:30', '2:05', '2' 

104 Proprietary extension. 

105 """ 

106 hour = self.data.hour % 12 or 12 

107 minute = self.data.minute 

108 return "%d:%02d" % (hour, minute) if minute else hour 

109 

110 def g(self): 

111 "Hour, 12-hour format without leading zeros; i.e. '1' to '12'" 

112 return self.data.hour % 12 or 12 

113 

114 def G(self): 

115 "Hour, 24-hour format without leading zeros; i.e. '0' to '23'" 

116 return self.data.hour 

117 

118 def h(self): 

119 "Hour, 12-hour format; i.e. '01' to '12'" 

120 return "%02d" % (self.data.hour % 12 or 12) 

121 

122 def H(self): 

123 "Hour, 24-hour format; i.e. '00' to '23'" 

124 return "%02d" % self.data.hour 

125 

126 def i(self): 

127 "Minutes; i.e. '00' to '59'" 

128 return "%02d" % self.data.minute 

129 

130 def O(self): # NOQA: E743, E741 

131 """ 

132 Difference to Greenwich time in hours; e.g. '+0200', '-0430'. 

133 

134 If timezone information is not available, return an empty string. 

135 """ 

136 if self.timezone is None: 

137 return "" 

138 

139 offset = self.timezone.utcoffset(self.data) 

140 seconds = offset.days * 86400 + offset.seconds 

141 sign = "-" if seconds < 0 else "+" 

142 seconds = abs(seconds) 

143 return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60) 

144 

145 def P(self): 

146 """ 

147 Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off 

148 if they're zero and the strings 'midnight' and 'noon' if appropriate. 

149 Examples: '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.' 

150 Proprietary extension. 

151 """ 

152 if self.data.minute == 0 and self.data.hour == 0: 

153 return _("midnight") 

154 if self.data.minute == 0 and self.data.hour == 12: 

155 return _("noon") 

156 return "%s %s" % (self.f(), self.a()) 

157 

158 def s(self): 

159 "Seconds; i.e. '00' to '59'" 

160 return "%02d" % self.data.second 

161 

162 def T(self): 

163 """ 

164 Time zone of this machine; e.g. 'EST' or 'MDT'. 

165 

166 If timezone information is not available, return an empty string. 

167 """ 

168 if self.timezone is None: 

169 return "" 

170 

171 return str(self.timezone.tzname(self.data)) 

172 

173 def u(self): 

174 "Microseconds; i.e. '000000' to '999999'" 

175 return "%06d" % self.data.microsecond 

176 

177 def Z(self): 

178 """ 

179 Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for 

180 timezones west of UTC is always negative, and for those east of UTC is 

181 always positive. 

182 

183 If timezone information is not available, return an empty string. 

184 """ 

185 if self.timezone is None: 

186 return "" 

187 

188 offset = self.timezone.utcoffset(self.data) 

189 

190 # `offset` is a datetime.timedelta. For negative values (to the west of 

191 # UTC) only days can be negative (days=-1) and seconds are always 

192 # positive. e.g. UTC-1 -> timedelta(days=-1, seconds=82800, microseconds=0) 

193 # Positive offsets have days=0 

194 return offset.days * 86400 + offset.seconds 

195 

196 

197class DateFormat(TimeFormat): 

198 def b(self): 

199 "Month, textual, 3 letters, lowercase; e.g. 'jan'" 

200 return MONTHS_3[self.data.month] 

201 

202 def c(self): 

203 """ 

204 ISO 8601 Format 

205 Example : '2008-01-02T10:30:00.000123' 

206 """ 

207 return self.data.isoformat() 

208 

209 def d(self): 

210 "Day of the month, 2 digits with leading zeros; i.e. '01' to '31'" 

211 return "%02d" % self.data.day 

212 

213 def D(self): 

214 "Day of the week, textual, 3 letters; e.g. 'Fri'" 

215 return WEEKDAYS_ABBR[self.data.weekday()] 

216 

217 def E(self): 

218 "Alternative month names as required by some locales. Proprietary extension." 

219 return MONTHS_ALT[self.data.month] 

220 

221 def F(self): 

222 "Month, textual, long; e.g. 'January'" 

223 return MONTHS[self.data.month] 

224 

225 def I(self): # NOQA: E743, E741 

226 "'1' if daylight saving time, '0' otherwise." 

227 if self.timezone is None: 

228 return "" 

229 return "1" if self.timezone.dst(self.data) else "0" 

230 

231 def j(self): 

232 "Day of the month without leading zeros; i.e. '1' to '31'" 

233 return self.data.day 

234 

235 def l(self): # NOQA: E743, E741 

236 "Day of the week, textual, long; e.g. 'Friday'" 

237 return WEEKDAYS[self.data.weekday()] 

238 

239 def L(self): 

240 "Boolean for whether it is a leap year; i.e. True or False" 

241 return calendar.isleap(self.data.year) 

242 

243 def m(self): 

244 "Month; i.e. '01' to '12'" 

245 return "%02d" % self.data.month 

246 

247 def M(self): 

248 "Month, textual, 3 letters; e.g. 'Jan'" 

249 return MONTHS_3[self.data.month].title() 

250 

251 def n(self): 

252 "Month without leading zeros; i.e. '1' to '12'" 

253 return self.data.month 

254 

255 def N(self): 

256 "Month abbreviation in Associated Press style. Proprietary extension." 

257 return MONTHS_AP[self.data.month] 

258 

259 def o(self): 

260 "ISO 8601 year number matching the ISO week number (W)" 

261 return self.data.isocalendar().year 

262 

263 def r(self): 

264 "RFC 5322 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'" 

265 value = self.data 

266 if not isinstance(value, datetime): 

267 # Assume midnight in default timezone if datetime.date provided. 

268 default_timezone = get_default_timezone() 

269 value = datetime.combine(value, time.min).replace(tzinfo=default_timezone) 

270 elif is_naive(value): 

271 value = make_aware(value, timezone=self.timezone) 

272 return format_datetime_rfc5322(value) 

273 

274 def S(self): 

275 """ 

276 English ordinal suffix for the day of the month, 2 characters; i.e. 

277 'st', 'nd', 'rd' or 'th'. 

278 """ 

279 if self.data.day in (11, 12, 13): # Special case 

280 return "th" 

281 last = self.data.day % 10 

282 if last == 1: 

283 return "st" 

284 if last == 2: 

285 return "nd" 

286 if last == 3: 

287 return "rd" 

288 return "th" 

289 

290 def t(self): 

291 "Number of days in the given month; i.e. '28' to '31'" 

292 return calendar.monthrange(self.data.year, self.data.month)[1] 

293 

294 def U(self): 

295 "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)" 

296 value = self.data 

297 if not isinstance(value, datetime): 

298 value = datetime.combine(value, time.min) 

299 return int(value.timestamp()) 

300 

301 def w(self): 

302 "Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)" 

303 return (self.data.weekday() + 1) % 7 

304 

305 def W(self): 

306 "ISO-8601 week number of year, weeks starting on Monday" 

307 return self.data.isocalendar().week 

308 

309 def y(self): 

310 """Year, 2 digits with leading zeros; e.g. '99'.""" 

311 return "%02d" % (self.data.year % 100) 

312 

313 def Y(self): 

314 """Year, 4 digits with leading zeros; e.g. '1999'.""" 

315 return "%04d" % self.data.year 

316 

317 def z(self): 

318 """Day of the year, i.e. 1 to 366.""" 

319 return self.data.timetuple().tm_yday 

320 

321 

322def format(value, format_string): 

323 "Convenience function" 

324 df = DateFormat(value) 

325 return df.format(format_string) 

326 

327 

328def time_format(value, format_string): 

329 "Convenience function" 

330 tf = TimeFormat(value) 

331 return tf.format(format_string)