How to handle datetimes from different timezones

2019-02-20 01:53发布

I have a django application and it stores in the database (postgres) the datetimes in UTC, it has users worldwide.

But in the application logic, I have some validations according to local time ranges, i.e. the user is on Guayaquil and some thing happens all sunday; I'm having issues to perform it and doing some debugging I found that in UTC is monday at 2AM at but the user (America/Guayaquil) UTC-5 is still on sunday 21:00. So it fails for the user because the DAY changed.

I am thinking of storing the user timezone in the user table but I don't know how to set it and how to prevent the previous scenario.

As a side question, how can I convert a UTC datetime stored in the database to the user's timezone for (example) email notification. (I'm not going to send him an email with datetime and timezone for him to do the math in his mind).

3条回答
甜甜的少女心
2楼-- · 2019-02-20 01:56

That is good you are storing everything in UTC. Now you don't need to store the user timezone in database what you can do is that on login page you can calculate the user offset from UTC and store in session vi ajax. Here is the simple script you can include in your login page template:

<script>
    $(document).ready(function(){
        var visitortime = new Date();
        var visitortimezone = - visitortime.getTimezoneOffset()/60;

        $.ajax({
            type: "POST",
            url: "{% url 'update_timezone' %}",
            data: {'time_zone': visitortimezone, 'csrfmiddlewaretoken': '{{csrf_token}}'},
            dataType: "text"
        });
    });
</script>

Add this url in your root urls.py:

url(r'^update_timezone/$',
    'MySite.views.update_timezone',
    name='update_timezone'),

Add this view:

def update_timezone(request):
    time_zone = request.POST.get('time_zone', 0)
    if time_zone:
        request.session['user_timezone_offset'] = float(time_zone)*60*60
    return HttpResponse(
        simplejson.dumps({}), mimetype='application/javascript')

Now you have user timezone offset in session. Now comes the part for calculation. You need these two simple helper functions:

from datetime import timedelta

def add_user_offset(dt, offset):
    if offset:
        dt = dt + timedelta(seconds=offset)
    return dt


def subtract_user_offset(dt, offset):
    if offset:
        dt = dt - timedelta(seconds=offset)
    return dt

Ok if you want to show the user time according to the his/her timezone you would do:

created = add_user_offset(obj.created, request.session.get('user_timezone_offset'))

If you have datetime or date coming from the form and you want to validate it e.g. the date chosen should not be less than current date you would do:

from django.utils import timezone

d = request.POST['date']
d_normalize = subtract_user_offset(d, request.session.get('user_timezone_offset'))
if d_normalize < timezone.now():
    raise Exception, 'Date must be in future'

The benefit of this approach is that you don't need to ask users to specify their timezones. We are doing everything automatically on our side.

查看更多
再贱就再见
3楼-- · 2019-02-20 01:56

Default django is take America/Chicago time zone. Best way you need to store the User time zone in database and whenever user login take the time zone from DB and change the configuration via code.

import datetime
from django.utils.timezone import utc
now = datetime.datetime.utcnow().replace(tzinfo=utc)

https://docs.djangoproject.com/en/dev/ref/settings/#time-zone

查看更多
三岁会撩人
4楼-- · 2019-02-20 02:06

The global strategy in django is to use UTC in persistence layer and use local time when interacting with the end user (you can have a look here. Consequently in your use case I would persist time in UTC in the db and use the current time zone for processing the calculation you need.

The timezone selection function are here . With get_current_timezone() you get the tzinfo object.

The current timezone is the one that is used for the rendering of the web page. It might not be the one you want, whether the user for instance wants to use a UTC-1 timezone but is currently seeing the web page in UTC+1. In this situation, it is better for you to keep the timezone in the session in a similar approach as explained by Aamir or to persist it.

The approach for persistence in model merely to use a CharField and put the timezone string you can get using the pytz package.

查看更多
登录 后发表回答