Find difference between DateTimes in List [duplicate] - c#

This question already has answers here:
Showing Difference between two datetime values in hours
(8 answers)
Closed 8 years ago.
I have a List of DateTimes
var times = CurrentMeter.SessionTimes.ToList();
How do I find the difference between them and add the results to a new List?
I want the hours, minutes, seconds and milliseconds between each datetime.

A difference between two DateTime objects is a TimeSpan.
This function will calculate the time delta between each consecutive pair of DateTimes.
It will iterate through each time in times, subtract the previous value from it, and add the difference to your result list.
IEnumerable<TimeSpan> CalculateDeltas(IEnumerable<DateTime> times) {
var time_deltas = new List<TimeSpan>();
DateTime prev = times.First();
foreach (var t in times.Skip(1)) {
time_deltas.Add(t - prev);
prev = t;
}
return time_deltas;
}

Here's an example to calculate the difference between each item in the list. The results are stored in a List<double> and represent the total of seconds between each datetime.
var times = new List<DateTime>
{
new DateTime(2014, 5, 28, 9, 57, 12),
new DateTime(2014, 5, 28, 9, 57, 43),
new DateTime(2014, 5, 28, 9, 58, 03),
new DateTime(2014, 5, 28, 9, 59, 46),
new DateTime(2014, 5, 28, 10, 0, 22),
};
var differences = new List<double>();
for(int i = 0; i < times.Count - 1; i++)
{
differences.Add((times[i+1] - times[i]).TotalSeconds);
}
Output:
31
20
103
36

How about:
var time = new []{
DateTime.Parse("14-4-2012 00:00:00"),
DateTime.Parse("14-4-2012 01:12:34"),
DateTime.Parse("14-4-2012 12:44:33"),
DateTime.Parse("14-4-2012 23:12:42"),
};
var result = time.Zip(time.Skip(1), (a, b) => b - a)
.Select(d => new {d.Hours, d.Minutes, d.Seconds})
.ToList();
result is now:
(Note that this is a very simply approach and only works for differences < 24h. If you have greater differences, take into account d.Days etc.)

Related

TimeSpan and ToString()

