I've just open sourced a small utility class based in joda-time. It's a 
downtime calculator, for a program that I'm rewriting in java. It has a rather 
comprehensive test class, but it's still not fully tested on production. It's 
to be found in https://sourceforge.net/projects/downtimecalc

You can give it a list of downtimes (Intervals) where no work is done, or you 
are not interested in. Then you can, given a point in time, and a duration, ask 
for the ending point in time that will "cover" the duration discounting the 
downtimes, that is, counting just the uptimes. Also you can get the 
uptime-duration between two points in time. Interals are joda Intervals, but 
the points in time are represented by long primitives. I know now that's not 
the recommended way, but I had it written when I discovered it, and I'm too 
lazy to modify it for now.

Well, that's it. If somebody finds it useful, there it's for the use. I paste 
the whole class too, for further info:



/*
 * File:        DowntimeCalc.java  Created: 08/11/2006 05:30:48 PM
 * 
 * Copyright 2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package emp.sqp.downtimecalc;

import java.util.ArrayList;
import java.util.List;

import org.joda.time.Interval;

/**
 * DowntimeCalc is a calculator based on a list of joda Intervals. The idea is 
that these intervals
 * represent the downtimes of some resource in time. These intervals will be 
ordered,
 * non-abutting and non-overlapping, for efficiency reasons.
 * If, when trying to add a new interval,this new interval abuts or overlaps 
one or
 * several already in the set, the DowntimeCalc will only save the resulting 
downtime
 * interval. For example, suppose we have in the set the intervals
 * [7:00 - 8:00]
 * [11:00 - 13:00]
 * [20:00 - 21:00]
 * If I add [7:30 - 12:00] the resulting set will have
 * [7:00 - 13:00]
 * [20:00 - 21:00]
 * If I add [8:00 - 9:00] the resulting set will have
 * [7:00 - 9:00]
 * [11:00 - 13:00]
 * [20:00 - 21:00]
 * If I add [15:00 - 16:00], this interval will simply be added, in third 
position.
 * 
 * It's expected that the intervals in the set are modified rather infrequently,
 * so the add() operations don't need to be particularly efficient. The set is
 * however expected to be traversed rather frequently.
 *  
 * @author Julen Parra
 *
 */
public class DowntimeCalc {
        // The storage of the intervals will be made in a List
        protected final List<Interval> list;
        
        public DowntimeCalc() {
                this.list = new ArrayList<Interval>();
        }

        /**
         * Calculates and returns the ending instant (as joda milliseconds)
         * corresponding to the given instant and duration, taking into account
         * the downtimes in the List.
        
         * @param start         starting instant (as joda milliseconds)
         * @param duration      duration (as joda milliseconds)
         */
        public long getEnd(long start, long duration) {
                // In acumDuration we'll accumulate the milliseconds till they 
are the same or bigger than duration
                long acumDuration = 0;
                // In currInstant we'll have the current instant where we are 
calculating
                long currInstant = start;
                
                int listSize = list.size();
                for (int i = 0; i < listSize; i++) {
                        Interval act = list.get(i);
                long downStart = act.getStartMillis();
                long downEnd = act.getEndMillis();
                if (downStart > currInstant) {
                        // If we are before a downtime, we accumulate the 
millis of
                                // uptime
                                // and set the new start to the end of the 
downtime
                                acumDuration += (downStart - currInstant);
                                if (acumDuration < duration) {
                                        currInstant = downEnd;
                                } else {
                                        currInstant += (downStart - 
currInstant);
                                        // We have enough milliseconds, we are 
done
                                        break;
                                }
                } else if (downStart <= currInstant) {
                                if (downEnd > currInstant) {
                                        // This means we are inside the 
downtime, we move to the end
                                        // of the downtime
                                        // adding nothing to the accumulate
                                        currInstant = downEnd;
                                } else if (downEnd < currInstant) {
                                        // This means the current instant is in 
the future of the downtime, so we do nothing 
                                } else {
                                        assert false;   // Execution should 
never reach this
                                }
                } else {
                                assert false;   // Execution should never reach 
this
                        }
                }
            if (acumDuration < duration) {
                // If we still don't have enough milliseconds, now there is no 
more downtime
                // so simply add the needed time
                currInstant += (duration - acumDuration);
            } else if (acumDuration >= duration) {
                // If we are too far away in the future, take back the plus 
millis
                currInstant -= (acumDuration - duration);
        }else {
                        assert false;   // Execution should never reach this
                }
            
                return currInstant;
        }
        
