简体   繁体   English

Haskell日期解析和格式化

[英]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 . 我一直在使用Haskell的Date.Time模块来解析12-4-19991-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... 而且我认为我希望我的几个月和几天只有两位数而不是1位或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? 另外,我想以这种格式打印出我的日子: 12/4/1999什么是Haskell的方式?

Thanks for the help. 谢谢您的帮助。

You can use the functions in Data.Time.Format to read in the dates. 您可以使用Data.Time.Format中的函数读取日期。 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. 换句话说,要解析格式为9-9-2012的日期,请在%和格式字符之间包含一个连字符。 So to parse "9-9-2012" you would need the format string "%-d-%-m-%Y". 因此,要解析“9-9-2012”,您需要格式字符串“%-d - % - m-%Y”。

Note: This answer is getting a bit long-in-the-tooth given the rate at which Haskell packages evolve. 注意:考虑到Haskell软件包的发展速度,这个答案有点长。 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. 我不再编写Haskell所以如果其他人创建了另一个答案会更好,因为这个问题在Google搜索结果中排名很高。

As of July 2017 , the below code uses the now deprecated parseTime. 截至2017年7月 ,以下代码使用现已弃用的parseTime。 You are encouraged to use parseTimeOrError now. 建议您立即使用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 .cabal文件中的版本: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. 截至2014年8月 ,语言环境最好从“System.Locale”包而不是Haskell 1998“Locale”包中获得。 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: **原创,2012年1月**答案:

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. Data.Time.Format可从“time”包中获得。 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. 如果您需要解析单个数字的月或日,换句话说,如9-9-2012之类的日期,则在%和格式字符之间包含一个连字符。 So to parse "9-9-2012" you would need the format string "%-d-%-m-%Y". 因此,要解析“9-9-2012”,您需要格式字符串“%-d - % - m-%Y”。

Using %-d and %-m instead of %d and %m means single digit day/month is OK, ie 使用%-d和%-m而不是%d和%m表示单个数字日/月是正常的,即

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. 这可能是sclv的意思,但他的评论对我来说有点过于神秘。

从最近开始,我建议使用strptime软件包来满足您所有的日期/时间解析需求。

Here is some old code that contains two types of homemade dates, dates with just YMD, no time or timezones, etc. 这里有一些旧代码,包含两种类型的自制日期,只有YMD的日期,没有时间或时区等。

It shows how to parse strings into dates using readDec . 它显示了如何使用readDec将字符串解析为日期。 See the parseDate function. 请参阅parseDate函数。 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. 使用readDec读取数字,与前导空格(由于filter )或前导零无关,并且解析在第一个非数字处停止。 Then used tail (to skip the non digit) to get to the next numerical field of the date. 然后使用tail (跳过非数字)到达日期的下一个数字字段。

It shows several ways of formatting for output, but the most flexible way is to use Text.printf . 它显示了几种格式化输出的方法,但最灵活的方法是使用Text.printf See instance Show LtDate . 请参见instance Show LtDate With printf , anything is possible! 有了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 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. 一旦你有(Y,M,D),很容易转换为Haskell库类型进行数据操作。 Once you are done with the HS libraries, Text.printf can be used to format a date for display. 完成HS库后,可以使用Text.printf格式化显示日期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM