Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/utils/timezone.py: 6%

126 statements  

« prev     ^ index     » next       coverage.py v7.0.5, created at 2023-01-17 06:13 +0000

1""" 

2Timezone-related classes and functions. 

3""" 

4 

5import functools 

6import sys 

7import warnings 

8 

9try: 

10 import zoneinfo 

11except ImportError: 

12 from backports import zoneinfo 

13 

14from contextlib import ContextDecorator 

15from datetime import datetime, timedelta, timezone, tzinfo 

16 

17from asgiref.local import Local 

18 

19from django.conf import settings 

20from django.utils.deprecation import RemovedInDjango50Warning 

21 

22__all__ = [ # noqa for utc RemovedInDjango50Warning. 

23 "utc", 

24 "get_fixed_timezone", 

25 "get_default_timezone", 

26 "get_default_timezone_name", 

27 "get_current_timezone", 

28 "get_current_timezone_name", 

29 "activate", 

30 "deactivate", 

31 "override", 

32 "localtime", 

33 "localdate", 

34 "now", 

35 "is_aware", 

36 "is_naive", 

37 "make_aware", 

38 "make_naive", 

39] 

40 

41# RemovedInDjango50Warning: sentinel for deprecation of is_dst parameters. 

42NOT_PASSED = object() 

43 

44 

45def __getattr__(name): 

46 if name != "utc": 

47 raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 

48 

49 warnings.warn( 

50 "The django.utils.timezone.utc alias is deprecated. " 

51 "Please update your code to use datetime.timezone.utc instead.", 

52 RemovedInDjango50Warning, 

53 stacklevel=2, 

54 ) 

55 

56 return timezone.utc 

57 

58 

59def get_fixed_timezone(offset): 

60 """Return a tzinfo instance with a fixed offset from UTC.""" 

61 if isinstance(offset, timedelta): 

62 offset = offset.total_seconds() // 60 

63 sign = "-" if offset < 0 else "+" 

64 hhmm = "%02d%02d" % divmod(abs(offset), 60) 

65 name = sign + hhmm 

66 return timezone(timedelta(minutes=offset), name) 

67 

68 

69# In order to avoid accessing settings at compile time, 

70# wrap the logic in a function and cache the result. 

71@functools.lru_cache 

72def get_default_timezone(): 

73 """ 

74 Return the default time zone as a tzinfo instance. 

75 

76 This is the time zone defined by settings.TIME_ZONE. 

77 """ 

78 if settings.USE_DEPRECATED_PYTZ: 

79 import pytz 

80 

81 return pytz.timezone(settings.TIME_ZONE) 

82 return zoneinfo.ZoneInfo(settings.TIME_ZONE) 

83 

84 

85# This function exists for consistency with get_current_timezone_name 

86def get_default_timezone_name(): 

87 """Return the name of the default time zone.""" 

88 return _get_timezone_name(get_default_timezone()) 

89 

90 

91_active = Local() 

92 

93 

94def get_current_timezone(): 

95 """Return the currently active time zone as a tzinfo instance.""" 

96 return getattr(_active, "value", get_default_timezone()) 

97 

98 

99def get_current_timezone_name(): 

100 """Return the name of the currently active time zone.""" 

101 return _get_timezone_name(get_current_timezone()) 

102 

103 

104def _get_timezone_name(timezone): 

105 """ 

106 Return the offset for fixed offset timezones, or the name of timezone if 

107 not set. 

108 """ 

109 return timezone.tzname(None) or str(timezone) 

110 

111 

112# Timezone selection functions. 

113 

114# These functions don't change os.environ['TZ'] and call time.tzset() 

115# because it isn't thread safe. 

116 

117 

118def activate(timezone): 

119 """ 

120 Set the time zone for the current thread. 

121 

122 The ``timezone`` argument must be an instance of a tzinfo subclass or a 

123 time zone name. 

124 """ 

125 if isinstance(timezone, tzinfo): 

126 _active.value = timezone 

127 elif isinstance(timezone, str): 