I want to display minutes, seconds, and milliseconds saved in TimeSpan. It should look like this:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
Console.WriteLine(d.ToString(#"mm\:ss\:fff"));
But it returns 47:30:000 which is only partially true, because it ignored one hour. I want it to be converted into minutes, not ignored.
I think you need TimeSpan.TotalMinutes.
https://learn.microsoft.com/en-us/dotnet/api/system.timespan.totalminutes?view=net-7.0
In your case it could be:
Console.WriteLine($#"{(int)d.TotalMinutes}:{d:ss\:fff}");
TimeSpan has a property for getting the total number of minutes contained in it, as a double value: TimeSpan.TotalMinutes Property.
Note that if your TimeSpan's duration is not in whole minutes you will get a fraction part:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
Console.WriteLine(d.TotalMinutes);
Output:
107.5
Note that TimeSpan has similar properties for TotalDays, TotalHours, TotalSeconds, TotalMilliseconds.
Update:
Based on the comments below, here's a solution that prints in a format splitting minutes, seconds and milliseconds:
var d1 = new DateTime(2020, 12, 1, 12, 00, 00);
var d2 = new DateTime(2020, 12, 1, 10, 12, 30);
var d = d1 - d2;
double totalMin = d.TotalMinutes;
int totalMinInt = (int)totalMin; // will get only the whole minutes.
Console.WriteLine(totalMinInt.ToString() + // print the whole minutes part
":" +
d.ToString(#"ss\:fff")); // print the fraction part
Output:
107:30:000

Continuous Overlapping of Multiple Date Ranges C#

I am working on an algorithm to calculate a continuous overlap of multiple date ranges. It also needs to have a set number of times that it overlaps. For the example image below, I need 3 dates to overlap continuously. The valid overlapping dates would be Aug 20 - Aug 23, as Aug 24 only has 2 overlapping.
I have attempted many approaches including looping through all dates and comparing each one indivually with the next. That code looks like this.
Here is a .net fiddle for better visualization: https://dotnetfiddle.net/x3LfHR#.
private bool Compare(CompareDate a, CompareDate b)
{
DateTime? tStartA = a.ActiveDate;
DateTime? tEndA = a.ExpireDate;
DateTime? tStartB = b.ActiveDate;
DateTime? tEndB= b.ExpireDate;
bool overlap = (tStartA <= tEndB || tEndB == null) && (tStartB <= tEndA || tEndA == null);
DateTime? overlapStart = null;
DateTime? overlapEnd = null;
if (overlap)
{
//Find maximum start date.
overlapStart = (tStartA >= tStartB) ? tStartA : tStartB;
//Find Min End date between the two
overlapEnd = (tEndA <= tEndB) ? tEndA : tEndB;
if (overlapStart > this.overlapStart || this.overlapStart == null)
{
this.overlapStart = overlapStart;
}
if (overlapEnd < this.overlapEnd || this.overlapEnd == null)
{
this.overlapEnd = overlapEnd;
}
However, this approach makes it tricky to figure out continuous overlap dates. I have attempted to use the .Net Time period library at https://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET, but its not relevant in my case. Any help is appreciated.
OK - LINQ to the rescue!
Note: In order to make the comparison work, you must remove the time component and strictly use the only the date (e.g. DateTime.Date). Based on your requirements, that's exactly how you need to do it, so it shouldn't be a problem.
public List<DateTime> CompareDates(List<DateTime[]> compareRanges, int overlapLevel = 1)
{
var grouped = compareRanges.SelectMany(r => r).GroupBy(d => d);
var thresholdMatch = grouped.Where(g => g.Count() >= overlapLevel)
.Select(g => g.Key)
.OrderBy(d => d)
.ToList();
return thresholdMatch;
}
You can test the logic in a sample console app, using the skeleton code below as an example:
static void Main()
{
var setA = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
};
var setB = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
};
var setC = new[]
{
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
new DateTime(2017, 8, 25),
new DateTime(2017, 8, 26),
};
var setD = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
};
var compareList = new List<DateTime[]>
{
setA, setB, setC, setD
};
// setting the threshold to 2 will cause 8/24 to be added to the result...
// setting this to 1 (default) will return all intersections
// for now, set it to 3, per the question!
var result = CompareDates(compareList, 3);
foreach (var intersectDate in result)
{
Console.WriteLine(intersectDate);
}
}
Hope this helps, I certainly had fun with it!
P.S. I forked your fiddle: https://dotnetfiddle.net/GUzhjh.
This contains the modified version of your original program, so you should be able to play around with it a bit.
Here is a start at an algorithm:
Sort all start and end datetimes, assigning a +1 to every start and -1 to every end.
proceed from interval-start to interval-end, aggregating the period assignments above looking for a +3 value. Note this datetime.
proceed forward until your aggregate value drops below +3.
Voila! (I think.) Remember this one and continue processing.
When a second occurs, then save the longest and discard the other.
Continue until end-interval found; and report result.

C# get whole hour values between 2 datetime objects

I am trying to get the affected hours between 2 datetime and all i found was a python solution.
For example 'start' is 09:30 and 'end' is 14:00 (same day). The values I'd like returned are
[9:00, 10:00, 11:00, 12:00, 13:00, 14:00]
Python get whole hour values between 2 datetime objects
I can't seem to find any equivalent to C#.
So you want a list of all hours between both dates? You can use this query:
TimeSpan ts = dt2 - dt1;
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)ts.TotalHours)
.Select(i => dt1.AddHours(i).Hour);
Sample dates:
DateTime dt1 = new DateTime(2013, 07, 08, 15, 50, 00);
DateTime dt2 = new DateTime(2013, 07, 10, 19, 30, 00);
TimeSpan ts = dt2 - dt1;
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)ts.TotalHours)
.Select(i => dt1.AddHours(i).Hour);
foreach (int hour in hoursBetween)
Console.WriteLine(hour);
Output:
15
16
17
18
19
20
21
22
23
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
The following will return the total hours between the 2 DateTime objects:
(datevalue1 - datevalue2).TotalHours
And for a custom behavior,such as displaying a list of hours, use a simple custom method on that Timespan created to get a list of hours in your desired format.
Code suggestion of the top of my head:
public List<String> GenerateHours(DateTime t1,DateTime t2){
if ((t2-t1).TotalHours >24)){
//decide what to do.
return null;
}else{
var currentHour = t2.Hour;
var list = new List<String>();
for (int i=0;i<(t2-t1).TotalHours;i++){
if (currentHour<10){
list.Add("0"+currentHour+":00");
}else if (currentHour>=10){
list.Add(currentHour+":00");
}
currentHour= (currentHour+1)%24;
}
return list;
}
}
public IEnumerable<DateTime> GetHoursBetween(DateTime start, DateTime end)
{
DateTime first = start.Date.AddHours(start.Hour);
for (DateTime dateTime = first; dateTime <= end; dateTime = dateTime.AddHours(1))
{
yield return dateTime;
}
}
TimeSpan ts = DateTime1 - DateTime2;
double totalHours = ts.TotalHours;
From MSDN: "Gets the value of the current TimeSpan structure expressed in whole and fractional hours."
EDIT: ok, now I see what you're asking for. How about this:
var d1 = DateTime.Today.AddHours(9.5);
var d2 = DateTime.Today.AddHours(14);
var first = new DateTime(d1.Year, d1.Month, d1.Day, d1.Minute == 0 ? d1.Hour : d1.Hour + 1, 0, 0);
var second = new DateTime(d2.Year, d2.Month, d2.Day, d2.Minute == 0 ? d2.Hour : d2.Hour + 1, 0, 0);
TimeSpan ts = second - first;
//returns DateTimes affected. I.e., Today at, [10:00, 11:00, 12:00, 13:00, 14:00]
IEnumerable<DateTime> dates = Enumerable.Range(0, (int)ts.TotalHours + 1).Select(hour => first.AddHours(hour));
//Or, if you just want the HOURs
//returns just ints: i.e., DateTimes 10,11,12,13,14
IEnumerable<int> hours = Enumerable.Range(0, (int)ts.TotalHours + 1).Select(hour => first.AddHours(hour).Hour);
The first method is needed if you actually have dates that span days. If you DON'T, then the second method that just returns the hours would work fine.
This should do the trick. Tested in LinqPad.
var startDate = new DateTime(2013, 8, 7, 9, 30, 0);
var endDate = new DateTime(2013, 8, 7, 14, 0, 0);
List<string> times = new List<string>();
var currentTime = startDate;
if (currentTime.Minute != 0 || currentTime.Second != 0) {
// Get next hour
currentTime = currentTime.AddHours(1).AddMinutes(currentTime.Minute * -1);
}
while (currentTime <= endDate) {
times.Add(string.Format("{0:00}:00", currentTime.Hour));
currentTime = currentTime.AddHours(1);
}
(int)Math.Abs((date1 - date2).TotalHours)
Simply subtract them and get the total of hours from the result. Something like this:
var totalHours = (dateTime1 - dateTime2).TotalHours;
Maybe something like this would work?
public static List<DateTime> GetAffectedHours(DateTime start, DateTime end)
{
List<DateTime> result = new List<DateTime>();
// Strip start of its minutes/seconds/etc
DateTime initial = new DateTime(start.Year, start.Month, start.Day, start.Hour, 0, 0);
// Go ahead and get the next hour
DateTime iterator = initial.AddHours(1.0);
// if it is still before the end
while (iterator < end)
{
// add it to the results list
result.Add(iterator);
// and get the next hour
iterator = iterator.AddHours(1.0);
}
return result;
}
You can use a For loop
List<int> allHoursBetween = new List<int>();
for (DateTime d = myStartDate; d <= myEndDate; d = d.AddHours(1))
allHoursBetween.Add(d.Hour);

DateTime Ticks, check for overlapping dates C#

Lets say I 2 longs a start and end, which are really two date times converted to ticks. How would I tell if these two values overlap?
As MPelletier mentioned: you need two pairs of DateTimes:
var start1 = new DateTime(2013, 4, 10).Ticks;
var end1 = new DateTime(2013, 4, 20).Ticks;
var start2 = new DateTime(2013, 4, 9).Ticks;
var end2 = new DateTime(2013, 4, 11).Ticks;
if (start2 < end1)
{
//// Overlapping
}

Get values from database with specific time interval

I want to fetch values from database with specific intervals in C# and need a single query for that.
This is how my Database looks like
Id SensorId Value CreatedOn
------------------------------------------------
1 8 33.5 15-11-2012 5:48 PM
2 5 49.2 15-11-2012 5:48 PM
3 8 33.2 15-11-2012 5:49 PM
4 5 48.5 15-11-2012 5:49 PM
5 8 31.8 15-11-2012 5:50 PM
6 5 42.5 15-11-2012 5:50 PM
7 8 36.5 15-11-2012 5:51 PM
8 5 46.5 15-11-2012 5:51 PM
9 8 39.2 15-11-2012 5:52 PM
10 5 44.4 15-11-2012 5:52 PM
11 8 36.5 15-11-2012 5:53 PM
12 5 46.5 15-11-2012 5:53 PM
13 8 39.2 15-11-2012 5:54 PM
14 5 44.4 15-11-2012 5:54 PM
.. .. ..... ...................
The interval is in minutes.
So, if the interval is 10 minutes, then we need the values at 5:48, 5:58, 6:08 and so on...
I tried doing it with a while loop but it is taking a lot of time as i shoot multiple queries to the database.
Is there any way of getting the data in a single query?
You can use datepart along with a modulus to get the matching rows (eg, #interval = 10, #offset = 8):
SELECT * FROM table
WHERE datepart(minute, CreatedOn) % #interval = #offset
Edit
Note that the above isn't a general solution of selecting by intervals. It will work across hours (and therefore across days) for intervals like 2, 3, 4, 5 ... any minute interval which divides into 60.
If you want to use a strange interval like 7 minutes, then you'd have to define a starting time for the interval and calculate the total minutes for each row, inclusive of hours/days. At that point you'd be best to create an indexed, computed column on the table, based on a user-defined function that calculates the interval in question.
Here is how you can do it, explanation is contained within comments in code:
/*We want 10-minute intervals starting
from minimum date to next day same time*/
DECLARE #startDateTime DATETIME = (
SELECT MIN(CreatedOn)
FROM #yourTable
)
DECLARE #endDateTime DATETIME = DATEADD(DAY, 1, #startDateTime)
DECLARE #startDateTimeTable TABLE (dt DATETIME)
INSERT #startDateTimeTable VALUES (#startDateTime)
/*Create a table that contains relevant datetimes (10-minute
intervals from starting date to end date)*/
;WITH a AS (
SELECT dt
FROM #startDateTimeTable
UNION ALL
SELECT DATEADD(MINUTE, 10, a.dt)
FROM a
JOIN #startDateTimeTable b ON a.dt <= #endDateTime
)
SELECT *
INTO #requiredDateTimes
FROM a
OPTION (MAXRECURSION 32767)
/*Now join data table to datetime table to
filter out only records with datetimes that we want*/
SELECT *
FROM #yourTable a
JOIN #requiredDateTimes b ON
a.CreatedOn = b.dt
Here is an SQL Fiddle
Any of the answers that recommend using modulus (%) are making several assumptions:
You will always have a reading on every sensor at the exact minute in question
You will never have more than one reading in a minute per sensor.
You will never have to deal with intervals smaller than a minute.
These are probably false assumptions, so you need a different approach. First, make a map of all of the time points you are querying over. Then take the last reading from each sensor on or before that point.
Here's a full unit test showing how it can be done in pure linq-to-objects. You may need some minor changes to the query to get it to work in linq-to-sql, but this is the right approach. I used the exact sample data you provided.
As an aside - I hope you are recording your CreatedOn dates in UTC, or you will have ambiguity of sensor readings during daylight savings time "fall-back" transitions. You need to record as DateTime in UTC, or using DateTimeOffset. Either are an appropriate representation of instantaneous time. A DateTime with .Kind of Local or Unspecified is only a valid representation of calendar time, which is not appropriate for sensor readings.
[TestClass]
public class LinqIntervalQueryTest
{
public class Item
{
public int Id { get; set; }
public int SensorId { get; set; }
public double Value { get; set; }
public DateTime CreatedOn { get; set; }
}
[TestMethod]
public void Test()
{
var data = new[]
{
new Item { Id = 1, SensorId = 8, Value = 33.5, CreatedOn = new DateTime(2012, 11, 15, 17, 48, 0, DateTimeKind.Utc) },
new Item { Id = 2, SensorId = 5, Value = 49.2, CreatedOn = new DateTime(2012, 11, 15, 17, 48, 0, DateTimeKind.Utc) },
new Item { Id = 3, SensorId = 8, Value = 33.2, CreatedOn = new DateTime(2012, 11, 15, 17, 49, 0, DateTimeKind.Utc) },
new Item { Id = 4, SensorId = 5, Value = 48.5, CreatedOn = new DateTime(2012, 11, 15, 17, 49, 0, DateTimeKind.Utc) },
new Item { Id = 5, SensorId = 8, Value = 31.8, CreatedOn = new DateTime(2012, 11, 15, 17, 50, 0, DateTimeKind.Utc) },
new Item { Id = 6, SensorId = 5, Value = 42.5, CreatedOn = new DateTime(2012, 11, 15, 17, 50, 0, DateTimeKind.Utc) },
new Item { Id = 7, SensorId = 8, Value = 36.5, CreatedOn = new DateTime(2012, 11, 15, 17, 51, 0, DateTimeKind.Utc) },
new Item { Id = 8, SensorId = 5, Value = 46.5, CreatedOn = new DateTime(2012, 11, 15, 17, 51, 0, DateTimeKind.Utc) },
new Item { Id = 9, SensorId = 8, Value = 39.2, CreatedOn = new DateTime(2012, 11, 15, 17, 52, 0, DateTimeKind.Utc) },
new Item { Id = 10, SensorId = 5, Value = 44.4, CreatedOn = new DateTime(2012, 11, 15, 17, 52, 0, DateTimeKind.Utc) },
new Item { Id = 11, SensorId = 8, Value = 36.5, CreatedOn = new DateTime(2012, 11, 15, 17, 53, 0, DateTimeKind.Utc) },
new Item { Id = 12, SensorId = 5, Value = 46.5, CreatedOn = new DateTime(2012, 11, 15, 17, 53, 0, DateTimeKind.Utc) },
new Item { Id = 13, SensorId = 8, Value = 39.2, CreatedOn = new DateTime(2012, 11, 15, 17, 54, 0, DateTimeKind.Utc) },
new Item { Id = 14, SensorId = 5, Value = 44.4, CreatedOn = new DateTime(2012, 11, 15, 17, 54, 0, DateTimeKind.Utc) },
};
var interval = TimeSpan.FromMinutes(3);
var startDate = data.First().CreatedOn;
var endDate = data.Last().CreatedOn;
var numberOfPoints = (int)((endDate - startDate + interval).Ticks / interval.Ticks);
var points = Enumerable.Range(0, numberOfPoints).Select(x => startDate.AddTicks(interval.Ticks * x));
var query = from item in data
group item by item.SensorId
into g
from point in points
let itemToUse = g.LastOrDefault(x => x.CreatedOn <= point)
orderby itemToUse.CreatedOn, g.Key
select new
{
itemToUse.CreatedOn,
itemToUse.Value,
SensorId = g.Key
};
var results = query.ToList();
Assert.AreEqual(6, results.Count);
Assert.AreEqual(data[1].CreatedOn, results[0].CreatedOn);
Assert.AreEqual(data[1].Value, results[0].Value);
Assert.AreEqual(data[1].SensorId, results[0].SensorId);
Assert.AreEqual(data[0].CreatedOn, results[1].CreatedOn);
Assert.AreEqual(data[0].Value, results[1].Value);
Assert.AreEqual(data[0].SensorId, results[1].SensorId);
Assert.AreEqual(data[7].CreatedOn, results[2].CreatedOn);
Assert.AreEqual(data[7].Value, results[2].Value);
Assert.AreEqual(data[7].SensorId, results[2].SensorId);
Assert.AreEqual(data[6].CreatedOn, results[3].CreatedOn);
Assert.AreEqual(data[6].Value, results[3].Value);
Assert.AreEqual(data[6].SensorId, results[3].SensorId);
Assert.AreEqual(data[13].CreatedOn, results[4].CreatedOn);
Assert.AreEqual(data[13].Value, results[4].Value);
Assert.AreEqual(data[13].SensorId, results[4].SensorId);
Assert.AreEqual(data[12].CreatedOn, results[5].CreatedOn);
Assert.AreEqual(data[12].Value, results[5].Value);
Assert.AreEqual(data[12].SensorId, results[5].SensorId);
}
}
Here's how you can do it in two calls to the database (untested):
int interval = 10;
DateTime firstDate = db.Items.Select(x => x.CreatedOn).Min();
var items = db.Items.Where(x => (x.CreatedOn - firstDate).TotalMinutes % interval == 0).ToList();

Categories