22 Dec 2011 @ 10:51 AM 

Or, at least that’s what I am calling it. The .NET framework provides quite a rich set of data structures for dealing with Dates. You’ve got DateTime which represents a single point in time, You’ve got TimeSpan which represents a interval.

However I noticed something somewhat absent- a DateTime Range, with a start time, and an ending time. This came about because I required a way to coalesce a set of DateTime’s organized as Starting and Ending time so that there weren’t any overlaps. For example, if I had datetimes for one interval from 9AM to 12PM, another from 8AM to 11AM, and a third from 5PM to 9PM, the coalesced result would be two intervals, one from 8AM to 12PM, and one from 5PM to 9PM. Basically, the point was to “simplify” a set of datetime intervals so that overlapping time would not be counted.

At first, I figured it would be built-in functionality of one of the DateTime classes. It’s not. so I did some googling, no real luck there either. So, I decided to write my own “DateRange” class that encapsulated a Start Time and an Ending time and had the functionality I required.

Obviously, the first thing we know is that the class is going to need Starting Time and Ending Time. Of course, once we’ve defined the data structures, the tricky part will be the “coalescing” code. The implementations for which can be found in the “CoalesceRanges” and “Coalesce” methods.

  1.  
  2.     public class DateRange : ICloneable
  3.  
  4.     {
  5.  
  6.  
  7.  
  8.         private DateTime _StartTime, _EndTime;
  9.  
  10.  
  11.  
  12.         public DateTime StartTime { get { return _StartTime;}}
  13.  
  14.         public DateTime EndTime { get { return _EndTime; }}
  15.  
  16.  
  17.  
  18.         public TimeSpan Span
  19.  
  20.         {
  21.  
  22.             get {
  23.  
  24.            
  25.  
  26.             return EndTime-StartTime;
  27.  
  28.             }
  29.  
  30.             set {
  31.  
  32.             _EndTime = StartTime+value;
  33.  
  34.            
  35.  
  36.             }
  37.  
  38.  
  39.  
  40.         }
  41.  
  42.         public DateRange(DateTime sTime, DateTime eTime)
  43.  
  44.         {
  45.  
  46.             if (sTime < eTime) _StartTime = sTime; else _StartTime=eTime;
  47.  
  48.             if (sTime > eTime) _EndTime = sTime; else _EndTime=eTime;
  49.  
  50.  
  51.  
  52.  
  53.  
  54.         }
  55.  
  56.         public override string ToString()
  57.  
  58.         {
  59.  
  60.             return "Range:(" + StartTime.ToString() + ")-(" + EndTime.ToString() + ")";
  61.  
  62.         }
  63.  
  64.         public static DateRange []  Coalesce(DateRange RangeA, DateRange RangeB)
  65.  
  66.         {
  67.  
  68.             bool CondA = RangeA.StartTime > RangeB.EndTime;
  69.  
  70.             bool CondB = RangeA.EndTime < RangeB.StartTime;
  71.  
  72.             bool overlaps = !(CondA || CondB);
  73.  
  74.  
  75.  
  76.  
  77.  
  78.            // bool overlaps = (RangeA.StartTime > RangeB.EndTime) && (RangeA.EndTime < RangeB.StartTime);
  79.  
  80.  
  81.  
  82.             //overlap exists if neither A or B is true.
  83.  
  84.             if (overlaps)
  85.  
  86.             {
  87.  
  88.                 //overlap exists.
  89.  
  90.                 //create a new starttime the lower of the given two…
  91.  
  92.                 DateTime newstart, newEnd;
  93.  
  94.  
  95.  
  96.                 if (RangeA.StartTime < RangeB.StartTime) newstart = RangeA.StartTime; else newstart = RangeB.StartTime;
  97.  
  98.                 if (RangeA.EndTime > RangeB.EndTime) newEnd = RangeA.EndTime; else newEnd = RangeB.EndTime;
  99.  
  100.  
  101.  
  102.                 return new DateRange []  { new DateRange(newstart, newEnd) };
  103.  
  104.  
  105.  
  106.             }
  107.  
  108.             else
  109.  
  110.             {
  111.  
  112.                 //there is no overlap, return a daterange array with both the same elements.
  113.  
  114.                 return new DateRange []  { (DateRange)RangeA.Clone(), (DateRange)RangeB.Clone() };
  115.  
  116.  
  117.  
  118.             }
  119.  
  120.  
  121.  
  122.  
  123.  
  124.         }
  125.  
  126.  
  127.  
  128.         public DateRange []  Coalesce(DateRange Otherdaterange)
  129.  
  130.         {
  131.  
  132.            
  133.  
  134.             return DateRange.Coalesce(this, Otherdaterange);
  135.  
  136.  
  137.  
  138.         }
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.         public static DateRange []  CoalesceRanges(DateRange []  ranges)
  149.  
  150.         {
  151.  
  152.             //coalesces/simplifies a given set of date ranges to the simplest form. For example:
  153.  
  154.            /*   a
  155.  
  156.             * |—-|
  157.  
  158.             *     b
  159.  
  160.             *   |—-|       c
  161.  
  162.             *              |—-|
  163.  
  164.             * */
  165.  
  166.             //will return an array of two Dateranges:
  167.  
  168.  
  169.  
  170.             /*    a
  171.  
  172.              * |—–|
  173.  
  174.              *               b
  175.  
  176.              *             |—-|
  177.  
  178.              
  179.  
  180.              * */
  181.  
  182.             //this is the "simplest" reduction of what the previous ranges were.
  183.  
  184.              List<daterange> userange = ranges.ToList();
  185.  
  186.  
  187.             List<daterange> workalist = userange;
  188.  
  189.             List<daterange> result = new List<daterange>();
  190.  
  191.  
  192.  
  193.            
  194.  
  195.             //algorithm
  196.  
  197.             //set a flag indicating a coalesce was discovered to true.
  198.  
  199.             //iterate while said flag is true.
  200.  
  201.  
  202.  
  203.             //set flag to false as loop begins.
  204.  
  205.             //iterate through every possible pair X and Y in the List. skip iterations where X==Y.
  206.  
  207.  
  208.  
  209.             //for each possible pairing:
  210.  
  211.             //coalesce the two DateRanges. If the results coalesce (the return value array has a length of 1)
  212.  
  213.             //mark x and y for removal, set the new Array to be AddRanged() to the List, and break out of both loops, and set the flag saying that
  214.  
  215.             //a coalesce was discovered.
  216.  
  217.            
  218.  
  219.             bool coalescefound =true;
  220.  
  221.             while (coalescefound)
  222.  
  223.             {
  224.  
  225.                 List<daterange> removalmarked = new List<daterange>();
  226.  
  227.                 List<daterange> addmarked = new List<daterange>();
  228.  
  229.                 bool breakouter=false;
  230.  
  231.                 coalescefound=false;
  232.  
  233.                 foreach (DateRange x in workalist)
  234.  
  235.                 {
  236.  
  237.                     foreach (DateRange y in workalist)
  238.  
  239.                     {
  240.  
  241.                         if (x != y)
  242.  
  243.                         {
  244.  
  245.                             DateRange []  coalresult = DateRange.Coalesce(x, y);
  246.  
  247.  
  248.  
  249.                             if (coalresult.Length == 1)
  250.  
  251.                             {
  252.  
  253.                                 coalescefound=true;
  254.  
  255.                                 //add new array to be added…
  256.  
  257.                                 addmarked.AddRange(coalresult);
  258.  
  259.                                 //remove both x and y. We can’t remove them here since we are iterating…
  260.  
  261.                                 removalmarked.Add(x);
  262.  
  263.                                 removalmarked.Add(y);
  264.  
  265.                                 breakouter=true;
  266.  
  267.                                 break;
  268.  
  269.  
  270.  
  271.                             }
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.                         }
  280.  
  281.                        
  282.  
  283.  
  284.  
  285.                     }
  286.  
  287.  
  288.  
  289.                     if (breakouter) break; //break out of outer iterator if flag set.
  290.  
  291.  
  292.  
  293.                 }
  294.  
  295.                 //add and remove the marked items.
  296.  
  297.                 foreach (var removeme in removalmarked)
  298.  
  299.                 {
  300.  
  301.  
  302.  
  303.                     workalist.Remove(removeme);
  304.  
  305.  
  306.  
  307.                 }
  308.  
  309.                 foreach (var addme in addmarked)
  310.  
  311.                 {
  312.  
  313.                     workalist.Add(addme);
  314.  
  315.  
  316.  
  317.                 }
  318.  
  319.                
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.             }
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.             return workalist.ToArray();
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.         }
  372.  
  373.  
  374.  
  375.         public object Clone()
  376.  
  377.         {
  378.  
  379.             return new DateRange(StartTime, EndTime);
  380.  
  381.         }
  382.  
  383.     }
  384.  

For this case I’ve decided that I would make the class Immutable. This should make our accessors simpler to implement. As a result, the StartTime and EndTime implementations only define get accessors. I also add a “Span” parameter that actually does change the EndTime in it’s setter, so I guess I lied. Oh well. Anyways, the meat of the class is in the various coalescing methods, which makes sense since that is what I sort of wrote the class to do. the class could surely be fleshed out with other, useful methods and properties, but as it is now it meets my own needs admirably so I’ve not really expanded it past what I’ve got here.

976 total views, 2 views today

Have something to say about this post? Comment!

Posted By: BC_Programming
Last Edit: 22 Dec 2011 @ 10:58 AM

EmailPermalink
Tags
Categories: Programming


 

Responses to this post » (None)

 
Post a Comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


 Last 50 Posts
 Back
Change Theme...
  • Users » 871
  • Posts/Pages » 193
  • Comments » 69
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.