Instead of using:
int noOfDaysInMonth = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
I want to use the 2 values passed in to get number of days in a month:
public ActionResult Index(int? month, int? year)
{
DateTime Month = System.Convert.ToDateTime(month);
DateTime Year = System.Convert.ToDateTime(year);
int noOfDaysInMonth = DateTime.DaysInMonth(Year, Month);
(Year, Month) is flagging as invalid arguments? any ideas? maybe system.conert.todatetime.month?
They are DateTime variables but DaysInMonth needs ints:
int noOfDaysInMonth = DateTime.DaysInMonth(year.Value, month.Value);
If they can be null:
int noOfDaysInMonth = -1;
if(year != null && month != null)
noOfDaysInMonth = DateTime.DaysInMonth(year.Value, month.Value);
There is no overload for the DateTime.DaysInMonth method that takes two DateTime instances. Instead of creating those two DateTime instances, just pass the parameters you received directly to DaysInMonth.
Notice that method can't take null values, so either drop the nullables or sanitize your input, i.e.: check if the year and month are null, and if they are, use some default value instead.
DateTime.DaysInMonth takes int parameter and not date time parameter
public static int DaysInMonth(
int year,
int month
)
Beware though, you are passing in nullable int's. So check before if they have value
if(month.HasValue && year.HasValue)
{
var numOfDays = DaysInMonth(year.Value, month.Value);
}
You don't need to use any DateTime object here, but you need to validate the input!
public ActionResult Index(int? month, int? year)
{
int noOfDaysInMonth = -1;
if(year.HasValue && year.Value > 0 &&
month.HasValue && month.Value > 0 && month.Value <=12)
{
noOfDaysInMonth = DateTime.DaysInMonth(year.Value, month.Value);
}
else
{
// parameters weren't there or they had wrong values
// i.e. month = 15 or year = -5 ... nope!
noOfDaysInMonth = -1; // not as redundant as it seems...
}
// rest of code.
}
The if works because conditions are evaluated from left to right.
Related
I am trying to find the nth DayOfWeek for a given month (in a given year).
For example: I am looking for the 3rd Saturday of May (2019).
I failed to come up with a working solution using the DayOfWeek extension method. Do I have to loop through the entire month to find the third Saturday?
You could of course loop through the entire month but I think this is a more elegant way (taken from here):
private static DateTime FindTheNthDayOfWeek(int year, int month, int nthOccurrence, DayOfWeek dayOfWeek)
{
if (month < 1 || month > 12)
{
throw new ArgumentOutOfRangeException("Invalid month");
}
if (nthOccurrence < 0 || nthOccurrence > 5)
{
throw new ArgumentOutOfRangeException("Invalid nth occurrence");
}
var dt = new DateTime(year, month, 1);
while (dt.DayOfWeek != dayOfWeek)
{
dt = dt.AddDays(1);
}
dt = dt.AddDays((nthOccurrence - 1) * 7);
if (dt.Month != month)
{
throw new ArgumentOutOfRangeException(string.Format("The given month has less than {0} {1}s", nthOccurrence, dayOfWeek));
}
return dt;
}
This private method doesn't loop through the entire month but stops already once the first DayOfWeek has been found. Then you simply add a week for each nth occurrence (minus the already added week ;-) ).
If you talk about dates it should be related to some calendar, in my example Gregorian.
public static class DataTimeExt
{
public static IEnumerable<DateTime> TakeWhileInclusive(this DateTime value,
Func<DateTime, bool> func)
{
DateTime dt = value;
yield return dt; //[first
while (func(dt = dt.AddDays(1))) yield return dt; //in between
yield return dt; //last]
}
}
then you could just iterate through the dates until Sunday and then add 14 days.
var calendar = new GregorianCalendar();
var dates = new DateTime(2019, 5, 1, calendar)
.TakeWhileInclusive(dt => calendar.GetDayOfWeek(dt) != DayOfWeek.Sunday);
Console.WriteLine(dates.Last().AddDays(14));
This is simple and clean with no looping. Just a little arithmetic.
static DateTime? NthWeekDayOfMonth( int n, DayOfWeek dow, int year , int month)
{
DateTime startOfMonth = new DateTime( year, month, 1 ) ;
int offset = ( 7 + dow - startOfMonth.DayOfWeek ) % 7 ;
DateTime nthWeekDayOfMonth = startOfMonth
.AddDays( offset )
.AddDays( 7 * (n-1) )
;
bool isSameMonth = startOfMonth.Year == nthWeekDayOfMonth.Year
&& startOfMonth.Month == nthWeekDayOfMonth.Month
;
return isSameMonth
? nthWeekDayOfMonth
: (DateTime?) null
;
}
I created two extension methods, where one gets the next DayOfWeek from the date, optionally including the date itself, and the other for the previous DayOfWeek, with the same functionality.
public static DateTime Next(
this DateTime source,
DayOfWeek dayOfWeek,
bool considerSameDate
) => ( dayOfWeek - source.DayOfWeek) is var difference
&& difference < (considerSameDate ? 0 : 1)
? source.AddDays(difference + 7)
: source.AddDays(difference)
;
and
public static DateTime Previous(
this DateTime source,
DayOfWeek dayOfWeek,
bool considerSameDate
) => dayOfWeek == source.DayOfWeek
? ( considerSameDate ? source : source.AddDays(-7) )
: source.AddDays(
( dayOfWeek - source.DayOfWeek ) is var difference
&& difference > 0
? difference - 7
: difference
);
Having these, one can ask the questions you posed:
var x = new System.DateTime(2019, 5, 1).Next(System.DayOfWeek.Saturday, true).AddDays(14);
I create a new DateTime (2019-05-01), call Next with Saturday and consider 5/1 as a candidate, and then add 14 days, which makes it to the third Saturday of May, 2019.
How to calculate actual working days of my when user checkin in hotel? I want to count working days only except Saturday and Sunday. Please check below function its count working days but in parameter I entered startdate and enddate.
I want send only startdate its automatically count 15 working days and return me enddate.
//Days count
public static double GetBusinessDays(DateTime startD, DateTime endD)
{
double calcBusinessDays =
1 + ((endD - startD).TotalDays * 5 -
(startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;
if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--;
if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--;
return calcBusinessDays;
}
I want like this:
public static Datetime GetBusinessDays(DateTime startDate)
{
Datetime After15WorkingDaysDate;
return After15WorkingDaysDate;
}
Here are two methods.
The idea is to generate each date in the range, decide whether it is a Business Day, and only then add it to the result list.
GetBusinessDaysInRange returns a list of the dates of the Business Days between the given start and end date. End date is exclusive, i.e. if the end date is a Business Day, it will not be part of the result.
// Returns a list of the dates of the Business Days between the given start and end date
public static IEnumerable<DateTime> GetBusinessDaysInRange(DateTime startDate, DateTime endDate, DayOfWeek[] closedOn) {
if (endDate < startDate) {
throw new ArgumentException("endDate must be before startDate");
}
var businessDays = new List<DateTime>();
var date = startDate;
while (date < endDate) {
if (!closedOn.Contains(date.DayOfWeek)) {
businessDays.Add(date);
}
date = date.AddDays(1);
}
return businessDays;
}
GetFixedNumberOfBusinessDays returns a list of the dates of the Business Days from the given start with the given number of days (the method you asked for).
// Returns a list of the dates of the Business Days from the given start with the given number of days
public static IEnumerable<DateTime> GetFixedNumberOfBusinessDays(DateTime startDate, int numberOfBusinessDays, DayOfWeek[] closedOn) {
if (numberOfBusinessDays < 0) {
throw new ArgumentException("numberOfBusinessDays must be zero or positive.");
}
var businessDays = new List<DateTime>();
var date = startDate;
while (businessDays.Count() < numberOfBusinessDays) {
if (!closedOn.Contains(date.DayOfWeek)) {
businessDays.Add(date);
}
date = date.AddDays(1);
}
return businessDays;
}
The parameter DayOfWeek[] closedOn was introduced because you do not want to hardcode the days of the week that are not Business Days.
The return type was changed to IEnumerable<DateTime> so this method is more universal. If you only want the number of days and are not interested in the actual dates, just run a .Count() on the result. If you want the end date, call .Last().
.Net Fiddle with usage examples:
var closedOn = new DayOfWeek[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
var start = new DateTime(2018, 07, 23);
var numberOfDays = 10;
var businessDays = GetFixedNumberOfBusinessDays(end, numberOfDays, closedOn);
int actualNumberOfBusinessDays = businessDays.Count(); // 10
DateTime endDate = businessDays.Last(); // Friday, August 3, 2018
It should be generic method. You can add different work day in another place.
public static DateTime AddWorkdays(this DateTime originalDate, int workDays)
{
DateTime tmpDate = originalDate;
while (workDays > 0)
{
tmpDate = tmpDate.AddDays(1);
if (tmpDate.DayOfWeek == DayOfWeek.Saturday ||
tmpDate.DayOfWeek == DayOfWeek.Sunday )
workDays--;
}
return tmpDate;
}
DateTime endDate = startDate.AddWorkdays(15);
How can I get the date of for example 'first wednesday of april 2013' using c# .net 2.0 ?
is there any helper methods for this kind of job in .net or should I write my own helper method? If there is no method for this kind of job please help me out for writing my own method.
DateTime GetFirstXDayFromY(string dayName, DateTime targetYearMonth)
{
///???
}
public static DateTime GetFirstDay(int year, int month, DayOfWeek day)
{
DateTime result = new DateTime(year, month, 1);
while (result.DayOfWeek != day)
{
result = result.AddDays(1);
}
return result;
}
If you were on .net >= 3.5 you could use Linq:
public static DateTime GetFirstDay(int year, int month, DayOfWeek dayOfWeek)
{
return Enumerable.Range(1, 7).
Select(day => new DateTime(year, month, day)).
First(dateTime => (dateTime.DayOfWeek == dayOfWeek));
}
The .NET Framework makes it easy to determine the ordinal day of the week for a particular date, and to display the localized weekday name for a particular date.
http://msdn.microsoft.com/en-us/library/bb762911.aspx
Please try with the below code snippet.
// Get the Nth day of the month
private static DateTime NthOf(DateTime CurDate, int Occurrence, DayOfWeek Day)
{
var fday = new DateTime(CurDate.Year, CurDate.Month, 1);
if (Occurrence == 1)
{
for (int i = 0; i < 7; i++)
{
if (fday.DayOfWeek == Day)
{
return fday;
}
else
{
fday = fday.AddDays(1);
}
}
return fday;
}
else
{
var fOc = fday.DayOfWeek == Day ? fday : fday.AddDays(Day - fday.DayOfWeek);
if (fOc.Month < CurDate.Month) Occurrence = Occurrence + 1;
return fOc.AddDays(7 * (Occurrence - 1));
}
}
How to call/use them?
NthOf(targetYearMonth, 1, DayOfWeek.Wednesday)
With the help of answers of #vc and #Jayesh I've come up with this method. Thanks a lot.
public static DateTime GetFirstDay(int year, int month, DayOfWeek day, int occurance)
{
DateTime result = new DateTime(year, month, 1);
int i = 0;
while (result.DayOfWeek != day || occurance != i)
{
result = result.AddDays(1);
if((result.DayOfWeek == day))
i++;
}
return result;
}
I have a what seems like simple date issue and I just can't wrap my head around trying to get it efficiently... I basically need to get the previous months date for a specific day.
For example: If today is the 3rd Thursday of the month, I want to get the 3rd Thursday's date of last month. Its important that its based of the number of the day...ie: First Monday, 4th Friday, 2nd Wednesday, etc.
What's the best way to get this done?
BTW...If there is not an equivalent previous months day that is fine. I can handle that. Also, currently I am counting the number or days ("Mondays", "Tuesdays", etc) manually to figure this out. I was just hoping there is a more elegant way to do it.
Here's what I would do:
static DateTime? GetLastMonthSameNthDayOfWeek(DateTime date)
{
int nth = (date.Day-1) / 7; // returns 0 if 1st, 1 if 2nd...
var prevMonthDay = date.AddMonths(-1);
// find the first date of month having the same day of week
var d = new DateTime(prevMonthDay.Year, prevMonthDay.Month, 1);
while(d.Day <= 7)
{
if (d.DayOfWeek == date.DayOfWeek)
break;
d = d.AddDays(1);
}
// go to nth day of week
d = d.AddDays(7 * nth);
// if we have passed the current month, there's no nth day of week
if (d.Month != prevMonthDay.Month)
return null;
return d;
}
Usage example:
// 3rd wednesday of August 2012
var a = new DateTime(2012, 8, 15);
var aPrev = GetLastMonthSameNthDayOfWeek(a);
// aPrev = July 18th 2012 (i.e. the 3rd wednesday of July 2012)
// 5th wednesday of August 2012
var b = new DateTime(2012, 8, 15);
var bPrev = GetLastMonthSameNthDayOfWeek(b);
// bPrev = null, because there's no 5th wednesday of July 2012
N.B. :
getting the ordinal position of the day of week inside a month is really easy:
int nth = ((date.Day-1) / 7) + 1; // 1 -> 1st, 2 -> 2nd, 3 -> 3rd ...
As I couldn't find a built-in way, I've written this simple extension method for DateTime, check it out:
public static class DateTimeExtension
{
public static DateTime GetPositionalDate(this DateTime BaseDate, DayOfWeek WeekDay, int position)
{
if (position < 1)
{
throw new Exception("Invalid position");
}
else
{
DateTime ReturnDate = new DateTime(BaseDate.Year, BaseDate.Month, BaseDate.Day);
int PositionControl = 1;
bool FoundDate = false;
while(ReturnDate.DayOfWeek != WeekDay)
{
ReturnDate = ReturnDate.AddDays(1);
}
while (!FoundDate && PositionControl <= position)
{
PositionControl++;
if (PositionControl == position)
{
FoundDate = true;
}
else
{
ReturnDate = ReturnDate.AddDays(7);
}
}
if (FoundDate)
{
return ReturnDate;
}
else
{
throw new Exception("Date not found");
}
}
}
}
Usage:
DateTime lastMonth = DateTime.Now.GetPositionalDate(DayOfWeek.Sunday, 2);
Regards
There is no, by default, a way that .Net understand this specific logic for dates. So using the following, you can get what you're looking for:
var now = DateTime.Now.Date;
Use DateTime.AddMonth(-1) to get last month.
Use DateTime.AddDays(now.Days * -1 + 1) to get the first of the month.
Use DateTime.DayOfWeek to determine the day and subtract or add days as necessary
Ok, what you can do is determine the day of the week of the first day of the month last month, take the difference between the day of the week you want and that day of the week, then add 7 * the weeks you want (less one week)...
// Let's get the 3rd Friday of last month:
// get starting date
DateTime date = new DateTime();
// get first day of last month
DateTime firstOfLastMonth = date.AddMonths(-1).AddDays(-1 * (date.Day + 1));
// subtract out the day of the week (get the previous Sunday, even if it is last month)
DateTime justBeforeMonth = firstOfLastMonth.AddDays((int)firstOfLastMonth.DayOfWeek);
// Add in the DayOfWeek number we are looking for
DateTime firstFridayOfMonth = justBeforeMonth.AddDays(DayOfWeek.Friday);
// if we are still in last month, add a week to get into this month
if (firstFridayOfMonth.Month != date.AddMonth(-1).Month) { firstFridayOfMonth.AddDays(7); }
// add in 2 weeks to get the third week of the month
DateTime thirdFridayOfMonth = firstFridayOfMonth.AddDays(14);
Here's the solution I came up with. If the day doesn't exist (e.g. 8th Saturday), GetDate() will return null:
{
DateTime lastMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(-1);
DateTime? date = GetDate(lastMonth.Month, lastMonth.Year, DayOfWeek.Thursday, 2);
}
private static DateTime? GetDate(int month, int year, DayOfWeek dayOfWeek, int which)
{
DateTime firstOfMonth = new DateTime(year, month, 1);
DateTime date;
for (date = firstOfMonth; date.DayOfWeek != dayOfWeek; date = date.AddDays(1))
;
date = date.AddDays(7 * (which - 1));
return date.Month == month && date.Year == year ? (DateTime?)date : null;
}
In my system ,the due date of the bill must be 14 days after the issued date.
I have due date and I want to know issued date .
I have to calculate :
issued date = 14 days prior to the due date
but 14 days must be business days ,not holidays.
Holidays is stored in a table 'tblHolidayMaster' like this,
Date Description
2012/05/13 Mother's
Day2012/06/02 Saturnday2012/12/25 Christmas
How can I calculate the issued date avoiding holidays?
Thank you for all of your interests and replies.
I would calculate the Date using a function like the one below (which i use)
public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
int extraDaysForHolidays =-1;
//Load holidays from DB into list
List<DateTime> dates = GetHolidays();
while(extraDaysForHolidays !=0)
{
var days = dates.Where(x => x >= date && x <= date.AddDays(extraDays)).Count;
extraDaysForHolidays =days;
extraDays+=days;
}
return date.AddDays(extraDays);
}
Haven't tested the ast section that does the holidays
I went with the straight forward looping solution, so it will be slow for long intervals. But for short intervals like 14 days, it should be quite fast.
You need to pass in the holidays in the constructor. An instance of BusinessDays is immutable and can be reused. In practice you probably will use an IoC singleton or a similar construct to get it.
AddBusinessDays throws an ArgumentException if the start date is a non business day, since you didn't specify how to treat that case. In particular AddBusinessDays(0) on a non business day would have strange properties otherwise. It'd either break time reversal symmetry, or return a non business day.
public class BusinessDays
{
private HashSet<DateTime> holidaySet;
public ReadOnlyCollection<DayOfWeek> WeekendDays{get; private set;}
public BusinessDays(IEnumerable<DateTime> holidays, IEnumerable<DayOfWeek> weekendDays)
{
WeekendDays = new ReadOnlyCollection<DayOfWeek>(weekendDays.Distinct().OrderBy(x=>x).ToArray());
if(holidays.Any(d => d != d.Date))
throw new ArgumentException("holidays", "A date must have time set to midnight");
holidaySet = new HashSet<DateTime>(holidays);
}
public BusinessDays(IEnumerable<DateTime> holidays)
:this(holidays, new[]{DayOfWeek.Saturday, DayOfWeek.Sunday})
{
}
public bool IsWeekend(DayOfWeek dayOfWeek)
{
return WeekendDays.Contains(dayOfWeek);
}
public bool IsWeekend(DateTime date)
{
return IsWeekend(date.DayOfWeek);
}
public bool IsHoliday(DateTime date)
{
return holidaySet.Contains(date.Date);
}
public bool IsBusinessDay(DateTime date)
{
return !IsWeekend(date) && !IsHoliday(date);
}
public DateTime AddBusinessDays(DateTime date, int days)
{
if(!IsBusinessDay(date))
throw new ArgumentException("date", "date bust be a business day");
int sign = Math.Sign(days);
while(days != 0)
{
do
{
date.AddDays(sign);
} while(!IsBusinessDay(date));
days -= sign;
}
return date;
}
}
I think that is what you required. It is simple and I have tested it and it is working... And it is not a bad approach to write a function or SP in databases rather to write the complex code in C#... (change column name of date as in your db.)
Make it function or SP as what you want.
Note: Comment the check of 'Saturday' and 'Sunday'. If it is already added in your table reocrds.
declare #NextWorkingDate datetime
declare #CurrentDate datetime
set #CurrentDate = GETDATE()
set #NextWorkingDate = #CurrentDate
declare #i int = 0
While(#i < 14)
Begin
if(((select COUNT(*) from dbo.tblHolidayMaster where convert(varchar(10),[Date],101) like convert(varchar(10),#NextWorkingDate,101)) > 0) OR DATENAME(WEEKDAY,#NextWorkingDate) = 'Saturday' OR DATENAME(WEEKDAY,#NextWorkingDate) = 'Sunday')
Begin
print 'a '
print #NextWorkingDate
set #NextWorkingDate = #NextWorkingDate + 1
CONTINUE
End
else
Begin
print 'b '
print #NextWorkingDate
set #NextWorkingDate = #NextWorkingDate + 1
set #i = #i + 1
CONTINUE
End
End
print #NextWorkingDate
I calculate the issued date avoid your holiday from your table 'tblHolidayMaster' only.
int addDay = -14;
DateTime dtInputDay = System.DateTime.Now;//Your input day
DateTime dtResultDate = new DateTime();
dtResultDate = dtInputDay.AddDays(addDay);
bool result = false;
string strExpression;
DataView haveHoliday;
while (!result) {
strExpression = "Date >='" + Convert.ToDateTime(dtResultDate.ToString("yyyy/MM/dd")) + "' and Date <='" + Convert.ToDateTime(dtInputDay.ToString("yyyy/MM/dd")) + "'";
haveHoliday = new DataView(tblHolidayMaster);
haveHoliday.RowFilter = strExpression;
if (haveHoliday.Count == 0) {
result = true;
} else {
addDay = -(haveHoliday.Count);
dtInputDay = dtResultDate.AddDays(-1);
dtResultDate = dtResultDate.AddDays(addDay);
}
}
Your issued date is dtResultDate
Try the following link,
http://www.c-sharpcorner.com/uploadfile/tirthacs/difference-between-two-dates-excluding-weekends/