2010-11-13 76 views
21

我一直在使用Haskell的Date.Time模块来解析日期,如12-4-19991-31-1999。我想:Haskell日期解析和格式化

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

而且我觉得它想要我的月份和日期有恰好为两个数字,而不是1个或2 ...

什么是做到这一点的正确方法?

另外,我想用这种格式打印我的日期:12/4/1999 Haskell的方式是什么?

感谢您的帮助。

+1

无论如何你不需要在%m,%d和%Y之间划线? – sclv 2010-11-14 19:42:44

+0

@AlexBaranosky你可能要考虑接受这个问题的不同答案:) – Ben 2016-10-11 14:23:54

回答

-2

这里是包含两种类型的国产日期的一些旧代码,只有年月日,没有时间或时区,日期等

它显示了如何解析字符串转换为使用readDec日期。请参阅parseDate函数。用readDec,读取这个数字,关于前导空格(因为filter)或前导零并不重要,并且解析停在第一个非数字上。然后使用tail(跳过非数字)到达日期的下一个数字字段。

它显示了输出格式的几种方式,但最灵活的方法是使用Text.printf。见instance Show LtDate。有了printf,任何事情都是可能的!

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 [email protected](_:ds') 
      | ds'==[] = '0':ds 
      | otherwise = ds 


-- | from LtDate to Date 
fromLtDate :: LtDate -> Date 
fromLtDate lt = (ltYear lt, ltMonth lt, ltDay lt) 

一旦你有(Y,M,d),可以很容易地转换成一个Haskell库类型的数据操作。完成HS库之后,可以使用Text.printf来格式化显示日期。

+5

这似乎很可笑地复杂!必须有一个更简单的方法,上帝帮助我 – 2010-11-14 00:17:12

+0

请仔细看看,代码是一个例子,并不是所有必要的:解析日期'parseDate'只需要4行;调用'readDec' 3次。要显示日期,请使用Text.printf'一次。加载文件并在你的日期运行它看看它的工作原理,然后看看'parseDat'e和'printf'。这parseDate忽略空格,并不关心日期中的分隔符' - '或'/'。它改变了'55到1955年等等。它是10行(包括'parseDate'')'ShowDate','fromLtDate'和'data'语句是不需要的。时钟和日历都不包括在内。 – frayser 2010-11-14 13:19:16

+1

删除'parseDate'和'parseDate''之外的所有内容,这会使它看起来更简单。保留'Data.Char'和'Numeric'的包含年解析器''parseDate''可以被另一个调用'readDec'替代。 – frayser 2010-11-14 13:30:49

7

因为最近,我会建议您使用strptime包来满足您所有的日期/时间解析需求。

46

您可以使用Data.Time.Format中的函数来读取日期。我在下面列出了一个简单的程序,它以一种格式读取日期,并以两种不同的格式写出日期。要读取单位数月或数天,请在%和格式说明符之间放置一个连字符( - )。

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 

输出看起来是这样的:

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

Data.Time.Format可从 “时间” 包。如果您需要解析一位数的月份或日期,换言之,像9-9-2012这样的日期,那么在%和格式字符之间包含一个连字符。因此,要解析“9-9-2012”,您需要格式字符串“%-d - % - m-%Y”。

截至2014年8月,语言环境现在最好从“System.Locale”包而不是Haskell 1998“Locale”包中获得。考虑到这一点,从现在上面的代码示例如下:

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 

输出现在看起来像这样:

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

随着2017年7月的,上面的代码使用现在已经过时分析时。鼓励您现在使用parseTimeOrError。代码变为:

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 

的从.cabal文件版本: 集结取决于:基础> = 4.9 & & < 4.10,时间> = 1.6.0.1

这就是说,这个答案越来越在哈斯克尔软件包发展速度方面有点长久。现在可能是寻找更好解决方案的时候了。

+5

对于懒惰,这是在'时间'包。 – user239558 2013-06-27 19:19:05

+1

男人!该%-d技巧应该在文档中。 – Bzzt 2014-11-18 05:53:50

+0

我得到'不明确的发生'defaultTimeLocale''这是它,'Data.Time.defaultTimeLocale'或'System.Locale.defaultTimeLocale'? – peer 2017-07-08 21:26:51

9

使用%D和%-m代替%d和%m表示单数位日/月是确定的,即

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

这可能是什么sclv意思,但他的评论有点太对我来说很神秘。

+1

用于表示可能的单位数月/日的'-'标志。我把头发拉出来,转向正规表演! – 2014-07-10 19:21:46