2
2
//!
3
3
//! # Supported types
4
4
//!
5
- //! | Rust type | Format | GraphQL scalar |
6
- //! |---------------------------------------|- ----------------------------|-----------------------|
7
- //! | [`civil::Date`] | `yyyy-MM-dd` | [`LocalDate`][s1] |
8
- //! | [`civil::Time`] | `HH:mm[:ss[.SSS]]` | [`LocalTime`][s2] |
9
- //! | [`civil::DateTime`] | `yyyy-MM-ddTHH:mm:ss` | [`LocalDateTime`][s3] |
10
- //! | [`Timestamp`] | [RFC 3339] string | [`DateTime`][s4] |
11
- //! | [`Zoned`][^1] | [RFC 9557] string | `ZonedDateTime` |
12
- //! | [`tz::TimeZone`][^1] | [IANA database][1] /`±hh:mm` | `TimeZoneOrUtcOffset` |
13
- //! | [`tz::TimeZone`] via [`TimeZone`][^1] | [IANA database][1] | [`TimeZone`][s5] |
14
- //! | [`tz::Offset`] | `±hh:mm` | [`UtcOffset`][s6] |
15
- //! | [`Span`] | [ISO 8601] duration | [`Duration`][s7] |
5
+ //! | Rust type | Format | GraphQL scalar |
6
+ //! |----------------------------------------| ----------------------------|-----------------------|
7
+ //! | [`civil::Date`] | `yyyy-MM-dd` | [`LocalDate`][s1] |
8
+ //! | [`civil::Time`] | `HH:mm[:ss[.SSS]]` | [`LocalTime`][s2] |
9
+ //! | [`civil::DateTime`] | `yyyy-MM-ddTHH:mm:ss` | [`LocalDateTime`][s3] |
10
+ //! | [`Timestamp`] | [RFC 3339] string | [`DateTime`][s4] |
11
+ //! | [`Zoned`] [^1] | [RFC 9557] string | `ZonedDateTime` |
12
+ //! | [`tz::TimeZone`] [^1] | [IANA] identifier /`±hh:mm` | `TimeZoneOrUtcOffset` |
13
+ //! | [`tz::TimeZone`] via [`TimeZone`] [^1] | [IANA] identifier | [`TimeZone`][s5] |
14
+ //! | [`tz::Offset`] | `±hh:mm` | [`UtcOffset`][s6] |
15
+ //! | [`Span`] | [ISO 8601] duration | [`Duration`][s7] |
16
16
//!
17
- //! [^1]: For these, crate [`jiff`] must be installed with a feature flag that provides access to
18
- //! the Time Zone Database (e.g. by using the crate's default feature flags). See [`jiff` time zone
19
- //! features][tz] for details.
17
+ //! # [`tz::TimeZone`] types
20
18
//!
21
- //! # Time zone types
19
+ //! [`tz::TimeZone`] values can be either [IANA] identifiers or fixed offsets, corresponding to
20
+ //! GraphQL scalars [`TimeZone`][s5] and [`UtcOffset`][s6] accordingly. While a [`UtcOffset`][s6]
21
+ //! GraphQL scalar can be serialized from a [`tz::Offset`] directly, the newtype [`TimeZone`]
22
+ //! handles serialization to a [`TimeZone`][s5] GraphQL scalar, with implementations [`TryFrom`] and
23
+ //! [`Into`] a [`tz::TimeZone`].
22
24
//!
23
- //! `tz::TimeZone` values can be IANA time zone identifiers or fixed offsets, corresponding to
24
- //! GraphQL scalars [`TimeZone`][s5] and [`UtcOffset`][s6]. While `UtcOffset` can be serialized from
25
- //! [`tz::Offset`] directly, newtype [`TimeZone`] handles serialization to `TimeZone`, with
26
- //! [`TryFrom`] and [`Into`] implementations from and to `tz::TimeZone`.
25
+ //! In addition, a [`tz::TimeZone`] serializes to a `TimeZoneOrUtcOffset` GraphQL scalar, containing
26
+ //! either an [IANA] identifier or a fixed offset for clients being able to consume both values.
27
27
//!
28
- //! In addition, `tz::TimeZone` serializes to `TimeZoneOrUtcOffset` which is a GraphQL scalar that
29
- //! contains either an IANA identifier or a fixed offset for clients that can consume both values.
28
+ //! [^1]: For these, crate [`jiff`] must be installed with a feature flag that provides access to
29
+ //! the [IANA Time Zone Database][IANA] (e.g. by using the crate's default feature flags).
30
+ //! See [`jiff` time zone features][1] for details.
30
31
//!
31
32
//! [`civil::Date`]: jiff::civil::Date
32
33
//! [`civil::DateTime`]: jiff::civil::DateTime
36
37
//! [`tz::Offset`]: jiff::tz::Offset
37
38
//! [`tz::TimeZone`]: jiff::tz::TimeZone
38
39
//! [`Zoned`]: jiff::Zoned
40
+ //! [IANA]: http://iana.org/time-zones
39
41
//! [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601#Durations
40
42
//! [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
41
43
//! [RFC 9557]: https://datatracker.ietf.org/doc/html/rfc9557#section-4.1
46
48
//! [s5]: https://graphql-scalars.dev/docs/scalars/time-zone
47
49
//! [s6]: https://graphql-scalars.dev/docs/scalars/utc-offset
48
50
//! [s7]: https://graphql-scalars.dev/docs/scalars/duration
49
- //! [tz]: https://docs.rs/jiff/latest/jiff/index.html#time-zone-features
50
- //! [1]: http://www.iana.org/time-zones
51
+ //! [1]: https://docs.rs/jiff/latest/jiff/index.html#time-zone-features
51
52
52
53
use std:: { error:: Error , fmt, str} ;
53
54
@@ -264,7 +265,6 @@ mod date_time {
264
265
/// Time zone aware instant in time.
265
266
///
266
267
/// Can be thought of as combination of the following types, all rolled into one:
267
- ///
268
268
/// - [`Timestamp`][3] for indicating precise instant in time.
269
269
/// - [`DateTime`][4] for indicating "civil" calendar date and clock time.
270
270
/// - [`TimeZone`][5] for indicating how to apply time zone transitions while performing arithmetic.
@@ -281,6 +281,7 @@ mod date_time {
281
281
#[ graphql_scalar(
282
282
with = zoned_date_time,
283
283
parse_token( String ) ,
284
+ specified_by_url = "https://datatracker.ietf.org/doc/html/rfc9557#section-4.1" ,
284
285
) ]
285
286
pub type ZonedDateTime = jiff:: Zoned ;
286
287
@@ -348,14 +349,20 @@ mod duration {
348
349
}
349
350
}
350
351
351
- /// Representation of time zone or UTC offset.
352
+ /// Representation of a time zone or UTC offset.
352
353
///
353
- /// [IANA database][1] or `±hh:mm`.
354
+ /// Can be one of three possible representations:
355
+ /// - Identifier from the [IANA Time Zone Database][0].
356
+ /// - Fixed offset from UTC (`±hh:mm`).
357
+ ///
358
+ /// May be seen as a combination of both [`TimeZone`][3] and [`UtcOffset` scalars][4].
354
359
///
355
360
/// See also [`jiff::tz::TimeZone`][2] for details.
356
361
///
357
- /// [1 ]: http://www. iana.org/time-zones
362
+ /// [0 ]: http://iana.org/time-zones
358
363
/// [2]: https://docs.rs/jiff/latest/jiff/tz/struct.TimeZone.html
364
+ /// [3]: https://graphql-scalars.dev/docs/scalars/time-zone
365
+ /// [4]: https://graphql-scalars.dev/docs/scalars/utc-offset
359
366
#[ graphql_scalar(
360
367
with = time_zone_or_utc_offset,
361
368
parse_token( String ) ,
@@ -365,7 +372,7 @@ pub type TimeZoneOrUtcOffset = jiff::tz::TimeZone;
365
372
mod time_zone_or_utc_offset {
366
373
use super :: * ;
367
374
368
- /// Format of a `TimeZoneOrUtcOffset` scalar.
375
+ /// Format of a [ `TimeZoneOrUtcOffset`] scalar.
369
376
const FORMAT : & str = "%:V" ;
370
377
371
378
pub ( super ) fn to_output < S > ( v : & TimeZoneOrUtcOffset ) -> Value < S >
@@ -376,8 +383,7 @@ mod time_zone_or_utc_offset {
376
383
|| {
377
384
// If no IANA time zone identifier is available, fall back to displaying the time
378
385
// offset directly (using format `[+-]HH:MM[:SS]` from RFC 9557, e.g. `+05:30`).
379
- //
380
- // <https://github.com/graphql-rust/juniper/pull/1278#discussion_r1719161686>
386
+ // See: https://github.com/graphql-rust/juniper/pull/1278#discussion_r1719161686
381
387
jiff:: Zoned :: now ( )
382
388
. with_time_zone ( v. clone ( ) )
383
389
. strftime ( FORMAT )
@@ -395,50 +401,51 @@ mod time_zone_or_utc_offset {
395
401
. ok_or_else ( || format ! ( "Expected `String`, found: {v}" ) )
396
402
. and_then ( |s| {
397
403
TimeZoneOrUtcOffset :: get ( s)
398
- . map_err ( TimeZoneError :: InvalidTimeZone )
404
+ . map_err ( TimeZoneParsingError :: InvalidTimeZone )
399
405
. or_else ( |_| utc_offset:: utc_offset_from_str ( s) . map ( TimeZoneOrUtcOffset :: fixed) )
400
406
. map_err ( |e| format ! ( "Invalid `TimeZoneOrUtcOffset`: {e}" ) )
401
407
} )
402
408
}
403
409
}
404
410
405
- /// Error while handling [`TimeZone`] value.
411
+ /// Error parsing a [`TimeZone`] value.
406
412
#[ derive( Clone ) ]
407
- pub enum TimeZoneError {
408
- /// Identifier could not be parsed by [`tz::TimeZone::get`]( jiff::tz::TimeZone::get) .
413
+ pub enum TimeZoneParsingError {
414
+ /// Identifier cannot not be parsed by the [` jiff::tz::TimeZone::get()`] method .
409
415
InvalidTimeZone ( jiff:: Error ) ,
416
+
410
417
/// GraphQL scalar [`TimeZone`] requires `tz::TimeZone` with IANA name.
411
418
MissingIanaName ( jiff:: tz:: TimeZone ) ,
412
419
}
413
420
414
- impl fmt:: Debug for TimeZoneError {
421
+ impl fmt:: Debug for TimeZoneParsingError {
415
422
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
416
423
match self {
417
- Self :: InvalidTimeZone ( err ) => write ! ( f, "TimeZoneError ::InvalidTimeZone({err :?})" ) ,
418
- Self :: MissingIanaName ( _value ) => write ! ( f, "TimeZoneError ::MissingIanaName(..)" ) ,
424
+ Self :: InvalidTimeZone ( e ) => write ! ( f, "TimeZoneParsingError ::InvalidTimeZone({e :?})" ) ,
425
+ Self :: MissingIanaName ( _ ) => write ! ( f, "TimeZoneParsingError ::MissingIanaName(..)" ) ,
419
426
}
420
427
}
421
428
}
422
429
423
- impl fmt:: Display for TimeZoneError {
430
+ impl fmt:: Display for TimeZoneParsingError {
424
431
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
425
432
match self {
426
- Self :: InvalidTimeZone ( err ) => err . fmt ( f) ,
427
- Self :: MissingIanaName ( _value ) => write ! ( f, "missing IANA name" ) ,
433
+ Self :: InvalidTimeZone ( e ) => e . fmt ( f) ,
434
+ Self :: MissingIanaName ( .. ) => write ! ( f, "missing IANA name" ) ,
428
435
}
429
436
}
430
437
}
431
438
432
- impl Error for TimeZoneError {
439
+ impl Error for TimeZoneParsingError {
433
440
fn source ( & self ) -> Option < & ( dyn Error + ' static ) > {
434
441
match self {
435
- Self :: InvalidTimeZone ( err ) => Some ( err ) ,
436
- Self :: MissingIanaName ( _ ) => None ,
442
+ Self :: InvalidTimeZone ( e ) => Some ( e ) ,
443
+ Self :: MissingIanaName ( .. ) => None ,
437
444
}
438
445
}
439
446
}
440
447
441
- /// Representation of time zone.
448
+ /// Representation of a time zone from the [IANA Time Zone Database][0] .
442
449
///
443
450
/// A set of rules for determining the civil time, via an offset from UTC, in a particular
444
451
/// geographic region. In many cases, the offset in a particular time zone can vary over the course
@@ -448,6 +455,7 @@ impl Error for TimeZoneError {
448
455
///
449
456
/// See also [`jiff::tz::TimeZone`][2] for details.
450
457
///
458
+ /// [0]: http://iana.org/time-zones
451
459
/// [1]: https://graphql-scalars.dev/docs/scalars/time-zone
452
460
/// [2]: https://docs.rs/jiff/latest/jiff/tz/struct.TimeZone.html
453
461
#[ graphql_scalar(
@@ -459,21 +467,22 @@ impl Error for TimeZoneError {
459
467
pub struct TimeZone ( jiff:: tz:: TimeZone ) ;
460
468
461
469
impl TryFrom < jiff:: tz:: TimeZone > for TimeZone {
462
- type Error = TimeZoneError ;
470
+ type Error = TimeZoneParsingError ;
463
471
464
472
fn try_from ( value : jiff:: tz:: TimeZone ) -> Result < Self , Self :: Error > {
465
473
if value. iana_name ( ) . is_none ( ) {
466
- return Err ( TimeZoneError :: MissingIanaName ( value) ) ;
474
+ return Err ( TimeZoneParsingError :: MissingIanaName ( value) ) ;
467
475
}
468
476
Ok ( Self ( value) )
469
477
}
470
478
}
471
479
472
480
impl str:: FromStr for TimeZone {
473
- type Err = TimeZoneError ;
481
+ type Err = TimeZoneParsingError ;
474
482
475
483
fn from_str ( value : & str ) -> Result < Self , Self :: Err > {
476
- let value = jiff:: tz:: TimeZone :: get ( value) . map_err ( TimeZoneError :: InvalidTimeZone ) ?;
484
+ let value =
485
+ jiff:: tz:: TimeZone :: get ( value) . map_err ( TimeZoneParsingError :: InvalidTimeZone ) ?;
477
486
value. try_into ( )
478
487
}
479
488
}
@@ -482,8 +491,10 @@ impl fmt::Display for TimeZone {
482
491
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
483
492
self . 0
484
493
. iana_name ( )
485
- // PANIC: We made sure that IANA name is available when constructing `Self`.
486
- . unwrap_or_else ( || panic ! ( "Failed to display `TimeZone`: no IANA name" ) )
494
+ . unwrap_or_else ( || {
495
+ // PANIC: We made sure that IANA name is available when constructing `Self`.
496
+ panic ! ( "failed to display `TimeZone`: no IANA name" )
497
+ } )
487
498
. fmt ( f)
488
499
}
489
500
}
@@ -495,8 +506,6 @@ impl From<TimeZone> for jiff::tz::TimeZone {
495
506
}
496
507
497
508
mod time_zone {
498
- use std:: str:: FromStr as _;
499
-
500
509
use super :: * ;
501
510
502
511
pub ( super ) fn to_output < S > ( v : & TimeZone ) -> Value < S >
@@ -512,11 +521,11 @@ mod time_zone {
512
521
{
513
522
v. as_string_value ( )
514
523
. ok_or_else ( || format ! ( "Expected `String`, found: {v}" ) )
515
- . and_then ( |s| TimeZone :: from_str ( s ) . map_err ( |e| format ! ( "Invalid `TimeZone`: {e}" ) ) )
524
+ . and_then ( |s| s . parse ( ) . map_err ( |e| format ! ( "Invalid `TimeZone`: {e}" ) ) )
516
525
}
517
526
}
518
527
519
- /// Represents fixed time zone offset.
528
+ /// Representation of a fixed time zone offset.
520
529
///
521
530
/// [`UtcOffset` scalar][1] compliant.
522
531
///
@@ -547,20 +556,16 @@ mod utc_offset {
547
556
Ok ( offset)
548
557
}
549
558
550
- fn utc_offset_to_string ( value : jiff:: tz:: Offset ) -> String {
551
- let mut buf = String :: new ( ) ;
552
- let tm = jiff:: fmt:: strtime:: BrokenDownTime :: from (
553
- & jiff:: Zoned :: now ( ) . with_time_zone ( jiff:: tz:: TimeZone :: fixed ( value) ) ,
554
- ) ;
555
- tm. format ( FORMAT , & mut buf) . unwrap ( ) ;
556
- buf
557
- }
558
-
559
559
pub ( super ) fn to_output < S > ( v : & UtcOffset ) -> Value < S >
560
560
where
561
561
S : ScalarValue ,
562
562
{
563
- Value :: scalar ( utc_offset_to_string ( * v) )
563
+ let mut buf = String :: new ( ) ;
564
+ let tm = jiff:: fmt:: strtime:: BrokenDownTime :: from (
565
+ & jiff:: Zoned :: now ( ) . with_time_zone ( jiff:: tz:: TimeZone :: fixed ( * v) ) ,
566
+ ) ;
567
+ tm. format ( FORMAT , & mut buf) . unwrap ( ) ;
568
+ Value :: scalar ( buf)
564
569
}
565
570
566
571
pub ( super ) fn from_input < S > ( v : & InputValue < S > ) -> Result < UtcOffset , String >
0 commit comments