为什么Django设置时区为TIME_ZONE = 'Asia/Shanghai' USE_TZ = True后,存入mysql中的时间只能是UTC时间

这个问题的解释还得看Django官方文档。其实是我理解错了,因为Mysql存储的时间不能灵活设置时区,不像datetime对象有一项参数专门指定时区,所以为了统一全球的时间,必须使用国际标准时间UTC,否则就会乱套。所有时间在存如数据库前,必须转换成UTC时间。比如北京时间8点,存入mysql变成0点(UTC)。

一般不跨时区的应用,可以不使用时区,即在settings.py设置

USE_TZ = False

这样存储的时间就是无时区的时间,英文叫naive datetime (datetime with no timezoneinformation)

一开始不理解,以为设了TIME_ZONE = 'Asia/Shanghai',数据库中存储的所有时间就会自动转为北京时间,或者数据库存储UTC时间在读取时自动转为北京时间(只有前台会自动转换,后台需要手动转换)。

看了官方文档,https://docs.djangoproject.com/en/2.1/topics/i18n/timezones/

第一句话就醍醐灌顶:

Time zones

Overview

When support for time zones is enabled, Django stores datetime information inUTC in the database, uses time-zone-aware datetime objects internally, andtranslates them to the end user’s time zone in templates and forms.

翻译一下:当使用时区时,Django存储在数据库中的所有日期时间信息都以UTC时区为准,在后台使用有时区的datetime,前台用户使用时,在网页上翻译成用户所在的时区。

看完后,一切疑虑都消除了,不是Django设置有误,也不是Mysql设置有误。


使用时区相对来说比较麻烦,设置时区后,怎么让时区起作用呢?

Usage

  1. I have a string "2012-02-21 10:28:45" and I know it’s in the"Europe/Helsinki" time zone. How do I turn that into an awaredatetime?

    This is exactly what pytz is for.

    >>> from django.utils.dateparse import parse_datetime
    >>> naive = parse_datetime("2012-02-21 10:28:45")
    >>> import pytz
    >>> pytz.timezone("Europe/Helsinki").localize(naive, is_dst=None)
    datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=<DstTzInfo 'Europe/Helsinki' EET+2:00:00 STD>)
    

    Note that localize is a pytz extension to the tzinfoAPI. Also, you may want to catch pytz.InvalidTimeError. Thedocumentation of pytz contains more examples. You should review itbefore attempting to manipulate aware datetimes.

  2. How can I obtain the local time in the current time zone?

    Well, the first question is, do you really need to?

    You should only use local time when you’re interacting with humans, and thetemplate layer provides filters and tagsto convert datetimes to the time zone of your choice.

    Furthermore, Python knows how to compare aware datetimes, taking intoaccount UTC offsets when necessary. It’s much easier (and possibly faster)to write all your model and view code in UTC. So, in most circumstances,the datetime in UTC returned by django.utils.timezone.now() will besufficient.

    For the sake of completeness, though, if you really want the local timein the current time zone, here’s how you can obtain it:

    >>> from django.utils import timezone
    >>> timezone.localtime(timezone.now())
    datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)
    

    In this example, the current time zone is "Europe/Paris".

我自己试了试从mysql取数后,在后台如何转换成当前时区,虽然官网说这样做没有必要,但是我想了解清楚:

sh = pytz.timezone('Asia/Shanghai')
Mro.objects.first().TimeStamp.astimezone(sh)
datetime.datetime(2018, 4, 28, 8, 54, 4, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
Mro.objects.first().TimeStamp
datetime.datetime(2018, 4, 28, 0, 54, 4, tzinfo=<UTC>)

后台向数据库输入日期时间时,日期时间应该带有时区信息,如果没有,输入数据库时会有警告,RuntimeWarning: DateTimeField received a naivedatetime (YYYY-MM-DD HH:MM:SS) while time zone support is active

可以使用pytz的localize将无时区信息的时间转为指定时区的datetime对象

具体的说明在pytz的官方说明中:

This library only supports two ways of building a localized time. Thefirst is to use the localize() method provided by the pytz library.This is used to localize a naive datetime (datetime with no timezoneinformation):

>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500

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

>>> ams_dt = loc_dt.astimezone(amsterdam)
>>> ams_dt.strftime(fmt)
'2002-10-27 12:00:00 CET+0100'

创建一个本地时间的两种方式:1、localize()将无时区的日期转为有时区的,2、astimezone()将有时区的日期时间从一个时区转为另一个时区。

This library also allows you to do date arithmetic using localtimes, although it is more complicated than working in UTC as youneed to use the normalize() method to handle daylight saving timeand other timezone transitions. In this example, loc_dt is setto the instant when daylight saving time ends in the US/Easterntimezone.

>>> before = loc_dt - timedelta(minutes=10)
>>> before.strftime(fmt)
'2002-10-27 00:50:00 EST-0500'
>>> eastern.normalize(before).strftime(fmt)
'2002-10-27 01:50:00 EDT-0400'
>>> after = eastern.normalize(before + timedelta(minutes=20))
>>> after.strftime(fmt)
'2002-10-27 01:10:00 EST-0500'

DST是Daylight Saving Time的缩写,即夏时制,夏时制的转换使用normalize()

夏时制在10月27日凌晨1点结束,所以0点50分时,使用夏时制转换函数转换后时间变为1点50,但1点10分再转换夏时制,结果还是1点10分。


实际上,按照Django的设计,不需要手动转换时区,而是再模板中加入语句,显示时区时自动转换。

Selecting the current time zone

The current time zone is the equivalent of the current locale for translations. However, there’s no equivalent of theAccept-Language HTTP header that Django could use to determine the user’stime zone automatically. Instead, Django provides time zone selectionfunctions. Use them to build the time zoneselection logic that makes sense for you.

Most websites that care about time zones just ask users in which time zone theylive and store this information in the user’s profile. For anonymous users,they use the time zone of their primary audience or UTC. pytz provideshelpers, like a list of time zones per country, that you can use to pre-selectthe most likely choices.

Here’s an example that stores the current timezone in the session. (It skipserror handling entirely for the sake of simplicity.)

Add the following middleware to MIDDLEWARE:

import pytz

from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin

class TimezoneMiddleware(MiddlewareMixin):
    def process_request(self, request):
        tzname = request.session.get('django_timezone')
        if tzname:
            timezone.activate(pytz.timezone(tzname))
        else:
            timezone.deactivate()

Create a view that can set the current timezone:

from django.shortcuts import redirect, render

def set_timezone(request):
    if request.method == 'POST':
        request.session['django_timezone'] = request.POST['timezone']
        return redirect('/')
    else:
        return render(request, 'template.html', {'timezones': pytz.common_timezones})

Include a form in template.html that will POST to this view:

{% load tz %}
{% get_current_timezone as TIME_ZONE %}
<form action="{% url 'set_timezone' %}" method="POST">
    {% csrf_token %}
    <label for="timezone">Time zone:</label>
    <select name="timezone">
        {% for tz in timezones %}
        <option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% endif %}>{{ tz }}</option>
        {% endfor %}
    </select>
    <input type="submit" value="Set">
</form>

猜你喜欢

转载自blog.csdn.net/qq_27361945/article/details/80580795