From 7eef0865df1482863f1347d1920e2a4e8dfdf32f Mon Sep 17 00:00:00 2001 From: Alexander Green Date: Thu, 24 Jul 2025 16:52:10 +0200 Subject: [PATCH 01/20] Added `/core/date-time` --- sections/designRules.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sections/designRules.md b/sections/designRules.md index 95403e7..7451938 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -125,6 +125,22 @@ A resource that corresponds to a single conceptual entity is referred to as a [= +
+

Use ISO 8601 for date and time formats

+
+
Statement
+
+

All date and time fields in requests and responses MUST be in ISO 8601 format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). The response MUST be in UTC. While the request can have a time offset, storing SHOULD be done in UTC.

+

If the time is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

+
+
Rationale
+
+

Implementing ISO 8601 in UTC removes ambiguity in date handling between systems and timezones.

+

Inserting a default or irrelevant time can lead to interpretation errors in international contexts. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich.

+
+
+
+ ## HTTP methods Although the REST architectural style does not impose a specific protocol, REST APIs are typically implemented using HTTP [[rfc9110]]. From 8b98d6f88017f09040e0ec47d17af5e1603f6741 Mon Sep 17 00:00:00 2001 From: Alexander Green Date: Thu, 24 Jul 2025 16:57:50 +0200 Subject: [PATCH 02/20] markdownlint --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index 7451938..375f984 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -131,7 +131,7 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
Statement

All date and time fields in requests and responses MUST be in ISO 8601 format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). The response MUST be in UTC. While the request can have a time offset, storing SHOULD be done in UTC.

-

If the time is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

+

If the time is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

Rationale
From c982b064d49f53c8bfc7d5c21ca9ab84b91c65d4 Mon Sep 17 00:00:00 2001 From: Alexander Green Date: Wed, 13 Aug 2025 16:02:02 +0200 Subject: [PATCH 03/20] Apply suggestions from code review Co-authored-by: Tim van der Lippe --- sections/designRules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 375f984..c77f4f6 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -130,8 +130,8 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
Statement
-

All date and time fields in requests and responses MUST be in ISO 8601 format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). The response MUST be in UTC. While the request can have a time offset, storing SHOULD be done in UTC.

-

If the time is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

+

All date and time fields in requests and responses MUST be in ISO 8601 format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). Fields in responses MUST be in UTC. Fields in requests MUST allow any time offset, which servers SHOULD normalize to (and store in) UTC.

+

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

Rationale
From 00ae4402c9260d67ad09912671a76afd6f27f16e Mon Sep 17 00:00:00 2001 From: Alexander Green Date: Fri, 22 Aug 2025 11:36:12 +0200 Subject: [PATCH 04/20] Update sections/designRules.md Co-authored-by: Tim van der Lippe --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index c77f4f6..f4d8ec1 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -130,7 +130,7 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
Statement
-

All date and time fields in requests and responses MUST be in ISO 8601 format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). Fields in responses MUST be in UTC. Fields in requests MUST allow any time offset, which servers SHOULD normalize to (and store in) UTC.

+

All date and time fields in requests and responses MUST follow [[RFC9557]] and thus be in [[ISO8601]] format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). Fields in responses containing timestamps MUST be in UTC (e.g. Z as offset). APIs MUST accept fields with timestamps with any time offset in requests and servers SHOULD normalize to (and store in) UTC.

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

Rationale
From 00d20c4c791a3655df297509123dbe56c9021126 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 11:20:38 +0200 Subject: [PATCH 05/20] Verwerk feedback op basis van werksessie --- sections/designRules.md | 67 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index f4d8ec1..c2acd82 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -125,18 +125,75 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
-
+## Date and time + +

Use ISO 8601 for date and time formats

Statement
-

All date and time fields in requests and responses MUST follow [[RFC9557]] and thus be in [[ISO8601]] format (e.g., YYYY-MM-DD for dates, YYYY-MM-DDTHH:mm:ssZ for timestamps). Fields in responses containing timestamps MUST be in UTC (e.g. Z as offset). APIs MUST accept fields with timestamps with any time offset in requests and servers SHOULD normalize to (and store in) UTC.

-

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted, stored, and returned.

+