128 if settings.USE_DEPRECATED_PYTZ: 

129 import pytz 

130 

131 _active.value = pytz.timezone(timezone) 

132 else: 

133 _active.value = zoneinfo.ZoneInfo(timezone) 

134 else: 

135 raise ValueError("Invalid timezone: %r" % timezone) 

136 

137 

138def deactivate(): 

139 """ 

140 Unset the time zone for the current thread. 

141 

142 Django will then use the time zone defined by settings.TIME_ZONE. 

143 """ 

144 if hasattr(_active, "value"): 

145 del _active.value 

146 

147 

148class override(ContextDecorator): 

149 """ 

150 Temporarily set the time zone for the current thread. 

151 

152 This is a context manager that uses django.utils.timezone.activate() 

153 to set the timezone on entry and restores the previously active timezone 

154 on exit. 

155 

156 The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a 

157 time zone name, or ``None``. If it is ``None``, Django enables the default 

158 time zone. 

159 """ 

160 

161 def __init__(self, timezone): 

162 self.timezone = timezone 

163 

164 def __enter__(self): 

165 self.old_timezone = getattr(_active, "value", None) 

166 if self.timezone is None: 

167 deactivate() 

168 else: 

169 activate(self.timezone) 

170 

171 def __exit__(self, exc_type, exc_value, traceback): 

172 if self.old_timezone is None: 

173 deactivate() 

174 else: 

175 _active.value = self.old_timezone 

176 

177 

178# Templates 

179 

180 

181def template_localtime(value, use_tz=None): 

182 """ 

183 Check if value is a datetime and converts it to local time if necessary. 

184 

185 If use_tz is provided and is not None, that will force the value to 

186 be converted (or not), overriding the value of settings.USE_TZ. 

187 

188 This function is designed for use by the template engine. 

189 """ 

190 should_convert = ( 

191 isinstance(value, datetime) 

192 and (settings.USE_TZ if use_tz is None else use_tz) 

193 and not is_naive(value) 

194 and getattr(value, "convert_to_local_time", True) 

195 ) 

196 return localtime(value) if should_convert else value 

197 

198 

199# Utilities 

200 

201 

202def localtime(value=None, timezone=None): 

203 """ 

204 Convert an aware datetime.datetime to local time. 

205 

206 Only aware datetimes are allowed. When value is omitted, it defaults to 

207 now(). 

208 

209 Local time is defined by the current time zone, unless another time zone 

210 is specified. 

211 """ 

212 if value is None: 

213 value = now() 

214 if timezone is None: 

215 timezone = get_current_timezone() 

216 # Emulate the behavior of astimezone() on Python < 3.6. 

217 if is_naive(value): 

218 raise ValueError("localtime() cannot be applied to a naive datetime") 

219 return value.astimezone(timezone) 

220 

221 

222def localdate(value=None, timezone=None): 

223 """ 

224 Convert an aware datetime to local time and return the value's date. 

225 

226 Only aware datetimes are allowed. When value is omitted, it defaults to 

227 now(). 

228 

229 Local time is defined by the current time zone, unless another time zone is 

230 specified. 

231 """ 

232 return localtime(value, timezone).date() 

233 

234 

235def now(): 

236 """ 

237 Return an aware or naive datetime.datetime, depending on settings.USE_TZ. 

238 """ 

239 return datetime.now(tz=timezone.utc if settings.USE_TZ else None) 

240 

241 

242# By design, these four functions don't perform any checks on their arguments. 

243# The caller should ensure that they don't receive an invalid value like None. 

244 

245 

246def is_aware(value): 

247 """ 

248 Determine if a given datetime.datetime is aware. 

249 

250 The concept is defined in Python's docs: 

251 https://docs.python.org/library/datetime.html#datetime.tzinfo 

252 

253 Assuming value.tzinfo is either None or a proper datetime.tzinfo, 

254 value.utcoffset() implements the appropriate logic. 

255 """ 

256 return value.utcoffset() is not None 

257 

258 

