You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Data.Time.Format.ISO8601 parses ISO 8601 durations into CalendarDiffTime, which stores only (months :: Integer, time :: NominalDiffTime). The structured Y / M / W / D / H / M / S grammar of
the input is collapsed at parse time, so iso8601Show . iso8601ParseM
is not the identity on perfectly valid ISO 8601 strings.
This is strictly required by XML Schema xs:duration (which
mandates lexical-space round-trip), but it is desirable in general.
Why this matters beyond XSD
Round-trip is the standard expectation for Show/Read. Today read . show on a CalendarDiffTime is fine, but iso8601ParseM
on user/wire input followed by iso8601Show silently rewrites the
string — surprising for any data-interchange use.
Canonical forms matter outside XSD too. JSON Schema
(format: "duration", RFC 3339 Appendix A), OData, ICS/RFC 5545
(DURATION), and most REST APIs that quote ISO 8601 expect the
producer's chosen form to be preserved. An SLA expressed as PT1H
in a contract should not become PT3600S after a parse/serialize.
Weeks are silently dropped.P1W parses, but serializes back
as P7D. Weeks are a first-class designator in ISO 8601:2004 sec.
4.4.3.2 and are mutually exclusive with the other designators.
No way to express "valid ISO 8601" at the type level. ISO 8601
permits at most one leading sign for the whole duration. CalendarDiffTime allows independently-signed ctMonths and ctTime, so values exist that cannot be losslessly rendered as
ISO 8601 / XSD.
Add a structure-preserving duration type alongside CalendarDiffTime,
e.g.
dataDuration=Duration{durSign::Sign-- one leading sign, per spec
, durYears::Integer
, durMonths::Integer
, durWeeks::MaybeInteger-- Just _ excludes Y/M/D/T per 4.4.3.2
, durDays::Integer
, durHours::Integer
, durMinutes::Integer
, durSeconds::Pico}deriving (Eq, Show, Generic, Data)
with:
iso8601Format :: Format Duration that round-trips bit-exact for
every valid ISO 8601 lexical form;
total conversions toCalendarDiffTime :: Duration -> CalendarDiffTime
and a partial fromCalendarDiffTime :: CalendarDiffTime -> Maybe Duration
(partial because CalendarDiffTime admits values outside ISO 8601's
grammar, e.g. mixed-sign);
arithmetic helpers (addDuration, diffDuration) implemented in
terms of existing addUTCDurationClip / diffUTCDurationClip.
The existing CalendarDiffTime instance stays as-is for backward
compatibility; Duration is the new recommended type for parsing
external ISO 8601 / XSD input.
Summary
Data.Time.Format.ISO8601parses ISO 8601 durations intoCalendarDiffTime, which stores only(months :: Integer, time :: NominalDiffTime). The structured Y / M / W / D / H / M / S grammar ofthe input is collapsed at parse time, so
iso8601Show . iso8601ParseMis not the identity on perfectly valid ISO 8601 strings.
This is strictly required by XML Schema
xs:duration(whichmandates lexical-space round-trip), but it is desirable in general.
Why this matters beyond XSD
Round-trip is the standard expectation for
Show/Read. Todayread . showon aCalendarDiffTimeis fine, butiso8601ParseMon user/wire input followed by
iso8601Showsilently rewrites thestring — surprising for any data-interchange use.
Canonical forms matter outside XSD too. JSON Schema
(
format: "duration", RFC 3339 Appendix A), OData, ICS/RFC 5545(
DURATION), and most REST APIs that quote ISO 8601 expect theproducer's chosen form to be preserved. An SLA expressed as
PT1Hin a contract should not become
PT3600Safter a parse/serialize.Weeks are silently dropped.
P1Wparses, but serializes backas
P7D. Weeks are a first-class designator in ISO 8601:2004 sec.4.4.3.2 and are mutually exclusive with the other designators.
No way to express "valid ISO 8601" at the type level. ISO 8601
permits at most one leading sign for the whole duration.
CalendarDiffTimeallows independently-signedctMonthsandctTime, so values exist that cannot be losslessly rendered asISO 8601 / XSD.
Concrete failures
All four inputs are valid; none round-trip.
Proposal
Add a structure-preserving duration type alongside
CalendarDiffTime,e.g.
with:
iso8601Format :: Format Durationthat round-trips bit-exact forevery valid ISO 8601 lexical form;
toCalendarDiffTime :: Duration -> CalendarDiffTimeand a partial
fromCalendarDiffTime :: CalendarDiffTime -> Maybe Duration(partial because
CalendarDiffTimeadmits values outside ISO 8601'sgrammar, e.g. mixed-sign);
addDuration,diffDuration) implemented interms of existing
addUTCDurationClip/diffUTCDurationClip.The existing
CalendarDiffTimeinstance stays as-is for backwardcompatibility;
Durationis the new recommended type for parsingexternal ISO 8601 / XSD input.
Prior art / context
duration) — requires lexical-spacepreservation.
iso8601-durationpackage on Hackage exists precisely becausetimedoes not provide this; expandingtimeto subsume it is great.