Skip to content

Datetime Handling ​

QDash stores native datetime objects in MongoDB, returns ISO8601 UTC strings via the API, and converts them to the configured UI timezone for display in the frontend. The default UI timezone is Asia/Tokyo.

Data Flow ​

Backend · write MongoDB Backend · read API JSON UI Timestamp now() aware datetime, UTC BSON Date UTC, tzinfo dropped ensure_timezone() naive → aware UTC "…T01:30:45Z" ISO 8601, UTC 2025-12-21 10:30:45 formatDateTime() · JST insert load serialize format Elapsed time timedelta task run duration float seconds parse_elapsed_time() float → timedelta "1:23:45" format_elapsed_time() 1:23:45 shown as-is store load serialize render
Timestamps stay in UTC until the UI formats them; elapsed time travels as seconds, then as an H:MM:SS string.

The diagram above shows the complete datetime and elapsed time handling flow across Backend, API, and Frontend layers.

Backend (Python) ​

Calendar Timezone Configuration ​

The server calendar timezone is configured in src/qdash/config.py and is used only for server-generated calendar labels such as recorded_date and execution ID date prefixes:

python
timezone: str = "Asia/Tokyo"

Utility Module ​

All datetime operations should use src/qdash/common/utils/datetime.py:

FunctionDescription
now()Get current UTC datetime for DB/API storage
local_now()Get current datetime in configured calendar timezone for server labels such as YYYYMMDD
now_iso()Get current UTC datetime as ISO8601 string
ensure_timezone(value)Ensure datetime is timezone-aware (handles naive MongoDB datetimes)
to_datetime(value)Convert string/datetime/pendulum to standard datetime
format_iso(dt)Format datetime as ISO8601 string
parse_elapsed_time(elapsed_str)Parse elapsed time string to timedelta
format_elapsed_time(elapsed)Format timedelta as "H:MM:SS" string

Pydantic Serialization ​

python
# In API schema models
@field_validator("elapsed_time", mode="before")
def _parse_elapsed_time(cls, v: Any) -> timedelta | None:
    return parse_elapsed_time(v)

@field_serializer("elapsed_time")
def _serialize_elapsed_time(cls, v: timedelta | None) -> str | None:
    return format_elapsed_time(v) if v else None

MongoDB Considerations ​

MongoDB returns naive datetimes (without timezone info), assumed to be UTC. Use ensure_timezone() to convert them:

python
# In dbmodel validators
@field_validator("start_at", "end_at", mode="before")
@classmethod
def _ensure_timezone(cls, v: Any) -> datetime | None:
    if v is None:
        return None
    if isinstance(v, datetime):
        return ensure_timezone(v)
    return v

Frontend (TypeScript) ​

Utility Module ​

All datetime formatting should use ui/src/lib/utils/datetime.ts:

FunctionFormatExample Output
formatDateTime(utcString)yyyy-MM-dd HH:mm:ss2025-12-21 10:30:45
formatDate(utcString)yyyy-MM-dd2025-12-21
formatTime(utcString)HH:mm:ss10:30:45
formatDateTimeCompact(utcString)MM/dd HH:mm12/21 10:30

By default, frontend utilities use NEXT_PUBLIC_TIMEZONE, falling back to Asia/Tokyo. Pass an explicit timezone only for tests or a UI that intentionally needs a different fixed timezone.

Usage Example ​

typescript
import { formatDateTime, formatDateTimeCompact } from "@/lib/utils/datetime";

// Full datetime
<span>{formatDateTime(execution.start_at)}</span>
// Output: "2025-12-21 10:30:45"

// Compact format for lists
<span>{formatDateTimeCompact(task.end_at)}</span>
// Output: "12/21 10:30"

// Null handling (returns "-" for null/undefined)
<span>{formatDateTime(task.start_at)}</span>
// Output: "-" if null/undefined

Dependencies ​

  • date-fns: Date manipulation utilities
  • date-fns-tz: Timezone conversion with formatInTimeZone()

Conventions ​

  • Always use utility functions — never use new Date().toLocaleString() directly
  • Use dateToDateInput(), dateToDateTimeLocal(), and toDateTimeLocal() before putting dates into native date/datetime inputs
  • Use toIsoSeconds() when submitting datetime-local values so the configured UI timezone offset is explicit
  • Handle null/undefined — API responses may have null datetime fields
  • Use formatDateTimeCompact() for space-constrained UIs
  • Store elapsed_time as seconds (float) before MongoDB storage

Implementation Files ​

  • src/qdash/common/utils/datetime.py — backend utilities
  • ui/src/lib/utils/datetime.ts — frontend utilities
  • src/qdash/api/schemas/*.py — API schemas
  • src/qdash/dbmodel/*.py — DB models

Released under the Apache 2.0 License.