purescript-contrib / purescript-formatters

Formatting and printing for numeric and date/time/interval values
Apache License 2.0
41 stars 29 forks source link

what's the point of YearTwoDigits #52

Open safareli opened 5 years ago

safareli commented 5 years ago

If YearTwoDigits is used and we have 95 as input string, it would be parsed to date where year is 95, is it expected? I think most users will expect to get 1995.
i.e. it should work like JS, for example: new Date(Date.UTC(95,1,1)).getUTCFullYear() // => 1995 but: new Date(Date.UTC(-17,1,1)).getUTCFullYear() // => -17

Either parsing should have side effect of looking at current century to have similar behavior or otherwise having YearTwoDigits has almost no point.

garyb commented 5 years ago

I use this as more of a printing library than a parsing library so haven't run into this particular problem, but yeah, it is kinda weird. I also agree YearTwoDigits is questionable in general, I can't think of a situation in which I'd ever want to use it, because of its ambiguity.

I don't really know how to resolve it though - whatever choice we make is going to be arbitrary. 40 means what? Year 40, 1940, or 2040? If it means 2040, why is 95 not 2095? We can definitely make some rules up to get more "sensible" behaviour, but they won't suit everyone in all situations.

Call me pessimistic, but I suspect we won't be using this library in the year 2100, so I don't think we need to make it effectful either, we can just hard code the century 😄.

garyb commented 5 years ago

Looks like JS special cases the years 0-99 as being in the 1900s.

safareli commented 5 years ago

If we change

-| YearTwoDigits
+| YearTwoDigits Century

and add with some kind of Century module:

fromCentury :: Year -> Century
setYear :: Year99 -> Century -> Year
getYear99 :: Year -> Year99
getCentury :: Year -> Century

Then we can have implementation which works like this:

parse  [YearTwoDigits (Century 19)] "96" `yearEquals` 1996
parse  [YearTwoDigits (Century 19)] "96" `yearEquals` 1996
parse  [YearTwoDigits (Century 20)] "41" `yearEquals` 2041
parse  [YearTwoDigits (Century 20)] "01" `yearEquals` 2001

print [YearTwoDigits (Century 19)] (dateFromYear 1996) `shouldEqual` "96"
print [YearTwoDigits (Century 19)] (dateFromYear 1996) `shouldEqual` "96"
print [YearTwoDigits (Century 20)] (dateFromYear 2041) `shouldEqual` "41"
print [YearTwoDigits (Century 20)] (dateFromYear 2001) `shouldEqual` "01"

print [YearTwoDigits (Century 20)] (dateFromYear 1996) `shouldEqual` "1996"
print [YearTwoDigits (Century 10)] (dateFromYear 2041) `shouldEqual` "2041"

last 2 cases are a bit wrong, but it's happening because same type is used for print format and for parse format.

another option would be to use higher kinded type thing for format:

data ForamtF f =
...
| YearTwoDigits (f Unit Century)

type PickPrintFormat a b = a
type PickParseFormat a b = a
type PrintFormat = ForamtF PickPrintFormat
type ParseFormat = ForamtF PickParseFormat