Подтвердить что ты не робот

Синхронизация и форматирование даты Haskell

Я работал с модулями Haskell Date.Time для анализа даты типа 12-4-1999 или 1-31-1999. Я пробовал:

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

И я думаю, что он хочет, чтобы мои месяцы и дни имели ровно две цифры вместо 1 или 2...

Каков правильный способ сделать это?

Кроме того, я хотел бы распечатать мой День в этом формате: 12/4/1999, каков путь Haskell?

Спасибо за помощь.

4b9b3361

Ответ 1

Вот какой-то старый код, который содержит два типа самодельных дат, даты только с YMD, время или время и т.д.

Он показывает, как анализировать строки в датах с помощью 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 можно использовать для форматирования даты для отображения.

Ответ 2

Вы можете использовать функции в Data.Time.Format для чтения в датах. Я включил тривиальную программу ниже, которая читается в дате в одном формате и записывает эту дату в двух разных форматах. Чтобы читать в однозначные месяцы или дни, поместите один дефис (-) между спецификатором% и 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 года, локаль теперь лучше всего получается из пакета 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 года, приведенный выше код использует устаревшее время parseTime. Теперь вам предлагается использовать 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:   build-depends: base >= 4.9 && & < 4,10, время >= 1,6,0,1

Тем не менее, этот ответ становится немного длинным в зубе, учитывая скорость, с которой развиваются пакеты Haskell. Возможно, пришло время искать лучшее решение.

Ответ 3

Использование% -d и% -m вместо% d и% m означает, что один день день/месяц в порядке, то есть

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

Это может быть то, что означал sclv, но его комментарий был для меня слишком загадочным.

Ответ 4

В последнее время я советую использовать strptime пакет для всех ваших синтаксических разборок даты/времени.