All date, datetime and time fields in requests and responses MUST adhere to [[RFC9557]] and [[ISO8601]] format. Each field in the OpenAPI specification MUST set "type": "string" and set "format" to the OpenAPI format as listed in the following table: + + + + + + + + + + + + + + + + + + + + + + + + + +
Field typeISO8601 formatOpenAPI format
Datefull-date (YYYY-MM-DD)"format": "date"
Datetimedate-time (YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss±hh:mm)"format": "date-time"
Timetime (hh:mm:ss)"format": "time-local"
+

+
Rationale
+
+

Implementing RFC9557 and ISO 8601 in UTC removes ambiguity in date handling between systems and timezones.

+
+
+
+ +
+

Allow all timezones in requests and use UTC in responses

+
+
Statement
+
+

Fields in responses containing timestamps MUST be in UTC (e.g. Z as offset). APIs MUST accept fields with timestamps with any time offset in requests. +

+
Rationale
+
+

TODO +

+
+
+ +
+

Omit time portion for date fields

+
+
Statement
+
+

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted and returned.

Rationale
-

Implementing ISO 8601 in UTC removes ambiguity in date handling between systems and timezones.

-

Inserting a default or irrelevant time can lead to interpretation errors in international contexts. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich.

+

Appending a default or irrelevant time portion to a date field can lead to interpretation errors. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich. +

From 375c4d96564eb5982202fe1513602bdeb54806c1 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 11:42:11 +0200 Subject: [PATCH 06/20] Vul tekst in bij TODOs --- sections/designRules.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index c2acd82..ea73655 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -128,11 +128,11 @@ A resource that corresponds to a single conceptual entity is referred to as a [= ## Date and time
-

Use ISO 8601 for date and time formats

+

Use standard format for date, datetime and time

Statement
-

All date, datetime and time fields in requests and responses MUST adhere to [[RFC9557]] and [[ISO8601]] format. Each field in the OpenAPI specification MUST set "type": "string" and set "format" to the OpenAPI format as listed in the following table: +

All date, datetime and time fields in requests and responses MUST adhere to [[RFC9557]] and [[ISO8601]] format. Each field in the OpenAPI specification MUST set "type": "string" and set "format" to the OpenAPI format as listed in the following table: @@ -172,11 +172,11 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
Statement
-

Fields in responses containing timestamps MUST be in UTC (e.g. Z as offset). APIs MUST accept fields with timestamps with any time offset in requests. +

APIs MUST accept fields with timestamps with any timezone offset in requests. Fields in responses containing timestamps MUST be in UTC (e.g. Z as timezone offset).

Rationale
-

TODO +

Allowing clients to use any timezone offset in requests results in flexibility and less complexity for users. Using UTC in responses results in clarity and removes ambiguity.

@@ -191,13 +191,17 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
Rationale

Appending a default or irrelevant time portion to a date field can lead to interpretation errors. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich. -

+Handling date and time is tricky and can lead to confusion among clients. The date-time rules remove ambiguity and provide clarity in the API contract between servers and clients. + + + ## HTTP methods Although the REST architectural style does not impose a specific protocol, REST APIs are typically implemented using HTTP [[rfc9110]]. From b61be80b05a0a65d0c1832c7cb12517157c46c36 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 11:48:59 +0200 Subject: [PATCH 07/20] Update de bewoording --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index ea73655..35be616 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -198,7 +198,7 @@ A resource that corresponds to a single conceptual entity is referred to as a [= Handling date and time is tricky and can lead to confusion among clients. The date-time rules remove ambiguity and provide clarity in the API contract between servers and clients. From 89fc8bc05ec6e8076bd7843c534a14f094aeb1e4 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 11:58:50 +0200 Subject: [PATCH 08/20] Zet het voorbeeld voor de regels --- sections/designRules.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 35be616..236b6ac 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -127,6 +127,13 @@ A resource that corresponds to a single conceptual entity is referred to as a [= ## Date and time +Handling date and time is tricky and can lead to confusion among clients. The date-time rules remove ambiguity and provide clarity in the API contract between servers and clients. + + +

Use standard format for date, datetime and time

@@ -195,13 +202,6 @@ A resource that corresponds to a single conceptual entity is referred to as a [=
-Handling date and time is tricky and can lead to confusion among clients. The date-time rules remove ambiguity and provide clarity in the API contract between servers and clients. - - - ## HTTP methods Although the REST architectural style does not impose a specific protocol, REST APIs are typically implemented using HTTP [[rfc9110]]. From daaefa1b222798638ae2a7a78f6435d524b72a88 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 12:11:31 +0200 Subject: [PATCH 09/20] Update bewoording --- sections/designRules.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 236b6ac..ee3f3d0 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -175,11 +175,11 @@ Handling date and time is tricky and can lead to confusion among clients. The da
-

Allow all timezones in requests and use UTC in responses

+

Allow all timezone offsets in requests and use UTC in responses

Statement
-

APIs MUST accept fields with timestamps with any timezone offset in requests. Fields in responses containing timestamps MUST be in UTC (e.g. Z as timezone offset). +

APIs MUST accept any timezone offset in fields in requests containing a datetime. Fields in responses containing a datetime MUST be in UTC (e.g. Z as timezone offset).

Rationale
@@ -188,12 +188,12 @@ Handling date and time is tricky and can lead to confusion among clients. The da
-
+

Omit time portion for date fields

Statement
-

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) SHOULD be accepted and returned. +

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) MUST be accepted and returned.

