How do I calculate timer intervals for monthly scheduling? - c#

I'm building a Windows Service, and I want it to process a bunch of different background tasks at configurable intervals.
I'm having a hard time working out how to calculate the length of time that the timer needs to wait before kicking off some task.
I store a StartDate against the task, and an interval type: daily, weekly, monthly, etc. I've nailed daily, but can't work out how to do monthly...
The rules are:
If the StartDate is in the future, then wait the length of time between Now and the StartDate.
If the StartDate is in the past, then start the task at the same day-of-month/time as the StartDate but in the current/next month. So if the StartDate is March 15th at 09:00 and today is May 25th then the next time the task should run is June 15th at 09:00.
I have got so far with this. Here is a test app, which takes a number of test cases and tries to calculate the number of hours between the current time (as per the test case) and the fixed start time of the task:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main() {
// Test cases
var times = new Dictionary<DateTime,TimeSpan> {
{ new DateTime(2016, 3, 1, 9, 0, 0), TimeSpan.FromHours(2016)},
{ new DateTime(2016, 3, 5, 3, 0, 0), TimeSpan.FromHours(1926)},
{ new DateTime(2016, 3, 5, 9, 0, 0), TimeSpan.FromHours(1920)},
{ new DateTime(2016, 3, 5, 15, 0, 0), TimeSpan.FromHours(1914)},
{ new DateTime(2016, 3, 24, 3, 0, 0), TimeSpan.FromHours(1470)},
{ new DateTime(2016, 3, 24, 9, 0, 0), TimeSpan.FromHours(1464)},
{ new DateTime(2016, 3, 24, 15, 0, 0), TimeSpan.FromHours(1458)},
{ new DateTime(2016, 4, 19, 9, 0, 0), TimeSpan.FromHours(840)},
{ new DateTime(2016, 4, 24, 3, 0, 0), TimeSpan.FromHours(726)},
{ new DateTime(2016, 4, 24, 9, 0, 0), TimeSpan.FromHours(720)},
{ new DateTime(2016, 4, 24, 15, 0, 0), TimeSpan.FromHours(714)},
{ new DateTime(2016, 4, 24, 21, 0, 0), TimeSpan.FromHours(708)},
{ new DateTime(2016, 5, 6, 3, 0, 0), TimeSpan.FromHours(438)},
{ new DateTime(2016, 5, 24, 3, 0, 0), TimeSpan.FromHours(6)},
{ new DateTime(2016, 5, 24, 9, 0, 0), TimeSpan.FromHours(0)},
{ new DateTime(2016, 5, 24, 15, 0, 0), TimeSpan.FromHours(738)},
{ new DateTime(2016, 5, 26, 3, 0, 0), TimeSpan.FromHours(702)},
{ new DateTime(2016, 5, 26, 9, 0, 0), TimeSpan.FromHours(696)},
{ new DateTime(2016, 5, 26, 15, 0, 0), TimeSpan.FromHours(690)},
{ new DateTime(2016, 6, 24, 3, 0, 0), TimeSpan.FromHours(6)},
{ new DateTime(2016, 6, 24, 9, 0, 0), TimeSpan.FromHours(0)},
{ new DateTime(2016, 6, 24, 15, 0, 0), TimeSpan.FromHours(714)},
{ new DateTime(2016, 7, 6, 3, 0, 0), TimeSpan.FromHours(438)},
{ new DateTime(2016, 7, 6, 9, 0, 0), TimeSpan.FromHours(432)},
{ new DateTime(2016, 7, 6, 15, 0, 0), TimeSpan.FromHours(426)},
{ new DateTime(2016, 7, 24, 3, 0, 0), TimeSpan.FromHours(6)},
{ new DateTime(2016, 7, 24, 9, 0, 0), TimeSpan.FromHours(0)},
{ new DateTime(2016, 7, 24, 15, 0, 0), TimeSpan.FromHours(738)},
};
var startTime = new DateTime(2016, 05, 24, 09, 00, 00);
var last = times.First().Key;
foreach (var time in times) {
var now = time.Key;
var expected = time.Value;
var timer = startTime.TimeOfDay - now.TimeOfDay;
if (now <= startTime)
timer += TimeSpan.FromDays((startTime.Date - now.Date).TotalDays);
else
timer += TimeSpan.FromDays((now.Date.AddMonths(1) - now.Date).TotalDays);
if (last.Date != now.Date) Console.WriteLine();
Console.WriteLine($"{now:yyyy-MM-dd HH:mm} -> {startTime:yyyy-MM-dd HH:mm} = {timer:dd\\.hh} {(timer != expected ? "EXPECTED " + expected.ToString("dd\\.hh") : "CORRECT ")}");
last = now;
}
}
}
Which produces the following output:
2016-03-01 09:00 -> 2016-05-24 09:00 = 84.00 CORRECT
2016-03-05 03:00 -> 2016-05-24 09:00 = 80.06 CORRECT
2016-03-05 09:00 -> 2016-05-24 09:00 = 80.00 CORRECT
2016-03-05 15:00 -> 2016-05-24 09:00 = 79.18 CORRECT
2016-03-24 03:00 -> 2016-05-24 09:00 = 61.06 CORRECT
2016-03-24 09:00 -> 2016-05-24 09:00 = 61.00 CORRECT
2016-03-24 15:00 -> 2016-05-24 09:00 = 60.18 CORRECT
2016-04-19 09:00 -> 2016-05-24 09:00 = 35.00 CORRECT
2016-04-24 03:00 -> 2016-05-24 09:00 = 30.06 CORRECT
2016-04-24 09:00 -> 2016-05-24 09:00 = 30.00 CORRECT
2016-04-24 15:00 -> 2016-05-24 09:00 = 29.18 CORRECT
2016-04-24 21:00 -> 2016-05-24 09:00 = 29.12 CORRECT
2016-05-06 03:00 -> 2016-05-24 09:00 = 18.06 CORRECT
2016-05-24 03:00 -> 2016-05-24 09:00 = 00.06 CORRECT
2016-05-24 09:00 -> 2016-05-24 09:00 = 00.00 CORRECT
2016-05-24 15:00 -> 2016-05-24 09:00 = 30.18 CORRECT
2016-05-26 03:00 -> 2016-05-24 09:00 = 31.06 EXPECTED 29.06
2016-05-26 09:00 -> 2016-05-24 09:00 = 31.00 EXPECTED 29.00
2016-05-26 15:00 -> 2016-05-24 09:00 = 30.18 EXPECTED 28.18
2016-06-24 03:00 -> 2016-05-24 09:00 = 30.06 EXPECTED 00.06
2016-06-24 09:00 -> 2016-05-24 09:00 = 30.00 EXPECTED 00.00
2016-06-24 15:00 -> 2016-05-24 09:00 = 29.18 CORRECT
2016-07-06 03:00 -> 2016-05-24 09:00 = 31.06 EXPECTED 18.06
2016-07-06 09:00 -> 2016-05-24 09:00 = 31.00 EXPECTED 18.00
2016-07-06 15:00 -> 2016-05-24 09:00 = 30.18 EXPECTED 17.18
2016-07-24 03:00 -> 2016-05-24 09:00 = 31.06 EXPECTED 00.06
2016-07-24 09:00 -> 2016-05-24 09:00 = 31.00 EXPECTED 00.00
2016-07-24 15:00 -> 2016-05-24 09:00 = 30.18 CORRECT
As you can see it goes a bit wrong towards the end. What are the calculations I need to work out the number of hours correctly?

