Django 1.11 - 如何确保 TruncYear 产生祖鲁时间
Django 1.11 - How can I ensure TruncYear to produce Zulu time
我正在使用 Django 1.11 和 Postgres 9.4。
如何确保 TruncYear 生成祖鲁时间 (2019-10-01T00:00:00Z)。我注意到它使用这样的时区创建日期时间 (2017-01-01T00:00:00+03:00)
这是我的 TruncYear 查询集代码:
from django.db.models import Count
from django.db.models.functions import TruncMonth, TruncYear, TruncDay, TruncHour
tracking_in_timeseries_data = Tracking.objects.annotate(
year=TruncYear('created_at')).values('year', 'venue').annotate(
count=Count('employee_id', distinct = True)).order_by('year')
>>> for exp in tracking_in_timeseries_data:
... print(exp['year'], exp['venue'], exp['count'])
2017-01-01 00:00:00+00:00 4 1
2019-01-01 00:00:00+00:00 2 2
2019-01-01 00:00:00+00:00 3 1
2019-01-01 00:00:00+00:00 4 1
2019-01-01 00:00:00+00:00 5 1
2019-01-01 00:00:00+00:00 6 1
>>> tracking_in_timeseries_data
<QuerySet [{'venue': 4, 'year': datetime.datetime(2017, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 2, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 2}, {'venue': 3, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 4, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 5, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 6, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}]>
如果我序列化它会产生这个:
serializer.py
class TimeseriesYearSerializer(serializers.ModelSerializer):
venue = VenueTSSerializer(read_only=True)
year = serializers.DateTimeField(read_only=True)
count = serializers.IntegerField(read_only=True)
class Meta:
model = Tracking
fields = ['venue', 'year', 'count']
输出:
[
{
"count": 1,
"year": "2017-01-01T00:00:00+03:00",
"venue_id": 2
},
{
"count": 1,
"year": "2018-01-01T00:00:00+03:00",
"venue_id": 1
},
{
"count": 1,
"year": "2018-01-01T00:00:00+03:00",
"venue_id": 2
},
{
"count": 3,
"year": "2019-01-01T00:00:00+03:00",
"venue_id": 1
},
{
"count": 3,
"year": "2019-01-01T00:00:00+03:00",
"venue_id": 2
}
]
我如何确保 TruncYear 查询集在祖鲁时间生成日期时间字符串,例如 2019-10-01T00:00:00Z 而不是时区 2019-01-01T00:00:00+03:00。
更新:
我注意到我通过重新启动 django 服务临时解决了这个问题。
sudo supervisorctl stop all
sudo supervisorctl start all
然后就可以产生这样的Z时间2019-10-01T00:00:00Z
但几个小时后,它开始产生这样的时区时间格式 2017-01-01T00:00:00+03:00
我还注意到如果我重新启动服务器,它不会有 Z 时间。我必须执行 supervisorctl stop 和 start 然后它似乎暂时修复了它。
这是我的主管重启代码片段
/home/user/myapp/gunicorn_start.bash
/etc/supervisor/conf.d/myapp.conf
https://gist.github.com/axilaris/01525b78fcdc03071fcd34818820d7f1
这是我的服务器版本Ubuntu 16.04.3 LTS
可能是什么问题以及如何解决它以使其持续产生祖鲁时间。
我检查了 Django Rest Framework 的源代码,看起来它默认使用“current timezone”作为 DateTimeField
,但您可以通过传递 default_timezone
等于 "pytz.timezone('Zulu')"
。这样它将 return 所需时区的字段值。
我试了一下,对我来说它按预期工作。例如,对于我的测试序列化程序(在我的项目中)的 Europe/Moscow
,我的输出类似于
{
"id": 1,
"title": "test 123",
"level": 0,
"slug": "test-123",
"icon_code": "123",
"image": null,
"brand": false,
"children": [],
"created_at": "2019-12-09T01:55:08.438442+03:00"
}
对于 Zulu
时区,我喜欢
{
"id": 1,
"title": "test 123",
"level": 0,
"slug": "test-123",
"icon_code": "123",
"image": null,
"brand": false,
"children": [],
"created_at": "2019-12-08T22:55:08.438442Z"
}
我的测试字段声明是
created_at = serializers.DateTimeField(format='iso-8601', default_timezone=pytz.timezone('Zulu'))
It seems like in your source code you override "current timezone" but don't set it to a default one or etc and that's why it works right after the server restart and stops in a few hours (when a request sets the wrong timezone). So you have 2 ways to fix it - declare your fields as I did above using the default_timezone
param or find where the current timezone value is changed). The easiest solution for the case, of course, is declaring the field timezone required but in general, of course, it should work without bugs :)
更多信息:
- https://github.com/encode/django-rest-framework/blob/95d4843abeecea96754a147f4f2cca33e620ad09/rest_framework/fields.py#L1199
- https://docs.djangoproject.com/en/3.0/topics/i18n/timezones/#selecting-the-current-time-zone
- https://www.django-rest-framework.org/api-guide/fields/#datetimefield(查看
default_timezone
参数)
我在这里注意到的第一件事是,您正在使用 serializers.DateTimeField
--DRF doc 字段作为 year 表示,其中年总是正整数.
关于时区问题,Django 在从数据库查询时使用 TIME_ZONE
settings。这是你这样做时发生的 postgress 中的最小 SQL 查询,
SELECT a,b,c,<b>DATE_TRUNC('year', "tracking_table"."created_at" AT TIME ZONE 'YOUR_TIME_ZONE_VALUE') AS "year"</b> FROM "tracking_table
但是,幸运的是,您可以在 tzinfo
参数中的TruncYear
class.
例子
<b>import pytz</b>
Tracking.objects.annotate(year=<b>TruncYear('created_at', tzinfo=pytz.UTC)</b>)
要在 json 响应中显示 year,请将序列化程序更改为,
class TimeseriesYearSerializer(serializers.ModelSerializer):
venue = VenueTSSerializer(read_only=True)
<b>year = serializers.IntegerField(read_only=True, source='year.year')</b>
count = serializers.IntegerField(read_only=True)
class Meta:
model = Tracking
fields = ['venue', 'year', 'count']
参考资料
我正在使用 Django 1.11 和 Postgres 9.4。
如何确保 TruncYear 生成祖鲁时间 (2019-10-01T00:00:00Z)。我注意到它使用这样的时区创建日期时间 (2017-01-01T00:00:00+03:00)
这是我的 TruncYear 查询集代码:
from django.db.models import Count
from django.db.models.functions import TruncMonth, TruncYear, TruncDay, TruncHour
tracking_in_timeseries_data = Tracking.objects.annotate(
year=TruncYear('created_at')).values('year', 'venue').annotate(
count=Count('employee_id', distinct = True)).order_by('year')
>>> for exp in tracking_in_timeseries_data:
... print(exp['year'], exp['venue'], exp['count'])
2017-01-01 00:00:00+00:00 4 1
2019-01-01 00:00:00+00:00 2 2
2019-01-01 00:00:00+00:00 3 1
2019-01-01 00:00:00+00:00 4 1
2019-01-01 00:00:00+00:00 5 1
2019-01-01 00:00:00+00:00 6 1
>>> tracking_in_timeseries_data
<QuerySet [{'venue': 4, 'year': datetime.datetime(2017, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 2, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 2}, {'venue': 3, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 4, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 5, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}, {'venue': 6, 'year': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<UTC>), 'count': 1}]>
如果我序列化它会产生这个:
serializer.py
class TimeseriesYearSerializer(serializers.ModelSerializer):
venue = VenueTSSerializer(read_only=True)
year = serializers.DateTimeField(read_only=True)
count = serializers.IntegerField(read_only=True)
class Meta:
model = Tracking
fields = ['venue', 'year', 'count']
输出:
[
{
"count": 1,
"year": "2017-01-01T00:00:00+03:00",
"venue_id": 2
},
{
"count": 1,
"year": "2018-01-01T00:00:00+03:00",
"venue_id": 1
},
{
"count": 1,
"year": "2018-01-01T00:00:00+03:00",
"venue_id": 2
},
{
"count": 3,
"year": "2019-01-01T00:00:00+03:00",
"venue_id": 1
},
{
"count": 3,
"year": "2019-01-01T00:00:00+03:00",
"venue_id": 2
}
]
我如何确保 TruncYear 查询集在祖鲁时间生成日期时间字符串,例如 2019-10-01T00:00:00Z 而不是时区 2019-01-01T00:00:00+03:00。
更新: 我注意到我通过重新启动 django 服务临时解决了这个问题。
sudo supervisorctl stop all
sudo supervisorctl start all
然后就可以产生这样的Z时间2019-10-01T00:00:00Z 但几个小时后,它开始产生这样的时区时间格式 2017-01-01T00:00:00+03:00
我还注意到如果我重新启动服务器,它不会有 Z 时间。我必须执行 supervisorctl stop 和 start 然后它似乎暂时修复了它。
这是我的主管重启代码片段
/home/user/myapp/gunicorn_start.bash
/etc/supervisor/conf.d/myapp.conf
https://gist.github.com/axilaris/01525b78fcdc03071fcd34818820d7f1
这是我的服务器版本Ubuntu 16.04.3 LTS
可能是什么问题以及如何解决它以使其持续产生祖鲁时间。
我检查了 Django Rest Framework 的源代码,看起来它默认使用“current timezone”作为 DateTimeField
,但您可以通过传递 default_timezone
等于 "pytz.timezone('Zulu')"
。这样它将 return 所需时区的字段值。
我试了一下,对我来说它按预期工作。例如,对于我的测试序列化程序(在我的项目中)的 Europe/Moscow
,我的输出类似于
{
"id": 1,
"title": "test 123",
"level": 0,
"slug": "test-123",
"icon_code": "123",
"image": null,
"brand": false,
"children": [],
"created_at": "2019-12-09T01:55:08.438442+03:00"
}
对于 Zulu
时区,我喜欢
{
"id": 1,
"title": "test 123",
"level": 0,
"slug": "test-123",
"icon_code": "123",
"image": null,
"brand": false,
"children": [],
"created_at": "2019-12-08T22:55:08.438442Z"
}
我的测试字段声明是
created_at = serializers.DateTimeField(format='iso-8601', default_timezone=pytz.timezone('Zulu'))
It seems like in your source code you override "current timezone" but don't set it to a default one or etc and that's why it works right after the server restart and stops in a few hours (when a request sets the wrong timezone). So you have 2 ways to fix it - declare your fields as I did above using the
default_timezone
param or find where the current timezone value is changed). The easiest solution for the case, of course, is declaring the field timezone required but in general, of course, it should work without bugs :)
更多信息:
- https://github.com/encode/django-rest-framework/blob/95d4843abeecea96754a147f4f2cca33e620ad09/rest_framework/fields.py#L1199
- https://docs.djangoproject.com/en/3.0/topics/i18n/timezones/#selecting-the-current-time-zone
- https://www.django-rest-framework.org/api-guide/fields/#datetimefield(查看
default_timezone
参数)
我在这里注意到的第一件事是,您正在使用 serializers.DateTimeField
--DRF doc 字段作为 year 表示,其中年总是正整数.
关于时区问题,Django 在从数据库查询时使用 TIME_ZONE
settings。这是你这样做时发生的 postgress 中的最小 SQL 查询,
SELECT a,b,c,<b>DATE_TRUNC('year', "tracking_table"."created_at" AT TIME ZONE 'YOUR_TIME_ZONE_VALUE') AS "year"</b> FROM "tracking_table
但是,幸运的是,您可以在 tzinfo
参数中的TruncYear
class.
例子
<b>import pytz</b>
Tracking.objects.annotate(year=<b>TruncYear('created_at', tzinfo=pytz.UTC)</b>)
要在 json 响应中显示 year,请将序列化程序更改为,
class TimeseriesYearSerializer(serializers.ModelSerializer):
venue = VenueTSSerializer(read_only=True)
<b>year = serializers.IntegerField(read_only=True, source='year.year')</b>
count = serializers.IntegerField(read_only=True)
class Meta:
model = Tracking
fields = ['venue', 'year', 'count']