module Fecha (Fecha,
nuevaFe,
diaFe,
mesFe,
anioFe,
sumaFe,
diasEntreFe) where
data Fecha = Fec Int deriving(Eq, Ord)
instance Show Fecha
where show f = show (diaFe f) ++ "/" ++ show (mesFe f) ++ "/" ++ show (anioFe f)
-----------------------
-- Funciones pedidas --
-----------------------
nuevaFe :: Int -> Int -> Int -> Fecha
nuevaFe dia mes anio = Fec (diasDesdeCero dia mes anio)
anioFe :: Fecha -> Int
anioFe (Fec d) = obtenerElAnio d
mesFe :: Fecha -> Int
mesFe (Fec d) = obtenerElMes (obtenerDiasSobrantesAnio d) (obtenerElAnio d)
diaFe (Fec d) = obtenerDiasSobrantesMes (obtenerDiasSobrantesAnio d) (obtenerElAnio d)
sumaFe :: Fecha -> Int -> Fecha
sumaFe (Fec f) d = Fec (f+d)
diasEntreFe :: Fecha -> Fecha -> Int
diasEntreFe (Fec f1) (Fec f2) = f2 - f1
----------------
-- Auxiliares --
----------------
-- devuelve el numero de dia desde el 1/1/0001
diasDesdeCero :: Int -> Int -> Int -> Int
diasDesdeCero d m a = diasHastaElAnio a + diasHastaElMes m a + d
-- devuelve la cantidad de dias hasta el comienzo del anio desde 1/1/0001
diasHastaElAnio :: Int -> Int
diasHastaElAnio a | a > 0 = auxDiasHastaElAnio a 1
auxDiasHastaElAnio :: Int -> Int -> Int
auxDiasHastaElAnio a c | a == c = 0
| esBisiesto a = 366 + auxDiasHastaElAnio a (c + 1)
| otherwise = 365 + auxDiasHastaElAnio a (c + 1)
-- devuelve los dias desde el comienzo del anio hasta el mes indicado
diasHastaElMes :: Int -> Int -> Int
diasHastaElMes m a | m > 0 && a > 0 = auxDiasHastaElMes m a 1
auxDiasHastaElMes :: Int -> Int -> Int -> Int
auxDiasHastaElMes m a c | c == m = 0
auxDiasHastaElMes m a c | c < m && esMesDeTreinta c = 30 + auxDiasHastaElMes m a (c + 1)
| c < m && esMesDeTreintaYUno c = 31 + auxDiasHastaElMes m a (c + 1)
| c < m && m == 2 && esBisiesto a = 29 + auxDiasHastaElMes m a (c + 1)
| c == 2 && not (esBisiesto a) = 28 + auxDiasHastaElMes m a (c + 1)
esBisiesto a = (a `mod` 4 == 0 && a `mod` 100 /= 0 ) || (a `mod` 400 == 0)
-- TODO: se puede usar `elem`?
esMesDeTreinta m = m `elem` [4,6,9,11]
-- TODO: se puede usar `elem`?
esMesDeTreintaYUno m = m `elem` [1,3,5,7,8,10,12]
-- devuelve la maxima cantidad posible de anios que se le
-- le puede restar a la cantidad de dias indicada
obtenerElAnio :: Int -> Int
obtenerElAnio d | d > 0 = auxObtenerElAnio d 1
auxObtenerElAnio :: Int -> Int -> Int
auxObtenerElAnio d c | d < 366 && esBisiesto c = c
| d < 365 = c
| not (esBisiesto c) = auxObtenerElAnio (d-365) (c+1)
| esBisiesto c = auxObtenerElAnio (d-366) (c+1)
-- devuelve el resto en dias despues de restar la mayor
-- cantidad posible de anios
obtenerDiasSobrantesAnio :: Int -> Int
obtenerDiasSobrantesAnio d | d > 0 = auxObtenerDiasSobrantesAnio d 1
auxObtenerDiasSobrantesAnio :: Int -> Int -> Int
auxObtenerDiasSobrantesAnio d c | d < 366 && esBisiesto c = d
| d < 365 = d
| not (esBisiesto c) = auxObtenerDiasSobrantesAnio (d-365) (c+1)
| esBisiesto c = auxObtenerDiasSobrantesAnio (d-366) (c+1)
-- analogo a obtenerElAnio pero con el numero de dia dentro de un anio
obtenerElMes :: Int -> Int -> Int
obtenerElMes d a | d > 0 && a > 0 = auxObtenerElMes d a 1
auxObtenerElMes :: Int -> Int -> Int -> Int
auxObtenerElMes d a c | d <= 31 && esMesDeTreintaYUno c = c
| d <= 30 && esMesDeTreinta c = c
| d <= 29 && c == 2 && esBisiesto a = c
| d <= 28 && c == 2 && not (esBisiesto a) = c
| esMesDeTreintaYUno c = auxObtenerElMes (d - 31) a (c + 1)
| esMesDeTreinta c = auxObtenerElMes (d - 30) a (c + 1)
| c == 2 && esBisiesto a = auxObtenerElMes (d - 29) a (c + 1)
| c == 2 && not (esBisiesto a) = auxObtenerElMes (d - 28) a (c + 1)
-- analogo a obtenerDiasSobrantesAnio pero con el numero de dia dentro de un anio
obtenerDiasSobrantesMes :: Int -> Int -> Int
obtenerDiasSobrantesMes d a | d > 0 && a > 0 = auxObtenerDiasSobrantesMes d a 1
auxObtenerDiasSobrantesMes :: Int -> Int -> Int -> Int
auxObtenerDiasSobrantesMes d a c | d <= 31 && esMesDeTreintaYUno c = d
| d <= 30 && esMesDeTreinta c = d
| d <= 29 && c == 2 && esBisiesto a = d
| d <= 28 && c == 2 && not (esBisiesto a) = d
| esMesDeTreintaYUno c = auxObtenerDiasSobrantesMes (d - 31) a (c + 1)
| esMesDeTreinta c = auxObtenerDiasSobrantesMes (d - 30) a (c + 1)
| c == 2 && esBisiesto a = auxObtenerDiasSobrantesMes (d - 29) a (c + 1)
| c == 2 && not (esBisiesto a) = auxObtenerDiasSobrantesMes (d - 28) a (c + 1)