Datetime elapsed - c#

I have this quote:
var deadline=System.DateTime.Now;
deadline = new System.DateTime(deadline.Year, deadline.Month, deadline.Day, deadline.Hour+4, deadline.Minute, 0);
And I would like to transform it with a workday system.
The current input is 2022-02-15 14:25:00.000
The output is: 2022-02-15 18:25:00.000
A regular workday would be weekdays 08:00-16:00
So right now the output is elapsed by 02:25, and I would like to add for the next workday.
New output: 2022-02-16 10:25:00.000
I am not sure what condition should I need and how could I skip weekends too at once.
Thank you in advance,

There are several steps here.
First, although I assume your input is going to logically be inside a working day, I would not just add 4 to the hours. On the off chance that DateTime.Now returns a value that is closer to midnight than those 4 hours, it would throw an exception.
And if you want your solution to be general, as in you would be able to say "the deadline is 30 hours from now, only counting working hours", it wouldn't be able to work at all, even within a working day.
So, with that in mind, here's my proposal, expressed as pseudo-code
left (TimeSpan) = 4 # can be changed to whatever
timestamp = DateTime.Now
while left > 0
if timestamp > end of working day OR not even a working day at all
then:
set timestamp to start of next working day
else:
calculate remainder = how much is left of current working day from "timestamp"
if less than left
then:
add left to timestamp
return timestamp as deadline
else:
subtract remainder from left
set timestamp to start of next working day
Here is C# code that should accomplish this.
Some of the pieces are missing error handling, and can probably be optimized a lot.
void Main()
{
var start = new DateTime(2022, 2, 21, 11, 34, 0);
var deadline = GetDeadline(start, TimeSpan.FromHours(30));
Console.WriteLine($"{start} + 30h in working hours = {deadline}");
}
public static DateTime GetDeadline(DateTime timestamp, TimeSpan timeUntilDeadline)
{
if (timeUntilDeadline <= TimeSpan.Zero)
return timestamp;
(DateTime start, DateTime end) nextWorkingDay;
while (timeUntilDeadline > TimeSpan.Zero)
{
var workingDay = GetWorkingDay(timestamp.Date);
if (!workingDay.HasValue || timestamp >= workingDay.Value.end)
{
nextWorkingDay = GetNextWorkingDay(timestamp.Date);
timestamp = nextWorkingDay.start;
continue;
}
if (timestamp < workingDay.Value.start)
timestamp = workingDay.Value.start;
var remainder = workingDay.Value.end - timestamp;
if (remainder > timeUntilDeadline)
remainder = timeUntilDeadline;
timestamp += remainder;
timeUntilDeadline -= remainder;
}
return timestamp;
}
public static (DateTime start, DateTime end)? GetWorkingDay(DateTime date)
{
// todo: handle holidays and vacations?
switch (date.DayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
return (date + TimeSpan.FromHours(8), date + TimeSpan.FromHours(16));
default:
return null;
}
}
public static (DateTime start, DateTime end) GetNextWorkingDay(DateTime date)
{
while (true)
{
date = date.AddDays(1);
var workingDay = GetWorkingDay(date);
if (workingDay.HasValue)
return workingDay.Value;
}
}
Output:
21.02.2022 11:34:00 + 30h in working hours = 25.02.2022 09:34:00
The output is due to:
The start timestamp is Tuesday, 11:34
30 hours, only counting working days would first fill out Tuesday, consuming 4:26, giving us 25:34 left to process
Wednesday, Thursday, Friday, all consume 8 hours each, reducing the processing value down to 1:34
On Monday 25th, the deadline is thus 09:34
Corner-cases considered in the code:
If your starting-time is on a day without working hours (Saturday, Sunday), the next working day start is used instead
If your starting-time is after the end of a working-day (18:00 on a Monday for instance), the start of the next working day is again used
If your starting-time is before the start of a working-day (06:00 on a Monday for instance), the start of that working day is used
You can easily enhance the "get the working hours for this date" method to handle such things as holidays, vacations, or even non-typical days with, say, only 4 hours work, or late shifts, or whatever.

Related

Get DateTime of the next nth day of the month

