Haskell date parsing and formatting

后端 未结 4 1776
悲&欢浪女
悲&欢浪女 2020-12-30 23:39

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 -&g         


        
相关标签:
4条回答
  • 2020-12-31 00:00

    Here is some old code that contains two types of homemade dates, dates with just YMD, no time or timezones, etc.

    It shows how to parse strings into dates using readDec. See the parseDate function. With readDec, read the number, it doesn't matter about leading spaces(because of filter) or leading zeros,and the parse stops at the first non-digit. Then used tail (to skip the non digit) to get to the next numerical field of the date.

    It shows several ways of formatting for output, but the most flexible way is to use Text.printf. See instance Show LtDate. With printf, anything is possible!

    import Char
    import Numeric
    import Data.Time.Calendar
    import Data.Time.Clock
    import Text.Printf
    -- ================================================================
    --                        LtDate
    -- ================================================================
    type Date=(Int,Int,Int)
    data LtDate = LtDate 
      { ltYear :: Int,
        ltMonth:: Int,
        ltDay  :: Int
      } 
    instance Show LtDate 
      where show d = printf "%4d-%02d-%02d" (ltYear d) (ltMonth d) (ltDay d)
    
    toLtDate :: Date -> LtDate
    toLtDate (y,m,d)= LtDate y m d
    
    -- =============================================================
    --                         Date
    -- =============================================================
    -- | Parse a String mm/dd/yy into tuple (y,m,d)
    -- accepted formats
    --
    -- @
    -- 12\/01\/2004
    -- 12\/ 1\' 4
    -- 12-01-99
    -- @
    parseDate :: String -> Date
    parseDate s = (y,m,d)
        where [(m,rest) ] = readDec (filter (not . isSpace) s)
              [(d,rest1)] = readDec (tail rest)
              [(y, _)   ] = parseDate' rest1
    
    -- | parse the various year formats used by Quicken dates
    parseDate':: String -> [(Int,String)]
    parseDate' (y:ys) =
      let [(iy,rest)] = readDec ys
          year=case y of '\''      -> iy + 2000
                         _  ->
                           if iy < 1900 then  iy + 1900 else iy
       in [(year,rest)]
    
    -- | Note some functions sort by this format
    -- | So be careful when changing it.
    showDate::(Int, Int, Int) -> String
    showDate (y,m,d)= yy ++ '-':mm ++ '-':dd
        where dd=zpad (show d)
              mm = zpad (show m)
              yy = show y
              zpad ds@(_:ds')
               | ds'==[] = '0':ds
               | otherwise = ds
    
    
    -- | from LtDate to Date
    fromLtDate :: LtDate -> Date
    fromLtDate  lt = (ltYear lt, ltMonth lt, ltDay lt)
    

    Once you have (Y,M,D), it's easy to convert to a Haskell library type for data manipulations. Once you are done with the HS libraries, Text.printf can be used to format a date for display.

    0 讨论(0)
  • 2020-12-31 00:00

    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.

    0 讨论(0)
  • 2020-12-31 00:05

    Since recently, I'll advice to use strptime package for all your date/time parsing needs.

    0 讨论(0)
  • 2020-12-31 00:12

    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, the below code uses the now deprecated parseTime. You are encouraged to use parseTimeOrError now. 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".

    0 讨论(0)
提交回复
热议问题