haskell / time

A time library
http://hackage.haskell.org/package/time
Other
120 stars 80 forks source link

Parsing any number to Day with %s gives 1970-01-01 #261

Closed simonmichael closed 2 months ago

simonmichael commented 2 months ago

Thank you for time!

A hledger csv rules user reported that parsing a seconds since epoch value with %s (to a date) always produces 1970-01-01. I reproduced with time 1.14's parseTimeM on a mac.

The doc says

For UTCTime and ZonedTime: %s number of whole seconds since the Unix epoch. For times before the Unix epoch, this is a negative number. Note that in %s.%q and %s%Q the decimals are positive, not negative. For example, 0.9 seconds before the Unix epoch is formatted as -1.1 with %s%Q.

whereas hledger is trying to parse a Day. In this case, should it not either: fail to parse, or (better) return the Day of the corresponding UTCTime ?

AshleyYakeley commented 2 months ago

Can you give me an example of what you're doing? In any case, %s doesn't apply to Day and shouldn't be used for that type.

simonmichael commented 2 months ago
ghci> parseTimeM False defaultTimeLocale "%s" "1724760000" :: Maybe Day
Just 1970-01-01
ghci> parseTimeM False defaultTimeLocale "%s" "42" :: Maybe Day
Just 1970-01-01
ghci> parseTimeM False defaultTimeLocale "%s" "-1" :: Maybe Day
Just 1970-01-01

If this is an unsupported/invalid operation, I think it should return Nothing. I expected "-1" to fail at least.

It came up in this real world use case, where hledger tries to parse dates from bank data: https://money.stackexchange.com/questions/163878/why-isnt-hledger-import-parsing-epoch-timestamps

Ideally for me, parsing a Day from a valid epoch-seconds value would succeed, parsing first to UTCTime and then keeping just the date part. Would there be problems with this ?

AshleyYakeley commented 2 months ago

This is explained in the documentation:

parseTimeM Parses a time value given a format string. Missing information will be derived from 1970-01-01 00:00 UTC (which was a Thursday).

It works like this:

  1. The format string is matched against the input text. If the text doesn't match, then it's Nothing. Each modifier is matched against text regardless of the type. So in this case, %s is matched against a number (which may be signed).
  2. The value of the type is assembled from the modifier-matches relevant to the type, with missing information defaulting to 1970-01-01 00:00 UTC. In this case there are no modifiers relevant to the type, so you get 1970-01-01.

In any case, can't you just parse this as a UTCTime, since it is a UTCTime, and extract the day from that?

chreekat commented 2 months ago

Just a comment from the peanut gallery: I read that bit of documentation after simonmichael mentioned his confusion on Matrix, and I did not come away thinking that the current behavior is expected, or even correct. What information is missing? By specifying seconds since the epoch, isn't there too much information for building a Day, rather than too little?

AshleyYakeley commented 2 months ago

OK, let me look in the code to see what I can do here.

AshleyYakeley commented 2 months ago

Done in master.

simonmichael commented 2 months ago

Great news, thank you for this work.