If given a date and a variable n, how can I calculate the DateTime for which the day of the month will be the nth Date?
For example, Today is the 17th of June.
I would like a function that when provided 15 would return the DateTime for July 15.
A few more examples:
Today is Feb. 26: Function would return March 30 when provided 30.
Today is Dec. 28. Function would return Jan 4 when provided 4.
Today is Feb. 28. Function would return March 29 when provided 29, unless it was a leap year, in which case it would return Feb 29.
Why not just do?
private DateTime GetNextDate(DateTime dt, int DesiredDay)
{
if (DesiredDay >= 1 && DesiredDay <= 31)
{
do
{
dt = dt.AddDays(1);
} while (dt.Day != DesiredDay);
return dt.Date;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
After many, many edits, corrections and re-writes, here is my final answer:
The method that follows returns a a DateTime representing the next time the day of number day comes up in the calendar. It does so using an iterative approach, and is written in the form of an extension method for DateTime objects, and thus isn't bound to today's date but will work with any date.
The code executes the following steps to get the desired result:
Ensure that the day number provided is valid (greater than zero and smaller than 32).
Enter into a while loop that keeps going forever (until we break).
Check if cDate's month works (the day must not have passed, and the month must have enough days in it).
If so, return.
If not, increase the month by one, set the day to one, set includeToday to true so that the first day of the new month is included, and execute the loop again.
The code:
static DateTime GetNextDate3(this DateTime cDate, int day, bool includeToday = false)
{
// Make sure provided day is valid
if (day > 0 && day <= 31)
{
while (true)
{
// See if day has passed in current month or is not contained in it at all
if ((includeToday && day > cDate.Day || (includeToday && day >= cDate.Day)) && day <= DateTime.DaysInMonth(cDate.Year, cDate.Month))
{
// If so, break and return
break;
}
// Advance month by one and set day to one
// FIXED BUG HERE (note the order of the two calls)
cDate = cDate.AddDays(1 - cDate.Day).AddMonths(1);
// Set includeToday to true so that the first of every month is taken into account
includeToday = true;
}
// Return if the cDate's month contains day and it hasn't passed
return new DateTime(cDate.Year, cDate.Month, day);
}
// Day provided wasn't a valid one
throw new ArgumentOutOfRangeException("day", "Day isn't valid");
}
The spec is a little bit unclear about to do when today is the dayOfMonth. I assumed it was it to return the same. Otherwise it would just be to change to <= today.Day
public DateTime FindNextDate(int dayOfMonth, DateTime today)
{
var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
if(dayOfMonth < today.Day){
nextMonth = nextMonth.AddMonths(1);
}
while(nextMonth.AddDays(-1).Day < dayOfMonth){
nextMonth = nextMonth.AddMonths(1);
}
var month = nextMonth.AddMonths(-1);
return new DateTime(month.Year, month.Month, dayOfMonth);
}
Stumbled upon this thread today while trying to figure out this same problem.
From my testing, the following seems to work well and the loop only needs two goes (I think? Maybe 3 max(?)) to get to the answer:
public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
{
if (dayNum >= 1 && dayNum <= 31)
{
DateTime result = start;
while (result.Day != dayNum)
result = dayNum > result.Day ? result.AddDays(dayNum - result.Day) : new DateTime(result.Month == 12 ? result.Year + 1 : result.Year, (result.Month % 12) + 1, 1);
return result;
}
else
return DateTime.Today;
}
Edit: As requested, here's a less compact version that walks through the logic step by step. I've also updated the original code to account for a required year change when we reach December.
public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
{
// Check if target day is valid in the Gregorian calendar
if (dayNum >= 1 && dayNum <= 31)
{
// Declare a variable which will hold our result & temporary results
DateTime result = start;
// While the current result's day is not the desired day
while (result.Day != dayNum)
{
// If the desired day is greater than the current day
if (dayNum > result.Day)
{
// Add the difference to try and skip to the target day (if we can, if the current month does not have enough days, we will be pushed into the next month and repeat)
result = result.AddDays(dayNum - result.Day);
}
// Else, get the first day of the next month, then try the first step again (which should get us where we want to be)
else
{
// If the desired day is less than the current result day, it means our result day must be in the next month (it obviously can't be in the current)
// Get the next month by adding 1 to the current month mod 12 (so when we hit december, we go to january instead of trying to use a not real 13th month)
// If result.Month is November, 11%12 = 11; 11 + 1 = 12, which rolls us into December
// If result.Month is December, 12%12 = 0; 0 + 1 = 1, which rolls us into January
var month = (result.Month % 12) + 1;
// Get current/next year.
// Since we are adding 1 to the current month, we can assume if the previous month was 12 that we must be entering into January of next year
// Another way to do this would be to check if the new month is 1. It accomplishes the same thing but I chose 12 since it doesn't require an extra variable in the original code.
// Below can be translated as "If last result month is 12, use current year + 1, else, use current year"
var year = result.Month == 12 ? result.Year + 1 : result.Year;
// Set result to the start of the next month in the current/next year
result = new DateTime(year, month, 1);
}
}
// Return result
return result;
}
else
// If our desired day is invalid, just return Today. This can be an exception or something as well, just using Today fit my use case better.
return DateTime.Today;
}
Fun little puzzle. I generated 100 DateTimes which represent the starting day of each month, then checked each month to see if it had the date we want. It's lazy so we stop when we find a good one.
public DateTime FindNextDate(int dayOfMonth, DateTime today)
{
DateTime yesterday = today.AddDays(-1);
DateTime currentMonthStart = new DateTime(today.Year, today.Month, 1);
var query = Enumerable.Range(0, 100)
.Select(i => currentMonthStart.AddMonths(i))
.Select(monthStart => MakeDateOrDefault(
monthStart.Year, monthStart.Month, dayOfMonth,
yesterday)
.Where(date => today <= date)
.Take(1);
List<DateTime> results = query.ToList();
if (!results.Any())
{
throw new ArgumentOutOfRangeException(nameof(dayOfMonth))
}
return results.Single();
}
public DateTime MakeDateOrDefault(
int year, int month, int dayOfMonth,
DateTime defaultDate)
{
try
{
return new DateTime(year, month, dayOfMonth);
}
catch
{
return defaultDate;
}
}

How to set a timer for Servicetimer.interval set to every 15th day and the last day of the month c#

Here is the code: Need to modify this to run the service every 15th day and last day of every month
this.serviceTimer.Interval = 300000; // 5 mins
this.serviceTimer.Elapsed += new ElapsedEventHandler (this.serviceTimer_Click);
this.serviceTimer.Start();
Logger.WriteEventLog("Service Started");
Just small Business logic to be done as shown below :
Set time interval for every 24 hours
timer.Interval = 60000*60*24;
Then check for current month and get number of days in current month so you will get total number of days then divide total number of days by 2 so that you have 2 dates
1) last day of month ie total no of days in month
2) middle date of month ie divided by 2
check for current date with these 2 days if it is equal then go for timeElapsed event
{
this.serviceTimer.Elapsed += new ElapsedEventHandler (this.serviceTimer_Click);
}
else
{
do nothing
}
In your serviceTimer_Click
{
...
DateTime nextExecute = DateTime.Now.AddMOnth(1);
timer.Stop();
timer.Interval = (nextExecute - DatTime.Now).TotalMilliseconds;
timer.Start();
}
And to start it:
{
...
DateTime now = DateTime.Now;
DateTime firstExecute = new DateTime( now.Year, now.Month, 15 ); //add time if needed...
if ( firstExecute < now )
{
firstExecute.AddMonth( 1 );
}
timer.Interval = (firstExecute - now).TotalMilliseconds;
}
Not compiled, but you get the drift...
Edit
Speaking about drift: to avoid your execution drifting to a later point in time, you could use a more clever way to build the nextExecute DateTime.

How to get date after N months with same day and same week of a given date

I am looking for some logic to get the date after N months having same day(Ex:Wednesday) and same week(ex: first or second...) of a given date.
ex: 12-06-2013(Wednesday & 3rd week of June) is the given date.
here I am adding 3 months to the given date.
the result should be is 14-Aug-2013(Wednesday & 3rd week of Aug).
please let me know if you need more clarification.
Thanks In advance.
Okay, so I'd personally use my Noda Time library to do this. It's entirely possible to do this with DateTime, but I'd personally find it harder. I'd also encourage you to use Noda Time in general, of course, as a better date/time API. So I'd have something like:
static LocalDate AddMonthsPreserveWeekDayAndWeek(LocalDate start, int months)
{
// This isn't the week of month in the "normal" sense; it's the nth
// occurrence of this weekday.
int week = ((start.DayOfMonth - 1) / 7) + 1;
// This will usually give the same day of month, but truncating where
// necessary
LocalDate monthsAdded = start.AddMonths(months);
LocalDate endOfPreviousMonth = monthsAdded.AddDays(-monthsAdded.Day);
// Get to the first occurrence of the right day-of-week
LocalDate firstRightDay = endOfPreviousMonth.Next(start.IsoDayOfWeek);
// Usually this will be right - but it might overflow to the next month,
// in which case we can just rewind by a week.
LocalDate candidate = firstRightDay.PlusWeeks(week - 1);
return candidate.Month == firstRightDay.Month ? candidate
: candidate.PlusWeeks(-1);
}
This is completely untested though - you should absolutely have a bunch of unit tests (ideally which you write before even including this code) which test all kinds of edge cases you're interested in.
Using standard MDSN year = 2013 month = 06 date = 12
1) Get day of the week from the specific date (Sunday is 0)
DateTime dateValue = new DateTime(year, month, date);
Console.WriteLine((int) dateValue.DayOfWeek); // Displays 3 implying it is Wed
2) Get the week of the month from the specific date
DayofWeek = 3 (from previous calculation)
Day = 12
EndOfWeek = Day + (6 - DayOfWeek) = 12 + 4 = 16
NoWeek = 0
while (EndOfWeek > 0)
{
EndOfWeek -= 7;
NoWeek++;
}
=> NoWeek = 3
3) Get first date after N month
DateTime newDate = new DateTime(year, month, 1)
newDate.AddMonths(N); // Let it be 2 => August 1, 2013
4) Get the day of the week for the new date
newDay = newDate.DayOfWeek // Return 4 implying Thursday
5) Get the last day after NoWeek
newDate.AddDays(6-newDay) => newDate.AddDays (6-4) => August 3,2013
NoWeek--;
while (NoWeek > 1)
{
newDate.AddDays(7);
NoWeek--;
}
=> newDate will be Augus 10,2013
6) Calculte required date
newDate.AddDays(DayofWeek) =>newDate will be August 14,2013