        /**
         * Gets the number of uptime milliseconds between two instants
         * @param start         start instant in milliseconds
         * @param end           end instant in milliseconds
         * @return the number of uptime milliseconds between the parameters, 
may be negative if end < start
         */
        public long diff(long start, long end) {
                
                boolean invert = (end < start);
                long downMillis = 0;
                if (invert) {
                        // We swap the values to use the same algorithm, then 
will add negative sign
                        long temp = start;
                        start = end;
                        end = temp;
                }
                if (start == end)
                        return 0;
                long max = end - start;
                // We cicle the downtimes

                int listSize = list.size();
                for (int i = 0; i < listSize; i++) {
                        Interval act = list.get(i);
                long downStart = act.getStartMillis();
                long downEnd = act.getEndMillis();
        
                if (downEnd > start && downStart < end) 
                        {       
                                downMillis += downEnd - downStart;
                                if (downEnd < start) downMillis -= (start - 
downStart);
                                if (downEnd > end) downMillis -= (downEnd - 
end);
                        }
                        if (downStart > end) break; 
                }
                return (invert)? -(max - downMillis):(max - downMillis);
        }
        
        /**
         * Adds, a new downtime interval to the Downtime List.
         * The number of resulting intervals is undetermined, depending on 
conditions
         * The resulting list will be ordered, non-abutting and non-overlapping
         * 
         * @param downtime      The new downtime interval
         * @return true if has modified the state of the class, false if not
         */
        public boolean add(Interval downtime){
        long downStart = downtime.getStartMillis();
        long downEnd = downtime.getEndMillis();
        // Empty Intervals add no information
        if (downStart == downEnd){
                return false;
        }
        for (int i = 0; i < list.size(); i++) {
                Interval act = list.get(i);
                long start = act.getStartMillis();
                long end = act.getEndMillis();
                
                if (downStart < start) {
                        // The new interval starts before the actual. Possible 
cases:
                         if (downEnd < start) {
                                        // The new interval is added as an 
independent downtime, and
                                        // we are done 
                                // {d}   [   dwt     ] -> [d]   [   dwt     ]
                                        list.add(i, downtime);
                                        return true;
                                } else if (downEnd <= end) {
                                        // The new interval will touch the 
actual interval, but not exceed it
                                        // We modify the actual interval start 
to reflect the new
                                        // span, and are done
                                        // {d}[   dwt     ] -> [d     dwt     ]
                                        list.set(i, 
act.withStartMillis(downStart));
                                        return true;
                                } else if (downEnd > end) {
                                        // The actual interval is completely 
embedded in the new,
                                        // we delete the actual interval
                                        // {d  [   dwt   ]  }  ->  [      dwt   
      ]   
                                        list.remove(i);
                                        i--;
                                        // Now we have to check if the new end 
touches any other intervals
                                        // So we don't exit the function
                                } else {
                                        assert false; // Execution should never 
reach this
                                } 
                } else if (downStart >= start && downStart <= end) {
                        // The new interval starts inside the actual. Possible 
cases:
                        if (downEnd < start) {
                                assert false;   // Execution should never reach 
this
                        } else if (downEnd <= end) {
                                        // The new interval is inside the 
actual, so adds no new information
                                // Do nothing, and exit
                                //    [  {d} dwt     ] -> [     dwt     ]
                                        return false;
                                } else if (downEnd > end) {
                                        // The new interval expands the actual
                                        // We grow the new interval to reflect 
the start of the actual
                                        //    [   dwt  {d  ]  } -> [      dwt   
     ]
                                        downtime = 
downtime.withStartMillis(start);
                                        downStart = downtime.getStartMillis();
                                        // and delete the actual interval
                                        list.remove(i);
                                        i--;
                                        // Now we have to check if the new end 
touches any other intervals
                                        // So we don't exit the function
                                } else {
                                        assert false; // Execution should never 
reach this
                                }
                        
                } else if (downStart >= start && downStart > end) {
                        // Do nothing, the new interval is after the actual
                } else {
                        assert false; // Execution should never reach this
                }       
        }