259def is_naive(value): 

260 """ 

261 Determine if a given datetime.datetime is naive. 

262 

263 The concept is defined in Python's docs: 

264 https://docs.python.org/library/datetime.html#datetime.tzinfo 

265 

266 Assuming value.tzinfo is either None or a proper datetime.tzinfo, 

267 value.utcoffset() implements the appropriate logic. 

268 """ 

269 return value.utcoffset() is None 

270 

271 

272def make_aware(value, timezone=None, is_dst=NOT_PASSED): 

273 """Make a naive datetime.datetime in a given time zone aware.""" 

274 if is_dst is NOT_PASSED: 

275 is_dst = None 

276 else: 

277 warnings.warn( 

278 "The is_dst argument to make_aware(), used by the Trunc() " 

279 "database functions and QuerySet.datetimes(), is deprecated as it " 

280 "has no effect with zoneinfo time zones.", 

281 RemovedInDjango50Warning, 

282 ) 

283 if timezone is None: 

284 timezone = get_current_timezone() 

285 if _is_pytz_zone(timezone): 

286 # This method is available for pytz time zones. 

287 return timezone.localize(value, is_dst=is_dst) 

288 else: 

289 # Check that we won't overwrite the timezone of an aware datetime. 

290 if is_aware(value): 

291 raise ValueError("make_aware expects a naive datetime, got %s" % value) 

292 # This may be wrong around DST changes! 

293 return value.replace(tzinfo=timezone) 

294 

295 

296def make_naive(value, timezone=None): 

297 """Make an aware datetime.datetime naive in a given time zone.""" 

298 if timezone is None: 

299 timezone = get_current_timezone() 

300 # Emulate the behavior of astimezone() on Python < 3.6. 

301 if is_naive(value): 

302 raise ValueError("make_naive() cannot be applied to a naive datetime") 

303 return value.astimezone(timezone).replace(tzinfo=None) 

304 

305 

306_PYTZ_IMPORTED = False 

307 

308 

309def _pytz_imported(): 

310 """ 

311 Detects whether or not pytz has been imported without importing pytz. 

312 

313 Copied from pytz_deprecation_shim with thanks to Paul Ganssle. 

314 """ 

315 global _PYTZ_IMPORTED 

316 

317 if not _PYTZ_IMPORTED and "pytz" in sys.modules: 

318 _PYTZ_IMPORTED = True 

319 

320 return _PYTZ_IMPORTED 

321 

322 

323def _is_pytz_zone(tz): 

324 """Checks if a zone is a pytz zone.""" 

325 # See if pytz was already imported rather than checking 

326 # settings.USE_DEPRECATED_PYTZ to *allow* manually passing a pytz timezone, 

327 # which some of the test cases (at least) rely on. 

328 if not _pytz_imported(): 

329 return False 

330 

331 # If tz could be pytz, then pytz is needed here. 

332 import pytz 

333 

334 _PYTZ_BASE_CLASSES = (pytz.tzinfo.BaseTzInfo, pytz._FixedOffset) 

335 # In releases prior to 2018.4, pytz.UTC was not a subclass of BaseTzInfo 

336 if not isinstance(pytz.UTC, pytz._FixedOffset): 

337 _PYTZ_BASE_CLASSES += (type(pytz.UTC),) 

338 

339 return isinstance(tz, _PYTZ_BASE_CLASSES) 

340 

341 

342def _datetime_ambiguous_or_imaginary(dt, tz): 

343 if _is_pytz_zone(tz): 

344 import pytz 

345 

346 try: 

347 tz.utcoffset(dt) 

348 except (pytz.AmbiguousTimeError, pytz.NonExistentTimeError): 

349 return True 

350 else: 

351 return False 

352 

353 return tz.utcoffset(dt.replace(fold=not dt.fold)) != tz.utcoffset(dt) 

354 

355 

356# RemovedInDjango50Warning. 

357_DIR = dir() 

358 

359 

360def __dir__(): 

361 return sorted([*_DIR, "utc"])