From b3a7ffc6a2d02cc5032ce98c1fd75cac4775457c Mon Sep 17 00:00:00 2001
From: Kai Ren <tyranron@gmail.com>
Date: Fri, 16 Aug 2024 01:30:05 +0200
Subject: [PATCH] Correct GraphQL scalars compliance for third-party crates
 (#1275)

- switch `LocalDateTime` scalars to `yyyy-MM-ddTHH:mm:ss` format in `chrono::NaiveDateTime` and `time::PrimitiveDateTime` types
- switch from `Date` scalar to `LocalDate` scalar in `chrono::NaiveDate` and `time::Date` types
- switch from `UtcDateTime` scalar to `DateTime` scalar in `bson::DateTime` type
- correct `TimeZone` scalar in `chrono_tz::Tz` type
- rename `Url` scalar to `URL` in `url::Url` type
- rename `Uuid` scalar to `UUID` in `uuid::Uuid` type
---
 book/src/types/scalars.md                     |  57 ++---
 juniper/CHANGELOG.md                          |  24 ++-
 juniper/Cargo.toml                            |   2 +-
 .../src/executor_tests/interfaces_unions.rs   |   1 +
 .../src/executor_tests/introspection/mod.rs   |   1 +
 juniper/src/integrations/bson.rs              | 201 ++++++++++++++++--
 juniper/src/integrations/chrono.rs            | 123 ++++++-----
 juniper/src/integrations/chrono_tz.rs         |  28 ++-
 juniper/src/integrations/jiff.rs              | 121 +++++++++--
 juniper/src/integrations/time.rs              | 107 ++++++----
 juniper/src/integrations/url.rs               |  30 ++-
 juniper/src/integrations/uuid.rs              |  31 ++-
 12 files changed, 536 insertions(+), 190 deletions(-)

diff --git a/book/src/types/scalars.md b/book/src/types/scalars.md
index 000f6856..6808ea07 100644
--- a/book/src/types/scalars.md
+++ b/book/src/types/scalars.md
@@ -385,35 +385,35 @@ mod date_scalar {
 
 [Juniper] provides out-of-the-box [GraphQL scalar][0] implementations for some very common [Rust] crates. The types from these crates will be usable in your schemas automatically after enabling the correspondent self-titled [Cargo feature].
 
-| [Rust] type                 | [GraphQL] scalar | [Cargo feature]  |
-|-----------------------------|------------------|------------------|
-| [`BigDecimal`]              | `BigDecimal`     | [`bigdecimal`]   |
-| [`bson::oid::ObjectId`]     | `ObjectId`       | [`bson`]         |
-| [`bson::DateTime`]          | `UtcDateTime`    | [`bson`]         |
-| [`chrono::NaiveDate`]       | [`Date`]         | [`chrono`]       |
-| [`chrono::NaiveTime`]       | [`LocalTime`]    | [`chrono`]       |
-| [`chrono::NaiveDateTime`]   | `LocalDateTime`  | [`chrono`]       |
-| [`chrono::DateTime`]        | [`DateTime`]     | [`chrono`]       |
-| [`chrono_tz::Tz`]           | `TimeZone`       | [`chrono-tz`]    |
-| [`Decimal`]                 | `Decimal`        | [`rust_decimal`] |
-| [`jiff::civil::Date`]       | [`LocalDate`]    | [`jiff`]         |
-| [`jiff::civil::Time`]       | [`LocalTime`]    | [`jiff`]         |
-| [`jiff::civil::DateTime`]   | `LocalDateTime`  | [`jiff`]         |
-| [`jiff::Timestamp`]         | [`DateTime`]     | [`jiff`]         |
-| [`jiff::Span`]              | [`Duration`]     | [`jiff`]         |
-| [`time::Date`]              | [`Date`]         | [`time`]         |
-| [`time::Time`]              | [`LocalTime`]    | [`time`]         |
-| [`time::PrimitiveDateTime`] | `LocalDateTime`  | [`time`]         |
-| [`time::OffsetDateTime`]    | [`DateTime`]     | [`time`]         |
-| [`time::UtcOffset`]         | [`UtcOffset`]    | [`time`]         |
-| [`Url`]                     | `Url`            | [`url`]          |
-| [`Uuid`]                    | `Uuid`           | [`uuid`]         |
+| [Rust] type                 | [GraphQL] scalar  | [Cargo feature]  |
+|-----------------------------|-------------------|------------------|
+| [`bigdecimal::BigDecimal`]  | `BigDecimal`      | [`bigdecimal`]   |
+| [`bson::oid::ObjectId`]     | `ObjectId`        | [`bson`]         |
+| [`bson::DateTime`]          | [`DateTime`]      | [`bson`]         |
+| [`chrono::NaiveDate`]       | [`LocalDate`]     | [`chrono`]       |
+| [`chrono::NaiveTime`]       | [`LocalTime`]     | [`chrono`]       |
+| [`chrono::NaiveDateTime`]   | [`LocalDateTime`] | [`chrono`]       |
+| [`chrono::DateTime`]        | [`DateTime`]      | [`chrono`]       |
+| [`chrono_tz::Tz`]           | [`TimeZone`]      | [`chrono-tz`]    |
+| [`rust_decimal::Decimal`]   | `Decimal`         | [`rust_decimal`] |
+| [`jiff::civil::Date`]       | [`LocalDate`]     | [`jiff`]         |
+| [`jiff::civil::Time`]       | [`LocalTime`]     | [`jiff`]         |
+| [`jiff::civil::DateTime`]   | [`LocalDateTime`] | [`jiff`]         |
+| [`jiff::Timestamp`]         | [`DateTime`]      | [`jiff`]         |
+| [`jiff::Span`]              | [`Duration`]      | [`jiff`]         |
+| [`time::Date`]              | [`LocalDate`]     | [`time`]         |
+| [`time::Time`]              | [`LocalTime`]     | [`time`]         |
+| [`time::PrimitiveDateTime`] | [`LocalDateTime`] | [`time`]         |
+| [`time::OffsetDateTime`]    | [`DateTime`]      | [`time`]         |
+| [`time::UtcOffset`]         | [`UtcOffset`]     | [`time`]         |
+| [`url::Url`]                | [`URL`]           | [`url`]          |
+| [`uuid::Uuid`]              | [`UUID`]          | [`uuid`]         |
 
 
 
 
 [`bigdecimal`]: https://docs.rs/bigdecimal
-[`BigDecimal`]: https://docs.rs/bigdecimal/latest/bigdecimal/struct.BigDecimal.html
+[`bigdecimal::BigDecimal`]: https://docs.rs/bigdecimal/latest/bigdecimal/struct.BigDecimal.html
 [`bson`]: https://docs.rs/bson
 [`bson::DateTime`]: https://docs.rs/bson/latest/bson/struct.DateTime.html
 [`bson::oid::ObjectId`]: https://docs.rs/bson/latest/bson/oid/struct.ObjectId.html
@@ -424,7 +424,6 @@ mod date_scalar {
 [`chrono::NaiveTime`]: https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html
 [`chrono-tz`]: https://docs.rs/chrono-tz
 [`chrono_tz::Tz`]: https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html
-[`Date`]: https://graphql-scalars.dev/docs/scalars/date
 [`DateTime`]: https://graphql-scalars.dev/docs/scalars/date-time
 [`Decimal`]: https://docs.rs/rust_decimal/latest/rust_decimal/struct.Decimal.html
 [`Duration`]: https://graphql-scalars.dev/docs/scalars/duration
@@ -436,6 +435,7 @@ mod date_scalar {
 [`jiff::Span`]: https://docs.rs/jiff/latest/jiff/struct.Span.html
 [`jiff::Timestamp`]: https://docs.rs/jiff/latest/jiff/struct.Timestamp.html
 [`LocalDate`]: https://graphql-scalars.dev/docs/scalars/local-date
+[`LocalDateTime`]: https://graphql-scalars.dev/docs/scalars/local-date-time
 [`LocalTime`]: https://graphql-scalars.dev/docs/scalars/local-time
 [`rust_decimal`]: https://docs.rs/rust_decimal
 [`ScalarValue`]: https://docs.rs/juniper/0.16.1/juniper/trait.ScalarValue.html
@@ -446,11 +446,14 @@ mod date_scalar {
 [`time::Time`]: https://docs.rs/time/latest/time/struct.Time.html
 [`time::UtcOffset`]: https://docs.rs/time/latest/time/struct.UtcOffset.html
 [`time::OffsetDateTime`]: https://docs.rs/time/latest/time/struct.OffsetDateTime.html
+[`TimeZone`]: https://graphql-scalars.dev/docs/scalars/time-zone
 [`url`]: https://docs.rs/url
-[`Url`]: https://docs.rs/url/latest/url/struct.Url.html
+[`url::Url`]: https://docs.rs/url/latest/url/struct.Url.html
+[`URL`]: https://graphql-scalars.dev/docs/scalars/url
 [`UtcOffset`]: https://graphql-scalars.dev/docs/scalars/utc-offset
 [`uuid`]: https://docs.rs/uuid
-[`Uuid`]: https://docs.rs/uuid/latest/uuid/struct.Uuid.html
+[`uuid::Uuid`]: https://docs.rs/uuid/latest/uuid/struct.Uuid.html
+[`UUID`]: https://graphql-scalars.dev/docs/scalars/uuid
 [Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html
 [GraphQL]: https://graphql.org
 [Juniper]: https://docs.rs/juniper
diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index e3d75e65..8310eb96 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -14,19 +14,41 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
 
 - Upgraded [`chrono-tz` crate] integration to [0.9 version](https://github.com/chronotope/chrono-tz/releases/tag/v0.9.0). ([#1252])
 - Bumped up [MSRV] to 1.75. ([#1272])
+- Corrected compliance with newer [graphql-scalars.dev] specs: ([#1275])
+    - Switched `LocalDateTime` scalars to `yyyy-MM-ddTHH:mm:ss` format in types:
+        - `chrono::NaiveDateTime`.
+        - `time::PrimitiveDateTime`.
+    - Switched from `Date` scalar to `LocalDate` scalar in types:
+        - `chrono::NaiveDate`.
+        - `time::Date`.
+    - Switched from `UtcDateTime` scalar to `DateTime` scalar in types:
+        - `bson::DateTime`.
+    - Corrected `TimeZone` scalar in types:
+        - `chrono_tz::Tz`.
+    - Renamed `Url` scalar to `URL` in types:
+        - `url::Url`.
+    - Renamed `Uuid` scalar to `UUID` in types:
+        - `uuid::Uuid`.
 
 ### Added
 
-- [`jiff` crate] integration behind `jiff` [Cargo feature]. ([#1271])
+- [`jiff` crate] integration behind `jiff` [Cargo feature]: ([#1271], [#1270])
+    - `jiff::civil::Date` as `LocalDate` scalar.
+    - `jiff::civil::Time` as `LocalTime` scalar.
+    - `jiff::civil::DateTime` as `LocalDateTime` scalar. ([#1275])
+    - `jiff::Timestamp` as `DateTime` scalar.
+    - `jiff::Span` as `Duration` scalar.
 
 ### Changed
 
 - Updated [GraphiQL] to [3.5.0 version](https://github.com/graphql/graphiql/blob/graphiql%403.5.0/packages/graphiql/CHANGELOG.md#350). ([#1274])
 
 [#1252]: /../../pull/1252
+[#1270]: /../../issues/1270
 [#1271]: /../../pull/1271
 [#1272]: /../../pull/1272
 [#1274]: /../../pull/1274
+[#1275]: /../../pull/1275
 
 
 
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 9f0401a2..6f950a6c 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -46,7 +46,7 @@ anyhow = { version = "1.0.47", optional = true }
 async-trait = "0.1.39"
 auto_enums = "0.8"
 bigdecimal = { version = "0.4", optional = true }
-bson = { version = "2.4", features = ["chrono-0_4"], optional = true }
+bson = { version = "2.4", optional = true }
 chrono = { version = "0.4.30", features = ["alloc"], default-features = false, optional = true }
 chrono-tz = { version = "0.9", default-features = false, optional = true }
 fnv = "1.0.5"
diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs
index 10d5e088..3023d019 100644
--- a/juniper/src/executor_tests/interfaces_unions.rs
+++ b/juniper/src/executor_tests/interfaces_unions.rs
@@ -6,6 +6,7 @@ mod interface {
         GraphQLObject,
     };
 
+    #[allow(dead_code)] // TODO: Consider this for the GraphQL interfaces in the expansion.
     #[graphql_interface(for = [Cat, Dog])]
     trait Pet {
         fn name(&self) -> &str;
diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs
index ee2501b2..f8de215f 100644
--- a/juniper/src/executor_tests/introspection/mod.rs
+++ b/juniper/src/executor_tests/introspection/mod.rs
@@ -24,6 +24,7 @@ enum Sample {
 struct Scalar(i32);
 
 /// A sample interface
+#[allow(dead_code)] // TODO: Consider this for the GraphQL interfaces in the expansion.
 #[graphql_interface(name = "SampleInterface", for = Root)]
 trait Interface {
     /// A sample field in the interface
diff --git a/juniper/src/integrations/bson.rs b/juniper/src/integrations/bson.rs
index 20c88fbe..a30e80c3 100644
--- a/juniper/src/integrations/bson.rs
+++ b/juniper/src/integrations/bson.rs
@@ -1,8 +1,30 @@
-//! GraphQL support for [bson](https://github.com/mongodb/bson-rust) types.
+//! GraphQL support for [`bson`] crate types.
+//!
+//! # Supported types
+//!
+//! | Rust type         | Format            | GraphQL scalar   |
+//! |-------------------|-------------------|------------------|
+//! | [`oid::ObjectId`] | HEX string        | `ObjectId`       |
+//! | [`DateTime`]      | [RFC 3339] string | [`DateTime`][s4] |
+//!
+//! [`DateTime`]: bson::DateTime
+//! [`ObjectId`]: bson::oid::ObjectId
+//! [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
+//! [s4]: https://graphql-scalars.dev/docs/scalars/date-time
 
 use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 
-#[graphql_scalar(with = object_id, parse_token(String))]
+/// [BSON ObjectId][0] represented as a HEX string.
+///
+/// See also [`bson::oid::ObjectId`][2] for details.
+///
+/// [0]: https://www.mongodb.com/docs/manual/reference/bson-types#objectid
+/// [2]: https://docs.rs/bson/*/bson/oid/struct.ObjectId.html
+#[graphql_scalar(
+    with = object_id,
+    parse_token(String),
+    specified_by_url = "https://www.mongodb.com/docs/manual/reference/bson-types#objectid",
+)]
 type ObjectId = bson::oid::ObjectId;
 
 mod object_id {
@@ -21,32 +43,49 @@ mod object_id {
     }
 }
 
-#[graphql_scalar(with = utc_date_time, parse_token(String))]
-type UtcDateTime = bson::DateTime;
+/// [BSON date][3] in [RFC 3339][0] format.
+///
+/// [BSON datetimes][3] have millisecond precision and are always in UTC (inputs with other
+/// timezones are coerced).
+///
+/// [`DateTime` scalar][1] compliant.
+///
+/// See also [`bson::DateTime`][2] for details.
+///
+/// [0]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
+/// [1]: https://graphql-scalars.dev/docs/scalars/date-time
+/// [2]: https://docs.rs/bson/*/bson/struct.DateTime.html
+/// [3]: https://www.mongodb.com/docs/manual/reference/bson-types#date
+#[graphql_scalar(
+    with = date_time,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/date-time",
+)]
+type DateTime = bson::DateTime;
 
-mod utc_date_time {
+mod date_time {
     use super::*;
 
-    pub(super) fn to_output<S: ScalarValue>(v: &UtcDateTime) -> Value<S> {
+    pub(super) fn to_output<S: ScalarValue>(v: &DateTime) -> Value<S> {
         Value::scalar(
             (*v).try_to_rfc3339_string()
-                .unwrap_or_else(|e| panic!("failed to format `UtcDateTime` as RFC3339: {e}")),
+                .unwrap_or_else(|e| panic!("failed to format `DateTime` as RFC 3339: {e}")),
         )
     }
 
-    pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<UtcDateTime, String> {
+    pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<DateTime, String> {
         v.as_string_value()
             .ok_or_else(|| format!("Expected `String`, found: {v}"))
             .and_then(|s| {
-                UtcDateTime::parse_rfc3339_str(s)
-                    .map_err(|e| format!("Failed to parse `UtcDateTime`: {e}"))
+                DateTime::parse_rfc3339_str(s)
+                    .map_err(|e| format!("Failed to parse `DateTime`: {e}"))
             })
     }
 }
 
 #[cfg(test)]
 mod test {
-    use bson::{oid::ObjectId, DateTime as UtcDateTime};
+    use bson::oid::ObjectId;
 
     use crate::{graphql_input_value, FromInputValue, InputValue};
 
@@ -60,21 +99,139 @@ mod test {
 
         assert_eq!(parsed, id);
     }
+}
+
+#[cfg(test)]
+mod date_time_test {
+    use crate::{graphql_input_value, FromInputValue as _, InputValue, ToInputValue as _};
+
+    use super::DateTime;
 
     #[test]
-    fn utcdatetime_from_input() {
-        use chrono::{DateTime, Utc};
+    fn parses_correct_input() {
+        for (raw, expected) in [
+            (
+                "2014-11-28T21:00:09+09:00",
+                DateTime::builder()
+                    .year(2014)
+                    .month(11)
+                    .day(28)
+                    .hour(12)
+                    .second(9)
+                    .build()
+                    .unwrap(),
+            ),
+            (
+                "2014-11-28T21:00:09Z",
+                DateTime::builder()
+                    .year(2014)
+                    .month(11)
+                    .day(28)
+                    .hour(21)
+                    .second(9)
+                    .build()
+                    .unwrap(),
+            ),
+            (
+                "2014-11-28T21:00:09+00:00",
+                DateTime::builder()
+                    .year(2014)
+                    .month(11)
+                    .day(28)
+                    .hour(21)
+                    .second(9)
+                    .build()
+                    .unwrap(),
+            ),
+            (
+                "2014-11-28T21:00:09.05+09:00",
+                DateTime::builder()
+                    .year(2014)
+                    .month(11)
+                    .day(28)
+                    .hour(12)
+                    .second(9)
+                    .millisecond(50)
+                    .build()
+                    .unwrap(),
+            ),
+        ] {
+            let input: InputValue = graphql_input_value!((raw));
+            let parsed = DateTime::from_input_value(&input);
 
-        let raw = "2020-03-23T17:38:32.446+00:00";
-        let input: InputValue = graphql_input_value!((raw));
+            assert!(
+                parsed.is_ok(),
+                "failed to parse `{raw}`: {:?}",
+                parsed.unwrap_err(),
+            );
+            assert_eq!(parsed.unwrap(), expected, "input: {raw}");
+        }
+    }
 
-        let parsed: UtcDateTime = FromInputValue::from_input_value(&input).unwrap();
-        let date_time = UtcDateTime::from_chrono(
-            DateTime::parse_from_rfc3339(raw)
-                .unwrap()
-                .with_timezone(&Utc),
-        );
+    #[test]
+    fn fails_on_invalid_input() {
+        for input in [
+            graphql_input_value!("12"),
+            graphql_input_value!("12:"),
+            graphql_input_value!("56:34:22"),
+            graphql_input_value!("56:34:22.000"),
+            graphql_input_value!("1996-12-1914:23:43"),
+            graphql_input_value!("1996-12-19 14:23:43Z"),
+            graphql_input_value!("1996-12-19T14:23:43"),
+            graphql_input_value!("1996-12-19T14:23:43ZZ"),
+            graphql_input_value!("1996-12-19T14:23:43.543"),
+            graphql_input_value!("1996-12-19T14:23"),
+            graphql_input_value!("1996-12-19T14:23:1"),
+            graphql_input_value!("1996-12-19T14:23:"),
+            graphql_input_value!("1996-12-19T23:78:43Z"),
+            graphql_input_value!("1996-12-19T23:18:99Z"),
+            graphql_input_value!("1996-12-19T24:00:00Z"),
+            graphql_input_value!("1996-12-19T99:02:13Z"),
+            graphql_input_value!("1996-12-19T99:02:13Z"),
+            graphql_input_value!("1996-12-19T12:02:13+4444444"),
+            graphql_input_value!("i'm not even a datetime"),
+            graphql_input_value!(2.32),
+            graphql_input_value!(1),
+            graphql_input_value!(null),
+            graphql_input_value!(false),
+        ] {
+            let input: InputValue = input;
+            let parsed = DateTime::from_input_value(&input);
 
-        assert_eq!(parsed, date_time);
+            assert!(parsed.is_err(), "allows input: {input:?}");
+        }
+    }
+
+    #[test]
+    fn formats_correctly() {
+        for (val, expected) in [
+            (
+                DateTime::builder()
+                    .year(1996)
+                    .month(12)
+                    .day(19)
+                    .hour(12)
+                    .build()
+                    .unwrap(),
+                graphql_input_value!("1996-12-19T12:00:00Z"),
+            ),
+            (
+                DateTime::builder()
+                    .year(1564)
+                    .month(1)
+                    .day(30)
+                    .hour(5)
+                    .minute(3)
+                    .second(3)
+                    .millisecond(1)
+                    .build()
+                    .unwrap(),
+                graphql_input_value!("1564-01-30T05:03:03.001Z"),
+            ),
+        ] {
+            let actual: InputValue = val.to_input_value();
+
+            assert_eq!(actual, expected, "on value: {val}");
+        }
     }
 }
diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs
index c74eaa01..ddb849c5 100644
--- a/juniper/src/integrations/chrono.rs
+++ b/juniper/src/integrations/chrono.rs
@@ -2,20 +2,21 @@
 //!
 //! # Supported types
 //!
-//! | Rust type         | Format                | GraphQL scalar    |
-//! |-------------------|-----------------------|-------------------|
-//! | [`NaiveDate`]     | `yyyy-MM-dd`          | [`Date`][s1]      |
-//! | [`NaiveTime`]     | `HH:mm[:ss[.SSS]]`    | [`LocalTime`][s2] |
-//! | [`NaiveDateTime`] | `yyyy-MM-dd HH:mm:ss` | `LocalDateTime`   |
-//! | [`DateTime`]      | [RFC 3339] string     | [`DateTime`][s4]  |
+//! | Rust type         | Format                | GraphQL scalar        |
+//! |-------------------|-----------------------|-----------------------|
+//! | [`NaiveDate`]     | `yyyy-MM-dd`          | [`LocalDate`][s1]     |
+//! | [`NaiveTime`]     | `HH:mm[:ss[.SSS]]`    | [`LocalTime`][s2]     |
+//! | [`NaiveDateTime`] | `yyyy-MM-ddTHH:mm:ss` | [`LocalDateTime`][s3] |
+//! | [`DateTime`]      | [RFC 3339] string     | [`DateTime`][s4]      |
 //!
 //! [`DateTime`]: chrono::DateTime
 //! [`NaiveDate`]: chrono::naive::NaiveDate
 //! [`NaiveDateTime`]: chrono::naive::NaiveDateTime
 //! [`NaiveTime`]: chrono::naive::NaiveTime
 //! [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
-//! [s1]: https://graphql-scalars.dev/docs/scalars/date
+//! [s1]: https://graphql-scalars.dev/docs/scalars/local-date
 //! [s2]: https://graphql-scalars.dev/docs/scalars/local-time
+//! [s3]: https://graphql-scalars.dev/docs/scalars/local-date-time
 //! [s4]: https://graphql-scalars.dev/docs/scalars/date-time
 
 use std::fmt;
@@ -29,42 +30,43 @@ use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 /// Represents a description of the date (as used for birthdays, for example).
 /// It cannot represent an instant on the time-line.
 ///
-/// [`Date` scalar][1] compliant.
+/// [`LocalDate` scalar][1] compliant.
 ///
 /// See also [`chrono::NaiveDate`][2] for details.
 ///
-/// [1]: https://graphql-scalars.dev/docs/scalars/date
+/// [1]: https://graphql-scalars.dev/docs/scalars/local-date
 /// [2]: https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDate.html
 #[graphql_scalar(
-    with = date,
+    with = local_date,
     parse_token(String),
-    specified_by_url = "https://graphql-scalars.dev/docs/scalars/date",
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/local-date",
 )]
-pub type Date = chrono::NaiveDate;
+pub type LocalDate = chrono::NaiveDate;
 
-mod date {
+mod local_date {
     use super::*;
 
-    /// Format of a [`Date` scalar][1].
+    /// Format of a [`LocalDate` scalar][1].
     ///
-    /// [1]: https://graphql-scalars.dev/docs/scalars/date
+    /// [1]: https://graphql-scalars.dev/docs/scalars/local-date
     const FORMAT: &str = "%Y-%m-%d";
 
-    pub(super) fn to_output<S>(v: &Date) -> Value<S>
+    pub(super) fn to_output<S>(v: &LocalDate) -> Value<S>
     where
         S: ScalarValue,
     {
         Value::scalar(v.format(FORMAT).to_string())
     }
 
-    pub(super) fn from_input<S>(v: &InputValue<S>) -> Result<Date, String>
+    pub(super) fn from_input<S>(v: &InputValue<S>) -> Result<LocalDate, String>
     where
         S: ScalarValue,
     {
         v.as_string_value()
             .ok_or_else(|| format!("Expected `String`, found: {v}"))
             .and_then(|s| {
-                Date::parse_from_str(s, FORMAT).map_err(|e| format!("Invalid `Date`: {e}"))
+                LocalDate::parse_from_str(s, FORMAT)
+                    .map_err(|e| format!("Invalid `LocalDate`: {e}"))
             })
     }
 }
@@ -140,19 +142,28 @@ mod local_time {
     }
 }
 
-/// Combined date and time (without time zone) in `yyyy-MM-dd HH:mm:ss` format.
+/// Combined date and time (without time zone) in `yyyy-MM-ddTHH:mm:ss` format.
 ///
-/// See also [`chrono::NaiveDateTime`][1] for details.
+/// [`LocalDateTime` scalar][1] compliant.
 ///
-/// [1]: https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html
-#[graphql_scalar(with = local_date_time, parse_token(String))]
+/// See also [`chrono::NaiveDateTime`][2] for details.
+///
+/// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
+/// [2]: https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html
+#[graphql_scalar(
+    with = local_date_time,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/local-date-time",
+)]
 pub type LocalDateTime = chrono::NaiveDateTime;
 
 mod local_date_time {
     use super::*;
 
-    /// Format of a `LocalDateTime` scalar.
-    const FORMAT: &str = "%Y-%m-%d %H:%M:%S";
+    /// Format of a [`LocalDateTime` scalar][1].
+    ///
+    /// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
+    const FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
 
     pub(super) fn to_output<S>(v: &LocalDateTime) -> Value<S>
     where
@@ -329,19 +340,19 @@ impl FromFixedOffset for chrono_tz::Tz {
 }
 
 #[cfg(test)]
-mod date_test {
+mod local_date_test {
     use crate::{graphql_input_value, FromInputValue as _, InputValue, ToInputValue as _};
 
-    use super::Date;
+    use super::LocalDate;
 
     #[test]
     fn parses_correct_input() {
         for (raw, expected) in [
-            ("1996-12-19", Date::from_ymd_opt(1996, 12, 19)),
-            ("1564-01-30", Date::from_ymd_opt(1564, 01, 30)),
+            ("1996-12-19", LocalDate::from_ymd_opt(1996, 12, 19)),
+            ("1564-01-30", LocalDate::from_ymd_opt(1564, 01, 30)),
         ] {
             let input: InputValue = graphql_input_value!((raw));
-            let parsed = Date::from_input_value(&input);
+            let parsed = LocalDate::from_input_value(&input);
 
             assert!(
                 parsed.is_ok(),
@@ -369,7 +380,7 @@ mod date_test {
             graphql_input_value!(false),
         ] {
             let input: InputValue = input;
-            let parsed = Date::from_input_value(&input);
+            let parsed = LocalDate::from_input_value(&input);
 
             assert!(parsed.is_err(), "allows input: {input:?}");
         }
@@ -379,15 +390,15 @@ mod date_test {
     fn formats_correctly() {
         for (val, expected) in [
             (
-                Date::from_ymd_opt(1996, 12, 19),
+                LocalDate::from_ymd_opt(1996, 12, 19),
                 graphql_input_value!("1996-12-19"),
             ),
             (
-                Date::from_ymd_opt(1564, 01, 30),
+                LocalDate::from_ymd_opt(1564, 01, 30),
                 graphql_input_value!("1564-01-30"),
             ),
             (
-                Date::from_ymd_opt(2020, 01, 01),
+                LocalDate::from_ymd_opt(2020, 01, 01),
                 graphql_input_value!("2020-01-01"),
             ),
         ] {
@@ -497,14 +508,14 @@ mod local_date_time_test {
     fn parses_correct_input() {
         for (raw, expected) in [
             (
-                "1996-12-19 14:23:43",
+                "1996-12-19T14:23:43",
                 LocalDateTime::new(
                     NaiveDate::from_ymd_opt(1996, 12, 19).unwrap(),
                     NaiveTime::from_hms_opt(14, 23, 43).unwrap(),
                 ),
             ),
             (
-                "1564-01-30 14:00:00",
+                "1564-01-30T14:00:00",
                 LocalDateTime::new(
                     NaiveDate::from_ymd_opt(1564, 1, 30).unwrap(),
                     NaiveTime::from_hms_opt(14, 00, 00).unwrap(),
@@ -530,15 +541,17 @@ mod local_date_time_test {
             graphql_input_value!("12:"),
             graphql_input_value!("56:34:22"),
             graphql_input_value!("56:34:22.000"),
-            graphql_input_value!("1996-12-19T14:23:43"),
-            graphql_input_value!("1996-12-19 14:23:43Z"),
-            graphql_input_value!("1996-12-19 14:23:43.543"),
-            graphql_input_value!("1996-12-19 14:23"),
-            graphql_input_value!("1996-12-19 14:23:"),
-            graphql_input_value!("1996-12-19 23:78:43"),
-            graphql_input_value!("1996-12-19 23:18:99"),
-            graphql_input_value!("1996-12-19 24:00:00"),
-            graphql_input_value!("1996-12-19 99:02:13"),
+            graphql_input_value!("1996-12-1914:23:43"),
+            graphql_input_value!("1996-12-19 14:23:43"),
+            graphql_input_value!("1996-12-19Q14:23:43"),
+            graphql_input_value!("1996-12-19T14:23:43Z"),
+            graphql_input_value!("1996-12-19T14:23:43.543"),
+            graphql_input_value!("1996-12-19T14:23"),
+            graphql_input_value!("1996-12-19T14:23:"),
+            graphql_input_value!("1996-12-19T23:78:43"),
+            graphql_input_value!("1996-12-19T23:18:99"),
+            graphql_input_value!("1996-12-19T24:00:00"),
+            graphql_input_value!("1996-12-19T99:02:13"),
             graphql_input_value!("i'm not even a datetime"),
             graphql_input_value!(2.32),
             graphql_input_value!(1),
@@ -560,14 +573,14 @@ mod local_date_time_test {
                     NaiveDate::from_ymd_opt(1996, 12, 19).unwrap(),
                     NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
                 ),
-                graphql_input_value!("1996-12-19 00:00:00"),
+                graphql_input_value!("1996-12-19T00:00:00"),
             ),
             (
                 LocalDateTime::new(
                     NaiveDate::from_ymd_opt(1564, 1, 30).unwrap(),
                     NaiveTime::from_hms_opt(14, 0, 0).unwrap(),
                 ),
-                graphql_input_value!("1564-01-30 14:00:00"),
+                graphql_input_value!("1564-01-30T14:00:00"),
             ),
         ] {
             let actual: InputValue = val.to_input_value();
@@ -737,7 +750,9 @@ mod integration_test {
         types::scalars::{EmptyMutation, EmptySubscription},
     };
 
-    use super::{Date, DateTime, FixedOffset, FromFixedOffset, LocalDateTime, LocalTime, TimeZone};
+    use super::{
+        DateTime, FixedOffset, FromFixedOffset, LocalDate, LocalDateTime, LocalTime, TimeZone,
+    };
 
     #[tokio::test]
     async fn serializes() {
@@ -784,8 +799,8 @@ mod integration_test {
 
         #[graphql_object]
         impl Root {
-            fn date() -> Date {
-                Date::from_ymd_opt(2015, 3, 14).unwrap()
+            fn local_date() -> LocalDate {
+                LocalDate::from_ymd_opt(2015, 3, 14).unwrap()
             }
 
             fn local_time() -> LocalTime {
@@ -794,7 +809,7 @@ mod integration_test {
 
             fn local_date_time() -> LocalDateTime {
                 LocalDateTime::new(
-                    Date::from_ymd_opt(2016, 7, 8).unwrap(),
+                    LocalDate::from_ymd_opt(2016, 7, 8).unwrap(),
                     LocalTime::from_hms_opt(9, 10, 11).unwrap(),
                 )
             }
@@ -802,7 +817,7 @@ mod integration_test {
             fn date_time() -> DateTime<chrono::Utc> {
                 DateTime::from_naive_utc_and_offset(
                     LocalDateTime::new(
-                        Date::from_ymd_opt(1996, 12, 20).unwrap(),
+                        LocalDate::from_ymd_opt(1996, 12, 20).unwrap(),
                         LocalTime::from_hms_opt(0, 39, 57).unwrap(),
                     ),
                     chrono::Utc,
@@ -819,7 +834,7 @@ mod integration_test {
         }
 
         const DOC: &str = r#"{
-            date
+            localDate
             localTime
             localDateTime
             dateTime,
@@ -837,9 +852,9 @@ mod integration_test {
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
                 graphql_value!({
-                    "date": "2015-03-14",
+                    "localDate": "2015-03-14",
                     "localTime": "16:07:08",
-                    "localDateTime": "2016-07-08 09:10:11",
+                    "localDateTime": "2016-07-08T09:10:11",
                     "dateTime": "1996-12-20T00:39:57Z",
                     "passDateTime": "2014-11-28T12:00:09Z",
                     "transformDateTime": "2014-11-28T12:00:09Z",
diff --git a/juniper/src/integrations/chrono_tz.rs b/juniper/src/integrations/chrono_tz.rs
index 5c30b211..bac4b5e5 100644
--- a/juniper/src/integrations/chrono_tz.rs
+++ b/juniper/src/integrations/chrono_tz.rs
@@ -2,27 +2,35 @@
 //!
 //! # Supported types
 //!
-//! | Rust type | Format             | GraphQL scalar |
-//! |-----------|--------------------|----------------|
-//! | [`Tz`]    | [IANA database][1] | `TimeZone`     |
+//! | Rust type | Format             | GraphQL scalar   |
+//! |-----------|--------------------|------------------|
+//! | [`Tz`]    | [IANA database][1] | [`TimeZone`][s1] |
 //!
 //! [`chrono-tz`]: chrono_tz
 //! [`Tz`]: chrono_tz::Tz
 //! [1]: http://www.iana.org/time-zones
+//! [s1]: https://graphql-scalars.dev/docs/scalars/time-zone
 
 use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 
-/// Timezone based on [`IANA` database][1].
+/// Timezone based on [`IANA` database][0].
 ///
-/// See ["List of tz database time zones"][2] `TZ database name` column for
+/// See ["List of tz database time zones"][3] `TZ database name` column for
 /// available names.
 ///
-/// See also [`chrono_tz::Tz`][3] for detals.
+/// [`TimeZone` scalar][1] compliant.
 ///
-/// [1]: https://www.iana.org/time-zones
-/// [2]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-/// [3]: https://docs.rs/chrono-tz/latest/chrono_tz/enum.Tz.html
-#[graphql_scalar(with = tz, parse_token(String))]
+/// See also [`chrono_tz::Tz`][2] for details.
+///
+/// [0]: https://www.iana.org/time-zones
+/// [1]: https://graphql-scalars.dev/docs/scalars/time-zone
+/// [2]: https://docs.rs/chrono-tz/*/chrono_tz/enum.Tz.html
+/// [3]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+#[graphql_scalar(
+    with = tz,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/time-zone",
+)]
 pub type TimeZone = chrono_tz::Tz;
 
 mod tz {
diff --git a/juniper/src/integrations/jiff.rs b/juniper/src/integrations/jiff.rs
index 197454d1..bea0bdaf 100644
--- a/juniper/src/integrations/jiff.rs
+++ b/juniper/src/integrations/jiff.rs
@@ -45,7 +45,7 @@ use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 /// See also [`jiff::civil::Date`][2] for details.
 ///
 /// [1]: https://graphql-scalars.dev/docs/scalars/local-date
-/// [2]: https://docs.rs/jiff/latest/jiff/civil/struct.Date.html
+/// [2]: https://docs.rs/jiff/*/jiff/civil/struct.Date.html
 #[graphql_scalar(
     with = local_date,
     parse_token(String),
@@ -91,7 +91,7 @@ mod local_date {
 /// See also [`jiff::civil::Time`][2] for details.
 ///
 /// [1]: https://graphql-scalars.dev/docs/scalars/local-time
-/// [2]: https://docs.rs/jiff/latest/jiff/civil/struct.Time.html
+/// [2]: https://docs.rs/jiff/*/jiff/civil/struct.Time.html
 #[graphql_scalar(
     with = local_time,
     parse_token(String),
@@ -162,7 +162,7 @@ mod local_time {
 /// See also [`jiff::civil::DateTime`][2] for details.
 ///
 /// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
-/// [2]: https://docs.rs/jiff/latest/jiff/civil/struct.DateTime.html
+/// [2]: https://docs.rs/jiff/*/jiff/civil/struct.DateTime.html
 #[graphql_scalar(
     with = local_date_time,
     parse_token(String),
@@ -176,7 +176,7 @@ mod local_date_time {
     /// Format of a [`LocalDateTime` scalar][1].
     ///
     /// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
-    const FORMAT: &str = "%Y-%m-%d %H:%M:%S";
+    const FORMAT: &str = "%Y-%m-%dT%H:%M:%S";
 
     pub(super) fn to_output<S>(v: &LocalDateTime) -> Value<S>
     where
@@ -207,7 +207,7 @@ mod local_date_time {
 /// See also [`jiff::Timestamp`][2] for details.
 ///
 /// [1]: https://graphql-scalars.dev/docs/scalars/date-time
-/// [2]: https://docs.rs/jiff/latest/jiff/struct.Timestamp.html
+/// [2]: https://docs.rs/jiff/*/jiff/struct.Timestamp.html
 #[graphql_scalar(
     with = date_time,
     parse_token(String),
@@ -252,7 +252,7 @@ mod date_time {
 /// See also [`jiff::Span`][2] for details.
 ///
 /// [1]: https://graphql-scalars.dev/docs/scalars/duration
-/// [2]: https://docs.rs/jiff/latest/jiff/struct.Span.html
+/// [2]: https://docs.rs/jiff/*/jiff/struct.Span.html
 #[graphql_scalar(
     with = duration,
     parse_token(String),
@@ -444,11 +444,11 @@ mod local_date_time_test {
     fn parses_correct_input() {
         for (raw, expected) in [
             (
-                "1996-12-19 14:23:43",
+                "1996-12-19T14:23:43",
                 LocalDateTime::constant(1996, 12, 19, 14, 23, 43, 0),
             ),
             (
-                "1564-01-30 14:00:00",
+                "1564-01-30T14:00:00",
                 LocalDateTime::constant(1564, 1, 30, 14, 00, 00, 0),
             ),
         ] {
@@ -471,15 +471,17 @@ mod local_date_time_test {
             graphql_input_value!("12:"),
             graphql_input_value!("56:34:22"),
             graphql_input_value!("56:34:22.000"),
-            graphql_input_value!("1996-12-19T14:23:43"),
-            graphql_input_value!("1996-12-19 14:23:43Z"),
-            graphql_input_value!("1996-12-19 14:23:43.543"),
-            graphql_input_value!("1996-12-19 14:23"),
-            graphql_input_value!("1996-12-19 14:23:"),
-            graphql_input_value!("1996-12-19 23:78:43"),
-            graphql_input_value!("1996-12-19 23:18:99"),
-            graphql_input_value!("1996-12-19 24:00:00"),
-            graphql_input_value!("1996-12-19 99:02:13"),
+            graphql_input_value!("1996-12-1914:23:43"),
+            graphql_input_value!("1996-12-19 14:23:43"),
+            graphql_input_value!("1996-12-19Q14:23:43"),
+            graphql_input_value!("1996-12-19T14:23:43Z"),
+            graphql_input_value!("1996-12-19T14:23:43.543"),
+            graphql_input_value!("1996-12-19T14:23"),
+            graphql_input_value!("1996-12-19T14:23:"),
+            graphql_input_value!("1996-12-19T23:78:43"),
+            graphql_input_value!("1996-12-19T23:18:99"),
+            graphql_input_value!("1996-12-19T24:00:00"),
+            graphql_input_value!("1996-12-19T99:02:13"),
             graphql_input_value!("i'm not even a datetime"),
             graphql_input_value!(2.32),
             graphql_input_value!(1),
@@ -498,11 +500,11 @@ mod local_date_time_test {
         for (val, expected) in [
             (
                 LocalDateTime::constant(1996, 12, 19, 0, 0, 0, 0),
-                graphql_input_value!("1996-12-19 00:00:00"),
+                graphql_input_value!("1996-12-19T00:00:00"),
             ),
             (
                 LocalDateTime::constant(1564, 1, 30, 14, 0, 0, 0),
-                graphql_input_value!("1564-01-30 14:00:00"),
+                graphql_input_value!("1564-01-30T14:00:00"),
             ),
         ] {
             let actual: InputValue = val.to_input_value();
@@ -586,6 +588,7 @@ mod date_time_test {
             graphql_input_value!("56:34:22"),
             graphql_input_value!("56:34:22.000"),
             graphql_input_value!("1996-12-1914:23:43"),
+            graphql_input_value!("1996-12-19 14:23:43"),
             graphql_input_value!("1996-12-19Q14:23:43Z"),
             graphql_input_value!("1996-12-19T14:23:43"),
             graphql_input_value!("1996-12-19T14:23:43ZZ"),
@@ -639,7 +642,7 @@ mod date_time_test {
 
 #[cfg(test)]
 mod duration_test {
-    use jiff::ToSpan;
+    use jiff::ToSpan as _;
 
     use crate::{graphql_input_value, FromInputValue as _, InputValue, ToInputValue as _};
 
@@ -731,3 +734,81 @@ mod duration_test {
         }
     }
 }
+
+#[cfg(test)]
+mod integration_test {
+    use jiff::{civil, tz::TimeZone, ToSpan as _};
+
+    use crate::{
+        execute, graphql_object, graphql_value, graphql_vars,
+        schema::model::RootNode,
+        types::scalars::{EmptyMutation, EmptySubscription},
+    };
+
+    use super::{DateTime, Duration, LocalDate, LocalDateTime, LocalTime};
+
+    #[tokio::test]
+    async fn serializes() {
+        struct Root;
+
+        #[graphql_object]
+        impl Root {
+            fn local_date() -> LocalDate {
+                LocalDate::constant(2015, 3, 14)
+            }
+
+            fn local_time() -> LocalTime {
+                LocalTime::constant(16, 7, 8, 0)
+            }
+
+            fn local_date_time() -> LocalDateTime {
+                LocalDateTime::constant(2016, 7, 8, 9, 10, 11, 0)
+            }
+
+            fn date_time() -> DateTime {
+                civil::DateTime::constant(2014, 11, 28, 12, 0, 9, 50_000_000)
+                    .to_zoned(TimeZone::UTC)
+                    .unwrap()
+                    .timestamp()
+            }
+
+            fn duration() -> Duration {
+                1.year()
+                    .months(1)
+                    .days(1)
+                    .hours(1)
+                    .minutes(1)
+                    .seconds(1)
+                    .milliseconds(100)
+            }
+        }
+
+        const DOC: &str = r#"{
+            localDate
+            localTime
+            localDateTime
+            dateTime,
+            duration,
+        }"#;
+
+        let schema = RootNode::new(
+            Root,
+            EmptyMutation::<()>::new(),
+            EmptySubscription::<()>::new(),
+        );
+
+        assert_eq!(
+            execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
+            Ok((
+                graphql_value!({
+                    "localDate": "2015-03-14",
+                    "localTime": "16:07:08",
+                    "localDateTime": "2016-07-08T09:10:11",
+                    "dateTime": "2014-11-28T12:00:09.05Z",
+                    "duration": "P1y1m1dT1h1m1.1s",
+                }),
+                vec![],
+            )),
+        );
+    }
+}
diff --git a/juniper/src/integrations/time.rs b/juniper/src/integrations/time.rs
index 84e178b9..37f40cd7 100644
--- a/juniper/src/integrations/time.rs
+++ b/juniper/src/integrations/time.rs
@@ -2,13 +2,13 @@
 //!
 //! # Supported types
 //!
-//! | Rust type             | Format                | GraphQL scalar      |
-//! |-----------------------|-----------------------|---------------------|
-//! | [`Date`]              | `yyyy-MM-dd`          | [`Date`][s1]        |
-//! | [`Time`]              | `HH:mm[:ss[.SSS]]`    | [`LocalTime`][s2]   |
-//! | [`PrimitiveDateTime`] | `yyyy-MM-dd HH:mm:ss` | `LocalDateTime`     |
-//! | [`OffsetDateTime`]    | [RFC 3339] string     | [`DateTime`][s4]    |
-//! | [`UtcOffset`]         | `±hh:mm`              | [`UtcOffset`][s5]   |
+//! | Rust type             | Format                | GraphQL scalar        |
+//! |-----------------------|-----------------------|-----------------------|
+//! | [`Date`]              | `yyyy-MM-dd`          | [`LocalDate`][s1]     |
+//! | [`Time`]              | `HH:mm[:ss[.SSS]]`    | [`LocalTime`][s2]     |
+//! | [`PrimitiveDateTime`] | `yyyy-MM-ddTHH:mm:ss` | [`LocalDateTime`][s3] |
+//! | [`OffsetDateTime`]    | [RFC 3339] string     | [`DateTime`][s4]      |
+//! | [`UtcOffset`]         | `±hh:mm`              | [`UtcOffset`][s5]     |
 //!
 //! [`Date`]: time::Date
 //! [`OffsetDateTime`]: time::OffsetDateTime
@@ -16,8 +16,9 @@
 //! [`Time`]: time::Time
 //! [`UtcOffset`]: time::UtcOffset
 //! [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
-//! [s1]: https://graphql-scalars.dev/docs/scalars/date
+//! [s1]: https://graphql-scalars.dev/docs/scalars/local-date
 //! [s2]: https://graphql-scalars.dev/docs/scalars/local-time
+//! [s3]: https://graphql-scalars.dev/docs/scalars/local-date-time
 //! [s4]: https://graphql-scalars.dev/docs/scalars/date-time
 //! [s5]: https://graphql-scalars.dev/docs/scalars/utc-offset
 
@@ -33,38 +34,40 @@ use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 /// Represents a description of the date (as used for birthdays, for example).
 /// It cannot represent an instant on the time-line.
 ///
-/// [`Date` scalar][1] compliant.
+/// [`LocalDate` scalar][1] compliant.
 ///
 /// See also [`time::Date`][2] for details.
 ///
-/// [1]: https://graphql-scalars.dev/docs/scalars/date
+/// [1]: https://graphql-scalars.dev/docs/scalars/local-date
 /// [2]: https://docs.rs/time/*/time/struct.Date.html
 #[graphql_scalar(
-    with = date,
+    with = local_date,
     parse_token(String),
-    specified_by_url = "https://graphql-scalars.dev/docs/scalars/date",
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/local-date",
 )]
-pub type Date = time::Date;
+pub type LocalDate = time::Date;
 
-mod date {
+mod local_date {
     use super::*;
 
-    /// Format of a [`Date` scalar][1].
+    /// Format of a [`LocalDate` scalar][1].
     ///
-    /// [1]: https://graphql-scalars.dev/docs/scalars/date
+    /// [1]: https://graphql-scalars.dev/docs/scalars/local-date
     const FORMAT: &[BorrowedFormatItem<'_>] = format_description!("[year]-[month]-[day]");
 
-    pub(super) fn to_output<S: ScalarValue>(v: &Date) -> Value<S> {
+    pub(super) fn to_output<S: ScalarValue>(v: &LocalDate) -> Value<S> {
         Value::scalar(
             v.format(FORMAT)
-                .unwrap_or_else(|e| panic!("failed to format `Date`: {e}")),
+                .unwrap_or_else(|e| panic!("failed to format `LocalDate`: {e}")),
         )
     }
 
-    pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<Date, String> {
+    pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<LocalDate, String> {
         v.as_string_value()
             .ok_or_else(|| format!("Expected `String`, found: {v}"))
-            .and_then(|s| Date::parse(s, FORMAT).map_err(|e| format!("Invalid `Date`: {e}")))
+            .and_then(|s| {
+                LocalDate::parse(s, FORMAT).map_err(|e| format!("Invalid `LocalDate`: {e}"))
+            })
     }
 }
 
@@ -129,20 +132,29 @@ mod local_time {
     }
 }
 
-/// Combined date and time (without time zone) in `yyyy-MM-dd HH:mm:ss` format.
+/// Combined date and time (without time zone) in `yyyy-MM-ddTHH:mm:ss` format.
+///
+/// [`LocalDateTime` scalar][1] compliant.
 ///
 /// See also [`time::PrimitiveDateTime`][2] for details.
 ///
+/// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
 /// [2]: https://docs.rs/time/*/time/struct.PrimitiveDateTime.html
-#[graphql_scalar(with = local_date_time, parse_token(String))]
+#[graphql_scalar(
+    with = local_date_time,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/local-date-time",
+)]
 pub type LocalDateTime = time::PrimitiveDateTime;
 
 mod local_date_time {
     use super::*;
 
-    /// Format of a [`LocalDateTime`] scalar.
+    /// Format of a [`LocalDateTime` scalar][1].
+    ///
+    /// [1]: https://graphql-scalars.dev/docs/scalars/local-date-time
     const FORMAT: &[BorrowedFormatItem<'_>] =
-        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
+        format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
 
     pub(super) fn to_output<S: ScalarValue>(v: &LocalDateTime) -> Value<S> {
         Value::scalar(
@@ -243,12 +255,12 @@ mod utc_offset {
 }
 
 #[cfg(test)]
-mod date_test {
+mod local_date_test {
     use time::macros::date;
 
     use crate::{graphql_input_value, FromInputValue as _, InputValue, ToInputValue as _};
 
-    use super::Date;
+    use super::LocalDate;
 
     #[test]
     fn parses_correct_input() {
@@ -257,7 +269,7 @@ mod date_test {
             ("1564-01-30", date!(1564 - 01 - 30)),
         ] {
             let input: InputValue = graphql_input_value!((raw));
-            let parsed = Date::from_input_value(&input);
+            let parsed = LocalDate::from_input_value(&input);
 
             assert!(
                 parsed.is_ok(),
@@ -285,7 +297,7 @@ mod date_test {
             graphql_input_value!(false),
         ] {
             let input: InputValue = input;
-            let parsed = Date::from_input_value(&input);
+            let parsed = LocalDate::from_input_value(&input);
 
             assert!(parsed.is_err(), "allows input: {input:?}");
         }
@@ -392,8 +404,8 @@ mod local_date_time_test {
     #[test]
     fn parses_correct_input() {
         for (raw, expected) in [
-            ("1996-12-19 14:23:43", datetime!(1996-12-19 14:23:43)),
-            ("1564-01-30 14:00:00", datetime!(1564-01-30 14:00)),
+            ("1996-12-19T14:23:43", datetime!(1996-12-19 14:23:43)),
+            ("1564-01-30T14:00:00", datetime!(1564-01-30 14:00)),
         ] {
             let input: InputValue = graphql_input_value!((raw));
             let parsed = LocalDateTime::from_input_value(&input);
@@ -415,16 +427,17 @@ mod local_date_time_test {
             graphql_input_value!("56:34:22"),
             graphql_input_value!("56:34:22.000"),
             graphql_input_value!("1996-12-1914:23:43"),
-            graphql_input_value!("1996-12-19T14:23:43"),
-            graphql_input_value!("1996-12-19 14:23:43Z"),
-            graphql_input_value!("1996-12-19 14:23:43.543"),
-            graphql_input_value!("1996-12-19 14:23"),
-            graphql_input_value!("1996-12-19 14:23:1"),
-            graphql_input_value!("1996-12-19 14:23:"),
-            graphql_input_value!("1996-12-19 23:78:43"),
-            graphql_input_value!("1996-12-19 23:18:99"),
-            graphql_input_value!("1996-12-19 24:00:00"),
-            graphql_input_value!("1996-12-19 99:02:13"),
+            graphql_input_value!("1996-12-19 14:23:43"),
+            graphql_input_value!("1996-12-19Q14:23:43"),
+            graphql_input_value!("1996-12-19T14:23:43Z"),
+            graphql_input_value!("1996-12-19T14:23:43.543"),
+            graphql_input_value!("1996-12-19T14:23"),
+            graphql_input_value!("1996-12-19T14:23:1"),
+            graphql_input_value!("1996-12-19T14:23:"),
+            graphql_input_value!("1996-12-19T23:78:43"),
+            graphql_input_value!("1996-12-19T23:18:99"),
+            graphql_input_value!("1996-12-19T24:00:00"),
+            graphql_input_value!("1996-12-19T99:02:13"),
             graphql_input_value!("i'm not even a datetime"),
             graphql_input_value!(2.32),
             graphql_input_value!(1),
@@ -443,11 +456,11 @@ mod local_date_time_test {
         for (val, expected) in [
             (
                 datetime!(1996-12-19 12:00 am),
-                graphql_input_value!("1996-12-19 00:00:00"),
+                graphql_input_value!("1996-12-19T00:00:00"),
             ),
             (
                 datetime!(1564-01-30 14:00),
-                graphql_input_value!("1564-01-30 14:00:00"),
+                graphql_input_value!("1564-01-30T14:00:00"),
             ),
         ] {
             let actual: InputValue = val.to_input_value();
@@ -630,7 +643,7 @@ mod integration_test {
         types::scalars::{EmptyMutation, EmptySubscription},
     };
 
-    use super::{Date, DateTime, LocalDateTime, LocalTime, UtcOffset};
+    use super::{DateTime, LocalDate, LocalDateTime, LocalTime, UtcOffset};
 
     #[tokio::test]
     async fn serializes() {
@@ -638,7 +651,7 @@ mod integration_test {
 
         #[graphql_object]
         impl Root {
-            fn date() -> Date {
+            fn local_date() -> LocalDate {
                 date!(2015 - 03 - 14)
             }
 
@@ -660,7 +673,7 @@ mod integration_test {
         }
 
         const DOC: &str = r#"{
-            date
+            localDate
             localTime
             localDateTime
             dateTime,
@@ -677,9 +690,9 @@ mod integration_test {
             execute(DOC, None, &schema, &graphql_vars! {}, &()).await,
             Ok((
                 graphql_value!({
-                    "date": "2015-03-14",
+                    "localDate": "2015-03-14",
                     "localTime": "16:07:08",
-                    "localDateTime": "2016-07-08 09:10:11",
+                    "localDateTime": "2016-07-08T09:10:11",
                     "dateTime": "1996-12-20T00:39:57Z",
                     "utcOffset": "+11:30",
                 }),
diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs
index 6121ac56..e7d9053f 100644
--- a/juniper/src/integrations/url.rs
+++ b/juniper/src/integrations/url.rs
@@ -1,8 +1,32 @@
-//! GraphQL support for [url](https://github.com/servo/rust-url) types.
+//! GraphQL support for [`url`] crate types.
+//!
+//! # Supported types
+//!
+//! | Rust type | GraphQL scalar |
+//! |-----------|----------------|
+//! | [`Url`]   | [`URL`][s1]    |
+//!
+//! [`Url`]: url::Url
+//! [s1]: https://graphql-scalars.dev/docs/scalars/url
 
 use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 
-#[graphql_scalar(with = url_scalar, parse_token(String))]
+/// [Standard URL][0] format as specified in [RFC 3986].
+///
+/// [`URL` scalar][1] compliant.
+///
+/// See also [`url::Url`][2] for details.
+///
+/// [0]: http://url.spec.whatwg.org
+/// [1]: https://graphql-scalars.dev/docs/scalars/url
+/// [2]: https://docs.rs/url/*/url/struct.Url.html
+/// [RFC 3986]: https://datatracker.ietf.org/doc/html/rfc3986
+#[graphql_scalar(
+    name = "URL",
+    with = url_scalar,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/url",
+)]
 type Url = url::Url;
 
 mod url_scalar {
@@ -15,7 +39,7 @@ mod url_scalar {
     pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<Url, String> {
         v.as_string_value()
             .ok_or_else(|| format!("Expected `String`, found: {v}"))
-            .and_then(|s| Url::parse(s).map_err(|e| format!("Failed to parse `Url`: {e}")))
+            .and_then(|s| Url::parse(s).map_err(|e| format!("Failed to parse `URL`: {e}")))
     }
 }
 
diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs
index 4fed7f5f..727c8bc0 100644
--- a/juniper/src/integrations/uuid.rs
+++ b/juniper/src/integrations/uuid.rs
@@ -1,10 +1,31 @@
-//! GraphQL support for [uuid](https://doc.rust-lang.org/uuid/uuid/struct.Uuid.html) types.
-
-#![allow(clippy::needless_lifetimes)]
+//! GraphQL support for [`uuid`] crate types.
+//!
+//! # Supported types
+//!
+//! | Rust type | GraphQL scalar |
+//! |-----------|----------------|
+//! | [`Uuid`]  | [`UUID`][s1]   |
+//!
+//! [`Uuid`]: uuid::Uuid
+//! [s1]: https://graphql-scalars.dev/docs/scalars/uuid
 
 use crate::{graphql_scalar, InputValue, ScalarValue, Value};
 
-#[graphql_scalar(with = uuid_scalar, parse_token(String))]
+/// [Universally Unique Identifier][0] (UUID).
+///
+/// [`UUID` scalar][1] compliant.
+///
+/// See also [`uuid::Uuid`][2] for details.
+///
+/// [0]: https://en.wikipedia.org/wiki/Universally_unique_identifier
+/// [1]: https://graphql-scalars.dev/docs/scalars/uuid
+/// [2]: https://docs.rs/uuid/*/uuid/struct.Uuid.html
+#[graphql_scalar(
+    name = "UUID",
+    with = uuid_scalar,
+    parse_token(String),
+    specified_by_url = "https://graphql-scalars.dev/docs/scalars/uuid",
+)]
 type Uuid = uuid::Uuid;
 
 mod uuid_scalar {
@@ -17,7 +38,7 @@ mod uuid_scalar {
     pub(super) fn from_input<S: ScalarValue>(v: &InputValue<S>) -> Result<Uuid, String> {
         v.as_string_value()
             .ok_or_else(|| format!("Expected `String`, found: {v}"))
-            .and_then(|s| Uuid::parse_str(s).map_err(|e| format!("Failed to parse `Uuid`: {e}")))
+            .and_then(|s| Uuid::parse_str(s).map_err(|e| format!("Failed to parse `UUID`: {e}")))
     }
 }