jayvynl / django-clickhouse-backend

Django clickhouse database backend.
MIT License
130 stars 21 forks source link

使用 DateTime64Field 类型时字段时区问题 #12

Closed magichuang77 closed 1 year ago

magichuang77 commented 1 year ago

你好~ 我在 django models 中定义 req_start_time = models.DateTime64Field(precision=3),执行 migrate 之后,发现数据库中的字段类型是 req_start_time DateTime64(3, 'UTC'),此时在插入 req_start_time=1684278000000,clickhouse 会存储为 -8 h 的时间;

我应该如何设置时区?比如设置成 Asia/Shanghai

仓库代码是这样定义的

"DateTime64Field": "DateTime64(%(precision)s, 'UTC')" if settings.USE_TZ else "DateTime64(%(precision)s)",

是否可以扩展一下,支持自定义时区?比如 req_start_time = models.DateTime64Field(precision=3, time_zone="Asia/Shanghai")

jayvynl commented 1 year ago

在clickhouse中,时间实际上都是存储为unix时间戳的(隐含了UTC时区)。 Django settings中默认是启用时区的,这也是该项目推荐的使用方式,在插入一条数据的时候,你可以有多种选择:

  1. 使用浮点值,在 DateTime 和 DateTime64 中,浮点值被视为 unix 时间戳。

  2. 使用整数值,整数值在 DateTime 中被视为 unix 时间戳。

在 DateTime64 中整数值的含义取决于 DateTime64 的精度。 例如,DateTime64(3) 将 int vale 视为毫秒精度 unix 时间戳,DateTime64(6) 将 int vale 视为微秒精度 unix 时间戳。

  1. 使用 datatime.datetime 类型数据,如果带时区,底层的 clickhouse_driver 驱动会自动处理时区,如果不带时区,默认是当前系统时区。

而django查询到时间都带UTC时区(此行为和postgresql backend一致),如果你需要本地时间,首先在settings中设置你的默认时区 TIME_ZONE = 'Asia/Shanghai',然后:

from django.utils import timezone

timezone.localtime(obj.req_start_time)
jayvynl commented 1 year ago

时间字段拥有很多其他查询,例如Dates querydatetimes querydate lookup,项目中包含很多针对这些查询的单元测试,如果自定义时间字段,需要确保能够通过测试。

magichuang77 commented 1 year ago

在clickhouse中,时间实际上都是存储为unix时间戳的(隐含了UTC时区)。 Django settings中默认是启用时区的,这也是该项目推荐的使用方式,在插入一条数据的时候,你可以有多种选择:

  1. 使用浮点值,在 DateTime 和 DateTime64 中,浮点值被视为 unix 时间戳。
  2. 使用整数值,整数值在 DateTime 中被视为 unix 时间戳。

在 DateTime64 中整数值的含义取决于 DateTime64 的精度。 例如,DateTime64(3) 将 int vale 视为毫秒精度 unix 时间戳,DateTime64(6) 将 int vale 视为微秒精度 unix 时间戳。

  1. 使用 datatime.datetime 类型数据,如果带时区,底层的 clickhouse_driver 驱动会自动处理时区,如果不带时区,默认是当前系统时区。

而django查询到时间都带UTC时区(此行为和postgresql backend一致),如果你需要本地时间,首先在settings中设置你的默认时区 TIME_ZONE = 'Asia/Shanghai',然后:

from django.utils import timezone

timezone.localtime(obj.req_start_time)

哦哦 之前我是将 13 位时间戳存进去,然后查询的时候发现是 -8 小时的时间,以为它会在存进去的时候帮我处理了:joy: