Skip to content

Commit 1097e73

Browse files
authored
Merge pull request #26 from safareli/ampm
fix for {12,0} {am,pm} cases
2 parents 1b7bdab + 5895fe9 commit 1097e73

File tree

4 files changed

+96
-62
lines changed

4 files changed

+96
-62
lines changed

src/Data/Formatter/DateTime.purs

+28-14
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Prelude
1616

1717
import Control.Alt ((<|>))
1818
import Control.Alternative (class Alternative)
19-
import Control.Apply (lift2)
19+
import Control.Apply (applySecond, lift2)
2020
import Control.Lazy as Z
2121
import Control.Monad.Reader.Trans (ReaderT, runReaderT, ask)
2222
import Control.Monad.State (State, modify, put, runState)
@@ -27,7 +27,7 @@ import Data.DateTime as DT
2727
import Data.DateTime.Instant (instant, toDateTime, fromDateTime, unInstant)
2828
import Data.Either (Either(..), either)
2929
import Data.Enum (fromEnum, toEnum)
30-
import Data.Foldable (foldMap)
30+
import Data.Foldable (foldMap, for_)
3131
import Data.Formatter.Internal (foldDigits)
3232
import Data.Formatter.Parser.Number (parseDigit)
3333
import Data.Formatter.Parser.Utils (runP, oneOfAs)
@@ -237,27 +237,41 @@ initialAccum =
237237
}
238238

239239
unformatAccumToDateTime UnformatAccum Either String DT.DateTime
240-
unformatAccumToDateTime a =
240+
unformatAccumToDateTime a = applySecond (validAccum a) $
241241
DT.DateTime
242242
<$> (D.canonicalDate
243243
<$> (maybe (Left "Incorrect year") pure $ toEnum $ fromMaybe zero a.year)
244244
<*> (maybe (Left "Incorrect month") pure $ toEnum $ fromMaybe one a.month)
245-
<*> (maybe (Left "Incorrect day") pure $ toEnum $ fromMaybe one a.day))
245+
<*> (maybe (Left "Incorrect day") pure $ toEnum
246+
$ adjustDay a.hour
247+
$ fromMaybe one a.day))
246248
<*> (T.Time
247249
<$> (maybe
248250
(Left "Incorrect hour") pure
249251
$ toEnum
250-
=<< (adjustMeridiem $ fromMaybe zero a.hour))
252+
$ fromMaybe zero
253+
$ adjustMeridiem a.meridiem <$> a.hour)
251254
<*> (maybe (Left "Incorrect minute") pure $ toEnum $ fromMaybe zero a.minute)
252255
<*> (maybe (Left "Incorrect second") pure $ toEnum $ fromMaybe zero a.second)
253256
<*> (maybe (Left "Incorrect millisecond") pure $ toEnum $ fromMaybe zero a.millisecond))
254-
where
255-
adjustMeridiem Int Maybe Int
256-
adjustMeridiem inp
257-
| a.meridiem /= Just PM = Just inp
258-
| inp == 12 = pure 0
259-
| inp < 12 = pure $ inp + 12
260-
| otherwise = Nothing
257+
258+
validAccum :: UnformatAccum Either String Unit
259+
validAccum { hour, minute, second, millisecond } = case hour of
260+
Just 24 → for_ [minute, second, millisecond] \val ->
261+
when (fromMaybe 0 val > 0) $ Left "When hour is 24, other time components must be 0"
262+
_ -> pure unit
263+
264+
adjustDay Maybe Int Int Int
265+
adjustDay (Just 24) n = n + 1
266+
adjustDay _ n = n
267+
268+
adjustMeridiem Maybe Meridiem Int Int
269+
adjustMeridiem (Just AM) 12 = 0
270+
adjustMeridiem (Just PM) 12 = 12
271+
adjustMeridiem (Just PM) n = n + 12
272+
adjustMeridiem (Just AM) n = n
273+
adjustMeridiem Nothing 24 = 0
274+
adjustMeridiem Nothing n = n
261275

262276

263277

