Get DateTime of the next nth day of the month - c#

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;
}
}

Related

Datetime elapsed

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.

Find real date based on "DayOfWeek, Day/Month"

Context:
In a weird Csv import we have some miss formated Date that needs to some search. The Csv can't be fix, no control on it, we have to import it.
In Csv line :
"FooBarName ;;Mer 30/05 ;Kbr5 08h00-13h00 (-00h10) Kbr5 13h15-16h55;e 07h59 S 13h00 e 13h12 s 16h02;08h00-13h00 13h15-16h55;6:30;6:30;;6:30;;;;"
We have a date in a weird format: It look like a handcrafed R/r Format, that get cut to 12 char like if it was a dd/MM/yyyy.
"ddd dd/MM "
I know that the last date are recent . And are ordered.
Sample date:
new string[] {
"Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
"Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
"Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
}
How do I convert a string like "ddd dd/MM" into a List<DateTime>?
For now to find the date that can give this output I compute all date on a range and compare them to the said format.
CultureInfo fr = new CultureInfo("fr-FR");
var endDate = DateTime.Now;
var startDate = new DateTime(DateTime.Now.Year-15,1,1);
var allDates = Enumerable.Range(0, 1 + endDate.Subtract(startDate).Days)
.Select(x => startDate.AddDays(x))
.Select(x => new { key = x, strDay = x.ToString("ddd"), strOther = x.ToString(" dd/MM", fr) })
// French format for ddd has a dot at the end. Let's remove it.
.Select(x =>new { key = x.key, str = x.strDay.Remove(x.strDay.Length - 1) + x.strOther})
.ToArray();
foreach (var wDate in datesToFind)
{
var results = dates.Where(x => x.str == wDate);
// etc..
}
What happens to the year in the datetime? Do you have any specific year or do you try to find for example "In which year was the 1st of July a Sunday"? If the latter is the case that's not reliable as for example the 1st of July was on Sunday in 2018, 2012 etc. Can you do something like:
var dateStrings = new string[] {
"Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
"Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
"Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
};
var year = GetYear(dateStrings[0]);
var listDates = dateStrings
.Select(x => {
var input = x.Split(' ')[1].Split('/');
return new DateTime(year, Convert.ToInt32(input[1]), Convert.ToInt32(input[0]));
}).ToList();
Technically you need just one day to get the year (if you know that the list of days is from the same year):
private int GetYear(string dateString){
var year = DateTime.Today.Year;
var tempString = dateString.Split(' ');
var weekDay = tempString[0];
var monthDate = tempString[1].Split('/');
var month = Convert.ToInt32(monthDate[1]);
var date = Convert.ToInt32(monthDate[0]);
DayOfWeek dayOfWeek;
switch (weekDay) {
case "Lun":
dayOfWeek = DayOfWeek.Monday;
break;
case "Mar":
dayOfWeek = DayOfWeek.Tuesday;
break;
case "Mer":
dayOfWeek = DayOfWeek.Wednesday;
break;
case "Jeu":
dayOfWeek = DayOfWeek.Thursday;
break;
case "Ven":
dayOfWeek = DayOfWeek.Friday;
break;
case "Sam":
dayOfWeek = DayOfWeek.Saturday;
break;
default:
dayOfWeek = DayOfWeek.Sunday;
break;
}
while (year > 2003) {
var temp = new DateTime(year, month, date);
if (temp.DayOfWeek == dayOfWeek) {
return year;
}
year--;
}
return year;
}
I hope this helps
First, it looks like you need to handle the concept of a "day in year" (localized in the french culture). This concept of "day in year" is year independant and should have the capability to yield every possible DateTime which are valid (since a starting year ?).
You can come up with something like that to implement this concept:
sealed class FrenchDayInYear
{
private readonly string _dayOfYear;
private readonly DateTimeFormatInfo _fr;
public FrenchDayInYear(string dayOfYear)
{
_dayOfYear = dayOfYear;
_fr = new CultureInfo("fr-FR").DateTimeFormat;
_fr.AbbreviatedDayNames = new[] { "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim" };
}
public IReadOnlyList<DateTime> PossibleDates(int startYear)
{
return Enumerable.Range(startYear, DateTime.Now.Year - startYear)
.Select(WithYear)
.OfType<DateTime>()
.ToList();
}
private DateTime? WithYear(int year)
{
if (DateTime.TryParseExact(_dayOfYear + year, "ddd dd/MMyyyy", _fr, DateTimeStyles.None, out var result))
{
return result;
}
else
{
return null;
}
}
}
A few notes about this code:
I had to define custom AbbreviatedDayNames since the standard is expecting a dot a the end and yours don't contain it
I requested the startYear since you didn't explicitely stated at which year one should try to find a possible date. I also assumed that the maximal year is the current year
The trick to be able to parse an incomplete DateTime without getting exceptions is to defer the parsing until you have enough information (in this case in WithYear the missing year information is provided)
OfType filters out null values
I returned an IReadOnlyCollection in PossibleDates() to make clear that the result set has been computed and is finished
Possible usage:
var inputs = new string[]
{
"Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
"Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
"Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
};
var output = inputs.ToDictionary(input => input, input => new FrenchDayInYear(input).PossibleDates(2000));
foreach (var kv in output)
{
Console.WriteLine("{0}=[{1}]", kv.Key, string.Join(",", kv.Value));
}
The above code will yield dates from the year 2004 and 2010, the two possible years between 2000 and now (2018) where these DayInYear were possible.
I went about this in a manner very similar to Svetoslav Petrov, but instead of assuming that all of the given dates fall within the same year, I work through the set of date strings in reverse order and in each iteration of the loop determine the most recent year (a) that is equal to or earlier than the year of the most recently converted date and (b) in which the given day of the week is valid. This approach should be valid as long as there isn't a many-year gap between any two consecutive dates in the series, or between the last date in the series and the current date.
void Test()
{
var dateStrings = new string[] {
"Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
"Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
"Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
};
var parsedDates = ParseDateStrings(dateStrings);
foreach (var date in parsedDates)
Console.WriteLine(date);
}
// Takes a set of date strings in the format described by the question and returns
// the analogous set of DateTime objects. This method assumes that the supplied
// dates are in chronological order.
List<DateTime> ParseDateStrings(IEnumerable<string> dateStrings)
{
var year = DateTime.Today.Year;
var parsedDates = new List<DateTime>();
// Since we can't know at first how many years are represented in the given
// data set, we can't really make any assumptions about the year in which the
// data begins. Instead we assume that the most recent date occurs in either
// the current year or the latest previous year in which that date was valid,
// and work through the set backwards.
foreach (var dateString in dateStrings.Reverse())
{
var dayOfWeek = GetDayOfWeek(dateString.Substring(0, 3));
var day = int.Parse(dateString.Substring(4, 2));
var month = int.Parse(dateString.Substring(7, 2));
year = GetMostRecentValidYear(year, month, day, dayOfWeek);
parsedDates.Add(new DateTime(year, month, day));
}
// Reversing our output again at this point puts the results back into the
// same order as the inputs.
parsedDates.Reverse();
return parsedDates;
}
// Gets the appropriate DayOfWeek value for the given three-character abbreviation.
DayOfWeek GetDayOfWeek(string abbreviation)
{
switch (abbreviation.ToLower())
{
case "dim": return DayOfWeek.Sunday;
case "lun": return DayOfWeek.Monday;
case "mar": return DayOfWeek.Tuesday;
case "mer": return DayOfWeek.Wednesday;
case "jeu": return DayOfWeek.Thursday;
case "ven": return DayOfWeek.Friday;
case "sam": return DayOfWeek.Saturday;
default: throw new ArgumentException();
}
}
// Gets the latest year that is equal to or earlier than the given year, and in
// which the given day of the given month fell on the given day of the week.
int GetMostRecentValidYear(int year, int month, int day, DayOfWeek dayOfWeek)
{
while (!YearIsValid(year, month, day, dayOfWeek))
--year;
return year;
}
// Returns a flag indicating whether the given day of the given month fell on the
// given day of the week in the given year.
bool YearIsValid(int year, int month, int day, DayOfWeek dayOfWeek) =>
(month != 2 || day != 29 || IsLeapYear(year)) &&
new DateTime(year, month, day).DayOfWeek == dayOfWeek;
// Returns a flag indicating whether the given year was a leap year.
bool IsLeapYear(int year) =>
(year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
Output:
2016-06-15 00:00:00
2016-06-16 00:00:00
2016-06-17 00:00:00
2016-06-18 00:00:00
2016-06-19 00:00:00
2016-06-20 00:00:00
2016-06-21 00:00:00
2016-06-23 00:00:00
2016-06-24 00:00:00
2016-06-25 00:00:00
2016-06-26 00:00:00
2016-06-27 00:00:00
2016-06-28 00:00:00
2016-06-29 00:00:00
2016-06-30 00:00:00
2016-07-01 00:00:00
2016-07-02 00:00:00
2016-07-03 00:00:00
2016-07-04 00:00:00
2016-07-05 00:00:00
2016-07-06 00:00:00
Edit: I was looking at this again and spotted a bug in my original implementation of YearIsValid: trying to construct a DateTime for a February 29th in a non-leap year will cause the constructor to throw. I added a test for leap years to work around this problem. YearIsValid will still throw if you give it an input that is not valid in any year, like February 30th, but in that case an exception is the intended behavior.

Comparing dates in C#

I am a beginning programmer in C# and asp.net. I am busy trying to create a Hotel application. Where somebody is able to book for example an 8 day vacation. But now I need to add a formula that calculates the price. The method I am writing is getting the price per night of the room from the database. And the days that the person is staying is entered in the view and passed down to the controller.So I want to calculate the price inside the controller. But now I have a problem cause the price of staying in the hotel is higher in the high-season than in the low-season. So the prices differs per day. But now I do not really now how to compare the dates so I am able to give an accurate total price.
I have looked over some threads on stack overflow and they often advice to use Timespan to compare dates. But I was wondering is Timespan the best solution for me? Cause for my project the price should flow and not be fixed. For example it should not be like 28 May - 10 July is €120 euro per night but more like 28 May €109, 29 May €112, 30 May €113 - 9 July €127, 10 July 130.
If I would succeed in creating a different price per day then the last thing should not be that hard I hope. The price of each date should be added to each other so I will have the total price.
So my questions are:
Is the best way to compare dates Timespan?
Is there an easy way to calculate this? I would not like fixed dates.
Are there any good tutorials for this?
I would just compare each Date object between the start and end dates to see if it falls within a defined range to determine the rate, and sum them as I go.
This is probably overkill for you, but I would encapsulate the different 'seasons' and their rates in a class, and add a method to the class that will determine if a date falls within that 'season'. This will simplify the other methods.
Then I would create a method that, given a single date, will return the rate for that date.
Finally, I would calculate the total price by calling the GetRate() method for each day between the client's start date (inclusive) and end date (exclusive).
Here's a sample of how I would do it. First, the class to hold a 'season'
public class Season
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int Rate { get; set; }
public bool ContainsDate(DateTime date)
{
// Assumption: Year is ignored - seasons are considered
// to start and end on the same date each year
//
// Rules: (remember a season may start in Dec and end in Jan,
// so you cant just check if the date is greater than
// the start or less than the end!)
//
// 1. If the start and end month are the same,
// return true if the month is equal to start (or end) month
// AND the day is between start and end days.
// 2. If the date is in the same month as the start month,
// return true if the day is greater than or equal to start day.
// 3. If the date is in the same month as the end month,
// return true if the day is less than or equal to end day.
// 4. If the StartMonth is less than the EndMonth,
// return true if the month is between them.
// 5. Otherwise, return true if month is NOT between them.
if (StartDate.Month == EndDate.Month)
return date.Month == StartDate.Month &&
date.Day >= StartDate.Day &&
date.Day <= EndDate.Day;
if (date.Month == StartDate.Month)
return date.Day >= StartDate.Day;
if (date.Month == EndDate.Month)
return date.Day <= EndDate.Day;
if (StartDate.Month <= EndDate.Month)
return date.Month > StartDate.Month && date.Month < EndDate.Month;
return date.Month < EndDate.Month || date.Month > StartDate.Month;
}
}
Next, a method that will calculate the rate for a specific date:
public static int GetRate(DateTime date)
{
// Normally these 'seasons' and rates would not be hard coded here
const int offSeasonRate = 125;
var winterSeason = new Season
{
StartDate = DateTime.Parse("November 15"),
EndDate = DateTime.Parse("January 12"),
Rate = 150
};
var springSeason = new Season
{
StartDate = DateTime.Parse("May 20"),
EndDate = DateTime.Parse("June 15"),
Rate = 140
};
var summerSeason = new Season
{
StartDate = DateTime.Parse("July 10"),
EndDate = DateTime.Parse("August 31"),
Rate = 170
};
// Create a list of all the seasons
var seasons = new List<Season> {winterSeason, springSeason, summerSeason};
// Loop through all the seasons and see if this date is in one of them
foreach (var season in seasons)
{
if (season.ContainsDate(date))
{
// Note: depending on your implementation, Rate could be a multiplier
// in which case you would return (offSeasonRate * season.Rate);
return season.Rate;
}
}
// If we get this far, the date was not in a 'season'
return offSeasonRate;
}
Finally, here is the method that gets the total price for a date range:
var startDate = DateTime.Today;
var endDate = startDate.AddDays(2);
var price = 0;
// Sum the rates for each day between
// start date (inclusive) and end date (exclusive).
for (var curDate = startDate; curDate < endDate; curDate = curDate.AddDays(1))
{
price += GetRate(curDate);
}
Console.WriteLine("The total cost from {0} to {1} is: €{2}",
startDate, endDate, price);

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 can I determine if a date is the nth List<WeekDay> in a month?

