Related
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.
I have a small problem because I do not always understand how to use the lessons of the day, for example, I want the time from the list to have the day time recalculated from that date, but if I have a new time, that conversion counts from the new time. It works fine for me if I only have one time, but if I have two times, foreach the loop calculates me both times for the day.
This is my code:
public TimeSpan GetHoursForDay(DateTime day) {
TimeSpan time = TimeSpan.Zero;
foreach (var times in shouldWorkTime)
{
if (times.Valid_from > day) //here's the real problem for me, do i want the hours to count from that date, for example: for 1.1.2020 it doesn't need to take hours from 1.12.2019
continue;
if (day.DayOfWeek == DayOfWeek.Monday)
{
time += times.ShouldWorkMonday;
}
if (day.DayOfWeek == DayOfWeek.Tuesday)
{
time += times.ShouldWorkTuesday;
}
if (day.DayOfWeek == DayOfWeek.Wednesday)
{
time += times.ShouldWorkWednesday;
}
if (day.DayOfWeek == DayOfWeek.Thursday)
{
time += times.ShouldWorkThursday;
}
if (day.DayOfWeek == DayOfWeek.Friday)
{
time += times.ShouldWorkFriday;
}
if (day.DayOfWeek == DayOfWeek.Saturday)
{
time += times.ShouldWorkSaturday;
}
if (day.DayOfWeek == DayOfWeek.Sunday)
{
time += times.ShouldWorkSunday;
}
}
return time;
}
}
These are the values I get in the list:
var shouldWorkTime = new List<ShouldWorkTime>
{
new ShouldWorkTime
{
Valid_from = new DateTime(2019, 12, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(8,0,0),
ShouldWorkTuesday= new TimeSpan(7,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(8,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
},
new ShouldWorkTime
{
Valid_from = new DateTime(2020, 01, 01, 0, 0, 0),
ShouldWorkMonday = new TimeSpan(4,0,0),
ShouldWorkTuesday= new TimeSpan(3,0,0),
ShouldWorkWednesday= new TimeSpan(6,0,0),
ShouldWorkThursday= new TimeSpan(5,0,0),
ShouldWorkFriday= new TimeSpan(9,0,0),
ShouldWorkSaturday = new TimeSpan(0,0,0),
ShouldWorkSunday = new TimeSpan(0,0,0)
}
};
for the day value, I always get for the current day from the calendar, so I want to be counted in this case for the days of 1.1.2020 values for the days that fall in the second count, and until then the values that fall in the first count.
so i need to return how many hours for a particular day a worker needs to make, but valid from the last date from (Valid_From).
How can I correct this? thank you all very much for your help
eg:
input 1.1.2020 output = 6,0,0;
input 1.12.2019 output = 0,0,0;
Here is a little modification of the Data structure.
Instead of a brunch of property The TimeSpan will be mapped to the DayOfWeek using a Dictionary. This will remove the need for a switch-case or a lot of If.
Using DayOfWeek as dictionary key ensure that only one TimeSpan is defined for a day.
public class WorkingTimeScheldure
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public Dictionary<DayOfWeek, TimeSpan> Scheldure { get; set; }
}
That way I can ask for the TimeSpan of a day using : Scheldure[test.DayOfWeek]
Note the addition of an End property. It may be usefull to select the right thing.
eg: 30/01/2020 is superior to 01/12/2019 and 01/01/2020..
So in order to take the most recent, I assume the list in ordered on Start date and pick the last one: .Last(x => x.Start <= day).
Online Demo
public class Program
{
static List<ShouldWork> WorkTimeScheldure;
public static void Main()
{
WorkTimeScheldure = new List<ShouldWork>
{
new ShouldWork
{
Start = new DateTime(2019, 12, 01, 0, 0, 0),
Scheldure= new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(8,0,0)},
{(DayOfWeek)2, new TimeSpan(7,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(8,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
},
new ShouldWork
{
Start = new DateTime(2020, 01, 01, 0, 0, 0),
Scheldure = new Dictionary<DayOfWeek, TimeSpan>()
{
{(DayOfWeek)0, new TimeSpan(0,0,0)},
{(DayOfWeek)1, new TimeSpan(4,0,0)},
{(DayOfWeek)2, new TimeSpan(3,0,0)},
{(DayOfWeek)3, new TimeSpan(6,0,0)},
{(DayOfWeek)4, new TimeSpan(5,0,0)},
{(DayOfWeek)5, new TimeSpan(9,0,0)},
{(DayOfWeek)6, new TimeSpan(0,0,0)}
}
}
};
var testValues = new[] {
new DateTime(2019, 12, 01, 0, 0, 0),
new DateTime(2019, 12, 02, 0, 0, 0),
new DateTime(2019, 12, 03, 0, 0, 0),
new DateTime(2019, 12, 04, 0, 0, 0),
new DateTime(2019, 12, 05, 0, 0, 0),
new DateTime(2019, 12, 06, 0, 0, 0),
new DateTime(2019, 12, 07, 0, 0, 0),
new DateTime(2019, 12, 08, 0, 0, 0),
new DateTime(2020, 01, 01, 0, 0, 0),
new DateTime(2020, 01, 02, 0, 0, 0),
new DateTime(2020, 01, 03, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 05, 0, 0, 0),
new DateTime(2020, 01, 06, 0, 0, 0),
new DateTime(2020, 01, 07, 0, 0, 0),
new DateTime(2020, 01, 08, 0, 0, 0),
};
foreach (var test in testValues) {
// Perhaps there is many possible, so I took the Last.
var workingTime = WorkTimeScheldure.Last(x => x.Start <= day);
//Please handle the case where there is no matching scheludre for this date.
var houtToWork = workingTime.Scheldure[day.DayOfWeek].Hours;
Console.WriteLine(
$"{day.ToShortDateString()} , it's a {day.DayOfWeek}" +
$" I have to work {houtToWork} Hour{(houtToWork>1?"s":"")}!"
);
}
}
}
Result :
12/01/2019 , it's a Sunday I have to work 0 Hour!
12/02/2019 , it's a Monday I have to work 8 Hours!
12/03/2019 , it's a Tuesday I have to work 7 Hours!
12/04/2019 , it's a Wednesday I have to work 6 Hours!
12/05/2019 , it's a Thursday I have to work 5 Hours!
12/06/2019 , it's a Friday I have to work 8 Hours!
12/07/2019 , it's a Saturday I have to work 0 Hour!
12/08/2019 , it's a Sunday I have to work 0 Hour!
01/01/2020 , it's a Wednesday I have to work 6 Hours!
01/02/2020 , it's a Thursday I have to work 5 Hours!
01/03/2020 , it's a Friday I have to work 9 Hours!
01/04/2020 , it's a Saturday I have to work 0 Hour!
01/05/2020 , it's a Sunday I have to work 0 Hour!
01/06/2020 , it's a Monday I have to work 4 Hours!
01/07/2020 , it's a Tuesday I have to work 3 Hours!
01/08/2020 , it's a Wednesday I have to work 6 Hours!
The test times.Valid_from > day is false for all element in shouldWorkTime if the value of day is big enough. That why time can be incremented multiple time.
If you want to increment only once and on the first/last acceptable value of Valid_from, you should ensure that shouldWorkTime is sorted in increasing/decreasing order and ensure that the increment is done only once.
In fact you didn't need increment but just return the corresponding TimeSpan:
public TimeSpan GetHoursForDay(DateTime day) {
// shouldWorkTime should have been sorted once for all at creation.
// This code use the first acceptable Valid_from
// By using OrderByDescending we take the last (in date) entry
var math = shouldWorkTime
.Where(v => day >= v.Valid_from) // We take only valid entry
.OrderByDescending(v => v.Valid_from) // We sort only on valid entry
.FirstOrDefault(); // we take the last (in date) valid entry
if (match == null)
return TimeSpan.Zero;
switch (day.DayOfWeek)
{
case DayOfWeek.Monday:
return match.ShouldWorkMonday;
case DayOfWeek.Tuesday:
return match.ShouldWorkTuesday;
case DayOfWeek.Wednesday:
return match.ShouldWorkWednesday;
case DayOfWeek.Thursday:
return match.ShouldWorkThursday;
case DayOfWeek.Friday:
return match.ShouldWorkFriday;
case DayOfWeek.Saturday:
return match.ShouldWorkSaturday;
case DayOfWeek.Sunday:
return match.ShouldWorkSunday;
}
}
Edit:
To avoid code duplication, the ShouldWorkTime class may provide a GetWorkTimeForDayOfWeek:
public TimeSpan GetWorkTimeForDayOfWeek(DayOfWeek dayOfWeek) {
...
}
And instead of storing seven values in seven fields, you may take a look to collections. I will choose a Dictionary<DayOfWeek, TimeSpan>.
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))
};
}
I need to get the number of weeks between 2 dates.
A week for me is monday to sunday.
So if the first date is on a saturday than this week should be included.
if the second date is on a monday than this week should be included.
What is the most efficient way to do this ?
example :
startdate enddate nbr of weeks
17/09/2016 26/09/2016 3 weeks
17/09/2016 25/09/2016 2 weeks
19/09/2016 26/09/2016 2 weeks
12/09/2016 25/09/2016 2 weeks
I found much answers for this, like this one for example how to calculate number of weeks given 2 dates? but they all end up with dividing the days with 7 and that does not gives the result I need.
The simplest way is probably to write a method to get the start of a week. Then you can subtract one date from another, divide the number of days by 7 and add 1 (to make it inclusive).
Personally I'd use Noda Time for all of this, but using DateTime:
// Always uses Monday-to-Sunday weeks
public static DateTime GetStartOfWeek(DateTime input)
{
// Using +6 here leaves Monday as 0, Tuesday as 1 etc.
int dayOfWeek = (((int) input.DayOfWeek) + 6) % 7;
return input.Date.AddDays(-dayOfWeek);
}
public static int GetWeeks(DateTime start, DateTime end)
{
start = GetStartOfWeek(start);
end = GetStartOfWeek(end);
int days = (int) (end - start).TotalDays;
return (days / 7) + 1; // Adding 1 to be inclusive
}
Complete example:
using System;
class Program
{
static void Main (string[] args)
{
ShowWeeks(new DateTime(2016, 9, 17), new DateTime(2016, 9, 26));
ShowWeeks(new DateTime(2016, 9, 17), new DateTime(2016, 9, 25));
ShowWeeks(new DateTime(2016, 9, 19), new DateTime(2016, 9, 26));
ShowWeeks(new DateTime(2016, 9, 12), new DateTime(2016, 9, 25));
}
static void ShowWeeks(DateTime start, DateTime end)
{
int weeks = GetWeeks(start, end);
Console.WriteLine($"{start:d} {end:d} {weeks}");
}
// Always uses Monday-to-Sunday weeks
public static DateTime GetStartOfWeek(DateTime input)
{
// Using +6 here leaves Monday as 0, Tuesday as 1 etc.
int dayOfWeek = (((int) input.DayOfWeek) + 6) % 7;
return input.Date.AddDays(-dayOfWeek);
}
public static int GetWeeks(DateTime start, DateTime end)
{
start = GetStartOfWeek(start);
end = GetStartOfWeek(end);
int days = (int) (end - start).TotalDays;
return (days / 7) + 1; // Adding 1 to be inclusive
}
}
Output (in my UK locale):
17/09/2016 26/09/2016 3
17/09/2016 25/09/2016 2
19/09/2016 26/09/2016 2
12/09/2016 25/09/2016 2
A bit "simplified", because the Monday is needed just for the start date:
static int weeks(DateTime d1, DateTime d2) {
var daysSinceMonday = ((int)d1.DayOfWeek + 6) % 7;
return ((d2 - d1).Days + daysSinceMonday) / 7 + 1;
}
See my method below, I "strechted" the weeks to monday untill sunday, and then calculated the total days / 7
public static void Main(string[] args)
{
Console.WriteLine(CalculateWeeks(new DateTime(2016, 9, 17), new DateTime(2016, 9, 26)));
Console.WriteLine(CalculateWeeks(new DateTime(2016, 9, 17), new DateTime(2016, 9, 25)));
Console.WriteLine(CalculateWeeks(new DateTime(2016, 9, 19), new DateTime(2016, 9, 26)));
Console.WriteLine(CalculateWeeks(new DateTime(2016, 9, 12), new DateTime(2016, 9, 25)));
}
public static double CalculateWeeks(DateTime from, DateTime to)
{
if (to.DayOfWeek != DayOfWeek.Sunday)
to = to.Add(new TimeSpan(7- (int) to.DayOfWeek, 0, 0, 0)).Date;
return Math.Ceiling((to - from.Subtract(new TimeSpan((int)from.DayOfWeek - 1, 0, 0, 0)).Date).TotalDays / 7);
}
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";