JSON REST API

Appendices: Date and Time Handling

Overview

This document explains how the JSON REST API handles date and time values throughout the system, from database storage to API responses. Understanding this behavior is crucial for developers working with temporal data.

Supported Date/Time Types

The API supports three temporal data types in schemas:

1. date

2. dateTime

3. time

Schema Definition

Define date/time fields in your schema like this:

const articleSchema = {
  publishedDate: { type: 'date', required: true },
  createdAt: { type: 'dateTime', defaultTo: 'now()' },
  updatedAt: { type: 'dateTime', defaultTo: 'now()' },
  dailyPostTime: { type: 'time', nullable: true }
};

Input Validation

On Write Operations (POST/PUT/PATCH)

The API validates and normalizes date/time inputs:

// POST /api/articles
{
  "data": {
    "type": "articles",
    "attributes": {
      "publishedDate": "2024-01-15",           // Valid date
      "createdAt": "2024-01-15T14:30:00Z",     // Valid dateTime (ISO 8601)
      "dailyPostTime": "14:30:00"              // Valid time
    }
  }
}

Accepted Input Formats:

Storage Format: All date/time values are converted to JavaScript Date objects before storage, allowing the database driver to handle the appropriate formatting for each database system.

Output Normalization

Database to API Response

All date/time values are normalized when returned from the API:

// GET /api/articles/123
{
  "data": {
    "type": "articles",
    "id": "123",
    "attributes": {
      "publishedDate": "2024-01-15",                    // date type
      "createdAt": "2024-01-15T14:30:00.000Z",         // dateTime type
      "updatedAt": "2024-01-15T16:45:30.000Z",         // dateTime type
      "dailyPostTime": "14:30:00"                      // time type
    }
  }
}

Key Normalization Behaviors:

  1. Boolean Normalization: Database values of 1/0 are converted to true/false
  2. Date Objects: All date/time values are returned as JavaScript Date objects internally, then serialized to ISO 8601 strings in JSON responses
  3. UTC Assumption: MySQL DATETIME values (which lack timezone info) are assumed to be UTC

Database-Specific Handling

MySQL

PostgreSQL

Best Practices

1. Always Store in UTC

// Good: Store timestamps in UTC
const article = {
  createdAt: new Date().toISOString() // "2024-01-15T14:30:00.000Z"
};

// Bad: Store in local timezone
const article = {
  createdAt: new Date().toString() // "Mon Jan 15 2024 09:30:00 GMT-0500 (EST)"
};

2. Use Appropriate Types

3. Timezone Handling

4. Filtering and Querying

When filtering by dates, use ISO 8601 format:

// Filter articles published after a date
GET /api/articles?filters[publishedDate][$gte]=2024-01-01

// Filter by datetime range
GET /api/articles?filters[createdAt][$gte]=2024-01-01T00:00:00Z&filters[createdAt][$lt]=2024-02-01T00:00:00Z

Migration Considerations

From Existing Systems

If migrating from a system that stores dates differently:

  1. Local Time Storage: Convert all dates to UTC before importing
  2. String Storage: Ensure strings match expected formats
  3. Numeric Timestamps: Use timestamp type for Unix timestamps

Database Configuration

For optimal date handling, configure your database connection:

MySQL (in Knex config):

{
  client: 'mysql2',
  connection: {
    // ... other config
    timezone: 'UTC'
  }
}

PostgreSQL (in Knex config):

{
  client: 'pg',
  connection: {
    // ... other config
  }
  // PostgreSQL handles timezones well by default
}

Common Issues and Solutions

Issue 1: Dates Shifting by Timezone Offset

Symptom: A date like 2024-01-15 becomes 2024-01-14 or 2024-01-16
Cause: Timezone conversion during parsing
Solution: The API handles this by parsing date-only values at UTC midnight

Issue 2: MySQL Dates Appear Wrong

Symptom: Stored 14:30:00 appears as 19:30:00 or 09:30:00
Cause: MySQL DATETIME interpreted in local timezone
Solution: The API assumes MySQL dates are UTC and adds ‘Z’ suffix

Issue 3: Time Values Need Date Context

Symptom: Can’t perform date arithmetic on time-only values
Cause: Time values lack date context
Solution: The API attaches times to epoch date (1970-01-01) in UTC

Technical Implementation Details

The date/time handling is implemented in two key areas:

  1. Input Validation (json-rest-schema):
    • Validates format on write operations
    • Converts all date inputs to JavaScript Date objects
    • Ensures date-only values parse at UTC midnight
    • Returns Date objects for storage (Knex handles DB-specific formatting)
  2. Output Normalization (database-value-normalizers.js):
    • Handles database-specific quirks (MySQL timezone issues)
    • Ensures Date objects are properly created from database values
    • Fixes MySQL datetime strings by assuming UTC
    • Maintains consistency across different database engines

This two-stage approach ensures data integrity on input and consistent formatting on output, regardless of the underlying database system. The key insight is that JavaScript Date objects are used as the common format throughout the pipeline, with database drivers handling the conversion to/from their native formats.