Bogdan Manole
Bogdan Manole

Reputation: 374

Better solution for a Custom Clock in f#

I need a solution to add time to a date ignoring a given time interval. For example if the given date is 01 jan 2015 19:00 , the off interval is 20:00 - 8:00 adding 3 hours will give as a result 02 jan 2015 10:00. Another example is Date: 01 jan 2015 03:00 AM , Interval 20:00 - 08:00 will give as result 01 jan 2015 11:00 AM

My solution is this:

    let nextInterval (d:DateTime) (s:TimeSpan) (f:TimeSpan) =
       let next (d:DateTime) (ts:TimeSpan) 
                = if d.TimeOfDay < ts 
                      then d.Date.Add(ts)
                      else d.Date.AddDays(1.0).Add(ts)
       let ns = next d s
       let nf = next d f
       (ns,nf)

    let addTime (d:DateTime) (n:TimeSpan) (s:TimeSpan) (f:TimeSpan) = 
      let rec loop (d:DateTime) (n:TimeSpan) = 
        let dt = d.TimeOfDay
        let (ns,nf) = nextInterval d s f //next start interval and next finish interval
        let outOfInterval = ((dt < s || dt >= f) && f >= s) ||
                            ((dt < s && dt >=f) && f < s)
        if n = TimeSpan.Zero
            then if outOfInterval then d
                                  else nf
            else 
             if outOfInterval 
                then if (d+n < ns) 
                        // before next start
                        then loop (d+n) TimeSpan.Zero
                        // intersecting with interval
                        else if (d+n < nf)
                                    then loop (d+n) (n - (ns - d)) 
                                    else loop nf    (n - (ns - d))
                else //in interval
                    loop nf n
       loop d n

My question is : has anybody a shorter solution for this?

Upvotes: 3

Views: 214

Answers (1)

Vandroiy
Vandroiy

Reputation: 6223

I'd use some form of modulo operation, not a loop. In other words, an easier way to think about the date would be:

  • Which period am I in? (I.e., which day did the current period of not-ignored time start on?)
  • How much time inside the current period has passed?

If you want to add time, add it to the time inside the current period and then split off full periods using division and remainder.

let day = TimeSpan(1, 0, 0, 0).Ticks

let addTime (dayBegin : TimeSpan) (daySpan : TimeSpan) (d : DateTime) (dt : TimeSpan) =
    if dt.Ticks < 0L then invalidArg "dt" "Negative time-spans are not supported."
    let t0 = (d - dayBegin).Ticks
    let rawSlot, rawSpan = t0 / day * day, t0 % day
    let slot, span = if rawSpan > daySpan.Ticks then rawSlot + day, dt.Ticks
                     else rawSlot, rawSpan + dt.Ticks
    DateTime(slot + span / daySpan.Ticks * day + span % daySpan.Ticks) + dayBegin

Test For 20:00 - 8:00 as the ignored interval, the day begins at 8:00 and takes 12h:

let test = addTime (TimeSpan(8, 0, 0)) (TimeSpan(12, 0, 0))

Using the first example as input:

test (DateTime(2015, 01, 01, 19, 0, 0)) (TimeSpan(3, 0, 0))
// result: 2015-01-02 10:00:00

Upvotes: 2

Related Questions