I've seen this question, which looks for the Nth weekday in a specific month, but I want something that will look for the Nth [weekday list] in a given month. Is there an easy way to get that without looping through every day in the month?
For example, if I say I want the 3rd [Mon, Wed, Fri] in a month, and the month starts on a Wednesday, it should return the 1st Monday (1 = Wed, 2 = Fri, 3 = Mon). If I say I want the 2nd last [Mon, Tue, Wed, Thurs] and the last day of the month is a Sunday, it should return the previous Wednesday.
Meh I mean you might be able to come up with something ugly and incomprehensible that is formula-based and avoids looping, but it will be ugly and incomprehensible so bug-prone and a maintenance disaster, and not at all fun to try to come up with.
Instead, just come up with what is easy to write using loops, and then hide the loops by using LINQ:
static class DateTimeExtensions {
private static IEnumerable<DateTime> DaysOfMonth(int year, int month) {
return Enumerable.Range(1, DateTime.DaysInMonth(year, month))
.Select(day => new DateTime(year, month, day));
}
public static DateTime NthDayOfMonthFrom(
this HashSet<DayOfWeek> daysOfWeek,
int year,
int month,
int nth
) {
return
DateTimeExtensions.DaysOfMonth(year, month)
.Where(
date => daysOfWeek.Contains(date.DayOfWeek)
)
.Skip(nth - 1)
.First();
}
}
Usage:
var daysOfWeek = new HashSet<DayOfWeek> {
DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday
};
DateTime date = daysOfWeek.NthDayOfMonthFrom(2011, 6, 3));
Assert.Equal(date, new DateTime(2011, 6, 6));
Similarly, using Reverse, you can easily write NthLastDayOfWeekFrom. I'll leave it to you to come up with a better name.
I think I'm accepting Jason's answer because I like his solution, however here's what I had come up with before I saw his post. I thought I'd post it because I like the idea of not looping thorough every date, especially if N is a higher value and I'd like to get a date outside the current month.
Instead of looping through all days, I fast-forwarded (or rewind if I'm going backwards) to the closest week, then looped through the next couple of days to find the Nth instance the weekdays specified.
public static DateTime? GetNthWeekDayInMonth(DateTime month, int nth, List<DayOfWeek> weekdays)
{
var multiplier = (nth < 0 ? -1 : 1);
var day = new DateTime(month.Year, month.Month, 1);
// If we're looking backwards, start at end of month
if (multiplier == -1) day = day.AddMonths(1).AddDays(-1);
var dayCount = weekdays.Count;
if (dayCount == 0)
return null;
// Fast forward (or rewind) a few days to appropriate week
var weeks = ((nth - (1 * multiplier)) / dayCount);
day = day.AddDays(weeks * 7);
// Current Nth value
var currentNth = (1 * multiplier) + (weeks * dayCount);
// Loop through next week looking for Nth value
for (var x = 0; x <= 7; x++)
{
if (weekdays.Contains(day.DayOfWeek))
{
if (currentNth == nth)
{
// Verify the nth instance is still in the current month
if (day.Month == month.Month)
return day;
else
return null;
}
currentNth += multiplier;
}
day = day.AddDays(multiplier);
}
return null;
}

Categories

Resources