Rationale
From aca3229c654b423fc36757dd6cb94cae7acceacc Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 23 Oct 2025 14:15:47 +0200 Subject: [PATCH 10/20] Update bewoording --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index ee3f3d0..c812fe1 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -193,7 +193,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Statement
-

If the time portion is not relevant, only the date portion (e.g., YYYY-MM-DD) MUST be accepted and returned. +

If the time portion is not relevant, date MUST be used instead of datetime.

Rationale
From b983c0e050518e26c68e10ca7eac4d55f1af47a4 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Wed, 29 Oct 2025 15:32:01 +0100 Subject: [PATCH 11/20] Update zin met time portion Op basis van review feedback. --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index c812fe1..f4730e3 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -193,7 +193,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Statement
-

If the time portion is not relevant, date MUST be used instead of datetime. +

If the time portion is not relevant, date format MUST be used instead of date-time format.

Rationale
From 17737cf07fd2f3d9aefc7ff2a85fa73e94ba845d Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 30 Oct 2025 11:53:52 +0100 Subject: [PATCH 12/20] Voeg noot toe over opslaan van originele timezone --- sections/designRules.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index f4730e3..6575224 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -169,7 +169,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Rationale
-

Implementing RFC9557 and ISO 8601 in UTC removes ambiguity in date handling between systems and timezones.

+

Implementing RFC9557 and ISO 8601 removes ambiguity in date handling between systems and timezones.

@@ -184,6 +184,9 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Rationale

Allowing clients to use any timezone offset in requests results in flexibility and less complexity for users. Using UTC in responses results in clarity and removes ambiguity. +

This specification does not state rules regarding storage in databases. + However, it is recommended to store the originally supplied timezone from the client request in the database, such that later the location of the client can be determined. + For example, an extra field in a response can contain the originally supplied timezone, if that's useful for the client.

From a186fe3c1cd47c572ed84a1cd61638a5e68c1ae0 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 30 Oct 2025 14:17:59 +0100 Subject: [PATCH 13/20] Update designRules.md --- sections/designRules.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 6575224..40b04d3 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -185,8 +185,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da

Allowing clients to use any timezone offset in requests results in flexibility and less complexity for users. Using UTC in responses results in clarity and removes ambiguity.

This specification does not state rules regarding storage in databases. - However, it is recommended to store the originally supplied timezone from the client request in the database, such that later the location of the client can be determined. - For example, an extra field in a response can contain the originally supplied timezone, if that's useful for the client. + However, if the original timezone of a given timestamp value is relevant for users (such as the timezone in which a value is registered), it is recommended to store and publish the timezone details (e.g. the zone offset) as a separate property.