@@ -342,9 +356,9 @@ unformatCommandParser = case _ of
342356
-- TODO we would need to use this value if we support date format using week number
343357
DayOfWeek → void $ parseInt 1 (validateRange 1 7) "Incorrect day of week"
344358
Hours24 → _{hour = _} `modifyWithParser`
345-
(parseInt 2 (validateRange 0 23 <> exactLength) "Incorrect 24 hour")
359+
(parseInt 2 (validateRange 0 24 <> exactLength) "Incorrect 24 hour")
346360
Hours12 → _{hour = _} `modifyWithParser`
347-
(parseInt 2 (validateRange 0 11 <> exactLength) "Incorrect 12 hour")
361+
(parseInt 2 (validateRange 0 12 <> exactLength) "Incorrect 12 hour")
348362
Meridiem → _{meridiem = _} `modifyWithParser` parseMeridiem
349363
MinutesTwoDigits → _{minute = _} `modifyWithParser`
350364
(parseInt 2 (validateRange 0 59 <> exactLength) "Incorrect 2-digit minute")

src/Data/Formatter/Parser/Utils.purs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ runP ∷ ∀ s a. PS.StringLike s ⇒ Parser s a → s → Either String a
2121
runP p s = lmap printError $ runParser s (p <* PS.eof)
2222

2323
printError ParseError String
24-
printError err = parseErrorMessage err <> "@" <> (printPosition $ parseErrorPosition err)
24+
printError err = parseErrorMessage err <> " " <> (printPosition $ parseErrorPosition err)
2525

2626
printPosition Position String
27-
printPosition (Position {line, column}) = show line <> ":" <> show column
27+
printPosition (Position {line, column}) = "(line " <> show line <> ", col " <>show column <> ")"

test/src/DateTime.purs

+29-9
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ module Test.DateTime (datetimeTest) where
22

33
import Prelude
44

5-
import Data.Formatter.DateTime as FDT
6-
import Data.List (fromFoldable)
5+
import Control.Alternative (class Alternative, empty)
6+
import Control.MonadZero (guard)
77
import Data.DateTime (DateTime)
88
import Data.Either (Either(..))
9-
import Control.MonadZero (guard)
10-
import Control.Alternative (class Alternative, empty)
11-
import Test.Spec (describe, Spec)
9+
import Data.Formatter.DateTime as FDT
10+
import Data.List (fromFoldable)
11+
import Test.Spec (describe, Spec, it)
1212
import Test.Spec.Assertions (shouldEqual)
1313
import Test.Utils (forAll, makeDateTime)
1414

@@ -28,6 +28,8 @@ datetimeTest = describe "Data.Formatter.DateTime" do
2828
, { format: "YY", dateStr: "00" , date: makeDateTime 0 4 12 0 0 0 0} -- Format 0 with YY
2929
, { format: "YY", dateStr: "01" , date: makeDateTime (-1) 4 12 0 0 0 0} -- Format -1 with YY
3030
, { format: "hh:m:s a", dateStr: "11:3:4 AM", date: makeDateTime 2017 4 12 11 3 4 234 }
31+
, { format: "hh a", dateStr: "12 AM", date: makeDateTime 0 1 1 0 0 0 0 }
32+
, { format: "hh a", dateStr: "12 PM", date: makeDateTime 0 1 1 12 0 0 0 }
3133
, { format: "hh:mm:ss a", dateStr: "11:03:04 AM", date: makeDateTime 2017 4 12 11 3 4 234 }
3234
, { format: "hh:mm:ss.SSS", dateStr: "11:12:30.123", date: makeDateTime 2017 4 10 11 12 30 123 }
3335
, { format: "hh:mm:ss.SSS", dateStr: "11:12:30.023", date: makeDateTime 2017 4 10 11 12 30 23 }
@@ -48,6 +50,24 @@ datetimeTest = describe "Data.Formatter.DateTime" do
4850
(void $ format `FDT.unformatDateTime` dateStr) `shouldEqual` (Right unit)
4951
)
5052

