django 时间 时区 语言 实战问题解决

django 时间 时区 语言问题

场景:

1.ORM操作ModelDateField, DateTimeField, TimeField 字段时, 数据库(以MySQL举例)中对应存储时间字段的实际情况.

2.django开发中, django服务内部调用其它服务的API接口返回了时间字段, 该时间字段如何处理成前端 (以React.js举例)框架可以正确转换成本地时间的对象.

前置知识:

1.什么是 UTC时间 (自行百度谷歌)

2.常见表示时间的规范: GMT UTC CST 见下面的参考资料1

场景1: 重要结论和实践操作

1.django可以通过setting.py文件中进行 时间 和 语言 的本地化 配置, 此配置是全局性的.
  • 配置意义: 如果用到原生的django admin等, 配置之后界面的时间由UTC变成本地时间, 语言变成本地语言.对用户更加友好.
  • 但是, 和数据库中存储的时间并无关系, 下面有详细说明.

默认配置:

本地化配置如下:

LANGUAGE_CODE = 'zh-Hans'  # 语言

TIME_ZONE = 'Asia/Shanghai'  # 时区

USE_I18N = True  # 语言

USE_L10N = True  # 数据和时间格式

USE_TZ = True  # 启用时区
2.django在写数据库的时候, 日期和时间 都用的UTC时间
  • 换句话说: 即使进行了上面的本地化配置, 实际 datatime 等时间字段在数据库中也是UTC时间, 会比本地时间(以东八区举例)慢 8h
  • 可以自己使用DateTimeField字段的 auto_now选项来测试每次save的时候, 数据库时间和当前时间的变动.
class XxModel(models.Model):

	created_time = models.DateTimeField(auto_now_add=True, editable=False, verbose_name="创建时间")
    ....
python runserver shell

>>>obj = XxModel.objects.creat(...)
>>> obj.created_time  

同数据库中保存的 created_time 字段的值进行比对.
3.前端(如react框架)可以直接接收后端API返回的datetime对象.
  • 该结论的意义: 前端可以通过后端返回的datetime对象, 进行 时区timezone 转换. 将UTC时间转换成local time时间.

  • 一般后端是从数据库返回的datetime对象, 经过前端本地化处理(如, +8h), 成功的把之前数据库相对django慢的8h加回来了.

  • Chrome的console来演示:

    • 谷歌浏览器页面按F12 打开之后, 选择 console的选项卡, 输入下面的内容
    • 切换操作系统的时区
>new Date("2020-02-06T01:56:21.744Z")
new Date("2020-02-06T01:56:21.744Z")
  • 结论:
    • 1.浏览器默认读取操作系统的时区设置, 然后将收到的时间参数进行本地化转换.
    • 2.提示: 不同操作系统默认的时区是不一样的. Win 和 Unix 系统就有区别, 可以深入研究.

场景2: 实践操作和解决方案优化

1.访问外部服务API获取的时间的时区 和 django本地化 的时区可能不一致, 需要具体问题具体处理.

详细背景:

开发中, django本地化采用的是东八区的datetime, 调用外部服务获取的datetime是UTC时间.

我返回给前端的时候没有直接返回datetime对象, 采用的是format后的datetime字符串, 由我手动将时间+8h, 然后再将加之后的时间进行format给前端.
  • 导致: 前端没法利用模块直接做时间转化.
  • 反思: 我这样的操作多此一举.
2.我的解法:
def datetime_monkey(dt: datetime) -> str:
    _ = dt + timedelta(hours=8)
    return datetime.strftime(_, "%Y-%m-%dT%H:%M:%S")

# 伪代码
return JsonResponse(data={"upload_time": datetime_monkey(obj.last_modified), ...})
# obj.last_modified 是一个 datetime 对象.
3.我优化的解法:
  • 上面的解法, 对时区处理直接 +8h 显然存在 hard code 的问题.

  • 厘清问题: 时间本身并不存在bug, 只是展示结果不是我们预期想要的(不够人性化)

  • 思路: 时区转换.

    • 工具: pytz

      The second way of building a localized time is by converting an existing localized time using the standard astimezone() method

    • 辅助验证工具: tzlocal

      查看本地时区

import pytz
import tzlocal
# 伪代码
from ....settings import TIME_ZONE  # 从django项目的settings.py文件中导入 TIME_ZONE 配置

local_tz = tzlocal.get_localzone()  # 查看本地所时区TimeZone
print(f">>{local_tz}<< {type(local_tz)}")  # Asia/Shanghai

# tz = pytz.timezone('Asia/Kuala_Lumpur')
tz = pytz.timezone(TIME_ZONE)  # TIME_ZONE = 'Asia/Shanghai'
# now_kl = obj.last_modified.replace(tzinfo=pytz.utc).astimezone(tz)
now_kl = obj.last_modified.astimezone(tz)
print(now_kl)
4.时区转换
  • 见参考资料4,5
5.当然, 最最优的方案就是:直接返回前端 datetime 对象, 由前端统一处理。

一点反思:

1.最正确的解决问题的思路还是看 python 官方相关文档;

2.当文档写的不好的时候, 或者时间紧迫的时候, 先解决问题。

3.对知识点理解的越透彻, 越容易化繁为简, 最终解决方案也许简单, 但是解决的过程可能充满挑战。

参考资料(均来自网络):

1.计算机系统中的时间

2.datetime—基本日期和时间类型

3.pytz - 世界时区

4.utc时间转换成其它时区的时间: python-convert-utc-to-timezone

5.converting-time-zones-datetime-objects-python

6.本地时间转UTC时间

你可能感兴趣的:(Python,python,web)