Try this code:
foreach (var time in times)
{
var now = time.Key;
var expected = time.Value;
TimeSpan timer;
if (now <= startTime) {
// no need to do anything here - just substract
timer = startTime - now;
}
else {
// normalize start time to current month
var normalized = new DateTime(now.Year, now.Month, startTime.Day, startTime.Hour, startTime.Minute, startTime.Second);
if (normalized >= now) {
// normalized date is later in the same month - substract
timer = normalized - now;
}
else {
// normalized date is before current - move to next month
timer = normalized.AddMonths(1) - now;
}
}
if (last.Date != now.Date) Console.WriteLine();
Console.WriteLine($"{now:yyyy-MM-dd HH:mm} -> {startTime:yyyy-MM-dd HH:mm} = {timer:dd\\.hh} {(timer != expected ? "EXPECTED " + expected.ToString("dd\\.hh") : "CORRECT ")}");
last = now;
}

Related

Determine if two dates are within an allowed time span

I got a StartDateTime and EndDateTime that I have to validate.
For them to be valid they have to be within allowed hours which are fully customizable.
// allowed hours
allowedStart = new TimeSpan(08, 00, 0);
allowedEnd = new TimeSpan(20, 00, 0);
Now the two dates (StartDateTime and EndDateTime) are coming in (some example test cases)
// Valid date
obj1.StartDateTime = new DateTime(2020, 1, 30, 19, 10, 0);
obj1.EndDateTime = new DateTime(2020, 1, 30, 19, 20, 0);
// End date exceeding
obj2.StartDateTime = new DateTime(2020, 1, 30, 19, 50, 0);
obj2.EndDateTime = new DateTime(2020, 1, 30, 20, 15, 0);
// Start and end date exceeding
obj3.StartDateTime = new DateTime(2020, 1, 30, 20, 10, 0);
obj3.EndDateTime = new DateTime(2020, 1, 30, 20, 35, 0);
// Invalid (overnight) both exceeding
obj4.StartDateTime = new DateTime(2020, 1, 30, 23, 50, 0);
obj4.EndDateTime = new DateTime(2020, 1, 31, 0, 35, 0);
// Start to early
obj5.StartDateTime = new DateTime(2020, 1, 31, 7, 50, 0);
obj5.EndDateTime = new DateTime(2020, 1, 31, 8, 15, 0);
I was wondering if there isn't some already implemented function I haven't found since my brain is dying right now. I've been trying to implement this myself like this but the obj4 testcase still kills it:
if ((obj.StartDateTime.Date.Add(allowedStart) <= obj.StartDateTime) &&
(allowedEnd < allowedStart
? obj.EndDateTime <= obj.EndDateTime.Date.AddDays(1).Add(allowedEnd)
: obj.EndDateTime <= obj.EndDateTime.Date.Add(allowedEnd))))
{
// valid
}
else
{
// invalid
}
Let's start from single value
private static bool WithinSpan(DateTime value, TimeSpan from, TimeSpan to) =>
value >= value.Date.Add(from) && value <= value.Date.Add(to);
Now we can implement the same with two values:
private static bool WithinSpan(DateTime startDate, DateTime endDate,
TimeSpan from, TimeSpan to) =>
// startDate <= endDate && // you may want to add this condition as well
startDate >= startDate.Date.Add(from) && startDate <= startDate.Date.Add(to) &&
endDate >= startDate.Date.Add(from) && endDate <= startDate.Date.Add(to);
Demo:
TimeSpan allowedStart = new TimeSpan(08, 00, 0);
TimeSpan allowedEnd = new TimeSpan(20, 00, 0);
(DateTime, DateTime)[] tests = new (DateTime, DateTime)[] {
(new DateTime(2020, 1, 30, 19, 10, 0), new DateTime(2020, 1, 30, 19, 20, 0)),
(new DateTime(2020, 1, 30, 19, 50, 0), new DateTime(2020, 1, 30, 20, 15, 0)),
(new DateTime(2020, 1, 30, 20, 10, 0), new DateTime(2020, 1, 30, 20, 35, 0)),
(new DateTime(2020, 1, 30, 23, 50, 0), new DateTime(2020, 1, 31, 0, 35, 0)),
(new DateTime(2020, 1, 31, 7, 50, 0), new DateTime(2020, 1, 31, 8, 15, 0)),
};
Func<DateTime, DateTime, string> within =
(t1, t2) => $"{(WithinSpan(t1, t2, allowedStart, allowedEnd) ? "Yes" : "No")}";
string report = string.Join(Environment.NewLine, tests
.Select(test => $"{test.Item1:yyyy-MM-dd HH:mm:ss} .. {test.Item2:yyyy-MM-dd HH:mm:ss} : {within(test.Item1, test.Item2)}"));
Console.Write(report);
Outcome:
2020-01-30 19:10:00 .. 2020-01-30 19:20:00 : Yes
2020-01-30 19:50:00 .. 2020-01-30 20:15:00 : No
2020-01-30 20:10:00 .. 2020-01-30 20:35:00 : No
2020-01-30 23:50:00 .. 2020-01-31 00:35:00 : No
2020-01-31 07:50:00 .. 2020-01-31 08:15:00 : No
Edit:
Elaborated version is
private static bool WithinSpan(DateTime startDate, DateTime endDate,
TimeSpan from, TimeSpan to) {
// Empty Period
if (startDate > endDate)
return false;
// [from..to] within single day
if (to >= from)
return startDate >= startDate.Date.Add(from) && startDate <= startDate.Date.Add(to) &&
endDate >= startDate.Date.Add(from) && endDate <= startDate.Date.Add(to);
// [from..midnight..to]
if (startDate.Day == endDate.Day)
return startDate >= startDate.Date.Add(from) || endDate <= endDate.Date.Add(to);
else {
to = to.Add(TimeSpan.FromDays(1));
return startDate >= startDate.Date.Add(from) && startDate <= startDate.Date.Add(to) &&
endDate >= startDate.Date.Add(from) && endDate <= startDate.Date.Add(to);
}
}
which removes empty periods, and treats from > to TimeSpan as containing midnight.
Demo:
// from 22:00 to midnight and then up to 06:00
TimeSpan allowedStart = new TimeSpan(22, 00, 00);
TimeSpan allowedEnd = new TimeSpan(06, 00, 00);
(DateTime, DateTime)[] tests = new (DateTime, DateTime)[] {
(new DateTime(2020, 1, 30, 19, 10, 0), new DateTime(2020, 1, 30, 19, 20, 0)),
(new DateTime(2020, 1, 30, 19, 50, 0), new DateTime(2020, 1, 30, 20, 15, 0)),
(new DateTime(2020, 1, 30, 20, 10, 0), new DateTime(2020, 1, 30, 20, 35, 0)),
(new DateTime(2020, 1, 30, 23, 50, 0), new DateTime(2020, 1, 31, 0, 35, 0)),
(new DateTime(2020, 1, 30, 23, 00, 0), new DateTime(2020, 1, 30, 23, 35, 0)),
(new DateTime(2020, 1, 30, 3, 00, 0), new DateTime(2020, 1, 30, 4, 00, 0)),
(new DateTime(2020, 1, 31, 4, 50, 0), new DateTime(2020, 1, 31, 8, 15, 0)),
};
Func<DateTime, DateTime, string> within =
(t1, t2) => $"{(WithinSpan(t1, t2, allowedStart, allowedEnd) ? "Yes" : "No")}";
string report = string.Join(Environment.NewLine, tests
.Select(test => $"{test.Item1:yyyy-MM-dd HH:mm:ss} .. {test.Item2:yyyy-MM-dd HH:mm:ss} : {within(test.Item1, test.Item2)}"));
Console.Write(report);
Outcome:
2020-01-30 19:10:00 .. 2020-01-30 19:20:00 : No
2020-01-30 19:50:00 .. 2020-01-30 20:15:00 : No
2020-01-30 20:10:00 .. 2020-01-30 20:35:00 : No
2020-01-30 23:50:00 .. 2020-01-31 00:35:00 : Yes
2020-01-30 23:00:00 .. 2020-01-30 23:35:00 : Yes
2020-01-30 03:00:00 .. 2020-01-30 04:00:00 : Yes
2020-01-31 04:50:00 .. 2020-01-31 08:15:00 : No
Next Edit:
Shortened verison that magically works:
if (startDate > endDate) {
return false;
}
if (startDate.Day == endDate.Day && to < from) {
return startDate >= startDate.Date.Add(from) || endDate <= endDate.Date.Add(to);
}
if (to < from) {
to = to.Add(TimeSpan.FromDays(1));
}
return startDate >= startDate.Date.Add(from) && endDate <= startDate.Date.Add(to);
Following, is the exact piece of code I use, for this not exact goal, in my program:
public bool IsInsideTimeframe(DateTime firstStart, DateTime firstEnd, DateTime secondStart, DateTime secondEnd)
{
bool isInside;
if (firstStart.Ticks >= secondStart.Ticks && firstEnd.Ticks <= secondEnd.Ticks)
isInside = true;
else
isInside = false;
return isInside;
}
Let's say you have two time frames, one between 11:00-14:00, the second is 10:00-15:00. In order to check whether the first timespan is inside the second, you use the function in following way:
IsInsideTimeframe(11:00, 14:00, 10:00, 15:00)
You can simply run this function on both of the dates you wish to validate, with the "allowed time span" as the "second" date given.

