
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