Синхронизация и форматирование даты 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?
Спасибо за помощь.
Ответы
Ответ 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 пакет для всех ваших синтаксических разборок даты/времени.