2010-11-13 41 views
21

He estado trabajando con los módulos Date.Time de Haskell para analizar una fecha como 12-4-1999 o 1-31-1999. Probé:Analizador de fecha Haskell y formateo

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

Y creo que quiere mis meses y días para que tengan exactamente dos dígitos en lugar de 1 o 2 ...

¿Cuál es la forma correcta de hacer esto?

Además, me gustaría imprimir mi día en este formato: 12/4/1999 ¿cuál es la forma de Haskell?

Gracias por la ayuda.

+1

¿no necesita guiones entre% m,% d, y% Y de todos modos? – sclv

+0

@AlexBaranosky es posible que desee considerar aceptar una respuesta diferente a esta pregunta :) – Ben

Respuesta

-2

Aquí hay un código antiguo que contiene dos tipos de fechas de fabricación casera, las fechas con sólo AMD, no hay tiempo o zonas horarias, etc.

Muestra cómo analizar cadenas en fechas usando readDec. Ver la función parseDate. Con readDec, lea el número, no importa los espacios iniciales (debido a filter) o los ceros a la izquierda, y el análisis se detiene en el primer dígito no. Luego usó tail (para omitir el dígito no) para ir al siguiente campo numérico de la fecha.

Muestra varias formas de formatear para la salida, pero la forma más flexible es usar Text.printf. Ver instance Show LtDate. Con printf, ¡todo es posible!

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) 

Una vez que tenga (Y, M, C), es fácil convertir a un tipo de biblioteca de Haskell manipulaciones de datos. Una vez que haya terminado con las bibliotecas HS, puede usar Text.printf para formatear una fecha para mostrar.

+5

que parece ridículamente complicado! Debe haber una manera más simple, dios ayúdeme –

+0

Mire de cerca, el código era un ejemplo y no es todo necesario: para analizar la fecha'parseDate' requiere solo 4 líneas; llama a 'readDec' 3 veces. Para mostrar una fecha use Text.printf' una vez. Cargue el archivo y ejecútelo en su fecha para ver si funciona, luego mire esos 'parseDat'e y' printf'. Este parseDate ignora espacios y no le importa el separador '-', o '/' en las fechas. Cambia '55 a 1955, etc. Son 10 líneas (incluye 'parseDate'')' ShowDate', y 'fromLtDate' y las declaraciones' data' no son necesarias. Ni Clock ni Calender incluyen. – frayser

+1

Borrar todo excepto 'parseDate' y' parseDate'' lo hará parecer más simple. Guarde los includes para 'Data.Char' y' Numeric'. El analizador del año, 'parseDate'' se puede reemplazar con otra llamada a' readDec'. – frayser

7

Desde hace poco, aconsejaré usar el paquete strptime para todas sus necesidades de análisis de fecha/hora.

46

Puede usar las funciones en Data.Time.Format para leer las fechas. He incluido un programa trivial a continuación que se lee en una fecha en un formato y escribe esa fecha en dos formatos diferentes. Para leer en meses o días de un solo dígito, coloque un solo guión (-) entre el% y el especificador de formato.

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 

La salida tiene el siguiente aspecto:

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

Data.Time.Format está disponible en el paquete de "tiempo". Si necesita analizar meses o días de un solo dígito, en otras palabras, fechas como 9-9-2012, incluya un único guión entre el% y los caracteres de formato. Por lo tanto, para analizar "9-9-2012", necesitaría la cadena de formato "% -d -% - m-% Y".

A partir de agosto de 2014, la configuración regional ahora se obtiene mejor del paquete "System.Locale" en lugar del paquete "Locale" de Haskell 1998.Con esto en mente, el código de ejemplo desde arriba ahora lee:

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 

de salida ahora se ve así:

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

partir de julio de 2017, el código anterior utiliza el parseTime ahora en desuso. Se le anima a usar parseTimeOrError ahora. El código se convierte en:

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 

Las versiones del archivo: .cabal acumulación depende: base de> = 4,9 & & < 4.10, hora> = 1.6.0.1

Dicho esto, esta respuesta es cada vez un poco largo en el diente dado el ritmo al que evolucionan los paquetes de Haskell. Puede ser hora de buscar una mejor solución.

+5

Para los perezosos, esto está en el paquete 'tiempo'. – user239558

+1

hombre! Ese truco de% d debe estar en la documentación. – Bzzt

+0

Obtengo 'ocurrencia ambigua 'defaultTimeLocale'' que es,' Data.Time.defaultTimeLocale' o 'System.Locale.defaultTimeLocale'? – peer

9

Usando% -d -m% y en lugar de% dy% m significa un dígito día/mes está bien, es decir

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

Esto puede ser lo que significaba SCLV, pero su comentario fue un poco demasiado críptico para mí.

+1

+1 para el indicador '-' para indicar el posible mes/día de un solo dígito. ¡Estaba tirando de mis cabellos sobre esto y a punto de recurrir a expresiones regulares! –

Cuestiones relacionadas