Concerning Time.TimeDiff

Simon Marlow simonmar@microsoft.com
Tue, 17 Jun 2003 13:53:48 +0100


=20
> On Mon, Jun 16, 2003 at 04:42:53PM +0100, Graham Klyne wrote:
> > ... I think it's quite usual for people and applications to deal
> > with a day as a consistent time interval without concern for
> > possible leap seconds, so dealing with days as intervals of 24*60*60
> > seconds is useful for a majority of applications that deal with such
> > intervals.
>=20
> What about the fact that days are not actually intervals of 24*60*60
> seconds (as I'm sure you know)?  Most applications don't care, but
> it's crucial to give correct and unsurprising results.
>=20
> I'm worried that the complexity to deal with leap seconds will end up
> being just as complicated as the complexity to deal with more general
> TimeDiff's as in the current library, in which case it may be
> necessary to bite the bullet and specify the behaviour of TimeDiffs
> completely and accurately.

Agreed, this discussion tends to get rather vague at times, so I'd like
to put it on a more concrete footing if I may.  I've put together rough
suggestion for a starting point for a replacement System.Time, appended
below.

Please comment!

Cheers,
	Simon

------------------------------------------------------------------------
----
-- * ClockTime

-- | A representation of absolute time
data ClockTime
  =3D ClockTime { ctSeconds :: Integer,
                ctPicoseconds :: Integer }
  deriving (Eq, Ord, Show, Read)

-- | returns the current absolute time
getClockTime :: IO ClockTime

-- | Difference between two 'ClockTime's
data TimeDiff
  =3D TimeDiff { tdSeconds :: Integer,
               tdPicoseconds :: Integer }
  deriving (Eq, Ord, Show, Read)

-- | An empty 'TimeDiff'
noTimeDiff :: TimeDiff

-- | Returns the difference between two 'ClockTime's
diffClockTimes :: ClockTime -> ClockTime -> TimeDiff

-- | Adds a 'TimeDiff' to a 'ClockTime'
addToClockTime :: ClockTime -> TimeDiff -> ClockTime

{-
Rationale:

  - TimeDiff is now an absolute measure of time period.
 =20
Invariants:

  t1 `addToClockTime` (t2 `diffClockTimes` t1) =3D=3D t2
  t1 `addToClockTime` noTimeDiff =3D=3D t1
  t1 `diffClockTimes` t1 =3D=3D noTimeDiff

TODO:
  - This representation of TimeDiff is maybe not the best.
    Two other possibilities: use just picoseconds, or
    have the type be abstract with a way to extract
    picoseconds.

  - If we keep the seconds/picoseconds representation,
    should we specify that the TimeDiff returned by
    diffClockTimes is normalised?  Should we provide
    a way to normalise a TimeDiff?
-}

------------------------------------------------------------------------
----
-- * CalendarTime

data CalendarTime=20
 =3D CalendarTime  {
     ctYear    :: Int,
     ctMonth   :: Month,
     ctDay     :: Int,
     ctHour    :: Int,
     ctMin     :: Int,
     ctSec     :: Int,
     ctPicosec :: Integer,
     ctTZ      :: Timezone
 }
 deriving (Eq, Ord, Read, Show)

data Timezone -- abstract

-- | Make a 'Timezone'
-- TODO: do we need to specify daylight savings time too?
timezoneFromOffset :: Int    -> Timezone
timezoneFromName   :: String -> Timezone

timezoneOffset :: Timezone -> Int
timezoneName   :: Timezone -> String

-- | Convert a 'ClockTime' to a 'CalendarTime' in the current timezone
clockTimeToCalendarTime :: ClockTime -> IO CalendarTime

-- | Convert a 'ClockTime' to a 'CalendarTime' in UTC
clockTimeToUTCTime :: ClockTime -> CalendarTime

-- | Convert a 'CalendarTime' to a 'ClockTime'
-- TODO: can this raise an exception if the CalendarTime does not
-- represent a valid time?  Or should it return Maybe ClockTime?
calendarTimeToClockTime :: CalendarTime -> ClockTime

-- OPTIONAL: these are hard to implement, and require
-- careful specification (see rationale below):
addPicoseconds :: CalendarTime -> Integer -> CalendarTime
addSeconds     :: CalendarTime -> Integer -> CalendarTime
addMinutes     :: CalendarTime -> Integer -> CalendarTime
addDays        :: CalendarTime -> Integer -> CalendarTime
addWeeks       :: CalendarTime -> Integer -> CalendarTime
addMonths      :: CalendarTime -> Integer -> CalendarTime
addYears       :: CalendarTime -> Integer -> CalendarTime

{-
   Rationale:=20
=20
   - Adding "irregular" time differences should be done on
     CalendarTimes, because these operations depend on the timezone.

   - Need to define the meaning when the offset doesn't exist.
     eg. adding a day at the end of the month clearly rolls over
     into the next month.  But what about adding a month to
     January 31st?
-}