From 8afad816529e2b9a266a6f4e7fc6f725b9ac1717 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Mon, 17 Nov 2025 14:37:59 +0100 Subject: [PATCH 14/20] Voeg linter regels toe voor datum en tijd --- linter/spectral.yml | 50 ++++ linter/testcases/cor-api/expected-output.txt | 51 ++-- .../testcases/date-time/expected-output.txt | 14 ++ linter/testcases/date-time/openapi.json | 238 ++++++++++++++++++ sections/designRules.md | 10 +- 5 files changed, 337 insertions(+), 26 deletions(-) create mode 100644 linter/testcases/date-time/expected-output.txt create mode 100644 linter/testcases/date-time/openapi.json diff --git a/linter/spectral.yml b/linter/spectral.yml index fae4e00..253c6f4 100644 --- a/linter/spectral.yml +++ b/linter/spectral.yml @@ -179,6 +179,56 @@ rules: field: "@key" message: Properties must be lowerCamelCase. + #/core/date-time/timezone + nlgov:date-time-ensure-timezone: + severity: error + given: $..properties[*].format + message: "Use date-time format which includes a timezone" + then: + function: pattern + functionOptions: + notMatch: "/^date-time-local$/" + + nlgov:time-without-timezone: + severity: error + given: $..properties[*].format + message: "Use time-local format without a timezone" + then: + function: pattern + functionOptions: + notMatch: "/^time$/" + + #/core/date-time/date-omit-time-portion + nlgov:specify-format-for-date-and-time: + severity: error + given: + - $..properties[date,datum] + - $..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))] + message: "Any date field must set 'format' to 'date'" + then: + function: schema + functionOptions: + schema: + anyOf: + - required: ["format"] + - properties: + allOf: + type: array + items: + required: ["format"] + required: ["allOf"] + + nlgov:use-date-instead-of-datetime: + severity: error + given: + - $..properties[date,datum]..format + - $..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))]..format + message: "Field represents a date and therefore must set 'format' to 'date'" + then: + function: pattern + functionOptions: + notMatch: "/^date-time$/" + nlgov:semver: severity: error message: "Version {{value}} is not in semver format." diff --git a/linter/testcases/cor-api/expected-output.txt b/linter/testcases/cor-api/expected-output.txt index 5c46649..6743309 100644 --- a/linter/testcases/cor-api/expected-output.txt +++ b/linter/testcases/cor-api/expected-output.txt @@ -1,28 +1,29 @@ /testcases/cor-api/openapi.json - 70:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[429].content - 80:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[503].content - 181:29 warning nlgov:paths-kebab-case /laatsteWijziging is not kebab-case. paths./laatsteWijziging - 211:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[400].content - 221:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[404].content - 231:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[405].content - 241:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[406].content - 251:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[429].content - 261:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[500].content - 271:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[503].content - 506:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[400].content - 516:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[404].content - 526:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[405].content - 536:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[406].content - 546:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[429].content - 556:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[500].content - 566:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[503].content - 684:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[400].content - 694:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[404].content - 704:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[405].content - 714:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[406].content - 724:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[429].content - 734:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[500].content - 744:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[503].content + 70:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[429].content + 80:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./heartbeat.get.responses[503].content + 181:29 warning nlgov:paths-kebab-case /laatsteWijziging is not kebab-case. paths./laatsteWijziging + 211:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[400].content + 221:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[404].content + 231:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[405].content + 241:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[406].content + 251:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[429].content + 261:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[500].content + 271:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./laatsteWijziging.get.responses[503].content + 506:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[400].content + 516:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[404].content + 526:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[405].content + 536:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[406].content + 546:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[429].content + 556:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[500].content + 566:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties.get.responses[503].content + 684:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[400].content + 694:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[404].content + 704:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[405].content + 714:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[406].content + 724:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[429].content + 734:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[500].content + 744:35 warning nlgov:use-problem-schema The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457. paths./organisaties/{oin}.get.responses[503].content + 978:27 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' components.schemas.LocalDateTime.format -✖ 24 problems (0 errors, 24 warnings, 0 infos, 0 hints) +✖ 25 problems (1 error, 24 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/date-time/expected-output.txt b/linter/testcases/date-time/expected-output.txt new file mode 100644 index 0000000..6a3cdc8 --- /dev/null +++ b/linter/testcases/date-time/expected-output.txt @@ -0,0 +1,14 @@ + +/testcases/date-time/openapi.json + 94:55 error nlgov:date-time-ensure-timezone Use date-time format which includes a timezone paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.date-time-local.format + 98:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.date.format + 102:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.datum.format + 106:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.geboorteDatum.format + 110:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.birthDate.format + 114:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.expiration_date.format + 118:55 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.expiration_Date.format + 122:55 error nlgov:time-without-timezone Use time-local format without a timezone paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.timestamp.format + 124:61 error nlgov:specify-format-for-date-and-time Any date field must set 'format' to 'date' paths./resources-with-time-incorrect.get.responses[200].content.application/json.schema.properties.missingFormatDate + 218:27 error nlgov:use-date-instead-of-datetime Field represents a date and therefore must set 'format' to 'date' components.schemas.LocalDateTimeIncorrect.format + +✖ 10 problems (10 errors, 0 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/date-time/openapi.json b/linter/testcases/date-time/openapi.json new file mode 100644 index 0000000..6a4349b --- /dev/null +++ b/linter/testcases/date-time/openapi.json @@ -0,0 +1,238 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Baseline", + "description": "Deze OpenAPI specification bevat het minimale om aan alle regels te voldoen.", + "contact": { + "name": "Beheerder", + "url": "https://www.example.com", + "email": "mail@example.com" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://example.com/api/v1" + } + ], + "security": [ + { + "default": [] + } + ], + "tags": [ + { + "name": "openapi" + }, + { + "name": "time" + } + ], + "paths": { + "/openapi.json": { + "get": { + "tags": [ + "openapi" + ], + "description": "OpenAPI document", + "operationId": "getOpenapiJSON", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + }, + "access-control-allow-origin": { + "description": "Alle origins mogen bij deze resource", + "schema": { + "type": "string" + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + }, + "/resources-with-time-incorrect": { + "get": { + "tags": [ + "time" + ], + "description": "Resources with time incorrect", + "operationId": "getResourcesIncorrect", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date-time-local": { + "type": "string", + "format": "date-time-local" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "datum": { + "type": "string", + "format": "date-time" + }, + "geboorteDatum": { + "type": "string", + "format": "date-time" + }, + "birthDate": { + "type": "string", + "format": "date-time" + }, + "expiration_date": { + "type": "string", + "format": "date-time" + }, + "expiration_Date": { + "type": "string", + "format": "date-time" + }, + "timestamp": { + "type": "string", + "format": "time" + }, + "missingFormatDate": { + "nullable": true + }, + "meerdereDatum": { + "type": "string", + "allOf": [ + { + "$ref": "#/components/schemas/LocalDateTimeIncorrect" + } + ] + } + } + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + }, + "/resources-with-time-correct": { + "get": { + "tags": [ + "time" + ], + "description": "Resources with time correct", + "operationId": "getResourcesCorrect", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "datum": { + "type": "string", + "format": "date" + }, + "geboorteDatum": { + "type": "string", + "format": "date" + }, + "birthDate": { + "type": "string", + "format": "date" + }, + "timestamp": { + "type": "string", + "format": "time-local" + }, + "meerdereDatum": { + "type": "string", + "allOf": [ + { + "$ref": "#/components/schemas/LocalDateTimeCorrect" + } + ] + } + } + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + } + }, + "components": { + "schemas": { + "LocalDateTimeIncorrect": { + "format": "date-time", + "type": "string" + }, + "LocalDateTimeCorrect": { + "format": "date", + "type": "string" + } + }, + "securitySchemes": { + "default": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://test.com", + "scopes": {} + } + } + } + } + } +} \ No newline at end of file diff --git a/sections/designRules.md b/sections/designRules.md index 40b04d3..0059c7e 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -171,10 +171,14 @@ Handling date and time is tricky and can lead to confusion among clients. The da

