Skip to content

Commit 6abd449

Browse files
authored
Merge pull request #816 from phadej/issue-811-time-compat-1.9.4
Add instances for time-1.11 / time-compat-1.9.4 types: Month, Quarter, QuarterOfYear
2 parents 8579faf + 9506d31 commit 6abd449

File tree

14 files changed

+202
-19
lines changed

14 files changed

+202
-19
lines changed

Data/Aeson/Encoding.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ module Data.Aeson.Encoding
4242

4343
-- ** Time
4444
, day
45+
, month
46+
, quarter
4547
, localTime
4648
, utcTime
4749
, timeOfDay

Data/Aeson/Encoding/Builder.hs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ module Data.Aeson.Encoding.Builder
2727
, quote
2828
, scientific
2929
, day
30+
, month
31+
, quarter
3032
, localTime
3133
, utcTime
3234
, timeOfDay
@@ -48,6 +50,8 @@ import Data.Scientific (Scientific, base10Exponent, coefficient)
4850
import Data.Text.Encoding (encodeUtf8BuilderEscaped)
4951
import Data.Time (UTCTime(..))
5052
import Data.Time.Calendar (Day(..), toGregorian)
53+
import Data.Time.Calendar.Month.Compat (Month, toYearMonth)
54+
import Data.Time.Calendar.Quarter.Compat (Quarter, toYearQuarter, QuarterOfYear (..))
5155
import Data.Time.LocalTime
5256
import Data.Word (Word8)
5357
import qualified Data.HashMap.Strict as HMS
@@ -147,6 +151,11 @@ ascii2 :: (Char, Char) -> BP.BoundedPrim a
147151
ascii2 cs = BP.liftFixedToBounded $ const cs BP.>$< BP.char7 >*< BP.char7
148152
{-# INLINE ascii2 #-}
149153

154+
ascii3 :: (Char, (Char, Char)) -> BP.BoundedPrim a
155+
ascii3 cs = BP.liftFixedToBounded $ const cs >$<
156+
BP.char7 >*< BP.char7 >*< BP.char7
157+
{-# INLINE ascii3 #-}
158+
150159
ascii4 :: (Char, (Char, (Char, Char))) -> BP.BoundedPrim a
151160
ascii4 cs = BP.liftFixedToBounded $ const cs >$<
152161
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
@@ -175,17 +184,40 @@ day dd = encodeYear yr <>
175184
where (yr,m,d) = toGregorian dd
176185
!(T mh ml) = twoDigits m
177186
!(T dh dl) = twoDigits d
178-
encodeYear y
179-
| y >= 1000 = B.integerDec y
180-
| y >= 0 = BP.primBounded (ascii4 (padYear y)) ()
181-
| y >= -999 = BP.primBounded (ascii5 ('-',padYear (- y))) ()
182-
| otherwise = B.integerDec y
183-
padYear y =
184-
let (ab,c) = fromIntegral y `quotRem` 10
185-
(a,b) = ab `quotRem` 10
186-
in ('0',(digit a,(digit b,digit c)))
187187
{-# INLINE day #-}
188188

189+
month :: Month -> Builder
190+
month mm = encodeYear yr <>
191+
BP.primBounded (ascii3 ('-',(mh,ml))) ()
192+
where (yr,m) = toYearMonth mm
193+
!(T mh ml) = twoDigits m
194+
{-# INLINE month #-}
195+
196+
quarter :: Quarter -> Builder
197+
quarter qq = encodeYear yr <>
198+
BP.primBounded (ascii3 ('-',('q',qd))) ()
199+
where (yr,q) = toYearQuarter qq
200+
qd = case q of
201+
Q1 -> '1'
202+
Q2 -> '2'
203+
Q3 -> '3'
204+
Q4 -> '4'
205+
{-# INLINE quarter #-}
206+
207+
-- | Used in encoding day, month, quarter
208+
encodeYear :: Integer -> Builder
209+
encodeYear y
210+
| y >= 1000 = B.integerDec y
211+
| y >= 0 = BP.primBounded (ascii4 (padYear y)) ()
212+
| y >= -999 = BP.primBounded (ascii5 ('-',padYear (- y))) ()
213+
| otherwise = B.integerDec y
214+
where
215+
padYear y' =
216+
let (ab,c) = fromIntegral y' `quotRem` 10
217+
(a,b) = ab `quotRem` 10
218+
in ('0',(digit a,(digit b,digit c)))
219+
{-# INLINE encodeYear #-}
220+
189221
timeOfDay :: TimeOfDay -> Builder
190222
timeOfDay t = timeOfDay64 (toTimeOfDay64 t)
191223
{-# INLINE timeOfDay #-}

Data/Aeson/Encoding/Internal.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ module Data.Aeson.Encoding.Internal
4747
, integerText, floatText, doubleText, scientificText
4848
-- ** Time
4949
, day
50+
, month
51+
, quarter
5052
, localTime
5153
, utcTime
5254
, timeOfDay
@@ -65,6 +67,8 @@ import Data.Int
6567
import Data.Scientific (Scientific)
6668
import Data.Text (Text)
6769
import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
70+
import Data.Time.Calendar.Month.Compat (Month)
71+
import Data.Time.Calendar.Quarter.Compat (Quarter)
6872
import Data.Typeable (Typeable)
6973
import Data.Word
7074
import qualified Data.Aeson.Encoding.Builder as EB
@@ -350,6 +354,12 @@ scientificText = Encoding . EB.quote . EB.scientific
350354
day :: Day -> Encoding' a
351355
day = Encoding . EB.quote . EB.day
352356

357+
month :: Month -> Encoding' a
358+
month = Encoding . EB.quote . EB.month
359+
360+
quarter :: Quarter -> Encoding' a
361+
quarter = Encoding . EB.quote . EB.quarter
362+
353363
localTime :: LocalTime -> Encoding' a
354364
localTime = Encoding . EB.quote . EB.localTime
355365

Data/Aeson/Parser/Time.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ module Data.Aeson.Parser.Time
44
(
55
run
66
, day
7+
, month
8+
, quarter
79
, localTime
810
, timeOfDay
911
, timeZone
@@ -16,6 +18,8 @@ import Prelude.Compat
1618
import Data.Attoparsec.Text (Parser)
1719
import Data.Text (Text)
1820
import Data.Time.Calendar (Day)
21+
import Data.Time.Calendar.Quarter.Compat (Quarter)
22+
import Data.Time.Calendar.Month.Compat (Month)
1923
import Data.Time.Clock (UTCTime(..))
2024
import qualified Data.Aeson.Types.Internal as Aeson
2125
import qualified Data.Attoparsec.Text as A
@@ -33,11 +37,23 @@ day :: Parser Day
3337
day = T.day
3438
{-# INLINE day #-}
3539

40+
-- | Parse a date of the form @[+,-]YYYY-MM@.
41+
month :: Parser Month
42+
month = T.month
43+
{-# INLINE month #-}
44+
45+
-- | Parse a date of the form @[+,-]YYYY-QN@.
46+
quarter :: Parser Quarter
47+
quarter = T.quarter
48+
{-# INLINE quarter #-}
49+
3650
-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
3751
timeOfDay :: Parser Local.TimeOfDay
3852
timeOfDay = T.timeOfDay
3953
{-# INLINE timeOfDay #-}
4054

55+
-- | Parse a quarter of the form @[+,-]YYYY-QN@.
56+
4157
-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
4258
-- zero. (This makes some speedups possible.)
4359
timeZone :: Parser (Maybe Local.TimeZone)

Data/Aeson/Types/FromJSON.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ import Data.Text (Text, pack, unpack)
105105
import Data.These (These (..))
106106
import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
107107
import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
108+
import Data.Time.Calendar.Month.Compat (Month)
109+
import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
108110
import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
109111
import Data.Time.Clock.System.Compat (SystemTime (..))
110112
import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
@@ -2123,6 +2125,32 @@ parseDayOfWeek t = case T.toLower t of
21232125
instance FromJSONKey DayOfWeek where
21242126
fromJSONKey = FromJSONKeyTextParser parseDayOfWeek
21252127

2128+
instance FromJSON QuarterOfYear where
2129+
parseJSON = withText "DaysOfWeek" parseQuarterOfYear
2130+
2131+
parseQuarterOfYear :: T.Text -> Parser QuarterOfYear
2132+
parseQuarterOfYear t = case T.toLower t of
2133+
"q1" -> return Q1
2134+
"q2" -> return Q2
2135+
"q3" -> return Q3
2136+
"e4 " -> return Q4
2137+
_ -> fail "Ivalid quarter of year"
2138+
2139+
instance FromJSONKey QuarterOfYear where
2140+
fromJSONKey = FromJSONKeyTextParser parseQuarterOfYear
2141+
2142+
instance FromJSON Quarter where
2143+
parseJSON = withText "Quarter" (Time.run Time.quarter)
2144+
2145+
instance FromJSONKey Quarter where
2146+
fromJSONKey = FromJSONKeyTextParser (Time.run Time.quarter)
2147+
2148+
instance FromJSON Month where
2149+
parseJSON = withText "Month" (Time.run Time.month)
2150+
2151+
instance FromJSONKey Month where
2152+
fromJSONKey = FromJSONKeyTextParser (Time.run Time.month)
2153+
21262154
-------------------------------------------------------------------------------
21272155
-- base Monoid/Semigroup
21282156
-------------------------------------------------------------------------------

Data/Aeson/Types/ToJSON.hs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ import Data.Tagged (Tagged(..))
8787
import Data.Text (Text, pack)
8888
import Data.These (These (..))
8989
import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
90+
import Data.Time.Calendar.Month.Compat (Month)
91+
import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
9092
import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
9193
import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
9294
import Data.Time.Clock.System.Compat (SystemTime (..))
@@ -2016,6 +2018,19 @@ instance ToJSON Day where
20162018
instance ToJSONKey Day where
20172019
toJSONKey = toJSONKeyTextEnc E.day
20182020

2021+
instance ToJSON Month where
2022+
toJSON = stringEncoding . E.month
2023+
toEncoding = E.month
2024+
2025+
instance ToJSONKey Month where
2026+
toJSONKey = toJSONKeyTextEnc E.month
2027+
2028+
instance ToJSON Quarter where
2029+
toJSON = stringEncoding . E.quarter
2030+
toEncoding = E.quarter
2031+
2032+
instance ToJSONKey Quarter where
2033+
toJSONKey = toJSONKeyTextEnc E.quarter
20192034

20202035
instance ToJSON TimeOfDay where
20212036
toJSON = stringEncoding . E.timeOfDay
@@ -2105,7 +2120,9 @@ instance ToJSON DayOfWeek where
21052120
toJSON Saturday = "saturday"
21062121
toJSON Sunday = "sunday"
21072122

2108-
toEncodingDayOfWeek :: DayOfWeek -> E.Encoding' Text
2123+
toEncoding = toEncodingDayOfWeek
2124+
2125+
toEncodingDayOfWeek :: DayOfWeek -> E.Encoding' a
21092126
toEncodingDayOfWeek Monday = E.unsafeToEncoding "\"monday\""
21102127
toEncodingDayOfWeek Tuesday = E.unsafeToEncoding "\"tuesday\""
21112128
toEncodingDayOfWeek Wednesday = E.unsafeToEncoding "\"wednesday\""
@@ -2117,6 +2134,21 @@ toEncodingDayOfWeek Sunday = E.unsafeToEncoding "\"sunday\""
21172134
instance ToJSONKey DayOfWeek where
21182135
toJSONKey = toJSONKeyTextEnc toEncodingDayOfWeek
21192136

2137+
instance ToJSON QuarterOfYear where
2138+
toJSON Q1 = "q1"
2139+
toJSON Q2 = "q2"
2140+
toJSON Q3 = "q3"
2141+
toJSON Q4 = "q4"
2142+
2143+
toEncodingQuarterOfYear :: QuarterOfYear -> E.Encoding' a
2144+
toEncodingQuarterOfYear Q1 = E.unsafeToEncoding "\"q1\""
2145+
toEncodingQuarterOfYear Q2 = E.unsafeToEncoding "\"q2\""
2146+
toEncodingQuarterOfYear Q3 = E.unsafeToEncoding "\"q3\""
2147+
toEncodingQuarterOfYear Q4 = E.unsafeToEncoding "\"q4\""
2148+
2149+
instance ToJSONKey QuarterOfYear where
2150+
toJSONKey = toJSONKeyTextEnc toEncodingQuarterOfYear
2151+
21202152
-------------------------------------------------------------------------------
21212153
-- base Monoid/Semigroup
21222154
-------------------------------------------------------------------------------

aeson.cabal

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ library
106106
ghc-prim >= 0.2 && < 0.8,
107107
template-haskell >= 2.9.0.0 && < 2.18,
108108
text >= 1.2.3.0 && < 1.3,
109-
time >= 1.4 && < 1.11
109+
time >= 1.4 && < 1.12
110110

111111
if impl(ghc >= 8.0)
112112
build-depends: bytestring >= 0.10.8.1
113113

114114
-- Compat
115115
build-depends:
116116
base-compat-batteries >= 0.10.0 && < 0.12,
117-
time-compat >= 1.9.2.2 && < 1.10
117+
time-compat >= 1.9.4 && < 1.10
118118

119119
if !impl(ghc >= 8.6)
120120
build-depends:
@@ -254,7 +254,7 @@ test-suite aeson-tests
254254
void >=0.7.2 && <0.8
255255

256256
if impl(ghc >= 7.8)
257-
build-depends: hashable-time >= 0.2 && <0.3
257+
build-depends: hashable-time >= 0.2.0.1 && <0.3
258258

259259
if flag(fast)
260260
ghc-options: -fno-enable-rewrite-rules

attoparsec-iso8601/Data/Attoparsec/Time.hs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ module Data.Attoparsec.Time
1919
, timeZone
2020
, utcTime
2121
, zonedTime
22+
, month
23+
, quarter
2224
) where
2325

2426
import Prelude.Compat
@@ -33,6 +35,8 @@ import Data.Fixed (Pico)
3335
import Data.Int (Int64)
3436
import Data.Maybe (fromMaybe)
3537
import Data.Time.Calendar (Day, fromGregorianValid)
38+
import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..), fromYearQuarter)
39+
import Data.Time.Calendar.Month.Compat (Month, fromYearMonthValid)
3640
import Data.Time.Clock (UTCTime(..))
3741
import qualified Data.Text as T
3842
import qualified Data.Time.LocalTime as Local
@@ -46,6 +50,28 @@ day = do
4650
d <- twoDigits <|> fail "date must be of form [+,-]YYYY-MM-DD"
4751
maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
4852

53+
-- | Parse a month of the form @[+,-]YYYY-MM@.
54+
month :: Parser Month
55+
month = do
56+
absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
57+
y <- (decimal <* char '-') <|> fail "month must be of form [+,-]YYYY-MM"
58+
m <- twoDigits <|> fail "month must be of form [+,-]YYYY-MM"
59+
maybe (fail "invalid month") return (fromYearMonthValid (absOrNeg y) m)
60+
61+
-- | Parse a quarter of the form @[+,-]YYYY-QN@.
62+
quarter :: Parser Quarter
63+
quarter = do
64+
absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
65+
y <- (decimal <* char '-') <|> fail "month must be of form [+,-]YYYY-MM"
66+
_ <- char 'q' <|> char 'Q'
67+
q <- parseQ
68+
return $! fromYearQuarter (absOrNeg y) q
69+
where
70+
parseQ = Q1 <$ char '1'
71+
<|> Q2 <$ char '2'
72+
<|> Q3 <$ char '3'
73+
<|> Q4 <$ char '4'
74+
4975
-- | Parse a two-digit integer (e.g. day of month, hour).
5076
twoDigits :: Parser Int
5177
twoDigits = do

attoparsec-iso8601/attoparsec-iso8601.cabal

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: attoparsec-iso8601
2-
version: 1.0.1.0
2+
version: 1.0.2.0
33
synopsis: Parsing of ISO 8601 dates, originally from aeson.
44
description: Parsing of ISO 8601 dates, originally from aeson.
55
license: BSD3
@@ -39,9 +39,9 @@ library
3939
attoparsec >= 0.13.0.1 && < 0.14.0.0,
4040
base >= 4.7 && < 5,
4141
base-compat-batteries >= 0.10.0 && < 0.12,
42-
time-compat >= 1.9.2.2 && < 1.10,
42+
time-compat >= 1.9.4 && < 1.10,
4343
text >= 1.1.1.0 && < 1.3.0.0,
44-
time >= 1.1.1.4 && < 1.11
44+
time >= 1.1.1.4 && < 1.12
4545

4646
if flag(fast)
4747
ghc-options: -O0

attoparsec-iso8601/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md](https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md).
22

3+
### 1.0.2.0
4+
5+
* Add `month :: Parser Month` and `quarter :: Parser Quarter`
6+
37
### 1.0.1.0
48

59
* Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann

0 commit comments

Comments
 (0)