Timestamp Handling in Bartendie
Timestamp Handling in Bartendie
Section titled “Timestamp Handling in Bartendie”This document outlines the standardized approach to timestamp handling throughout the Bartendie application, as established by ADR-0032: Standardize UTC timestamps with microsecond precision.
Overview
Section titled “Overview”All timestamps in the Bartendie application use UTC timezone with microsecond precision for consistency, accuracy, and proper timezone handling.
Database Schema
Section titled “Database Schema”Ecto Schemas
Section titled “Ecto Schemas”All Ecto schemas use timestamps(type: :utc_datetime_usec) for automatic timestamp fields:
schema "events" do field :name, :string field :description, :string # ... other fields
timestamps(type: :utc_datetime_usec)endManual Timestamp Fields
Section titled “Manual Timestamp Fields”For custom timestamp fields, use :utc_datetime_usec type:
schema "shopping_lists" do field :name, :string field :generated_at, :utc_datetime_usec
timestamps(type: :utc_datetime_usec)endGraphQL Schema
Section titled “GraphQL Schema”All timestamp fields in GraphQL schemas use the :datetime scalar type:
object :event do field :id, :id field :name, :string field :created_at, :datetime field :updated_at, :datetimeendApplication Code
Section titled “Application Code”Creating Timestamps
Section titled “Creating Timestamps”Always use DateTime.utc_now() for creating timestamps:
# Goodtimestamp = DateTime.utc_now()
# For explicit microsecond precisiontimestamp = DateTime.utc_now(:microsecond)
# Bad - don't use NaiveDateTimetimestamp = NaiveDateTime.utc_now()Event Sourcing
Section titled “Event Sourcing”Event sourcing projections should use UTC DateTime:
def handle(%EventCreated{} = event, _metadata) do %Event{ id: event.event_id, name: event.name, inserted_at: DateTime.utc_now(), updated_at: DateTime.utc_now() } |> Repo.insert()endFrontend Integration
Section titled “Frontend Integration”Date Input Handling
Section titled “Date Input Handling”The frontend converts date inputs to the correct format for GraphQL:
// Convert datetime input to date-only format for GraphQLconst dateForGraphQL = new Date(formData.date).toISOString().split('T')[0];
// Example: "2025-06-25T14:22:00.000Z" becomes "2025-06-25"Timestamp Display
Section titled “Timestamp Display”GraphQL returns timestamps in ISO 8601 format with microsecond precision:
{ "createdAt": "2025-06-19T15:30:45.123456Z", "updatedAt": "2025-06-19T15:30:45.123456Z"}Migration Notes
Section titled “Migration Notes”The migration from naive_datetime to utc_datetime_usec was performed in migration 20250619151518_convert_timestamps_to_utc_datetime_usec.exs. This migration:
- Converts all existing timestamp columns to
:utc_datetime_usec - Assumes existing naive timestamps were in UTC (safe assumption for our data)
- Preserves all existing timestamp data
- Provides rollback capability
Configuration
Section titled “Configuration”The application is configured to use UTC timestamps by default:
config :bartendie, ecto_repos: [Bartendie.Repo], generators: [timestamp_type: :utc_datetime_usec]Benefits
Section titled “Benefits”- Consistent API Responses: All GraphQL timestamp fields serialize correctly
- Timezone Handling: Proper support for users in different timezones
- Event Ordering: Microsecond precision enables accurate ordering of high-frequency events
- Integration: External systems can rely on consistent, timezone-aware timestamp format
- Debugging: Clear understanding that all timestamps are UTC eliminates timezone confusion
Best Practices
Section titled “Best Practices”- Always use UTC: Never store local time without timezone information
- Use DateTime.utc_now(): For creating new timestamps in application code
- Microsecond precision: Use
:utc_datetime_usecfor all new timestamp fields - GraphQL consistency: Use
:datetimescalar type for all timestamp fields - Frontend conversion: Convert datetime inputs to appropriate format for GraphQL
Testing
Section titled “Testing”When writing tests, ensure you account for timezone-aware timestamps:
test "creates event with proper timestamps" do before_creation = DateTime.utc_now()
{:ok, event} = Events.create_event(%{name: "Test Event"})
after_creation = DateTime.utc_now()
assert DateTime.compare(event.inserted_at, before_creation) in [:gt, :eq] assert DateTime.compare(event.inserted_at, after_creation) in [:lt, :eq]endTroubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”- GraphQL Serialization Errors: Ensure GraphQL schema uses
:datetimenot:naive_datetime - Type Mismatches: Verify Ecto schemas use
timestamps(type: :utc_datetime_usec) - Frontend Date Format: Ensure date inputs are converted to correct format for GraphQL
Migration Issues
Section titled “Migration Issues”If you encounter issues after the timestamp migration:
- Check that all GraphQL schema types use
:datetime - Verify all Ecto schemas use
:utc_datetime_usec - Ensure application code uses
DateTime.utc_now() - Test GraphQL queries to confirm proper serialization