Skip to main content

A lightweight Django template tags and middleware library for automatic client-site timezone conversion

Project description

django-usertz-localize

A lightweight Django template tags and middleware library for automatic client-side timezone conversion. This library automatically detects the user's timezone and converts all datetime values to their local timezone.

Features

  • Automatic client-side timezone detection
  • Seamless datetime conversion in templates
  • Flexible datetime formatting options (date, time, year, month, custom formats)
  • Cookie-based timezone persistence
  • Zero JavaScript framework dependencies
  • Lightweight and production-ready

Installation

Using pip

pip install django-usertz-localize

Manual Installation

  1. Clone the repository
  2. Copy the django-usertz-localize directory to your Django project

Quick Start

1. Add to Django Settings

Add django-usertz-localize to your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    # ... other apps
    'django_usertz_localize',
]

2. Add Middleware

Add the timezone middleware to your MIDDLEWARE list in settings.py:

MIDDLEWARE = [
    # ... other middleware
    'django_usertz_localize.middleware.TimezoneMiddleware',
]

3. Load Template Tags

In your Django templates, load the template tags at the top(Never load before/above {% extend 'index.html' %}):

{% load usertz %}

4. Initialize Timezone Detection

Include the timezone loader script in your base template (typically in <head> or before closing </body>):

{% usertz %}
{{ usertz_lib.get }}

This injects a JavaScript snippet that:

  • Detects the user's timezone using the browser's Intl API
  • Saves it to a cookie named django_user_tz
  • Reloads the page if the cookie wasn't set properly

Usage

Automatic Timezone Conversion

Once configured, all datetime values passed through the |tz filter will be converted to the user's local timezone.

Full Datetime Format (Default)

{{ item.created_at|tz }}

