本文介绍时区感知日期/时间字段如何通过mssql-django后端处理SQL Server,以及如何在启用时区支持时迁移现有数据。
时区支持的工作原理
Django 的设置USE_TZsettings.py控制日期/时间字段是否具有时区感知:
| 设置 | 列类型 | Behavior |
|---|---|---|
USE_TZ=False |
datetime2 | 存储不带时区信息的天真日期时间。 |
USE_TZ=True |
datetimeoffset | 存储具有 UTC 偏移量的时区感知日期时间。 |
Note
Django 在所有版本中默认使用 USE_TZ=False,包括 Django 5.x 和 6.0。 如果您的项目需要时区支持,则必须在 settings.py 中显式设置 USE_TZ=True。 有关详细信息 ,请参阅 Django 中的时区支持 。 请注意, datetimeoffset 通常对相同的时间戳精度使用比 datetime2 更多的存储,因此在迁移后,应在大型表上验证存储和查询计划。
启用时区支持
在你的settings.py中设置USE_TZ=True:
USE_TZ=True
TIME_ZONE = "UTC"
启用后 USE_TZ ,Django 会以 UTC 格式存储所有日期/时间,并将其转换为本地时区进行显示。
从 mssql-django 1.7.2 开始,后端还会在 USE_TZ=True 时,使 Django Now() 的行为与时区感知的 SQL 生成保持一致。
迁移现有日期/时间列
如果 Django 应用在启用DateTimeField之前具有USE_TZ=True列,则必须手动将 datetime2 列迁移到 datetimeoffset 并将本地时间转换为 UTC。
后端mssql-django在USE_TZ=False时,对DateTimeField使用datetime2作为底层列类型。 启用 USE_TZ 不会自动转换现有列。
在以下步骤中,替换占位符:
-
<table-name>:包含该列的表名。 -
<datetime-column>:要转换的列名。 -
<offset>:现有数据的时区偏移量,格式为字符串{+|-}HH:MM(例如,'-05:00'美国东部)。
本文中的代码示例使用 AdventureWorks2025 或 AdventureWorksDW2025 示例数据库,可以从 Microsoft SQL Server 示例和社区项目 主页下载该数据库。
步骤 1:更改列类型
对包含 datetime2 列的每个表运行以下 SQL。 SQL Server隐式转换值并分配+00:00偏移量:
ALTER TABLE <table-name>
ALTER COLUMN <datetime-column> DATETIMEOFFSET;
步骤 2:转换为 UTC
在该列为 datetimeoffset 类型后,使用原始本地时区偏移量重新标记每个值,然后将其转换为 UTC:
UPDATE <table-name>
SET <datetime-column> = TODATETIMEOFFSET(<datetime-column>, <offset>) AT TIME ZONE 'UTC';
TODATETIMEOFFSET 替换 datetimeoffset 值的偏移量部分,以便时间戳反映原始本地时间。
AT TIME ZONE 'UTC' 然后将结果转换为 UTC。
Important
仅当所有源值共享同一偏移量时,单个固定偏移量才有效。 如果数据跨越夏令时转换,则按日期派生偏移量或使用基于时区名称的转换策略,而不是对每行应用一个常量偏移量。
SQL Server时区名称
使用AT TIME ZONE转换时,请使用 Windows 时区名称。 常见示例:
| Region | SQL Server时区名称 | DST 转换日期 (2026) |
|---|---|---|
| 美国东部 | Eastern Standard Time |
3 月 8 日 – 11 月 1 日 |
| 美国中部 | Central Standard Time |
3 月 8 日 – 11 月 1 日 |
| 美国太平洋 | Pacific Standard Time |
3 月 8 日 – 11 月 1 日 |
| UTC | UTC |
无(无 DST) |
| 欧洲/伦敦 | GMT Standard Time |
3 月 29 日至 10 月 25 日 |
有关完整列表,请查询SQL Server:
SELECT name, current_utc_offset, is_currently_dst
FROM sys.time_zone_info
ORDER BY name;
有关完整文档,请参阅 sys.time_zone_info。
示例:将美国东部时间转换为 UTC(考虑夏令时)
此示例使用现有 AdventureWorks2025 架构表并在迁移过程中演示正确的 DST 处理:
-- Test with a DST transition date (March 8, 2026)
SELECT TOP 10 SalesOrderID,
CAST (OrderDate AS DATETIME2) AS OriginalDateTime2,
(CAST (OrderDate AS DATETIME2) AT TIME ZONE 'Eastern Standard Time') AS EasternTime,
(CAST (OrderDate AS DATETIME2) AT TIME ZONE 'Eastern Standard Time' AT TIME ZONE 'UTC') AS ConvertedToUtc
FROM Sales.SalesOrderHeader
WHERE MONTH(OrderDate) = 3 AND DAY(OrderDate) = 8
ORDER BY SalesOrderID;
Tip
始终使用跨越夏令时切换日期的数据来测试时区转换。 先前的查询测试了 3 月 8 日(夏令时开始),也就是美国东部时间从 EST(UTC-5)切换为 EDT(UTC-4)的那一天。
若要迁移应用程序表,请将相同的 AT TIME ZONE 转换模式应用于自己的 DateTimeField 列。 对于每个表,添加 datetimeoffset 列并从现有列填充该列:
ALTER TABLE [your_schema].[your_table]
ADD [date_column_datetimeoffset] datetimeoffset NULL;
UPDATE [your_schema].[your_table]
SET [date_column_datetimeoffset] = CAST([old_date_column] AS DATETIME2) AT TIME ZONE 'Eastern Standard Time' AT TIME ZONE 'UTC';
验证转换结果后,删除旧列并重命名新列:
ALTER TABLE [your_schema].[your_table]
DROP COLUMN [old_date_column];
EXECUTE sp_rename '[your_schema].[your_table].[date_column_datetimeoffset]', 'date_column', 'COLUMN';
使用与源数据区域匹配的SQL Server Windows时区名称。 有关详细信息,请参阅 sys.time_zone_info。
步骤 3:完成 Django 迁移
转换数据库列后,请遵循 Django 文档中关于迁移在添加时区支持之前就已开始的项目的说明。
Important
在所有表中的所有 DateTimeField 列上运行此迁移。 缺少任何列会导致这些字段的时区处理不正确。
Limitations
- 时区和时间增量:并非所有涉及时区和时间增量的操作都得到完全支持。 有关详细信息,请参阅 mssql-django 中的限制和不支持的功能。
- 日期时间运算:启用时区支持时,右侧幂运算以及对日期时间值进行算术运算可能无法按预期工作。