53+
describe "hour 24" do
54+
it "24 hour and more then 0 time component" $ do
55+
let err = (Left "When hour is 24, other time components must be 0 (line 1, col 24)")
56+
shouldEqual (FDT.unformatDateTime "YYYY-DD-MM HH:mm:ss:SSS" "0000-01-01 24:00:00:001") err
57+
shouldEqual (FDT.unformatDateTime "YYYY-DD-MM HH:mm:ss:SSS" "0000-01-01 24:00:01:000") err
58+
shouldEqual (FDT.unformatDateTime "YYYY-DD-MM HH:mm:ss:SSS" "0000-01-01 24:01:00:000") err
59+
60+
it "+1" $ shouldEqual
61+
(FDT.unformatDateTime "YYYY-DD-MM HH:mm:ss:SSS" "0000-01-01 24:00:00:000")
62+
(Right $ makeDateTime 0 1 2 0 0 0 0 )
63+
64+
describe "hour {0,12} {am,pm}" do
65+
let format = "hh a"
66+
it "00 AM" $ FDT.unformatDateTime format "00 AM" `shouldEqual` (Right $ makeDateTime 0 1 1 0 0 0 0 )
67+
it "00 PM" $ FDT.unformatDateTime format "00 PM" `shouldEqual` (Right $ makeDateTime 0 1 1 12 0 0 0 )
68+
it "12 PM" $ FDT.unformatDateTime format "12 PM" `shouldEqual` (Right $ makeDateTime 0 1 1 12 0 0 0 )
69+
it "12 AM" $ FDT.unformatDateTime format "12 AM" `shouldEqual` (Right $ makeDateTime 0 1 1 0 0 0 0 )
70+
5171
describe "parseFormatString" do
5272
forAll
5373
_.str
@@ -59,7 +79,7 @@ datetimeTest = describe "Data.Formatter.DateTime" do
5979
_.str
6080
"shouldn't parse"
6181
invalidDateformats
62-
(\f → (FDT.parseFormatString f.str) `shouldEqual` (Left $ "Expected EOF@" <> f.pos))
82+
(\f → (FDT.parseFormatString f.str) `shouldEqual` (Left $ "Expected EOF " <> f.pos))
6383

6484
forAll
6585
(\a → a.format <> " | " <> a.date)
@@ -96,9 +116,9 @@ dates =
96116

