Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell date parsing and formatting

I've been working with Haskell's Date.Time modules to parse a date like 12-4-1999 or 1-31-1999. I tried:

parseDay :: String -> Day
parseDay s = readTime defaultTimeLocale "%m%d%Y" s

And I think it wants my months and days to have exactly two digits instead of 1 or 2...

What's the proper way to do this?

Also, I'd like to print out my Day in this format: 12/4/1999 what's the Haskell way to?

Thanks for the help.

like image 381
Alex Baranosky Avatar asked Nov 13 '10 19:11

Alex Baranosky


2 Answers

You can use the functions in Data.Time.Format to read in the dates. I've included a trivial program below that reads in a date in one format and writes that date back out in two different formats. To read in single-digit months or days, place a single hyphen (-) between the % and format specifier. In other words to parse dates formatted like 9-9-2012 then include a single hyphen between the % and the format characters. So to parse "9-9-2012" you would need the format string "%-d-%-m-%Y".

Note: This answer is getting a bit long-in-the-tooth given the rate at which Haskell packages evolve. It might be time to look for a better solution. I'm no longer writing Haskell so it would be nice if someone else created another answer since this question ranks highly in Google results.

As of July 2017, you are encouraged to use parseTimeOrError. The code becomes:

import Data.Time

main =
  do
    let dateString = "26 Jan 2012 10:54 AM"
    let timeFromString = parseTimeOrError True defaultTimeLocale "%d %b %Y %l:%M %p" dateString :: UTCTime
    -- Format YYYY/MM/DD HH:MM
    print $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M" timeFromString
    -- Format MM/DD/YYYY hh:MM AM/PM
    print $ formatTime defaultTimeLocale "%m/%d/%Y %I:%M %p" timeFromString

    -- now for a string with single digit months and days:
    let dateString = "9-8-2012 10:54 AM"
    let timeFromString = parseTimeOrError True defaultTimeLocale "%-d-%-m-%Y %l:%M %p" dateString :: UTCTime
    -- Format YYYY/MM/DD HH:MM
    print $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M" timeFromString

The versions from the .cabal file: build-depends: base >=4.9 && <4.10, time >= 1.6.0.1

As of August, 2014, the locale was best obtained from the "System.Locale" package rather than the Haskell 1998 "Locale" package. With that in mind, the sample code from above now reads:

import System.Locale
import Data.Time
import Data.Time.Format

main =
  do
    let dateString = "26 Jan 2012 10:54 AM"
    let timeFromString = readTime defaultTimeLocale "%d %b %Y %l:%M %p" dateString :: UTCTime
    -- Format YYYY/MM/DD HH:MM
    print $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M" timeFromString
    -- Format MM/DD/YYYY hh:MM AM/PM
    print $ formatTime defaultTimeLocale "%m/%d/%Y %I:%M %p" timeFromString

    -- now for a string with single digit months and days:
    let dateString = "9-8-2012 10:54 AM"
    let timeFromString = readTime defaultTimeLocale "%-d-%-m-%Y %l:%M %p" dateString :: UTCTime
    -- Format YYYY/MM/DD HH:MM
    print $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M" timeFromString

output now looks like this:

"2012/01/26 10:54"
"01/26/2012 10:54 AM"
"2012/08/09 10:54"

**Original, January 2012 ** answer:

import Locale
import Data.Time
import Data.Time.Format

main =
  do
    let dateString = "26 Jan 2012 10:54 AM"
    let timeFromString = readTime defaultTimeLocale "%d %b %Y %l:%M %p" dateString :: UTCTime
    -- Format YYYY/MM/DD HH:MM
    print $ formatTime defaultTimeLocale "%Y/%m/%d %H:%M" timeFromString
    -- Format MM/DD/YYYY hh:MM AM/PM
    print $ formatTime defaultTimeLocale "%m/%d/%Y %I:%M %p" timeFromString

The output looks like this:

"2012/01/26 10:54"
"01/26/2012 10:54 AM"

Data.Time.Format is available from the "time" package. If you need to parse single-digit months or days, in other words dates like 9-9-2012 then include a single hyphen between the % and the format characters. So to parse "9-9-2012" you would need the format string "%-d-%-m-%Y".

like image 199
Tim Perry Avatar answered Sep 23 '22 01:09

Tim Perry


Using %-d and %-m instead of %d and %m means single digit day/month is OK, i.e.

parseDay :: String -> Day
parseDay s = readTime defaultTimeLocale "%-m%-d%Y" s

This may be what sclv meant, but his comment was a little too cryptic for me.

like image 27
tesujimath Avatar answered Sep 23 '22 01:09

tesujimath