diff --git a/docs/i18n/README.zh.md b/docs/i18n/README.zh.md index 1d2f861..14f1894 100644 --- a/docs/i18n/README.zh.md +++ b/docs/i18n/README.zh.md @@ -562,7 +562,7 @@ Django ORM Lens 处在 **编辑器工具** 与 **AI agent 工具** 的交叉地 |---|---|---| | [Queryset](../rules/README.md) | `DOL001`–`DOL007` | `.count() > 0` → `.exists()`、循环中访问 FK(N+1) | | [模型定义](../rules/README.md) | `DOL011`–`DOL015` | 缺少 `on_delete` 的 `ForeignKey`、字符串字段上的 `null=True` | -| [日期时间](../rules/README.md) | `DOL021`–`DOL022` | `datetime.now()` → `timezone.now()` | +| 日期时间 | [`DOL021`](rules/zh/DOL021.md) · [`DOL022`](rules/zh/DOL022.md) | `datetime.now()` → `timezone.now()` | | [表单 / 视图](../rules/README.md) | `DOL031`–`DOL032` | `render()` 中使用 `locals()`、`Meta.fields = '__all__'` | | [迁移风险](../rules/migrations.md) | 16 条规则 | 无默认值的 NOT NULL 新增、锁表的索引构建、不可逆的数据迁移 | | [静态 N+1](../rules/nplusone.md) | 1 个分析器 | 循环中访问 FK/M2M 而未用 `select_related` / `prefetch_related` | diff --git a/docs/i18n/rules/zh/DOL021.md b/docs/i18n/rules/zh/DOL021.md new file mode 100644 index 0000000..cf9b2ad --- /dev/null +++ b/docs/i18n/rules/zh/DOL021.md @@ -0,0 +1,33 @@ +# DOL021 — 应使用 timezone.now(),而不是 datetime.now() + +**默认严重程度:** warning · **适用性:** suggestion · **类别:** datetime + +检测直接调用 `datetime.now()` 的情况(即使用 `from datetime import datetime` 导入的形式;不匹配 `datetime.datetime.now()` 之类的属性调用形式)。`datetime.now()` 返回基于服务器本地时间的无时区信息日期时间(naive datetime)。在 `USE_TZ=True` 时,将其保存到 `DateTimeField` 会触发 `RuntimeWarning: DateTimeField received a naive datetime`;将其与 Django 在其他位置生成的带时区信息日期时间(aware datetime)进行比较,则会引发 `TypeError`。 + +Django 中的惯用方式是使用 `django.utils.timezone.now()`,其返回值取决于该设置:`USE_TZ=True` 时返回带有 UTC 时区信息的日期时间,`USE_TZ=False` 时返回基于本地时间的无时区信息日期时间。因此,无论该设置取何值,它都是正确的调用方式。QuickFix(“Replace with timezone.now()”)只会改写函数调用;你需要自行添加 `timezone` 导入。该规则属于 suggestion,因为明确使用 `USE_TZ=False` 的项目可能需要无时区信息的日期时间。 + +**哪些项目实际启用了 `USE_TZ=True`?** 这里有两个容易混淆的概念。`django.conf.global_settings` 中的框架默认值在 Django 4.2 及之前一直是 `False`,直到 Django 5.0 才改为 `True`。另一方面,自 Django 4.0 起,`startproject` 模板会在其生成的设置文件中写入 `USE_TZ = True`。因此,在 Django 4.0 及之后版本中创建的项目会因为模板主动启用该设置而使用时区感知日期时间;而升级到 Django 4.x 的已有项目仍会保持 `False`,直到有人明确修改该设置。在 Django 4.x 中,应检查项目的实际设置,而不能直接假定它为 `True`。 + +## 错误示例 + +```python +from datetime import datetime + +order.confirmed_at = datetime.now() +``` + +## 正确示例 + +```python +from django.utils import timezone + +order.confirmed_at = timezone.now() +``` + +## 禁用规则 + +```python +# django-orm-lens-disable-next-line DOL021 +``` + +也可以在工作区的 `.vscode/settings.json` 中进行设置:`{"djangoOrmLens.rules": {"DOL021": "off"}}`。 \ No newline at end of file diff --git a/docs/i18n/rules/zh/DOL022.md b/docs/i18n/rules/zh/DOL022.md new file mode 100644 index 0000000..e035045 --- /dev/null +++ b/docs/i18n/rules/zh/DOL022.md @@ -0,0 +1,33 @@ +# DOL022 — datetime.utcnow() 已被弃用 + +**默认严重程度:** warning · **适用性:** suggestion · **类别:** datetime + +检测直接调用 `datetime.utcnow()` 的情况。`utcnow()` 自 Python 3.12 起已被弃用,而且尽管名称中包含 UTC,它返回的仍是无时区信息日期时间(naive datetime):即不附带 `tzinfo` 的 UTC 时钟时间。 + +将其与启用 `USE_TZ=True` 的 Django 项目所生成的带时区信息日期时间(aware datetime)混用,会产生与 DOL021 相同的问题:保存时触发 `RuntimeWarning`,比较时引发 `TypeError`;如果该值之后被当作本地时间解释,还可能产生不易察觉的时区偏移错误。 + +在 Django 代码中应使用 `django.utils.timezone.now()`;在不依赖框架、仅使用 Python 标准库的代码中,应使用 `datetime.now(timezone.utc)`。QuickFix(“Replace with timezone.now()”)采用 Django 的写法;你需要自行添加相应的导入。 + +## 错误示例 + +```python +from datetime import datetime + +token.issued_at = datetime.utcnow() +``` + +## 正确示例 + +```python +from django.utils import timezone + +token.issued_at = timezone.now() +``` + +## 禁用规则 + +```python +# django-orm-lens-disable-next-line DOL022 +``` + +也可以在工作区的 `.vscode/settings.json` 中进行设置:`{"djangoOrmLens.rules": {"DOL022": "off"}}`。 \ No newline at end of file