Round DateTime nearest nth minute

I'm trying to round a DateTime to the nearest 7 minute.
I've seen many rounding functions for c#, but for some reason, I'm getting different results to what I'm expecting.
Given the following time:
var d = new DateTime(2019, 04, 15, 9, 40, 1, 0);
If I want to round to the nearest 7th minute then I would expect the answer to be
2019-04-15 9:42:00 // 0, 7, 14, 21, 28, 35, 42 ?
Input / Expected result
new DateTime(2019, 04, 15, 9, 40, 0, 0); // 9:42
new DateTime(2019, 04, 15, 9, 03, 0, 0); // 9:07
new DateTime(2019, 04, 15, 9, 31, 0, 0); // 9:35
new DateTime(2019, 04, 15, 9, 21, 0, 0); // 9:21
new DateTime(2019, 04, 15, 9, 0, 0, 0); // 9:00
new DateTime(2019, 04, 15, 9, 58, 0, 0); // 10:00 (start again)
Various DateTime rounding functions that I've seen show the following answers, which I can't understand why unless I'm missing something
9:41 or
9:43
Example of rounding functions
public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
var modTicks = dt.Ticks % d.Ticks;
var delta = modTicks != 0 ? d.Ticks - modTicks : 0;
return new DateTime(dt.Ticks + delta, dt.Kind);
}
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
}
static DateTime RoundUpNMinute(DateTime dt, int n)
{
var minute = dt.Minute;
if (minute % n == 0)
return dt;
var minuteRoundUp = minute / n * n + n;
if(minuteRoundUp > 60)
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0, dt.Kind).AddHours(1);
else
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, minuteRoundUp, 0, dt.Kind);
}
Got same results for all your examples.

