Skip to main content

Custom Timezone FDG

A reusable Python package for working with a configurable business time zone, including daylight-saving-time (DST) edge cases. It can be used in regular Python programs, background workers and web applications, with optional Django app and middleware integration.

  • Distribution: custom_timezone_fdg
  • Python import: custom_timezone
  • Initial package version: 0.1.0 (as defined in src/custom_timezone/__init__.py)
  • License: MIT

The core uses Python's datetime and zoneinfo; Django is not required to use the core helpers. The distribution also depends on tzdata, which supplies IANA time-zone data on platforms that do not provide it themselves.

Features

  • Configurable business time zone using an explicit override, process configuration, Django setting or environment variable.
  • Parsing and formatting datetimes with IANA time-zone names, including America/Toronto.
  • Explicit handling of nonexistent and ambiguous local times around DST transitions.
  • Thread- and asyncio-task-local temporary time-zone overrides.
  • Optional Django middleware that activates the configured time zone per request and restores the previous one afterward.
  • Django system checks for invalid time-zone settings and disabled timezone awareness.
  • Type information via py.typed and compatibility imports through custom_timezone.timezone_utils.

Requirements and installation

The package declares Python 3.9+. Optional Django support declares Django 4.2 through 6.x, subject to the Python versions supported by each Django release. Test your particular Python/Django combination; the declared ranges do not mean that every combination has been independently verified.

Install the core utilities:

python -m pip install custom_timezone_fdg

Install with Django integration dependencies:

python -m pip install "custom_timezone_fdg[django]"

The distribution name contains _fdg, but imports use custom_timezone:

import custom_timezone as ct

If you're contributing from this repository, install it in editable mode:

python -m pip install -e ".[test,django]"

Quick start (without Django)

import custom_timezone as ct

# Set the business time zone for this Python process.
ct.configure("America/Toronto")

# Interpret a wall-clock time entered without an offset.
appointment = ct.parse_local_datetime("2026-09-26T14:30")
print(appointment)                    # 2026-09-26 14:30:00-04:00
print(ct.format_datetime(appointment))  # 2026-09-26 14:30:00 EDT

# Convert a UTC instant to the configured business time zone.
from datetime import datetime, timezone
stored_utc = datetime(2026, 1, 1, 17, 0, tzinfo=timezone.utc)
print(ct.to_custom_timezone(stored_utc))    # 2026-01-01 12:00:00-05:00
print(ct.format_datetime_local(stored_utc)) # 2026-01-01T12:00

# Use another zone temporarily (also works in async tasks).
with ct.override("Asia/Shanghai"):
    print(ct.now())

Choose the business time zone

The first available value is used:

Priority Configuration Scope
1 with ct.override("Asia/Shanghai") Current thread/asyncio task
2 ct.configure("America/Toronto") Python process
3 Django CUSTOM_TIME_ZONE If Django settings are already loaded
4 CUSTOM_TIME_ZONE environment variable Python process
5 UTC Default fallback

Configure a non-Django application using an environment variable:

export CUSTOM_TIME_ZONE=America/Toronto

Or in Python:

ct.configure("America/Toronto")
ct.configure(None)  # Clear the process override; use other configuration sources.

An invalid time-zone name raises TimezoneConfigurationError, which subclasses ValueError.

Core API

Function Purpose
get_timezone() Return the active zoneinfo.ZoneInfo
configure(tz) Set or clear the process-wide time zone
override(tz) Temporarily use another time zone
parse_local_datetime(text, ...) Parse ISO 8601 datetime text into an aware datetime
localize_datetime(dt, ...) Attach the selected time zone to a naive datetime
to_custom_timezone(dt, ...) Convert a datetime to the configured time zone
format_datetime(dt, ...) Format a datetime in the configured time zone
format_datetime_local(dt, ...) Format for HTML datetime-local input (YYYY-MM-DDTHH:MM)
now() Return the current time in the configured time zone

Most helpers also accept tz="Europe/Paris" to select a zone for a single call.

Parsing rules

parse_local_datetime() accepts ISO-style date and time strings with a T or space separator, optionally including an offset. An explicit offset is retained. None or blank text returns None; invalid input raises ValueError.

A date without a time is intentionally rejected by default:

ct.parse_local_datetime("2026-09-26")  # ValueError
ct.parse_local_datetime("2026-09-26", allow_date_only=True)  # Local midnight

Daylight-saving transitions

For America/Toronto in 2026, March 8 at 02:30 does not exist because clocks move forward. November 1 at 01:30 occurs twice when clocks move back.

from datetime import datetime

missing = datetime(2026, 3, 8, 2, 30)
ct.localize_datetime(missing, nonexistent="raise")          # Default: NonExistentTimeError
ct.localize_datetime(missing, nonexistent="shift_forward") # 03:30 EDT
ct.localize_datetime(missing, nonexistent="shift_backward")# 01:30 EST

repeated = datetime(2026, 11, 1, 1, 30)
ct.localize_datetime(repeated, ambiguous="earliest")  # First occurrence (EDT)
ct.localize_datetime(repeated, ambiguous="latest")    # Second occurrence (EST)
ct.localize_datetime(repeated, ambiguous="raise")     # AmbiguousTimeError