how to get no of days and nights between two dates using c#

i am doing a project on cab services.in this rate is different for day and night.
in the form only journey start date and end date is selected.based on this i have to calculate the no of days and nights.
here i am confused how to calculate the no of days and night.
thanks in advance.
private List<DateTime> GetDateRange(DateTime StartingDate, DateTime EndingDate)
{
if (StartingDate > EndingDate)
{
return null;
}
List<DateTime> rv = new List<DateTime>();
DateTime tmpDate = StartingDate;
do
{
rv.Add(tmpDate);
tmpDate = tmpDate.AddDays(1);
} while (tmpDate <= EndingDate);
return rv;
}
To view this code in action, copy and paste the following code into SnippetCompiler:
DateTime StartingDate = DateTime.Parse("02/25/2007");
DateTime EndingDate = DateTime.Parse("03/06/2007");
foreach (DateTime date in GetDateRange(StartingDate,EndingDate))
{
WL(date.ToShortDateString());
}
Sample output :
2/25/2007
2/26/2007
2/27/2007
2/28/2007
3/1/2007
3/2/2007
3/3/2007
3/4/2007
3/5/2007
3/6/2007
Use the Subtract method to get the difference, which is a TimeSpan value. Example:
TimeSpan diff = SecondDate.Subtract(FirstDate);
You can get the length of the time span for example in hours:
double hours = diff.TotalHours;
I'm not sure which time unit "days and nights" could be interpreted as, though. Perhaps days?
double days = diff.TotalDays;
DateTime dt1,dt2;
//...
TimeSpan period = dt1 - dt2;
int days = period.Days;
It sounds like a very long Cab journey that takes days and nights!
I think you need to define what a day and a night is more clearly in order to get your perfect answer. You also need to think about what impact Daylight Saving Time has on your calculations.
If say:
a day was the period from 6am to 6pm
the night was the rest - from 6pm to 6am
and you wanted to really count hours rather than days
In this case then a calculation would require you to:
iterate a currentDateTime from the startDateTime to the endDateTime
choose the increment in the currentDateTime so that it jumps to the next time barrier (6am, 6pm or the endDateTime)
within each loop, then add to your cumulative calculation of numDayHours or numNightHours so far.
Note that:
you could make this calculation quicker by counting whole days along the way
you need to be very careful about the time zone you are calculating in (I just hope that your taxi doesn't cross time zone boundaries!)
you need to be very careful about local time changes - especially "daylight savings time" type changes - the duration from 6pm to 6am is not always 12 hours!
Some pseudo code:
var numDayHours = 0.0;
var numNightHours = 0.0;
var current = startDateTime;
while (current < endDateTime)
{
next_hop = calculate_next_hop (current, endDateTime);
// select next date time
switch (next_hop.hop_type)
{
case HopType.night_time_hop:
numNightHours += next_hop.num_hours;
break;
case HopType.day_time_hop:
numDayHours += next_hop.num_hours;
break;
}
current = next_hop.EndDateTime;
}
// and here is the result
double numDays = numDayHours / 12.0;
double numHours = numNightHours / 12.0;

Is there a more efficient way to get the previous Monday for a given date in C#

So I have an application that needs to get a date focus so it can run appropriately. Given a particular date to focus on it needs to know what week it is in. I'm calculating weeks based on Monday dates. And I'm wondering if my focus on Mondays is excessive.
public static DateTime PreviousMonday(this DateTime dt)
{
var dateDayOfWeek = (int)dt.DayOfWeek;
if (dateDayOfWeek==0)
{
dateDayOfWeek = dateDayOfWeek + 7;
}
var alterNumber = dateDayOfWeek - ((dateDayOfWeek*2)-1);
return dt.AddDays(alterNumber);
}
/// <summary>
/// Personal tax week starts on the first Monday after the week with 6th April in unless 6th April is a Monday in
/// which case that starts the first week. In a leap year this means you can have a week 53 which due to the mod 4 approach of calculating
/// flexi week means you get a 5 week flexi period.
/// As such this method forces the weeks into the range 1 - 52 by finding the week number for the week containing 6th April and
/// the number for the current week. Treating the 6th April week as week 1 and using the difference to calculate the tax week.
/// </summary>
public static int GetTaxWeek(this DateTime dt)
{
var startTaxYear = GetActualWeekNumber(new DateTime(DateTime.Now.Year, 4, 6));
var thisWeekNumber = GetActualWeekNumber(dt);
var difference = thisWeekNumber - startTaxYear;
return difference < 0 ? 53 + difference : difference + 1;
}
private static int GetActualWeekNumber(DateTime dt)
{
var ci = System.Threading.Thread.CurrentThread.CurrentCulture;
var cal = ci.Calendar;
var calWeekRule = ci.DateTimeFormat.CalendarWeekRule;
var fDoW = ci.DateTimeFormat.FirstDayOfWeek;
return cal.GetWeekOfYear(dt, calWeekRule, fDoW);
}
public static int PeriodWeek(this DateTime dt)
{
var rawPeriodWeek = GetTaxWeek(dt) % 4;
return rawPeriodWeek == 3 ? 1 : rawPeriodWeek + 2;
}
}
The system runs a rolling 4 week schedule starting in the first tax week and needs to behave differently depending on where in the schedule it is. So you can see...
Get a date from a user (say userDate)
Call userDate=userDate.PreviousMonday();
to get to the Monday of the week
given - where Sunday is the week end
Call userDate.PeriodWeek(); and get
the Period you are in from 1 to 4.
GetTaxWeek is public because it is used elsewhere... I also replace the date as it is used more than once and I don't want to have to remember to change it more than once.
Can I see the wood for the trees? Or is there a more error free way of doing this.
I think you can greatly simplify your code using the GregorianCalendar inside System.Globalization. Here you can get the week number for a given date like this:
GregorianCalendar gc = new GregorianCalendar();
int weekno = gc.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
You see here that you can give the rules for how to caclulate the week number according to your local rules. Like here in Norway, we have Monday as our first week day, and the first week of the year is the first week that has four or more days. Set this to your culture specific rules to get the correct week numbers.
Some of your specific handling you still ahve to do by hand, but some of the clutter can be removed using this at least :)
why are you not using a DateTimePicker control? it will tell you the day for the user selected date. Then you can simply subtract no. of days from it to get date for monday. For example:
I'm using a DateTimePicker control and named it dtpTemp. the event used is
dtpTemp_ValueChanged()
dtpTemp.Value.DayOfWeek - will give you the day: tuesday, wednesday, thursday etc.
then you can use following code with switch case accordingly:
dtpTemp.Value.AddDays(num); to get date for monday
here num will have -ve values which will depend on day calculated above. Values: -1 for tuesday, -2 for wednesday, -3 for thursday and so on.
plus, using a datetimepicker will also have a positive impact on the UI itself.

Categories

Resources