Django 使用当前时区的 UTC 偏移量
Django use UTC offsets for current timezone
Django 试图通过在内部以 UTC 格式存储日期并将其转换为客户端的时区进行显示来解决时区问题。这在理论上听起来不错,直到你意识到两件重要的事情:
- 许多时区可以并且确实存在于同一个 UTC 偏移内。
- 由于没有时区HTTP headers,我们需要手动确定客户端的时区,这需要使用JavaScript。但是,JavaScript 只能可靠地确定客户端的 UTC 偏移量,可能无法猜测正确的时区。
考虑到这两个问题,我假设一个简单的解决方案是完全忽略时区、DST 等,而是依赖于客户端当前的 UTC 偏移量。在每次页面加载时,客户端上的 JavaScript 将使用客户端当前的 UTC 偏移更新客户端的 cookie,Django 中的中间件将为每个请求加载该值。
问题在于:Django 使用 get_current_timezone()
从上次调用 timezone.activate()
时设置的值中检索数据。 timezone.activate()
将时区 object 作为参数。
有没有一种方法可以仅使用 UTC 偏移来使用 timezone.activate()
?
您描述的解决方案是获取客户端当前的 UTC 偏移量并通过 cookie 或其他机制发送回服务器,这是一种常见的方法。不幸的是它有缺陷。仅仅因为人们这样做并不是一个好主意。
问题是您从客户端收集的偏移量是针对特定时刻的。但是,您可能不会在服务器上及时处理同一时刻。
例如,您可以在客户端上调用 new Date().getTimezoneOffset()
,它会为您提供值 480
,即 UTC 以西 480 分钟,或 UTC-08:00
(注意符号反转)。因此,您将 480 传递给服务器,以 UTC 格式从数据库加载日期,然后应用偏移量。除了,也许您加载的日期是几个月前的,而客户对 that 日期的偏移量是 UTC-07:00
。因此,您应用了错误的偏移量,并生成了一个与应有的值相差一个小时的结果值。
不能仅通过偏移量来识别时区。时区标识符看起来像 "America/Los_Angeles"
,而不仅仅是 UTC-8
。这是一个很常见的错误。在 the timezone tag wiki.
的 "time zone != offset" 下阅读更多内容
只有两种正确的方法来处理这种情况:
使用像 jsTimeZoneDetect or moment-timezone 这样的库来 猜测 浏览器的时区,然后让用户选择他们的时区,默认为猜测值。然后,您可以在 server-side 代码中将所选或猜测的时区与 Django 或其他软件一起使用。
只向客户端发送 UTC,在浏览器中使用 JavaScript 将 UTC 时间转换为本地时间。 (浏览器理解 行为 它所在的本地时区 运行,即使它很难 识别 它。)这里要注意的是 - 由于 this bug,较旧的浏览器可能会错误地转换较旧的日期。但在大多数情况下,这仍然是一种合理的方法。
Django 试图通过在内部以 UTC 格式存储日期并将其转换为客户端的时区进行显示来解决时区问题。这在理论上听起来不错,直到你意识到两件重要的事情:
- 许多时区可以并且确实存在于同一个 UTC 偏移内。
- 由于没有时区HTTP headers,我们需要手动确定客户端的时区,这需要使用JavaScript。但是,JavaScript 只能可靠地确定客户端的 UTC 偏移量,可能无法猜测正确的时区。
考虑到这两个问题,我假设一个简单的解决方案是完全忽略时区、DST 等,而是依赖于客户端当前的 UTC 偏移量。在每次页面加载时,客户端上的 JavaScript 将使用客户端当前的 UTC 偏移更新客户端的 cookie,Django 中的中间件将为每个请求加载该值。
问题在于:Django 使用 get_current_timezone()
从上次调用 timezone.activate()
时设置的值中检索数据。 timezone.activate()
将时区 object 作为参数。
有没有一种方法可以仅使用 UTC 偏移来使用 timezone.activate()
?
您描述的解决方案是获取客户端当前的 UTC 偏移量并通过 cookie 或其他机制发送回服务器,这是一种常见的方法。不幸的是它有缺陷。仅仅因为人们这样做并不是一个好主意。
问题是您从客户端收集的偏移量是针对特定时刻的。但是,您可能不会在服务器上及时处理同一时刻。
例如,您可以在客户端上调用 new Date().getTimezoneOffset()
,它会为您提供值 480
,即 UTC 以西 480 分钟,或 UTC-08:00
(注意符号反转)。因此,您将 480 传递给服务器,以 UTC 格式从数据库加载日期,然后应用偏移量。除了,也许您加载的日期是几个月前的,而客户对 that 日期的偏移量是 UTC-07:00
。因此,您应用了错误的偏移量,并生成了一个与应有的值相差一个小时的结果值。
不能仅通过偏移量来识别时区。时区标识符看起来像 "America/Los_Angeles"
,而不仅仅是 UTC-8
。这是一个很常见的错误。在 the timezone tag wiki.
只有两种正确的方法来处理这种情况:
使用像 jsTimeZoneDetect or moment-timezone 这样的库来 猜测 浏览器的时区,然后让用户选择他们的时区,默认为猜测值。然后,您可以在 server-side 代码中将所选或猜测的时区与 Django 或其他软件一起使用。
只向客户端发送 UTC,在浏览器中使用 JavaScript 将 UTC 时间转换为本地时间。 (浏览器理解 行为 它所在的本地时区 运行,即使它很难 识别 它。)这里要注意的是 - 由于 this bug,较旧的浏览器可能会错误地转换较旧的日期。但在大多数情况下,这仍然是一种合理的方法。