Both exceptions subclass ValueError; the same options are available in parse_local_datetime().

Naive datetimes

With to_custom_timezone(), use the naive= parameter to specify how an unaware datetime should be interpreted: "local" (default), "utc", or "raise".

Optional Django integration

Install with the django extra and configure your new or existing Django project:

# settings.py
import os

INSTALLED_APPS = [
    # Django's standard and project apps...
    "custom_timezone.apps.CustomTimezoneConfig",
]

MIDDLEWARE = [
    # Normal Django middleware...
    "custom_timezone.middleware.CustomTimezoneMiddleware",
]

TIME_ZONE = "UTC"
USE_TZ = True
CUSTOM_TIME_ZONE = os.getenv("CUSTOM_TIME_ZONE", "America/Toronto")

Add these entries to your existing INSTALLED_APPS and MIDDLEWARE lists; do not replace the other middleware that Django requires. The shorter app entry "custom_timezone" is also supported.

  • The middleware activates the selected business time zone for each request, including Django templates, forms and Admin; the previous active zone is restored afterward.
  • Django can keep storing timestamps in UTC when USE_TZ=True.
  • python manage.py check reports custom_timezone.E001 for an invalid configured zone and custom_timezone.W001 if USE_TZ=False.
  • This Django app has no models or migrations.

Verify with:

python manage.py check
python manage.py test

Using in other frameworks

No framework-specific integration is required for regular scripts, FastAPI, Flask or task workers. To select a time zone for a single request or task, use with ct.override("Area/City") and ensure the context spans only the intended operation.

Replacing an existing in-project custom_timezone app

The import paths custom_timezone and custom_timezone.timezone_utils are retained for common usage. However, the standalone package intentionally changes certain behaviours, so test before replacing an existing application-specific implementation.

  1. Remove or rename the old in-project custom_timezone/ module so it cannot shadow the installed distribution.
  2. Install custom_timezone_fdg[django] in your project's environment.
  3. Retain or adapt your existing INSTALLED_APPS, middleware, imports and CUSTOM_TIME_ZONE setting.
  4. Run tests for datetime parsing, API validation and DST transitions before deploying.
Input/behaviour Older in-project app Package behaviour
Nonexistent local time Could silently shift forward Raises NonExistentTimeError by default
Date-only string Could be treated as midnight Rejected unless allow_date_only=True
Invalid non-string input Could raise TypeError or act as empty Raises ValueError
Invalid business time zone Could raise ImproperlyConfigured Raises TimezoneConfigurationError
Middleware cleanup Could reset to Django default Restores previously active time zone

Development, testing and publishing

This repository uses the src/ layout: distributable code lives in src/custom_timezone/, while tests and packaging files are at the repository root. Consumers still import custom_timezone—never src.custom_timezone.

python3.13 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[test,django]"
python -m pytest

The 0.1.0 version is defined in src/custom_timezone/__init__.py through __version__. Update that value before creating a future release. The repository's CHANGELOG.md records release details.

Build and inspect the distributions:

python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
unzip -l dist/*.whl

Before publishing, install the built wheel in a separate clean virtual environment and run its import and (if relevant) Django integration checks. Test on TestPyPI before publishing to PyPI; new uploads cannot overwrite an existing version's distribution filename.

These links assume the repository is published under the shown name; update them if you choose a different repository name.

Contact

Email: huachenlihx@gmail.com

License

This project is licensed under the MIT License. See the LICENSE file for the full license text.

Metadata

Release files for custom-timezone-fdg 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for custom-timezone-fdg 0.1.0
File Size Uploaded Provenance Actions
custom_timezone_fdg-0.1.0.tar.gz 22.0 kB Without provenance Details for custom_timezone_fdg-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for custom-timezone-fdg 0.1.0
File Interpreter ABI Platform Actions
custom_timezone_fdg-0.1.0-py3-none-any.whl Python 3 none any Details for custom_timezone_fdg-0.1.0-py3-none-any.whl

Total release size: 36.9 kB

Release files / custom_timezone_fdg-0.1.0.tar.gz

Download URL custom_timezone_fdg-0.1.0.tar.gz
Size 22.0 kB
Tags Source
SHA-256 checksum
How to use checksums
864175bb668fa2c78b08554812a89c031caa1d50268cf0518e766b4383884bb9
BLAKE2b-256 checksum
How to use checksums
cb6599429a92f5c11460a98fdcb64b5e808c3b24071a1d26e92d7cac58b32f54
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.2

Release files / custom_timezone_fdg-0.1.0-py3-none-any.whl

Download URL custom_timezone_fdg-0.1.0-py3-none-any.whl
Size 14.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7500aca85089cab7415b3467ad401feceabf4e4018186a2763f2152e80a13fe3
BLAKE2b-256 checksum
How to use checksums
1f3eb66a5d4def1c41fcac0291d74ff7907e9c8962573dc73259d45a6395ada0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.2

Security for custom-timezone-fdg 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page