public static DateRange[] Coalesce(DateRange RangeA, DateRange RangeB) { bool CondA = RangeA.StartTime > RangeB.EndTime; bool CondB = RangeA.EndTime < RangeB.StartTime; bool overlaps = !(CondA || CondB); // bool overlaps = (RangeA.StartTime > RangeB.EndTime) && (RangeA.EndTime < RangeB.StartTime); //overlap exists if neither A or B is true. if (overlaps) { //overlap exists. //create a new starttime the lower of the given two... DateTime newstart, newEnd; if (RangeA.StartTime < RangeB.StartTime) newstart = RangeA.StartTime; else newstart = RangeB.StartTime; if (RangeA.EndTime > RangeB.EndTime) newEnd = RangeA.EndTime; else newEnd = RangeB.EndTime; return new DateRange[] { new DateRange(newstart, newEnd) }; } else { //there is no overlap, return a daterange array with both the same elements. return new DateRange[] { (DateRange)RangeA.Clone(), (DateRange)RangeB.Clone() }; } }
/// <summary> /// retrieves the elapsed Times for Users and Orders /// </summary> /// <param name="dr"></param> /// <param name="orderTimes">resulting dictionary of order times, indexed by RO#. Values</param> /// <param name="userTimes"></param> /// <remarks></remarks> public void GetElapsedData(DateRange dr, out Dictionary<String, TimeSpan> orderTimes,out Dictionary<String,TimeSpan> userTimes ) { Dictionary<String, Stack<DateTime>> StartTimeOrderStack = new Dictionary<string, Stack<DateTime>>(); Dictionary<String, Stack<DateTime>> StartTimeUserStack = new Dictionary<string, Stack<DateTime>>(); Dictionary<String, TimeSpan> orderresult = new Dictionary<string, TimeSpan>(); Dictionary<String, TimeSpan> userresult = new Dictionary<string, TimeSpan>(); TimeSpan PrevOrderChunk = new TimeSpan(); TimeSpan PrevUserChunk = new TimeSpan(); List<ClockDataEntry> clockdata = new List<ClockDataEntry>(); String clockedorder = String.Format("SELECT * FROM `CLOCKED` ORDER BY `STAMP` WHERE `STAMP`<\"{0}\" AND `STAMP`>\"{1}\"",dr.StartTime.getSQLfmt(),dr.EndTime.getSQLfmt()); DbConnection dbcon; DbCommand dcmd = GetCommand(out dbcon); lock (this) { using(DbDataReader dreader = dcmd.ExecuteReader()) { while (dreader.Read()) { int get_ID = dreader.GetInt32(dreader.GetOrdinal("ID")); String get_PIN = dreader.GetString(dreader.GetOrdinal("UserPIN")); String get_Order = dreader.GetString(dreader.GetOrdinal("OrderID")); DateTime get_STAMP = dreader.GetDateTime(dreader.GetOrdinal("STAMP")); String get_Event = dreader.GetString(dreader.GetOrdinal("EventType")); if (!orderresult.ContainsKey(get_Order)) orderresult.Add(get_Order, new TimeSpan()); if (!userresult.ContainsKey(get_PIN)) userresult.Add(get_PIN, new TimeSpan()); if (!StartTimeOrderStack.ContainsKey(get_Order)) StartTimeOrderStack.Add(get_Order, new Stack<DateTime>()); if (!StartTimeUserStack.ContainsKey(get_PIN)) StartTimeUserStack.Add(get_PIN, new Stack<DateTime>()); clockdata.Add(new ClockDataEntry(get_ID, get_PIN, get_Order, get_STAMP, get_Event)); } } } foreach(var iterate in (from m in clockdata orderby m.STAMP ascending select m)) { if (iterate.EventType == "IN") { StartTimeOrderStack[iterate.OrderID].Push(iterate.STAMP); StartTimeUserStack[iterate.PIN].Push(iterate.STAMP); } else if (iterate.EventType == "OUT") { //if the stack is empty, this has no corresponding IN. //this can happen if there was a clock-in before the daterange and we find the out entry after. if (StartTimeOrderStack[iterate.OrderID].Count == 0) { //add the interval from the start of the range to this value. //subtract prevorder chunk so we only end up with the "largest" value, if there are multiple time outs. orderresult[iterate.OrderID] += (iterate.STAMP - dr.StartTime)-PrevOrderChunk; PrevOrderChunk = (iterate.STAMP - dr.StartTime); } else { DateTime dopop = StartTimeOrderStack[iterate.OrderID].Pop(); if (StartTimeOrderStack[iterate.OrderID].Count == 0) { //if it is empty add the interval to the timespan. orderresult[iterate.OrderID] += (iterate.STAMP - dopop); } } //if the user stack is empty, then there is no corresponding user time in event for this item. if (StartTimeUserStack[iterate.PIN].Count == 0) { //add the interval from the start of the range to this value. userresult[iterate.PIN] += (iterate.STAMP - dr.StartTime)-PrevUserChunk; PrevUserChunk = (iterate.STAMP - dr.StartTime); } else { DateTime dopopu = StartTimeUserStack[iterate.PIN].Pop(); if (StartTimeUserStack[iterate.PIN].Count == 0) { userresult[iterate.PIN] += (iterate.STAMP - dopopu); } } } } //lastly, we want to get the earliest Time in the StartTimeUserStack and StartTimeOrderStack, for each Order and User, //and add it to the respective orderresult and userresult entries. (to account for cases where we have a Time IN but no time out because of the daterange) //iterate through the KeyValuePairs... if (StartTimeOrderStack.Count > 0) { foreach (var kvp in StartTimeOrderStack) { //find the earliest Time in this stack. DateTime currearliest = DateTime.MaxValue; foreach (var iterateearly in kvp.Value) { if (iterateearly < currearliest) currearliest = iterateearly; } orderresult[kvp.Key] += (dr.EndTime-currearliest); } } //repeat for the UserStack... if (StartTimeUserStack.Count > 0) { foreach (var kvp in StartTimeUserStack) { //find earliest time... DateTime earlyusertime = DateTime.MaxValue; foreach (var iterateearly in kvp.Value) { if (iterateearly < earlyusertime) earlyusertime = iterateearly; } userresult[kvp.Key] += (dr.EndTime - earlyusertime); } } //and return the results. orderTimes = orderresult; userTimes = userresult; }
public List<ClockDataEntry> GetClockedInRange(DateRange userange) { String sqlquery = "SELECT * FROM CLOCKED WHERE `STAMP`>\"{0}\" AND `STAMP`<\"{1}\""; string sqluse = String.Format(sqlquery, userange.StartTime.getSQLfmt(), userange.EndTime.getSQLfmt()); DbConnection dcon; DbCommand dcmd = GetCommand(out dcon); List<ClockDataEntry> result = new List<ClockDataEntry>(); dcmd.CommandText = sqluse; lock (this) { using (DbDataReader dreader = dcmd.ExecuteReader()) { while (dreader.Read()) { int _ID = dreader.GetInt32(dreader.GetOrdinal("ID")); String _PIN = dreader.GetString(dreader.GetOrdinal("UserPIN")); String _OrderID = dreader.GetString(dreader.GetOrdinal("OrderID")); DateTime _Stamp = dreader.GetDateTime(dreader.GetOrdinal("STAMP")); String _EType = dreader.GetString(dreader.GetOrdinal("EventType")); result.Add(new ClockDataEntry(_ID, _PIN, _OrderID, _Stamp, _EType)); } } } return result; }
public DateRange[] GetUserWorkOrderRanges(String UserID, String OrderID) { String RelevantRecordsSQL = qAdapter["USERTOTALTIMEQUERY"]; //select all records in the CLOCKED table (which is used to store all clock-in/out events) that have the given UserPIN and OrderID. DbConnection dcon; DbCommand usecmd = GetCommand(out dcon); usecmd.CommandText = String.Format(RelevantRecordsSQL, UserID, OrderID); DateTime usecurrent = DBTime(); //NOTE: ASSUMPTION: we assume, naturally, that a user cannot clock into a order twice. This could be the case as far as the CLOCKED table, though in case of DB corruption //or other circumstances, so if that happens we will throw a exception. //DatabaseStructureCorruptedException List<DateRange> buildlisting = new List<DateRange>(); DateTime? getstarttime = null, getendtime = null; TimeSpan accumulator = new TimeSpan(); using (DbDataReader readdata = usecmd.ExecuteReader()) { DateTime laststamp = new DateTime(); while (readdata.Read()) { DateTime getstamp = readdata.GetDateTime(readdata.GetOrdinal("STAMP")); laststamp = getstamp; String etype = readdata.GetString(readdata.GetOrdinal("EventType")); if (etype == "IN") { getstarttime = getstamp; } else { if (getstarttime != null) { getendtime = getstamp; DateRange buildrange = new DateRange(getstarttime.Value, getendtime.Value); buildlisting.Add(buildrange); getstarttime = null; //accumulator += buildrange.Span; } } } if (getstarttime != null) { if (laststamp > usecurrent) { Debug.Print("laststamp > DBTime()"); } DateRange buildrange2 = new DateRange(laststamp, usecurrent); Debug.Print(buildrange2.ToString()); buildlisting.Add(buildrange2); //accumulator+=buildrange2.Span; //TimeSpan addvalue = (DBTime() - getstarttime.Value).Duration(); // Debug.Print("adding accumulated value:" + addvalue.ToString()); } } return buildlisting.ToArray(); }
public TimeSpan GetUserTotalTimeOnOrder(String UserID, String OrderID, out DateRange[] ranges) { String RelevantRecordsSQL = qAdapter["USERTOTALTIMEQUERY"]; //select all records in the CLOCKED table (which is used to store all clock-in/out events) that have the given UserPIN and OrderID. DbConnection dcon; DbCommand usecmd = GetCommand(out dcon); usecmd.CommandText = String.Format(RelevantRecordsSQL,UserID,OrderID); DateTime usecurrent = DBTime(); if (UserID.Equals("0812")) { Debug.Print("bug..."); } List<DateRange> buildlisting = GetUserWorkOrderRanges(UserID, OrderID).ToList(); long sumticks = 0; foreach (var blisting in buildlisting) { sumticks+=blisting.Span.Ticks; } ranges = buildlisting.ToArray(); TimeSpan totalresult = new TimeSpan(sumticks); return totalresult; //return accumulator; }
public static DateRange[] CoalesceRanges(DateRange[] ranges) { //coalesces/simplifies a given set of date ranges to the simplest form. For example: /* a * |----| * b * |----| c * |----| * */ //will return an array of two Dateranges: /* a * |-----| * b * |----| * */ //this is the "simplest" reduction of what the previous ranges were. List<DateRange> userange = ranges.ToList(); //userange.Sort((w,p)=>w.StartTime.CompareTo(p.StartTime)); List<DateRange> workalist = userange; List<DateRange> result = new List<DateRange>(); //algorithm //set a flag indicating a coalesce was discovered to true. //iterate while said flag is true. //set flag to false as loop begins. //iterate through every possible pair X and Y in the List. skip iterations where X==Y. //for each possible pairing: //coalesce the two DateRanges. If the results coalesce (the return value array has a length of 1) //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 //a coalesce was discovered. bool coalescefound = true; while (coalescefound) { List<DateRange> removalmarked = new List<DateRange>(); List<DateRange> addmarked = new List<DateRange>(); bool breakouter = false; coalescefound = false; foreach (DateRange x in workalist) { foreach (DateRange y in workalist) { if (x != y) { DateRange[] coalresult = DateRange.Coalesce(x, y); if (coalresult.Length == 1) { coalescefound = true; //add new array to be added... addmarked.AddRange(coalresult); //remove both x and y. We can't remove them here since we are iterating... removalmarked.Add(x); removalmarked.Add(y); breakouter = true; break; } } } if (breakouter) break; //break out of outer iterator if flag set. } //add and remove the marked items. foreach (var removeme in removalmarked) { workalist.Remove(removeme); } foreach (var addme in addmarked) { workalist.Add(addme); } } return workalist.ToArray(); }
public DateRange[] Coalesce(DateRange Otherdaterange) { return DateRange.Coalesce(this, Otherdaterange); }