Xử lý ngày tháng trong Django khi làm việc với nhiều múi giờ

Mình đang thực hiện 1 project Django có nhiều người dùng ở các nước khác nhau. Đúng là đụng chạm đến múi giờ là thấy ngay vấn đề lưu ngày tháng và các tính toán liên quan đến ngày tháng đang rất lung tung. Vì thế để xử lý mình thực hiện theo nguyên tắc “Chuyển hết ngày tháng về múi giờ gốc UTC, thực hiện tính toán trên đó, và hiện thị ra cho người dùng theo múi giờ của họ”.

Nếu bạn chỉ xử dụng 1 múi giờ thì hãy bỏ qua bài viết này.

1. Naive và Aware

Đây là 2 loại ngày tháng của class datetime. Như chúng ta đã biết, class datetime khởi tạo các đối tượng datetime chứa các thông tin bao gồm năm, tháng, ngày, giờ, phút, giây… và múi giờ (thuộc tính tzinfo). Hiểu đơn giản thế này, nếu đối tượng datetime chứa thông tin về múi giờ thì đó là aware, còn không chứa thông tin về múi giờ UTC(+00:00) thì đó là naive.

Vậy thì 2 ông này liên quan gì đến django?

Nếu trong file setting.py, bạn cài đặt hỗ trợ múi giờ gốc USE_TZ = False thì Django sử dụng đối tượng datetime là naive và sẽ nhận ngày tháng của bạn là naive khi lưu vào database, tức là lưu tại múi giờ UTC(+00:00). Ngược lại nếu USE_TZ = True thì nó sẽ sử dụng đối tượng datetime là aware. Lúc lưu vào database thì nó lại nhận là naive nên nếu không cẩn thận bạn sẽ bị lỗi dữ liệu.

Quảng cáo

Ủng hộ website

2. Cài đặt Django

Mình sẽ cài đặt 2 thông số sau để ngày tháng khi xử lý đều trả về UTC

TIME_ZONE = 'UTC'
USE_TZ = True

Sau đó mình sẽ xử lý đến ngày tháng hiển thị với người dùng. Bởi vì thẻ header Accept-Language HTTP không chứa thông tin múi giờ của người truy cập nên mỗi người dùng truy cập hệ thống (người dùng xác thực, đã đăng ký với hệ thống) sẽ được cài đặt 1 múi giờ, còn với người dùng thông thường thì đặt 1 múi giờ mặc định hoặc sử dụng múi giờ UTC. Giống như một số trang đa ngôn ngữ, thường hay dẫn người dùng đến một trang để lựa chọn vùng, sau đó lưu vào thông tin người dùng.

import zoneinfo

from django.utils import timezone

class TimezoneMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        tzname = request.session.get("django_timezone")
        if tzname:
            timezone.activate(zoneinfo.ZoneInfo(tzname))
        else:
            timezone.deactivate()
        return self.get_response(request)

Danh sách timezone mà django sử dụng https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Ta sẽ tạo ra 1 MIDDLEWARE xử lý thông tin múi giờ mặc định với người dùng bằng activate()

3. Các cách xử lý múi giờ khác

Sử dụng các template filter

{% load tz %}

{{ value|localtime }}
{{ value|utc }}
{{ value|timezone:"Europe/Paris" }}

Sử dụng các thuộc tính để chuyển đổi giữa naive và aware

from django.utils import timezone as tz
tz.is_aware(value)
tz.is_naive(value)
tz.make_aware(value, timezone=None)
tz.make_naive(value, timezone=None)

Bài viết sử dụng Django 5.0 .

Chúc các bạn thành công

Add Comment