97117
invalidDateformats Array { str String , pos String }
98118
invalidDateformats =
99-
[ { str: "YY-h-dddd HH:mm Z", pos: "1:4" }
100-
, { str: "YYYY-MM-DD M", pos: "1:12" }
101-
, { str: "YYYYM", pos: "1:5" }
119+
[ { str: "YY-h-dddd HH:mm Z", pos: "(line 1, col 4)" }
120+
, { str: "YYYY-MM-DD M", pos: "(line 1, col 12)" }
121+
, { str: "YYYYM", pos: "(line 1, col 5)" }
102122
]
103123

104124
dateformats Array { str String , lossless Boolean, format FDT.Formatter }

test/src/Interval.purs

+37-37
Original file line numberDiff line numberDiff line change
@@ -59,50 +59,50 @@ durations =
5959

6060
invalidDurations Array { err String, str String}
6161
invalidDurations =
62-
[ { err: errInvalidISO "Hour" <> "1:13", str: "P1DT1.5H0M1S" }
63-
, { err: errInvalidISO "Year" <> "1:10", str: "P1.5Y0.5M" }
64-
, { err: errInvalidISO "Year" <> "1:8", str: "P1.5Y1M" }
65-
, { err: errInvalidISO "Month" <> "1:12", str: "P1.5MT10.5S" }
66-
, { err: errInvalidComponent <> "1:2", str: "P" }
67-
, { err: errInvalidComponent <> "1:2", str: "PW" }
68-
, { err: errInvalidComponent <> "1:2", str: "PD" }
69-
, { err: errNoTimeComponent <> "1:3", str: "PT" }
70-
, { err: errNoTimeComponent <> "1:3", str: "PTH" }
71-
, { err: errNoTimeComponent <> "1:5", str: "P1YT" }
72-
, { err: errPrefix <> "1:1", str: "" }
73-
, { err: errPrefix <> "1:1", str: "T" }
74-
, { err: errPrefix <> "1:1", str: "~P1Y" }
75-
, { err: errPrefix <> "1:1", str: ".P1Y" }
76-
, { err: errEOF <> "1:4", str: "P1Y1W" }
62+
[ { err: errInvalidISO "Hour" <> "(line 1, col 13)", str: "P1DT1.5H0M1S" }
63+
, { err: errInvalidISO "Year" <> "(line 1, col 10)", str: "P1.5Y0.5M" }
64+
, { err: errInvalidISO "Year" <> "(line 1, col 8)", str: "P1.5Y1M" }
65+
, { err: errInvalidISO "Month" <> "(line 1, col 12)", str: "P1.5MT10.5S" }
66+
, { err: errInvalidComponent <> "(line 1, col 2)", str: "P" }
67+
, { err: errInvalidComponent <> "(line 1, col 2)", str: "PW" }
68+
, { err: errInvalidComponent <> "(line 1, col 2)", str: "PD" }
69+
, { err: errNoTimeComponent <> "(line 1, col 3)", str: "PT" }
70+
, { err: errNoTimeComponent <> "(line 1, col 3)", str: "PTH" }
71+
, { err: errNoTimeComponent <> "(line 1, col 5)", str: "P1YT" }
72+
, { err: errPrefix <> "(line 1, col 1)", str: "" }
73+
, { err: errPrefix <> "(line 1, col 1)", str: "T" }
74+
, { err: errPrefix <> "(line 1, col 1)", str: "~P1Y" }
75+
, { err: errPrefix <> "(line 1, col 1)", str: ".P1Y" }
76+
, { err: errEOF <> "(line 1, col 4)", str: "P1Y1W" }
7777
]
7878
where
79-
errInvalidComponent = "Must contain valid duration components@"
80-
errPrefix = "Expected \"P\"@"
81-
errEOF = "Expected EOF@"
79+
errInvalidComponent = "Must contain valid duration components "
80+
errPrefix = "Expected \"P\" "
81+
errEOF = "Expected EOF "
8282
errInvalidISO c =
8383
"Extracted Duration is not valid ISO duration " <>
84-
"(Invalid usage of Fractional value at component `" <> c <> "`)@"
85-
errNoTimeComponent = "None of valid duration components ([\"H\",\"M\",\"S\"]) were present@"
84+
"(Invalid usage of Fractional value at component `" <> c <> "`) "
85+
errNoTimeComponent = "None of valid duration components ([\"H\",\"M\",\"S\"]) were present "
8686

8787
invalidIntervals Array {err String, str String}
8888
invalidIntervals =
89-
[ { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00ZP1Y2M10DT2H30M" }
90-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z-P1Y2M10D" }
91-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z~P1Y2M10D" }
92-
, { err: "Expected EOF@1:15", str: "P1Y2M10DT2H30M2007-03-01T13:00:00Z" }
93-
, { err: "Expected EOF@1:9", str: "P1Y2M10D-2007-03-01T13:00:00Z" }
94-
, { err: "Expected EOF@1:9", str: "P1Y2M10D~2007-03-01T13:00:00Z" }
95-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z2008-05-11T15:30:00Z" }
96-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z-2008-05-11T15:30:00Z" }
97-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z~2008-05-11T15:30:00Z" }
98-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/" }
99-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/P" }
100-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/PT" }
101-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/2010-0-09" }
102-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/2010-05-09T103012+0400" }
103-
, { err: "Expected \"P\"@1:1", str: "2007-03-01T13:00:00Z/2014-W15-02T10:11:12Z" }
104-
, { err: "Expected EOF@1:9", str: "P1Y2M10D/P1Y2M10D" }
105-
, { err: "Expected EOF@1:8", str: "P1Y0.5M/P1Y0.5M" }
89+
[ { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00ZP1Y2M10DT2H30M" }
90+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z-P1Y2M10D" }
91+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z~P1Y2M10D" }
92+
, { err: "Expected EOF (line 1, col 15)", str: "P1Y2M10DT2H30M2007-03-01T13:00:00Z" }
93+
, { err: "Expected EOF (line 1, col 9)", str: "P1Y2M10D-2007-03-01T13:00:00Z" }
94+
, { err: "Expected EOF (line 1, col 9)", str: "P1Y2M10D~2007-03-01T13:00:00Z" }
95+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z2008-05-11T15:30:00Z" }
96+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z-2008-05-11T15:30:00Z" }
97+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z~2008-05-11T15:30:00Z" }
98+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/" }
99+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/P" }
100+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/PT" }
101+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/2010-0-09" }
102+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/2010-05-09T103012+0400" }
103+
, { err: "Expected \"P\" (line 1, col 1)", str: "2007-03-01T13:00:00Z/2014-W15-02T10:11:12Z" }
104+
, { err: "Expected EOF (line 1, col 9)", str: "P1Y2M10D/P1Y2M10D" }
105+
, { err: "Expected EOF (line 1, col 8)", str: "P1Y0.5M/P1Y0.5M" }
106106
]
107107

108108
recurrences Array { str String, rec Maybe Int }

0 commit comments

Comments
 (0)