Overzicht
Datums en tijdstippen zijn gevoelig voor bugs door kleine verschillen. Dit artikel schetst snel de concepten van naive/aware, ISO8601, timestamp (UNIX epoch) bij het werken met tijd in Django en Python, en biedt veilige gebruiksmethoden met voorbeelden.
De twee gezichten van datetime: naive vs aware
datetime objecten worden onderverdeeld op basis van de aanwezigheid van tijdzone-informatie.
-
naive: object zonder tijdzone-informatie (
tzinfo=None) -
aware: object met tijdzone-informatie (
tzinfo=...)
De regels voor operaties zijn eenvoudig. Alleen binnen hetzelfde type kunnen optellingen en aftrekkingen worden uitgevoerd.
-
naive-naive✅ (mogelijk) -
aware-aware(zelfde tijdzone) ✅ (mogelijk) -
naive-aware❌ (TypeError optredend)
from datetime import datetime, timezone
# naive: geen tijdzone-informatie
naive = datetime(2025, 11, 14, 9, 25, 1) # tzinfo=None
# aware: tijdzone-informatie (in dit geval UTC) expliciet vermeld
aware = datetime(2025, 11, 14, 9, 25, 1, tzinfo=timezone.utc)
_ = aware - datetime.now(timezone.utc) # OK (aware - aware)
# _ = aware - datetime.now() # TypeError (aware - naive)
Django's timezone en USE_TZ
De USE_TZ instelling in settings.py van Django is cruciaal.
-
USE_TZ=True(aanbevolen):django.utils.timezone.now()retourneert een aware object gebaseerd op UTC. -
USE_TZ=False: retourneert een naive object gebaseerd op lokale servertijd.
Voor de meeste webapplicaties is het het veiligst om USE_TZ=True in te stellen en alle interne logica en gegevensopslag in UTC te houden. Enkel wanneer er aan de gebruiker gepresenteerd wordt, wordt het omgezet naar de lokale tijd.
from django.utils import timezone
# Met USE_TZ=True, retourneert now() altijd een aware UTC object
now = timezone.now()
ISO8601 en isoformat()
De methode datetime.isoformat() genereert een string volgens de ISO8601 standaard.
-
Een UTC aware object bevat meestal een offset zoals
2025-11-14T09:25:01+00:00. -
Zis een andere notatie die UTC (+00:00) betekent. (bijv....T09:25:01Z)
Belangrijke valstrik: De datetime.fromisoformat() van de Python standaardbibliotheek kan het Z achtervoegsel niet direct parseren. Als je met Z notaties te maken hebt, is het veiliger om django.utils.dateparse.parse_datetime() te gebruiken.
from datetime import datetime, timezone
from django.utils.dateparse import parse_datetime
dt = datetime(2025, 11, 14, 9, 25, 1, tzinfo=timezone.utc)
s = dt.isoformat() # '2025-11-14T09:25:01+00:00'
_aware = datetime.fromisoformat(s) # OK
_aware2 = parse_datetime("2025-11-14T09:25:01Z") # OK (Django util)
Timestamp (UNIX epoch) goed begrijpen
Timestamp is de waarde die aangeeft hoeveel seconden er zijn verstreken sinds 1970-01-01 00:00:00 UTC (UNIX epoch).
-
Geheel gedeelte: het aantal "seconden" sinds het referentiepunt
-
Decimaal gedeelte: precisie onder het niveau van seconden (microseconden)
De time.time() van Python retourneert de huidige UTC epoch seconden. De .timestamp() methode van een datetime object gebruikt ook dezelfde UTC epoch basis.
import time
from datetime import datetime, timezone
# huidige tijd in epoch (deze waarden zijn feitelijk hetzelfde)
t1 = time.time()
t2 = datetime.now(timezone.utc).timestamp()
# specifieke tijd ↔ epoch wederzijds omzetten
exp_ts = 1731576301.25 # decimaal gedeelte 0.25 seconde (250ms)
exp_dt = datetime.fromtimestamp(exp_ts, tz=timezone.utc) # aware UTC
back_ts = exp_dt.timestamp() # 1731576301.25
Timestamp is nuttig wanneer het nodig is om gegevens tussen servers uit te wisselen of om vervaltijden te berekenen, omdat het numerieke berekeningen vermijdt.
Veilige conversie-/rekenpatronen (voorbeeld)
1. String (ISO8601) → datetime
Gebruik parse_datetime om Z achtervoegsels of verschillende offsets veilig te verwerken.
from django.utils.dateparse import parse_datetime
from django.utils import timezone
s = "2025-11-14T09:25:01Z" # of ...+00:00
dt = parse_datetime(s)
if dt is None:
raise ValueError("Ongeldige datetime string")
# Als de geparseerde tijd naive is (als er geen offset-informatie was)
if timezone.is_naive(dt):
# Specificeer expliciet de tijdzone volgens de bedrijfsregels (bijv. als UTC beschouwen)
from zoneinfo import ZoneInfo
dt = timezone.make_aware(dt, ZoneInfo("UTC"))
2. datetime (aware) ↔ timestamp
Het is duidelijker om altijd te converteren op basis van timezone.utc.
from datetime import datetime, timezone
dt = datetime(2025, 11, 14, 9, 25, 1, tzinfo=timezone.utc)
ts = dt.timestamp() # float
dt2 = datetime.fromtimestamp(ts, tz=timezone.utc) # wederzijdse conversie OK
3. Berekenen van de resterende tijd (seconden) tot verval
Trek twee aware objecten af om een timedelta te krijgen en gebruik .total_seconds().
from datetime import datetime, timezone
exp = datetime(2025, 11, 14, 9, 25, 1, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
remaining = (exp - now).total_seconds() # float seconden (negatief mogelijk)
Hoofdpunt: Alle bewerkingen moeten worden uitgevoerd met aware objecten (bij voorkeur UTC) voor maximale veiligheid.
Let op: het tz|tzinfo argument voor datetime() moet timezone.utc zijn, en niet django.utils.timezone, maar datetime.timezone.
Veelvoorkomende valstrikken en snelle uithoeken
-
aware-naiveoperaties: Dit veroorzaakt eenTypeError.- → Zorg ervoor dat beide
aware(bij voorkeur UTC) zijn of beidenaive, maar gebruik ze bewust.
- → Zorg ervoor dat beide
-
fromisoformat()en het "Z" achtervoegsel:- → De standaardbibliotheek ondersteunt
Zniet. Gebruik Django'sparse_datetime(), of behandel het metreplace("Z", "+00:00").
- → De standaardbibliotheek ondersteunt
-
Afhankelijkheid van lokale tijd (
datetime.now()):- → Dit kan variëren afhankelijk van de serveruitvoeringsomgeving (lokaal, ontwikkelingsserver, productie), wat fouten kan veroorzaken. Interne logica moet altijd op basis van UTC (
timezone.now()) worden geschreven.
- → Dit kan variëren afhankelijk van de serveruitvoeringsomgeving (lokaal, ontwikkelingsserver, productie), wat fouten kan veroorzaken. Interne logica moet altijd op basis van UTC (
Conclusie
-
De eerste regel van tijdsberekeningen is niet te mixen tussen
naiveenaware. Streef naar eenheid met aware (UTC). -
Gebruik ISO8601-indeling voor het omgaan met strings, maar overweeg eerst
parse_datetime()voor parsing dieZkan verwerken. -
Gebruik timestamps voor numerieke berekeningen zoals het berekenen van vervaltijden.
댓글이 없습니다.