A plugin for flake8 to ban the usage of unsafe naive datetime class.
The checks of this plugin have been adopted into Ruff
as its flake8-datetimez (DTZ) rule set.
Many thanks to the Ruff project for carrying these rules forward — their
implementation is faster, more actively maintained, and handles more cases than
this plugin does.
If you are starting a new project, or already run Ruff, please prefer it:
# pyproject.toml
[tool.ruff.lint]
extend-select = ["DTZ"]This plugin remains for codebases that are still on flake8, and is maintained on a best-effort basis for backward compatibility — keeping the existing rules working across the supported Python and flake8 versions, rather than growing new ones.
-
DTZ001 : The use of
datetime.datetime()withouttzinfoargument is not allowed. -
DTZ002 : The use of
datetime.datetime.today()is not allowed. Usedatetime.datetime.now(tz=)instead. -
DTZ003 : The use of
datetime.datetime.utcnow()is not allowed. Usedatetime.datetime.now(tz=)instead. -
DTZ004 : The use of
datetime.datetime.utcfromtimestamp()is not allowed. Usedatetime.datetime.fromtimestamp(, tz=)instead. -
DTZ005 : The use of
datetime.datetime.now()withouttzargument is not allowed. -
DTZ006 : The use of
datetime.datetime.fromtimestamp()withouttzargument is not allowed. -
DTZ007 : The use of
datetime.datetime.strptime()without %z must be followed by.replace(tzinfo=). -
DTZ011 : The use of
datetime.date.today()is not allowed. Usedatetime.datetime.now(tz=).date()instead. -
DTZ012 : The use of
datetime.date.fromtimestamp()is not allowed. Usedatetime.datetime.fromtimestamp(, tz=).date()instead. -
DTZ901 : The use of
datetime.datetime.minordatetime.datetime.maxwithout.replace(tzinfo=)is not allowed.
Calling .astimezone() on a naive datetime attaches the local timezone to it, so it is
accepted as a way to resolve DTZ001, DTZ002, DTZ005, DTZ006 and DTZ007:
datetime.datetime.now().astimezone() # ok
datetime.datetime(2000, 1, 1).astimezone() # okIt is deliberately not accepted for the remaining warnings:
-
DTZ003, DTZ004 :
utcnow()andutcfromtimestamp()return a UTC wall clock. Reading that value as local time shifts the instant, soutcnow().astimezone()is off by the local UTC offset, which is exactly what these two warnings are meant to prevent. -
DTZ011, DTZ012 :
datetime.datehas no.astimezone()at all. -
DTZ901 :
datetime.minanddatetime.maxoverflow when converted to another timezone.
Install with pip
$ pip install flake8-datetimez
- Python 3.8 or above (tested on 3.8 through 3.14)
- flake8 3.0.0 or above
The test suite runs against every supported Python version with tox. No interpreter has to be installed beforehand, they are downloaded automatically by uv:
$ uvx --with tox-uv tox
That covers py38 through py314 plus a lint environment, and it is exactly
what CI runs, one job per environment. To run a single environment, or the tests
with the current interpreter only:
$ uvx --with tox-uv tox -e py314
$ python -m unittest discover -p "test_*.py" -v
The code is formatted and linted with ruff:
$ uvx ruff format .
$ uvx ruff check .
MIT