{
  "openapi": "3.1.0",
  "info": {
    "title": "The Time API",
    "version": "2.2.0",
    "description": "A small, dependency-free API for current time data, IANA timezone conversion, optional locale-aware weekday/month names, named calendar fields, and PHP-compatible date formatting. English remains the default and existing time-data response structures remain unchanged."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current host"
    }
  ],
  "tags": [
    {
      "name": "Time",
      "description": "Current time and timezone conversion"
    },
    {
      "name": "Fields",
      "description": "Focused calendar fields and custom formatting"
    },
    {
      "name": "Discovery",
      "description": "API and timezone discovery"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getDocumentation",
        "summary": "Open the documentation and interactive playground",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "HTML documentation homepage",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/time/{timezone}.json": {
      "get": {
        "operationId": "getTimeByTimezone",
        "summary": "Get the current time in one timezone",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/TimezonePath"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Current time in the requested timezone",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegacyTime"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidTimezone"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/times.json": {
      "get": {
        "operationId": "getAllTimes",
        "summary": "Get the current time in all IANA timezones",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/OptionalTimezone"
          },
          {
            "$ref": "#/components/parameters/OptionalTimezoneAlias"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Nested region and city map, or one legacy time object when a timezone query is supplied",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/LegacyTime"
                    },
                    {
                      "$ref": "#/components/schemas/TimezoneRegionMap"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidTimezone"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/auto.json": {
      "get": {
        "operationId": "getAutomaticTimezone",
        "summary": "Detect a timezone from the client IP",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Detected timezone data and GeoIP record",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["time", "geoip"],
                  "properties": {
                    "time": {
                      "$ref": "#/components/schemas/LegacyTime"
                    },
                    "geoip": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/location.json": {
      "get": {
        "operationId": "getAutomaticLocation",
        "summary": "Alias of /auto.json",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Detected timezone data and GeoIP record",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["time", "geoip"],
                  "properties": {
                    "time": {
                      "$ref": "#/components/schemas/LegacyTime"
                    },
                    "geoip": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/plain.txt": {
      "get": {
        "operationId": "getPlainTimes",
        "summary": "Get current time data as plain CSV-style lines",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/OptionalTimezone"
          },
          {
            "$ref": "#/components/parameters/OptionalTimezoneAlias"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Flattened comma-separated key and value lines",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid timezone",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "const": "Invalid timezone"
                }
              }
            }
          }
        }
      }
    },
    "/plain/{timezone}.txt": {
      "get": {
        "operationId": "getPlainTimeByTimezone",
        "summary": "Get one timezone as plain CSV-style lines",
        "tags": ["Time"],
        "parameters": [
          {
            "$ref": "#/components/parameters/TimezonePath"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "Flattened comma-separated key and value lines",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid timezone"
          }
        }
      }
    },
    "/get.json": {
      "get": {
        "operationId": "getDateFields",
        "summary": "Get named date fields or a custom formatted value",
        "description": "Use either field or format. field accepts one name or a comma/pipe-separated list. With neither parameter, all named fields are returned. format accepts the same formatting characters as PHP DateTimeInterface::format, up to 128 characters. locale optionally localizes weekday and month names; English remains the default.",
        "tags": ["Fields"],
        "parameters": [
          {
            "$ref": "#/components/parameters/OptionalTimezone"
          },
          {
            "$ref": "#/components/parameters/OptionalTimezoneAlias"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          },
          {
            "name": "field",
            "in": "query",
            "description": "One field or a comma/pipe-separated list, for example weekday|dow|dom",
            "schema": {
              "type": "string",
              "examples": ["weekday", "weekday|dow|dom"]
            }
          },
          {
            "name": "format",
            "in": "query",
            "description": "A PHP-compatible DateTime format, for example Y-m-d H:i:s P",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "examples": ["Y-m-d H:i:s P", "l, j F Y"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Named fields or a custom-formatted value",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/FieldResponse"
                    },
                    {
                      "$ref": "#/components/schemas/FormatResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid timezone, locale, field, or format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldError"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/format.txt": {
      "get": {
        "operationId": "getFormattedTime",
        "summary": "Get a custom formatted value as plain text",
        "tags": ["Fields"],
        "parameters": [
          {
            "$ref": "#/components/parameters/OptionalTimezone"
          },
          {
            "$ref": "#/components/parameters/OptionalTimezoneAlias"
          },
          {
            "$ref": "#/components/parameters/LocaleQuery"
          },
          {
            "name": "format",
            "in": "query",
            "description": "A PHP-compatible DateTime format; defaults to c",
            "schema": {
              "type": "string",
              "default": "c",
              "minLength": 1,
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Formatted current time",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid timezone, locale, or format"
          },
          "500": {
            "description": "Unable to retrieve the current time"
          }
        }
      }
    },
    "/timezones.json": {
      "get": {
        "operationId": "listTimezones",
        "summary": "List accepted IANA timezone identifiers",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "Supported timezone identifiers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["count", "timezones"],
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "timezones": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/health.json": {
      "get": {
        "operationId": "getHealth",
        "summary": "Check application readiness",
        "description": "Checks the system clock, timezone database, and UID-isolated writable runtime storage.",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "All readiness checks passed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          },
          "503": {
            "description": "At least one readiness check failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      },
      "head": {
        "operationId": "headHealth",
        "summary": "Check application readiness without a response body",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "All readiness checks passed"
          },
          "503": {
            "description": "At least one readiness check failed"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpecification",
        "summary": "Get this OpenAPI 3.1 specification",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "TimezonePath": {
        "name": "timezone",
        "in": "path",
        "required": true,
        "description": "IANA timezone identifier. Slashes remain path separators, for example Europe/Berlin.",
        "schema": {
          "type": "string",
          "examples": ["Europe/Berlin", "America/Port_of_Spain", "Etc/GMT+5"]
        }
      },
      "OptionalTimezone": {
        "name": "timezone",
        "in": "query",
        "description": "IANA timezone identifier",
        "schema": {
          "type": "string",
          "default": "UTC",
          "example": "Europe/Berlin"
        }
      },
      "OptionalTimezoneAlias": {
        "name": "tz",
        "in": "query",
        "description": "Short alias of the timezone query parameter",
        "schema": {
          "type": "string",
          "example": "Europe/Berlin"
        }
      },
      "LocaleQuery": {
        "name": "locale",
        "in": "query",
        "description": "Optional locale using a language tag such as de_AT or de-AT. English is used when omitted. Localizes weekday/month fields and PHP format characters D, l, M, and F; standardized ISO 8601 and RFC 2822 values remain unchanged.",
        "schema": {
          "type": "string",
          "minLength": 2,
          "maxLength": 35,
          "pattern": "^[A-Za-z]{2,3}(?:[_-][A-Za-z0-9]{2,8})*$",
          "example": "de_AT"
        }
      }
    },
    "responses": {
      "InvalidTimezone": {
        "description": "Invalid timezone",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/LegacyError"
            }
          }
        }
      },
      "ServerError": {
        "description": "The current time could not be retrieved",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/LegacyError"
            }
          }
        }
      }
    },
    "schemas": {
      "LegacyTime": {
        "type": "object",
        "description": "Stable response structure used by the original API endpoints",
        "required": [
          "Timezone",
          "Date",
          "Time",
          "ISO8601",
          "PowerShell",
          "DateTime",
          "RFC2822",
          "UnixTimestamp",
          "DST",
          "Offset",
          "UTCTimezone"
        ],
        "properties": {
          "Timezone": {
            "type": "string",
            "example": "Europe/Berlin"
          },
          "Date": {
            "type": "string",
            "format": "date"
          },
          "Time": {
            "type": "string",
            "example": "14:30:05"
          },
          "ISO8601": {
            "type": "string",
            "format": "date-time"
          },
          "PowerShell": {
            "type": "string",
            "example": "2026-07-27T14:30:05+02:00"
          },
          "DateTime": {
            "type": "string",
            "example": "2026-07-27 14:30:05"
          },
          "RFC2822": {
            "type": "string"
          },
          "UnixTimestamp": {
            "type": "integer"
          },
          "DST": {
            "type": "boolean"
          },
          "Offset": {
            "type": "object",
            "required": ["SecondsAbsolute", "HoursAbsolute", "MinutesAbsolute"],
            "properties": {
              "SecondsAbsolute": {
                "type": "integer"
              },
              "HoursAbsolute": {
                "type": "number"
              },
              "MinutesAbsolute": {
                "type": "number"
              }
            }
          },
          "UTCTimezone": {
            "type": "string",
            "example": "UTC+2"
          }
        }
      },
      "TimezoneRegionMap": {
        "type": "object",
        "additionalProperties": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/components/schemas/LegacyTime"
          }
        }
      },
      "FieldResponse": {
        "type": "object",
        "required": ["timezone", "fields", "values", "timestamp"],
        "properties": {
          "timezone": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "description": "Normalized locale, present only when locale was requested",
            "example": "de_AT"
          },
          "fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "values": {
            "type": "object",
            "additionalProperties": true
          },
          "timestamp": {
            "type": "integer"
          }
        }
      },
      "FormatResponse": {
        "type": "object",
        "required": ["timezone", "format", "value", "timestamp"],
        "properties": {
          "timezone": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "description": "Normalized locale, present only when locale was requested",
            "example": "de_AT"
          },
          "format": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "timestamp": {
            "type": "integer"
          }
        }
      },
      "FieldError": {
        "type": "object",
        "required": ["error", "available_fields"],
        "properties": {
          "error": {
            "type": "string"
          },
          "available_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": ["status", "timestamp", "checks"],
        "properties": {
          "status": {
            "type": "string",
            "enum": ["ok", "error"]
          },
          "timestamp": {
            "type": "integer"
          },
          "checks": {
            "type": "object",
            "required": ["clock", "timezone_database", "runtime_storage"],
            "properties": {
              "clock": {
                "type": "string",
                "enum": ["ok", "error"]
              },
              "timezone_database": {
                "type": "string",
                "enum": ["ok", "error"]
              },
              "runtime_storage": {
                "type": "string",
                "enum": ["ok", "error"]
              }
            }
          }
        }
      },
      "LegacyError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
