git.iko.soy/typeable/http-api-data
on master help sign in
Merge pull request #109 from fizruk/time-compat-1.9.5

Add Quarter, QuarterOfYear and Month instances

commit640178b39ac4071fb7443579cd7366817114304b authorOleg Grenrus <oleg.grenrus@iki.fi> dateMon 4 Jan 2021, 02:06 (6 years ago) parent8e8062e3375ca6
8 files changed+101−16
.ghci−1 .github/workflows/haskell-ci.yml+5−5 CHANGELOG.md+6 cabal.haskell-ci+1−1 http-api-data.cabal+9−9 src/Web/Internal/FormUrlEncoded.hs+8 src/Web/Internal/HttpApiData.hs+66 test/Web/Internal/HttpApiDataSpec.hs+6
.ghcideleted−1
@@ -1 +0,0 @@
1−:set -itest -isrc -optP-include -optP .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/autogen/cabal_macros.h -optP-I -optPinclude
.github/workflows/haskell-ci.yml+5−5
@@ -110,7 +110,7 @@ jobs:
110110 - name: cache (tools)
111111 uses: actions/cache@v2
112112 with:
113− key: ${{ runner.os }}-${{ matrix.ghc }}-tools-0f645703
113+ key: ${{ runner.os }}-${{ matrix.ghc }}-tools-b869866c
114114 path: ~/.haskell-ci-tools
115115 - name: install cabal-plan
116116 run: |
@@ -123,8 +123,8 @@ jobs:
123123 cabal-plan --version
124124 - name: install doctest
125125 run: |
126− if [ $((HCNUMVER >= 80000 && HCNUMVER < 81000)) -ne 0 ] ; then $CABAL --store-dir=$HOME/.haskell-ci-tools/store v2-install $ARG_COMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.17' ; fi
127− if [ $((HCNUMVER >= 80000 && HCNUMVER < 81000)) -ne 0 ] ; then doctest --version ; fi
126+ if [ $((HCNUMVER >= 80000)) -ne 0 ] ; then $CABAL --store-dir=$HOME/.haskell-ci-tools/store v2-install $ARG_COMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.17' ; fi
127+ if [ $((HCNUMVER >= 80000)) -ne 0 ] ; then doctest --version ; fi
128128 - name: checkout
129129 uses: actions/checkout@v2
130130 with:
@@ -178,8 +178,8 @@ jobs:
178178 - name: doctest
179179 run: |
180180 perl -i -e 'while (<ARGV>) { print unless /package-id\s+(base-compat)-\d+(\.\d+)*/; }' .ghc.environment.*
181− if [ $((HCNUMVER >= 80000 && HCNUMVER < 81000)) -ne 0 ] ; then cd ${PKGDIR_http_api_data} || false ; fi
182− if [ $((HCNUMVER >= 80000 && HCNUMVER < 81000)) -ne 0 ] ; then doctest src/ ; fi
181+ if [ $((HCNUMVER >= 80000)) -ne 0 ] ; then cd ${PKGDIR_http_api_data} || false ; fi
182+ if [ $((HCNUMVER >= 80000)) -ne 0 ] ; then doctest src/ ; fi
183183 - name: cabal check
184184 run: |
185185 cd ${PKGDIR_http_api_data} || false
CHANGELOG.md+6
@@ -1,3 +1,9 @@
1+0.4.3
2+-----
3+
4+* Add `Quarter`, `QuarterOfYear` and `Month` instances
5+* Support `bytestring-0.11`
6+
170.4.2
28-----
39
cabal.haskell-ci+1−1
@@ -1,5 +1,5 @@
11branches: master
2−doctest: <8.10
2+doctest: True
33doctest-filter-packages: base-compat
44
55constraint-set text-show
http-api-data.cabal+9−9
@@ -1,6 +1,6 @@
11cabal-version: >= 1.10
22name: http-api-data
3−version: 0.4.2
3+version: 0.4.3
44
55synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
66category: Web
@@ -56,15 +56,15 @@ library
5656 -- other-dependencies
5757 build-depends:
5858 attoparsec >= 0.13.2.2 && < 0.14
59− , attoparsec-iso8601 >= 1.0.1.0 && < 1.1
59+ , attoparsec-iso8601 >= 1.0.2.0 && < 1.1
6060 , base-compat >= 0.10.5 && < 0.12
6161 , cookie >= 0.4.3 && < 0.5
6262 , hashable >= 1.2.7.0 && < 1.4
6363 , http-types >= 0.12.3 && < 0.13
6464 , tagged >= 0.8.5 && < 0.9
65− , time-compat >= 1.9.2 && < 1.10
66− , unordered-containers >= 0.2.10.0 && < 0.3
67− , uuid-types >= 1.0.3 && <1.1
65+ , time-compat >= 1.9.5 && < 1.10
66+ , unordered-containers >= 0.2.10.0 && < 0.3
67+ , uuid-types >= 1.0.3 && < 1.1
6868
6969 if !impl(ghc >= 7.10)
7070 build-depends:
@@ -112,10 +112,10 @@ test-suite spec
112112 if !impl(ghc >= 7.10)
113113 build-depends: nats
114114
115− build-depends: HUnit >= 1.6.0.0 && <1.7
116− , hspec >= 2.7.1 && <2.8
117− , QuickCheck >= 2.13.1 && <2.15
118− , quickcheck-instances >= 0.3.21 && <0.4
115+ build-depends: HUnit >= 1.6.0.0 && <1.7
116+ , hspec >= 2.7.1 && <2.8
117+ , QuickCheck >= 2.13.1 && <2.15
118+ , quickcheck-instances >= 0.3.25.2 && <0.4
119119
120120source-repository head
121121 type: git
src/Web/Internal/FormUrlEncoded.hs+8
@@ -52,6 +52,8 @@ import Data.Text.Encoding.Error (lenientDecode)
5252import qualified Data.Text.Lazy as Lazy
5353import Data.Time.Compat (Day, LocalTime, NominalDiffTime,
5454 UTCTime, ZonedTime)
55+import Data.Time.Calendar.Month.Compat (Month)
56+import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
5557import Data.Void (Void)
5658import Data.Word (Word16, Word32, Word64, Word8)
5759import GHC.Exts (Constraint, IsList (..))
@@ -113,6 +115,9 @@ instance ToFormKey LocalTime where toFormKey = toQueryParam
113115instance ToFormKey ZonedTime where toFormKey = toQueryParam
114116instance ToFormKey UTCTime where toFormKey = toQueryParam
115117instance ToFormKey NominalDiffTime where toFormKey = toQueryParam
118+instance ToFormKey Quarter where toFormKey = toQueryParam
119+instance ToFormKey QuarterOfYear where toFormKey = toQueryParam
120+instance ToFormKey Month where toFormKey = toQueryParam
116121
117122instance ToFormKey String where toFormKey = toQueryParam
118123instance ToFormKey Text where toFormKey = toQueryParam
@@ -172,6 +177,9 @@ instance FromFormKey LocalTime where parseFormKey = parseQueryParam
172177instance FromFormKey ZonedTime where parseFormKey = parseQueryParam
173178instance FromFormKey UTCTime where parseFormKey = parseQueryParam
174179instance FromFormKey NominalDiffTime where parseFormKey = parseQueryParam
180+instance FromFormKey Quarter where parseFormKey = parseQueryParam
181+instance FromFormKey QuarterOfYear where parseFormKey = parseQueryParam
182+instance FromFormKey Month where parseFormKey = parseQueryParam
175183
176184instance FromFormKey String where parseFormKey = parseQueryParam
177185instance FromFormKey Text where parseFormKey = parseQueryParam
src/Web/Internal/HttpApiData.hs+66
@@ -55,6 +55,9 @@ import Data.Time.Compat (Day, FormatTime, LocalTime,
5555 secondsToNominalDiffTime)
5656import Data.Time.Format.Compat (defaultTimeLocale,
5757 iso8601DateFormat)
58+import Data.Time.Calendar.Month.Compat (Month)
59+import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..),
60+ toYearQuarter)
5861import Data.Typeable (Typeable)
5962import qualified Data.UUID.Types as UUID
6063import Data.Version (Version, parseVersion,
@@ -516,6 +519,42 @@ instance ToHttpApiData DayOfWeek where
516519
517520 toEncodedUrlPiece = unsafeToEncodedUrlPiece
518521
522+-- |
523+-- >>> toUrlPiece Q4
524+-- "q4"
525+instance ToHttpApiData QuarterOfYear where
526+ toUrlPiece Q1 = "q1"
527+ toUrlPiece Q2 = "q2"
528+ toUrlPiece Q3 = "q3"
529+ toUrlPiece Q4 = "q4"
530+
531+-- |
532+-- >>> import Data.Time.Calendar.Quarter.Compat (Quarter (..))
533+-- >>> MkQuarter 8040
534+-- 2010-Q1
535+--
536+-- >>> toUrlPiece $ MkQuarter 8040
537+-- "2010-q1"
538+--
539+instance ToHttpApiData Quarter where
540+ toUrlPiece q = case toYearQuarter q of
541+ (y, qoy) -> T.pack (show y ++ "-" ++ f qoy)
542+ where
543+ f Q1 = "q1"
544+ f Q2 = "q2"
545+ f Q3 = "q3"
546+ f Q4 = "q4"
547+
548+-- |
549+-- >>> import Data.Time.Calendar.Month.Compat (Month (..))
550+-- >>> MkMonth 24482
551+-- 2040-03
552+--
553+-- >>> toUrlPiece $ MkMonth 24482
554+-- "2040-03"
555+--
556+instance ToHttpApiData Month where
557+ toUrlPiece = T.pack . formatTime defaultTimeLocale "%Y-%m"
519558
520559instance ToHttpApiData NominalDiffTime where
521560 toUrlPiece = toUrlPiece . nominalDiffTimeToSeconds
@@ -704,6 +743,9 @@ instance FromHttpApiData ZonedTime where parseUrlPiece = runAtto Atto.zonedTime
704743-- Right 2015-10-03 00:14:24 UTC
705744instance FromHttpApiData UTCTime where parseUrlPiece = runAtto Atto.utcTime
706745
746+-- |
747+-- >>> parseUrlPiece "Monday" :: Either Text DayOfWeek
748+-- Right Monday
707749instance FromHttpApiData DayOfWeek where
708750 parseUrlPiece t = case Map.lookup (T.toLower t) m of
709751 Just dow -> Right dow
@@ -715,6 +757,30 @@ instance FromHttpApiData DayOfWeek where
715757
716758instance FromHttpApiData NominalDiffTime where parseUrlPiece = fmap secondsToNominalDiffTime . parseUrlPiece
717759
760+-- |
761+-- >>> parseUrlPiece "2021-01" :: Either Text Month
762+-- Right 2021-01
763+instance FromHttpApiData Month where parseUrlPiece = runAtto Atto.month
764+
765+-- |
766+-- >>> parseUrlPiece "2021-q1" :: Either Text Quarter
767+-- Right 2021-Q1
768+instance FromHttpApiData Quarter where parseUrlPiece = runAtto Atto.quarter
769+
770+-- |
771+-- >>> parseUrlPiece "q2" :: Either Text QuarterOfYear
772+-- Right Q2
773+--
774+-- >>> parseUrlPiece "Q3" :: Either Text QuarterOfYear
775+-- Right Q3
776+instance FromHttpApiData QuarterOfYear where
777+ parseUrlPiece t = case T.toLower t of
778+ "q1" -> return Q1
779+ "q2" -> return Q2
780+ "q3" -> return Q3
781+ "q4" -> return Q4
782+ _ -> Left "Invalid quarter of year"
783+
718784instance FromHttpApiData All where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
719785instance FromHttpApiData Any where parseUrlPiece = coerce (parseUrlPiece :: Text -> Either Text Bool)
720786
test/Web/Internal/HttpApiDataSpec.hs+6
@@ -14,6 +14,8 @@ import qualified Data.Text as T
1414import qualified Data.Text.Encoding as TE
1515import qualified Data.Text.Lazy as L
1616import Data.Time.Compat
17+import Data.Time.Calendar.Month.Compat (Month)
18+import Data.Time.Calendar.Quarter.Compat (Quarter, QuarterOfYear (..))
1719import qualified Data.UUID.Types as UUID
1820import Data.Version
1921import Data.Word
@@ -87,6 +89,10 @@ spec = do
8789 checkUrlPiece (Proxy :: Proxy UTCTime) "UTCTime"
8890 checkUrlPiece (Proxy :: Proxy NominalDiffTime) "NominalDiffTime"
8991 checkUrlPiece (Proxy :: Proxy DayOfWeek) "DayOfWeek"
92+ checkUrlPiece (Proxy :: Proxy Quarter) "Quarter"
93+ checkUrlPiece (Proxy :: Proxy QuarterOfYear) "QuarterOfYear"
94+ checkUrlPiece (Proxy :: Proxy Month) "Month"
95+ checkUrlPiece (Proxy :: Proxy DayOfWeek) "DayOfWeek"
9096 checkUrlPiece (Proxy :: Proxy Version) "Version"
9197 checkUrlPiece (Proxy :: Proxy UUID.UUID) "UUID"
9298 checkUrlPiece' setCookieGen "Cookie"