Converting datetime (local or UTC) to Moscow and Kiev timezone issues depending on Daylight saving time

There are some timezones which switch to Daylight saving time and not. I know that Russia doesn't switch to this time and Ukraine switches to Daylight saving time.
As I know from this link .NET TimeZoneInfo from Olson time zone Moscow uses Russian Standard Time and Kiev(Ukraine) uses FLE Standard time.
My test is:
Winter, Russia, DateTimeKind.Utc
Winter, Russia, DateTimeKind.Local
Summer, Russia, DateTimeKind.Utc
Summer, Russia, DateTimeKind.Local
Winter, Kiev, DateTimeKind.Utc
Winter, Kiev, DateTimeKind.Local
Summer, Kiev, DateTimeKind.Utc
Summer, Kiev, DateTimeKind.Local
My PC time is (UTC+00:00) London +1 hour (now is Daylight saving time).
In my tests I used time 15:00.
Let it be variable dst=1 hour (for my local Daylight saving time), I want to understand why I get this output:
In Russia UTC+3, so 15:00utc + 3 = 18:00
In Russia UTC+3, so 15:00local + 3 = 18:00 (dst*0 since it is winter)
In Russia UTC+3, so 15:00utc + 3 = 18:00
Why it is 17:00 ??? (how it is calculated?)
In Kiev UTC+2, so 15:00utc + 2 = 17:00
In Kiev UTC+2, so 15:00local + 2 = 17:00
Why it is 18:00 ??? (how it is calculated?)
Why it is 17:00 ??? (how it is calculated?)
Windows 7
Time zone: (UTC+00:00) Dublin, Edinburgh, Lisbon, London.
Culture: United Kingdom
DateTime time = TimeZoneInfo.ConvertTime(new DateTime(2018, 11, 23, 15, 0, 0, DateTimeKind.Utc),
TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 11, 23, 15, 0, 0, DateTimeKind.Local),
TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 8, 2, 15, 0, 0, DateTimeKind.Utc),
TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 8, 2, 15, 0, 0, DateTimeKind.Local),
TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 11, 23, 15, 0, 0, DateTimeKind.Utc),
TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 11, 23, 15, 0, 0, DateTimeKind.Local),
TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 8, 2, 15, 0, 0, DateTimeKind.Utc),
TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"));
Console.WriteLine(time);
time = TimeZoneInfo.ConvertTime(new DateTime(2018, 8, 2, 15, 0, 0, DateTimeKind.Local),
TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"));
Console.WriteLine(time);
Because the timezone "London" is not UTC+0. It's only UTC+0 in Winter, but UTC+1 in Summer.
If you take your dates and before converting them to "FLE", show them in UTC, you will see what I mean.
Example:
var fleTimeZone = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time");
var local = new DateTime(2018, 8, 2, 15, 0, 0, DateTimeKind.Local);
var utc = local.ToUniversalTime();
var fle = TimeZoneInfo.ConvertTime(local, fleTimeZone);
Console.WriteLine(TimeZoneInfo.Local);
Console.WriteLine(TimeZoneInfo.Local + ": " + local);
Console.WriteLine(TimeZoneInfo.Utc + ": " + utc);
Console.WriteLine(fleTimeZone + ": " + fle);
(UTC+00:00) Dublin, Edinburgh, Lissabon, London
(UTC+00:00) Dublin, Edinburgh, Lissabon, London: 02.08.2018 15:00:00
UTC: 02.08.2018 14:00:00
(UTC+02:00) Helsinki, Kiew, Riga, Sofia, Tallinn, Wilna: 02.08.2018 17:00:00
As nvoigt explained, London's summer time (called BST) is UTC+1. When you use DateTimeKind.Local, the value is local to your machine.
Thus, to finish out your chart:
4) London is UTC+1 (BST) so 15:00 BST = 14:00 UTC. Russia is UTC+3, so 14:00 + 3 = 17:00
7) 15:00 UTC. Kiev is UTC+3 (EEST), so 15:00 + 3 = 18:00
8) London is UTC+1 (BST) so 15:00 BST = 14:00 UTC. Kiev is UTC+3, so 14:00 + 3 = 17:00
All calculations are working as expected.