//      If we havent exited the function yet, the new downtime has to be added
        list.add(downtime);
        return true;
        }

        /**
         * Adds an uptime to the Downtime List.
         * The number of resulting downtime intervals is undetermined, 
depending on conditions
         * The resulting list will be ordered, non-abutting and non-overlapping
         * 
         * @param uptime        The new downtime interval
         * @return true if has modified the state of the class, false if not
         */
        public boolean addUptime(Interval uptime){
                boolean modified = false;
        long upStart = uptime.getStartMillis();
        long upEnd = uptime.getEndMillis();
        // Empty Intervals add no information
        if (upStart == upEnd){
                return false;
        }
        for (int i = 0; i < list.size(); i++) {
                Interval act = list.get(i);
                long start = act.getStartMillis();
                long end = act.getEndMillis();
                
                if (upStart <= start) {
                        // The uptime starts before or abutting the actual 
downtime. Possible cases:
                         if (upEnd <= start) {
                                        // The uptime does not overlap the 
downtime, so it falls already in uptime,
                                 // so it adds no information, so we do 
nothing.    
                                 // [  dwt  ] {x} [     dwt     ] ->   [  dwt  
]     [     dwt     ] 
                                        return modified;
                                } else if (upEnd < end) {
                                        // The uptime will reduce the size of 
the actual interval
                                        // We modify the actual interval start 
to reflect the new
                                        // span, and are done [  dwt  ]     
[{u}   dwt     ] -> 
                                        list.set(i, act.withStartMillis(upEnd));
                                        return true;
                                } else if (upEnd >= end) {
                                        // The actual interval is completely 
embedded in the uptime
                                        // we delete the actual interval [  dwt 
 ]    {uu[   dwt     ]u} -> [  dwt  ]   
                                        list.remove(i);
                                        i--;
                                        modified = true;
                                        // Still have to check if the uptime 
end touches any other intervals
                                        // So we don't exit the function
                                } else {
                                        assert false; // Execution should never 
reach this
                                } 
                } else if (upStart > start && upStart < end) {
                        // The uptime starts inside the actual. Possible cases:
                        if (upEnd < start) {
                                assert false;   // Execution should never reach 
this
                        } else if (upEnd < end) {
                                        // The new interval is inside the 
actual, so it splits it into two
                                // [  dwt  ]  [ dwt  {u}  dwt ]  -> [  dwt  ]  
[ dwt  ]  [ dwt ]
                                list.set(i, act.withEndMillis(upStart));
                                list.add(i+1,new Interval(new 
Interval(act.withStartMillis(upEnd))));
                                        return true;
                                } else if (upEnd >= end) {
                                        // The uptime simply cuts the end of 
the downtime
                                        // [  dwt  ]  [     dwt   {u}]  ->  [  
dwt  ]  [     dwt   ]
                                        list.set(i, act.withEndMillis(upStart));
                                        modified = true;
                                        // Now we have to check if the new end 
touches any other intervals
                                        // So we don't exit the function
                                } else {
                                        assert false; // Execution should never 
reach this
                                }
                        
                } else if (upStart >= start && upStart > end) {
                        // Do nothing, the new interval is after the actual
                } else {
                        assert false; // Execution should never reach this
                }
                
        }
        return modified;
        }
        /**
         * @return the number of downtimes in the list
         */
        public int size() {
                return list.size();
        }

        /**
         * @param i
         * @return the downtime Interval in position i
         */
        public Interval get(int i) {
                return list.get(i);
        }
        
        /**
         * Deletes all downtimes from the list
         */
        public void clear() {
                list.clear();
        }


        
}



-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Joda-interest mailing list
Joda-interest@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/joda-interest

Reply via email to