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