How to Find Contiguous Dates within two Sets of Dates

All, I have two sets of dates for which I need to find all date pairs having contiguous dates where the hours connect. I would like the result to return the start and end date for each contiguous pair or pairs of dates to be used later as arguments to a linq query against a master list which contains all possible dates.
Below : Sample data to be evaluated. Desired output at bottom.
Thanks
// The result should return all pairs of start/end times (hour) where dates are contiguous. Where a set has more than two pairs,
// the result would return the start time of the first start record and the end time of the latest end record.
var ranges = new List<DateRange>
{new DateRange(DateTime.Parse("1/19/2018 10:00 AM"), DateTime.Parse("1/19/2018 12:00 PM")),
new DateRange(DateTime.Parse("1/19/2018 12:00 PM"), DateTime.Parse("1/19/2018 02:00 PM")),
new DateRange(DateTime.Parse("1/19/2018 02:00 PM"), DateTime.Parse("1/19/2018 04:00 PM")),
new DateRange(DateTime.Parse("1/19/2018 07:00 PM"), DateTime.Parse("1/19/2018 08:00 PM")),
new DateRange(DateTime.Parse("1/19/2018 04:00 PM"), DateTime.Parse("1/19/2018 05:00 PM")),
new DateRange(DateTime.Parse("1/19/2018 10:00 PM"), DateTime.Parse("1/19/2018 11:00 PM"))
};
// Sample Result
// Set 1
// "1/19/2018 10:00 AM", "1/19/2018 05:00 PM"
// Set 2
// "1/19/2018 07:00 PM", "1/19/2018 08:00 PM"
// Set 3
// "1/19/2018 10:00 PM", "1/19/2018 11:00 PM"``
The example-code will not work. Because of this i have to guess what you want to do. Your example-data look like some log-in/out data of a single user.
My guess: You got no overlapping intervalls - every start is unique and there is no other start before it ends.
Example:
1/18/2018 10 AM - 1/18/2018 11 AM
1/18/2018 11 AM - 1/18/2018 01 PM
You want to merge this to..
1/18/2018 10 AM - 1/18/2018 01 PM
And this is not allowed, because it's overlapping:
1/18/2018 10 AM - 1/18/2018 01 PM
1/18/2018 11 AM - 1/18/2018 02 PM
If this is what you want to do, you should edit your question. But this could be your solution:
// A class for each intervall
public class MyIntervall
{
public MyIntervall(DateTime start, DateTime end)
{
this.Start = start;
this.End = end;
}
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public static void Main(params string[] args)
{
// Your data..
List<MyIntervall> intervalls = new List<MyIntervall>()
{
new MyIntervall(new DateTime(2018, 1, 19, 10, 0, 0), new DateTime(2018, 1, 19, 12, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 12, 0, 0), new DateTime(2018, 1, 19, 14, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 14, 0, 0), new DateTime(2018, 1, 19, 16, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 19, 0, 0), new DateTime(2018, 1, 19, 20, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 16, 0, 0), new DateTime(2018, 1, 19, 17, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 22, 0, 0), new DateTime(2018, 1, 19, 23, 0, 0))
};
MyIntervall lastIntervall = null;
List<MyIntervall> mergedIntervalls = new List<MyIntervall>();
// Loop through your list.
foreach (MyIntervall currenIntervall in intervalls.OrderBy(o => o.Start))
{
if (lastIntervall == null)
{
// Start-condition
lastIntervall = currenIntervall;
}
else if (lastIntervall.End == currenIntervall.Start)
{
// If the last intervall ends at the start of the next one, we merge them into one bigger intervall by moving the end.
lastIntervall.End = currenIntervall.End;
}
else
{
// If end doesn't match the next start, we know that the intervall is closed. Wo move to the next one.
mergedIntervalls.Add(lastIntervall);
lastIntervall = currenIntervall;
}
}
// In the end, we add the last intervall to the merge-list, because it has no following intervall.
mergedIntervalls.Add(lastIntervall);
// output our merge-result
int set = 0;
foreach (MyIntervall currenIntervall in mergedIntervalls)
{
Console.WriteLine("#{0}: Start {1}, End {2}", ++set, currenIntervall.Start, currenIntervall.End);
}
// this is the result of the merge
List<MyIntervall> resultList = new List<MyIntervall>()
{
new MyIntervall(new DateTime(2018, 1, 19, 10, 0, 0), new DateTime(2018, 1, 19, 17, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 19, 0, 0), new DateTime(2018, 1, 19, 20, 0, 0)),
new MyIntervall(new DateTime(2018, 1, 19, 22, 0, 0), new DateTime(2018, 1, 19, 23, 0, 0))
};
}

Get the closest time to 24:00:00 between less 23:00:00 to greater 24:00:00 in C#

I am trying to get the time closer to 24:00:00 between two values, before midnight and after midnight.
EDIT: This is just an an example of what I am trying to do. In this case I should get both items.
var dt1 = new DateTime(2014, 11, 11, 23, 50, 00);
var dt2 = new DateTime(2014, 12, 11, 00, 50, 00);
var l = new List<DateTime>();
for (int i = 0; i < l.Count - 1; i++)
{
TimeSpan ts1 = new TimeSpan(l[i].Hour, l[i].Minute, l[i].Second);
TimeSpan ts2 = new TimeSpan(l[i + 1].Hour, l[i + 1].Minute, l[i + 1].Second);
if (ts1.TotalHours <= 23 && ts2.TotalHours >= 00)
{
Console.WriteLine("00:00:00 - {0} {1} \n", ts1, ts2);
}
}
Thank you for any help and advise.
Your question is quite confusing and not totally clear what it is you're trying to achieve, but I've made some assumptions, and come up with what I think maybe what you're after:
var l = new List<DateTime> {
new DateTime(2014, 11, 11, 22, 0, 0),
new DateTime(2014, 11, 11, 23, 45, 0),
new DateTime(2014, 11, 11, 23, 55, 0),
new DateTime(2014, 11, 11, 23, 59, 59),
new DateTime(2014, 11, 12, 0, 0, 0),
new DateTime(2014, 11, 12, 0, 4, 0),
new DateTime(2014, 11, 12, 0, 15, 0),
new DateTime(2014, 11, 12, 1, 0, 0),
new DateTime(2014, 11, 12, 10, 0, 0),
};
for (int i = 0; i < l.Count - 1; i++) {
if (l[i].TimeOfDay.TotalMinutes < 5 || l[i].TimeOfDay.TotalMinutes >= 23*60 + 55)
Console.WriteLine("{0} is close to midnight", l[i]);
else
Console.WriteLine("{0} is NOT close to midnight", l[i]);
}
I've loaded the list of dates/times with some test data, and the code simply prints out whether each date/time is within 5 minutes either side of midnight.
Another attempt at answering you're ambiguous question is as follows:
var l = new List<DateTime> {
new DateTime(2014, 11, 11, 15, 0, 0), // 15:00:00
new DateTime(2014, 11, 11, 16, 0, 0), // 16:00:00
new DateTime(2014, 11, 11, 17, 0, 0), // 17:00:00
new DateTime(2014, 11, 11, 17, 20, 0), // 17:20:00
new DateTime(2014, 11, 11, 18, 15, 0), // 18:15:00
new DateTime(2014, 11, 11, 19, 0, 0), // 19:00:00
new DateTime(2014, 11, 11, 22, 0, 0), // 22:00:00
new DateTime(2014, 11, 11, 23, 45, 0), // 23:45:00
new DateTime(2014, 11, 11, 23, 50, 00), // 23:50:00
new DateTime(2014, 12, 11, 00, 50, 00), // 00:50:00
new DateTime(2014, 11, 12, 1, 0, 0), // 01:00:00
new DateTime(2014, 11, 12, 10, 0, 0), // 10:00:00
};
var time = new TimeSpan(18, 0, 0); // <- Set the target time here
var offsetBefore = new TimeSpan(1, 0, 0, 0).TotalMilliseconds - time.TotalMilliseconds;
var offsetAfter = time.TotalMilliseconds * -1;
var closestBefore =
l.Aggregate(
(current, next) =>
next.AddMilliseconds(offsetBefore).TimeOfDay.TotalMilliseconds > current.AddMilliseconds(offsetBefore).TimeOfDay.TotalMilliseconds
? next
: current);
var closestAfter =
l.Aggregate(
(current, next) =>
next.AddMilliseconds(offsetAfter).TimeOfDay.TotalMilliseconds < current.AddMilliseconds(offsetAfter).TimeOfDay.TotalMilliseconds
? next
: current);
Console.WriteLine("{0} is the closest date/time before {1}.", closestBefore, time);
Console.WriteLine("{0} is the closest date/time after {1}.", closestAfter, time);
Console.WriteLine("00:00:00 - {0} {1} \n", closestBefore, closestAfter);
// OUTPUTS:
// 11/11/2014 17:20:00 is the closest date/time before 18:00:00.
// 11/11/2014 18:15:00 is the closest date/time after 18:00:00.
// 00:00:00 - 11/11/2014 17:20:00 11/11/2014 18:15:00
This will return the closest date/time in the list to midnight that is before midnight, and also separately the closest date/time in the list to midnight that is after midnight.
Hope this helps!
Try this:
var dt1 = new DateTime(2014, 11, 11, 23, 50, 00);
var dt2 = new DateTime(2014, 12, 11, 00, 50, 00);
var dt1temp = new DateTime(dt1.Year, dt1.Month, dt1.Day, 00, 00, 00);
var dt2temp = new DateTime(dt2.Year, dt2.Month, dt2.Day, 00, 00, 00);
TimeSpan time1 = new TimeSpan();
TimeSpan time2 = new TimeSpan();
TimeSpan time24 = new TimeSpan(24, 0, 0);
time1 = dt1 - dt1temp;
time2 = dt2 - dt2temp;
if (time1.Hours >= 12) time1 = time24 - time1;
if (time2.Hours >= 12) time2 = time24 - time2;
string result = "";
if (time1 < time2) result = "Time1 nearer to 00:00";
else result = "Time2 nearer to 00:00";

Categories

Resources