Output: 2026-06-11 14:30:45+02:00 (in user's timezone)

Date Only

{{ item.created_at|tz:"date" }}

Output: 2026-06-11

Time Only

{{ item.created_at|tz:"time" }}

Output: 14:30:45

Year Only

{{ item.created_at|tz:"year" }}

Output: 2026

Month Name

{{ item.created_at|tz:"month" }}

Output: June

Custom Format

Use Python's strftime format codes with the custom: prefix:

{{ item.created_at|tz:"custom:%B %d, %Y" }}

Output: June 11, 2026

{{ item.created_at|tz:"custom:%A, %b %d at %I:%M %p" }}

Output: Wednesday, Jun 11 at 02:30 PM

Common Format Examples

<!-- Long date format -->
{{ item.created_at|tz:"custom:%B %d, %Y" }}

<!-- Short date format -->
{{ item.created_at|tz:"custom:%m/%d/%Y" }}

<!-- 12-hour time format -->
{{ item.created_at|tz:"custom:%I:%M %p" }}

<!-- 24-hour time format -->
{{ item.created_at|tz:"custom:%H:%M:%S" }}

<!-- ISO format -->
{{ item.created_at|tz:"custom:%Y-%m-%d %H:%M:%S" }}

<!-- Combined readable format -->
{{ item.created_at|tz:"custom:%A, %B %d, %Y at %I:%M %p" }}

How It Works

1. Browser Timezone Detection

When a page loads, the JavaScript snippet uses the browser's built-in Intl.DateTimeFormat API to detect the user's timezone and stores it in a cookie:

const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
document.cookie = "django_user_tz=" + tz + "; path=/; max-age=31536000; SameSite=Lax";

Supported Timezone Format: IANA timezone identifiers (e.g., America/New_York, Europe/London, Asia/Tokyo)

2. Middleware Processing

The TimezoneMiddleware reads the timezone cookie from each request and activates it for the entire request cycle:

user_timezone = request.COOKIES.get('django_user_tz')
if user_timezone:
    timezone.activate(zoneinfo.ZoneInfo(user_timezone))

3. Template Filter

The |tz filter converts datetime objects to the active timezone and formats them according to the specified format:

localized = timezone.localtime(value)  # Convert to user's timezone
return localized.strftime(format_code)  # Format as requested

Real-World Example

View

from django.shortcuts import render
from .models import Post

def blog_list(request):
    posts = Post.objects.all()
    return render(request, 'blog/list.html', {'posts': posts})

Template

{% load usertz %}

<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
    {% usertz %}
</head>
<body>
    <h1>Blog Posts</h1>
    <table>
        <thead>
            <tr>
                <th>Title</th>
                <th>Created</th>
                <th>Updated</th>
            </tr>
        </thead>
        <tbody>
            {% for post in posts %}
            <tr>
                <td>{{ post.title }}</td>
                <td>{{ post.created_at|tz:"custom:%B %d, %Y at %I:%M %p" }}</td>
                <td>{{ post.updated_at|tz:"date" }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

User Experience

  • User in New York sees: June 11, 2026 at 10:30 AM (EDT)
  • User in London sees: June 11, 2026 at 03:30 PM (BST)
  • User in Tokyo sees: June 12, 2026 at 12:30 AM (JST)

All displayed times are automatically localized to each user's timezone.

Timezone Format Codes

Code Meaning Example
%Y Year with century 2026
%m Month (01-12) 06
%d Day (01-31) 11
%B Full month name June
%b Abbreviated month Jun
%A Full weekday name Wednesday
%a Abbreviated weekday Wed
%H Hour (00-23) 14
%I Hour (01-12) 02
%M Minute (00-59) 30
%S Second (00-59) 45
%p AM/PM PM
%Z Timezone name EDT

Troubleshooting

Timezone Not Detecting

  1. Ensure {% usertz %} is included in your template
  2. Check that TimezoneMiddleware is added to MIDDLEWARE in settings
  3. Verify cookies are enabled in the browser
  4. Open browser DevTools → Application → Cookies and look for django_user_tz

All Users Seeing Same Timezone

  • Check that the middleware is properly added to MIDDLEWARE
  • Verify the cookie name matches: django_user_tz (case-sensitive)
  • Clear browser cookies and reload

Naive Datetime Warnings

If you see warnings about naive datetimes, ensure your model datetimes are timezone-aware:

from django.db import models
from django.utils import timezone

class Post(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)  # Timezone-aware
    updated_at = models.DateTimeField(auto_now=True)      # Timezone-aware

Configuration

Custom Cookie Name

If you need to change the cookie name, modify the JavaScript in your template or create a custom template tag:

# settings.py
USER_TIMEZONE_COOKIE_NAME = 'my_custom_tz'

User Timezone Model Fallback

Set USER_TIMEZONE_USER_MODEL = True to enable storing a user's timezone history and using their last known timezone as a fallback when the cookie is missing.

# settings.py
USER_TIMEZONE_USER_MODEL = True

When enabled, the middleware will:

  • read the timezone cookie first
  • if the cookie is missing, look up the user's latest timezone from the stored history
  • record the user timezone on each authenticated request

When USER_TIMEZONE_USER_MODEL = False, no timezone history model will be loaded or used and the middleware will rely only on the cookie plus the default TIME_ZONE fallback.

Fallback Timezone

Set a default timezone in settings.py for users without a detected timezone or stored history:

TIME_ZONE = 'UTC'  # Fallback timezone
USE_TZ = True      # Enable timezone support

Browser Support

  • Chrome/Edge 24+
  • Firefox 20+
  • Safari 10+
  • Opera 11+
  • IE 11+ (partial support)

All modern browsers support the Intl.DateTimeFormat API used for timezone detection.

Performance

  • Zero database queries for timezone detection
  • Single HTTP cookie per user
  • No external API calls
  • Lightweight middleware with minimal overhead

License

MIT License - See LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

Support

For issues, questions, or suggestions, please open an issue on the repository.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_usertz_localize-0.1.1.tar.gz (11.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_usertz_localize-0.1.1-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file django_usertz_localize-0.1.1.tar.gz.

File metadata

  • Download URL: django_usertz_localize-0.1.1.tar.gz
  • Upload date:
  • Size: 11.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for django_usertz_localize-0.1.1.tar.gz
Algorithm Hash digest
SHA256 fa6d293077e8acea157efd63689181b59e00583665f7d7878a24a1c0b82d11ca
MD5 9e3b3f0816a461ccd061334d946fdd2f
BLAKE2b-256 8bd50850e2d3479d5a51f0f8463668432d52549f38f5cc4fc4ad18ef46fcab95

See more details on using hashes here.

File details

Details for the file django_usertz_localize-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_usertz_localize-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fabd8a8071ba87f0d1512b20101527e79f597b14fe72b8bc48d2ecc4e1f6884b
MD5 001d19abbdaf61e31dfe605c6c657988
BLAKE2b-256 c09146c307cedfcde963827e9cd36084ed74dccd83678b261d4bf1584bad3e8c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page