Implementing RFC9557 and ISO 8601 removes ambiguity in date handling between systems and timezones.

+
How to test
+
+ Analyse all fields and if the field represents a date, date-time or time, ensure it has the correct format according to the table above. +
-
+

Allow all timezone offsets in requests and use UTC in responses

Statement
@@ -201,6 +205,10 @@ Handling date and time is tricky and can lead to confusion among clients. The da

Appending a default or irrelevant time portion to a date field can lead to interpretation errors. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich.

+
How to test
+
+ Analyse all fields that set format to "date-time" and ensure that the fields do not represent solely a date. +
From 54ddb4be74ab579ff2c32bd06dd7ea07be4bad0a Mon Sep 17 00:00:00 2001 From: Joost Farla Date: Thu, 5 Feb 2026 09:43:08 +0100 Subject: [PATCH 15/20] Storage recommendations (#298) --- sections/designRules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 0059c7e..b9f2461 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -188,8 +188,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Rationale

Allowing clients to use any timezone offset in requests results in flexibility and less complexity for users. Using UTC in responses results in clarity and removes ambiguity. -

This specification does not state rules regarding storage in databases. - However, if the original timezone of a given timestamp value is relevant for users (such as the timezone in which a value is registered), it is recommended to store and publish the timezone details (e.g. the zone offset) as a separate property. +

While storage formats are outside the scope of this specification, it is recommended to use appropriate temporal datatypes (such as DATE and TIMESTAMPTZ). Many database systems store these values internally in UTC and handle timezone conversion automatically on read/write. When the original timezone is relevant for users (such as the timezone in which a value is registered), store and publish the zone offset as a separate property.

@@ -204,6 +203,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Rationale

Appending a default or irrelevant time portion to a date field can lead to interpretation errors. A publish date of 2025-07-24T00:00:00Z could for instance be rendered as July 23 in Ireland. A default time of 23:59 would in turn cause date confusion east of Greenwich. +

To prevent conversion errors between storage and API, it is recommended to also store date values without a time portion (i.e. not as a timestamp).

How to test
From 567912bc46d28ced70e3c9b8c9e122e8d7c57c6d Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 5 Feb 2026 13:31:44 +0100 Subject: [PATCH 16/20] Remove example formats for ISO8601 To avoid confusion whether other variations of these accepted formats are allowed. --- sections/designRules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index 7816e28..c94c65e 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -240,17 +240,17 @@ Handling date and time is tricky and can lead to confusion among clients. The da
- + - + - + From 675264713414eda90e256610e9657d9dbd33441d Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Thu, 5 Feb 2026 13:47:49 +0100 Subject: [PATCH 17/20] Gebruik meest recente versie van ISO8601 --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index c94c65e..7f2e2d2 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -228,7 +228,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Statement
-

All date, datetime and time fields in requests and responses MUST adhere to [[RFC9557]] and [[ISO8601]] format. Each field in the OpenAPI specification MUST set "type": "string" and set "format" to the OpenAPI format as listed in the following table: +

All date, datetime and time fields in requests and responses MUST adhere to [[RFC9557]] and [[ISO8601-1]] format. Each field in the OpenAPI specification MUST set "type": "string" and set "format" to the OpenAPI format as listed in the following table:

Datefull-date (YYYY-MM-DD)full-date "format": "date"
Datetimedate-time (YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss±hh:mm)date-time "format": "date-time"
Timetime (hh:mm:ss)time "format": "time-local"
From 66a0af8866ed968aff0c9841e26ea405e6f9354f Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Tue, 10 Feb 2026 12:52:09 +0100 Subject: [PATCH 18/20] Response in UTC als SHOULD --- sections/designRules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/designRules.md b/sections/designRules.md index 7f2e2d2..362daed 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -272,7 +272,7 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Statement
-

APIs MUST accept any timezone offset in fields in requests containing a datetime. Fields in responses containing a datetime MUST be in UTC (e.g. Z as timezone offset). +

APIs MUST accept any timezone offset in fields in requests containing a datetime. Fields in responses containing a datetime SHOULD be in UTC (e.g. Z as timezone offset).

Rationale
From e92479bf85d90056f92dd1f8f271b25966ccccc3 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Mon, 16 Feb 2026 16:52:54 +0100 Subject: [PATCH 19/20] Leg het verschil uit tussen RFC9557 en ISO8601 Hiermee voorkomen we het probleem dat het vereist is om ISO8601 te kopen (dus blijft het een open standaard). --- sections/designRules.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sections/designRules.md b/sections/designRules.md index 362daed..c28f1ce 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -259,6 +259,13 @@ Handling date and time is tricky and can lead to confusion among clients. The da
Rationale

Implementing RFC9557 and ISO 8601 removes ambiguity in date handling between systems and timezones. +

RFC9557 is a profile on ISO8601, but is not a strict subset of allowed notations. Practically to adhere to both, the following limitations MUST be applied to RFC9557: +
    +
  • In a field with a date-time value, the date and time component MUST be separated by a "T" in uppercase. +
  • The timezone offset "Z" must be uppercase. +
  • "-00:00" is not allowed as timezone offset. +
+
How to test
From b90bd82006803479f2230c09965e07ea3be5a2bd Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Mon, 16 Feb 2026 16:56:23 +0100 Subject: [PATCH 20/20] Fix MUST notatie --- sections/designRules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/designRules.md b/sections/designRules.md index c28f1ce..94a1241 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -262,8 +262,8 @@ Handling date and time is tricky and can lead to confusion among clients. The da
RFC9557 is a profile on ISO8601, but is not a strict subset of allowed notations. Practically to adhere to both, the following limitations MUST be applied to RFC9557:
  • In a field with a date-time value, the date and time component MUST be separated by a "T" in uppercase. -
  • The timezone offset "Z" must be uppercase. -
  • "-00:00" is not allowed as timezone offset. +
  • The timezone offset "Z" MUST be uppercase. +
  • "-00:00" MUST NOT be used as timezone offset.