# Time Library Date Normalisation and Arithmetic

Ashley Yakeley ashley at semantic.org
Sat Jul 9 07:36:41 EDT 2005

I'm thinking of something along these lines:

Normalisation
-------------

class (Eq a,Ord a) => Normalisable a where
isNormal :: a -> Bool
normaliseTruncate :: a -> a
normaliseRollover :: a -> a

The normalise functions would work like this:

normaliseTruncate
2005/14/32 -> 2005/12/32 -> 2005/12/31
2005/-2/-4 -> 2005/01/-4 -> 2005/01/01

normaliseRollover
2005/14/32 -> 2006/02/32 -> 2006/03/04
2005/-2/-4 -> 2004/10/-4 -> 2004/09/26

(doing both steps in each call)

Calendar Arithmetic
-------------------

This is one way of providing arithmetic for such things as Gregorian
dates:

data TimeUnit d = TimeUnit
addTimeUnitTruncate :: Integer -> d -> d
addTimeUnitRollover :: Integer -> d -> d
diffTimeUnitFloor :: d -> d -> Integer

days :: (DayEncoding d) => TimeUnit d
gregorianMonths :: (DayEncoding d) => TimeUnit d
gregorianYears :: (DayEncoding d) => TimeUnit d

So for instance, to add three months to a date d, you do this:

d' = addTimeUnitTruncate gregorianMonths 3 d

This might also be used for minutes and so forth. And you might also be
able to do this:

multipleTimeUnit :: Integer -> TimeUnit d -> TimeUnit d
weeks = multipleTimeUnit 7 days

There's an alternative way using classes, for days only:

class (Integral u) => TimeUnit u where
addTimeUnitTruncate :: (DayEncoding d) => u -> d -> d
addTimeUnitRollover :: (DayEncoding d) => u -> d -> d
diffTimeUnitFloor :: (DayEncoding d) => d -> d -> u

newtype Days = Days Integer
instance TimeUnit Days
newtype GregorianMonths = GregorianMonths Integer
instance TimeUnit GregorianMonths
newtype GregorianYears = GregorianYears Integer
instance TimeUnit GregorianYears

d' = addTimeUnitTruncate (GregorianMonths 3) d

My preference is for the former.

--
Ashley Yakeley, Seattle WA