Get next given day after a month in c# - c#

How can I get the next particular day after a month in c#,
for example if today is Monday I need to get the first Monday after 1 month from now, but in more generic way, for example if today is 17/11/2021 which is Wednesday I need to get the first Wednesday after a month from now.
I am using this function but it will return for next week and not next month:
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
{
// The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
return start.AddDays(daysToAdd);
}

Typical month has 30 or 31 days, however, the next same day of week will be after either 28 (4 * 7) or 35 (5 * 7) days. We need a compromise. Let for February add 28 days and for all the other months add 35 days:
public static DateTime GetSameDayAfterMonth(DateTime start) =>
start.AddDays((start.AddMonths(1) - start).TotalDays < 30 ? 28 : 35);
Sure, you can elaborate other rules: say, when after adding 28 days we are still in the same month (1 May + 28 days == 29 May) we should add 35:
public static DateTime GetSameDayAfterMonth(DateTime start) =>
start.AddDays(start.Month == start.AddDays(28).Month ? 35 : 28);

Add a month, instead of a week to the start date:
public static DateTime GetSameDayAfterMonth(DateTime start)
{
var compare = start.AddMonths(1);
// The % 7 ensures we end up with a value in the range [0, 6]
int daysToAdd = ((int)start.DayOfWeek - (int)compare.DayOfWeek) % 7;
return compare.AddDays(daysToAdd);
}
Example:
var dayInNextMonth = GetSameDayAfterMonth(DateTime.Parse("2021-11-17")); // 2021-12-15
var weekday = dayInNextMonth.DayOfWeek; // Wednesday

Related

Calculate exact no of week for date range

A person is engaged in different works in different time duration which will start from Monday and end on friday as follows.Monday to Friday will be considered as 1 week.Any overlapping weeks be considered as 1 week.
Below are the scenario
"AssignedEngagementdate":[
{"Startdate":"01/03/2022","Enddate":"01/07/2022"},
{"Startdate":"01/10/2022","Enddate":"01/14/2022"},
{"Startdate":"01/10/2022","Enddate":"01/21/2022"},
{"Startdate":"02/14/2022","Enddate":"02/18/2022"}
]
Here I need to find the no of weeks assigned by this person and it should be 4 since one week is from 10th Jan to 14 Feb is overlapping in 2 engagement.
How I can do this in C# using linq. I was trying to fetch min start date and max end date from list and find the difference and converting in no of weeks but it has not given the actual result since date assigned is not consistent.
The trick is to convert each date range into an enumerated week range, or at least a list of distinct dates (such as Sundays) corresponding to each week or partial week. SelectMany() lets you gather them all together, after which a distinct count gives you an answer.
Try something like:
int numberOfWeeks = AssignedEngagementdate
.SelectMany(a => {
DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); // May be same as firstSunday
int weeks = (lastSunday - firstSunday).Days / 7 + 1;
// Enumerate one Sunday per week
return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
})
.Distinct()
.Count();
You may still need to consider and test cases where assignments start or end on days other than Monday and Friday. The above should handle most such cases.
Without linq, you can try this Datetime extension :
public static int NumberOfWeeksBetween(this DateTime start, DateTime end)
{
TimeSpan span = end.Subtract(start);
if (span.Days <= 7)
{
if (start.DayOfWeek > end.DayOfWeek) return 2;
else return 1;
}
int days = span.Days - 7 + (int)start.DayOfWeek;
int weekCount = 1;
int dayCount = 0;
for (weekCount = 1; dayCount < days; weekCount++) dayCount += 7;
return weekCount;
}

Return the correct number of weeks for the Gregorian Calendar

I have a problem with the calendar that I use to get me a smaller number of weeks for certain months
For example, this happens to me at Sept 2019, where my number is 5 or in July 2018, which is also 5.
How can I fix this?
this is my current code:
private DateTime _calendarDate;
int numWeeks = NumberOfWeeks(_calendarDate.Year, _calendarDate.Month);
private int NumberOfWeeks(int year, int month)
{
return NumberOfWeeks(new DateTime(year, month, DateTime.DaysInMonth(year, month)));
}
private int NumberOfWeeks(DateTime date)
{
var beginningOfMonth = new DateTime(date.Year, date.Month, 1);
while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
date = date.AddDays(1);
return (int)Math.Truncate(date.Subtract(beginningOfMonth).TotalDays / 7f) + 1;
}
additional information on CultureInfo
The problem is that it always comes back to me one week less, so my calendar doesn't display it properly and then I get the error,
Here's an example for April 2018 where you give me 5 weeks, and then another one is missing and that's why I'm getting the error
can anyone guess how i could solve this problem?
When you can use System.Globalization.Calendar you can get the count of weeks in a month by using GetWeekOfYear for the first and the last day of the month and then calculate the difference (and add 1 to include the first week).
This would change your NumberOfWeeks() to the following:
private int NumberOfWeeks(DateTime date)
{
Calendar calendar = CultureInfo.CurrentCulture.Calendar;
var firstOfMonth = new DateTime(date.Year, date.Month, 1);
var week1 = calendar.GetWeekOfYear(firstOfMonth, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
var week2 = calendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
int numberOfWeeks = (week2 - week1) + 1;
return numberOfWeeks;
}
For your example (April 2018) this will give you 6 as a result and for May 2018 it will give you 5.
MSDN GetWeekOfYear
Here is a bit more mathematical solution:
int myYear = 2019, myMonth = 12; // the example for December 2019
var firstMonthDay = new DateTime(myYear, myMonth, 1);
int delta = firstMonthDay.DayOfWeek - System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
if (delta < 0)
delta += 7;
int daysInMonth = DateTime.DaysInMonth(myYear, myMonth);
int weekLinesNumber = (int)Math.Ceiling((daysInMonth + delta) / 7F); // the result
We count the delta (could be from 0 to 6), this is the number of days from the first day of the week to the first next month day. Then we count the resulting number of the weeks using Math.Ceiling function.
In short, we add to the month some delta days at the beginning in order to get new Extra Month block starting from the First Day of a Week (according to the culture). And then we count the total number of the occupied week lines. No need to add +1 when we use Math.Ceiling function.

Convert week and year to milliseconds

I need to convert, as the title says, weeks and year to milliseconds since 1970. What is the best way to do this in .Net?
I only have information of the week and year that some event occurred. The week stars on Monday. I think DateTime is not the answer since it can't handle the week of the year.
What I need is something like a method double getMili(int week, int year).
Thanks anyway
You can put it like that:
// Let's convert 15th Monday in 2014
int MondaysNumber = 15;
DateTime source = new DateTime(2014, 1, 1);
int delta = 7 + DayOfWeek.Monday - source.DayOfWeek;
if (delta >= 7)
delta -= 7;
source = source.AddDays((MondaysNumber - 1) * 7 + delta);
// Finally, convert it into milliseconds
Double result = (source - new DateTime(1970, 1, 1)).TotalMilliseconds;

How to simplify logic when week starts from Monday?

I have this code to get date in a week. But this code takes Monday as hardcoded. How to make those 4 lines flexible where its commented "//Special check for Sunday because acc. to our business week Starts from Monday and DayOfWeek Enum starts from Sunday."
I mean can var be calculated in a single line without if else condition? So that tomorrow if its decided that Sunday is Start Date, then no changes needs to be done.
class Program
{
static void Main(string[] args)
{
DateTime weekDate = GetWeekDate(DateTime.Now, DayOfWeek.Friday);
Console.WriteLine(weekDate); //prints 14-02-2014
Console.ReadLine();
}
private static DateTime GetWeekDate(DateTime dt, DayOfWeek dow)
{
DateTime sow;
int var = 0;
sow = StartOfWeek(dt);
if (dow == 0) //Special check for Sunday because acc. to our business week Starts from Monday and DayOfWeek Enum starts from Sunday.
var = 6;
else
var = (int)dow - 1;
return sow.AddDays(var);
}
public static DateTime StartOfWeek(DateTime dt)
{
int days = dt.DayOfWeek - DayOfWeek.Monday; //Week Starts from Monday
if (days < 0)
days += 7;
return dt.AddDays(-1 * days).Date;
}
}
It's called the modulo operator - % (also known as integral division remainder).
For example, (7 + dow - 1) % 7 should give you the proper value.
To shift the start of week, you just have to change the value you subtract, eg. to start the week with sunday, it will be zero, to start with monday it will be 1, to start with tuesday it will be 2 ((7 + dow - 2) % 7) etc.
What you can do is depend on a constant, or even accept as parameter, the start of the business week, and use that value in your calculations instead of hard-coding it.

Subtracting TimeSpan from date

I want to subtract a time-span from a date-time object using a 30-day month and ignoring leap years etc.
Date is 1983/5/1 13:0:0 (y/m/d-h:m:s)
Time span is 2/4/28-2:51:0 (y/m/d-h:m:s)
I can use DateTime and TimeSpan objects to do this, after converting years and months of the time-span to days (assuming a 30 day month and a ~364 day year).
new DateTime(1981,5,1,13,0,0).Subtract(new TimeSpan(878,13,51,0));
With this i get the result:
{12/4/1978 11:09:00 PM}
Above answer obviously doesn't ignore the factors i want ignored and gives me an accurate answer. But in this case that's not what i want so i wrote the below code.
public static CustomDateTime operator -(CustomDateTime DT1,CustomDateTime DT2)
{
CustomDateTime retVal = new CustomDateTime();
try
{
const int daysPerYear = 364.25;
const int monthsPerYear = 12;
const int daysPerMonth = 30;
const int hoursPerDay = 24;
const int minutesPerHour = 60;
retVal.Minute = DT1.Minute - DT2.Minute;
if (retVal.Minute < 0)
{
retVal.Minute += minutesPerHour;
DT1.Hour -= 1;
}
retVal.Hour = DT1.Hour - DT2.Hour;
if (retVal.Hour < 0)
{
retVal.Hour += hoursPerDay;
DT1.Day -= 1;
}
retVal.Day = DT1.Day - DT2.Day;
if (retVal.Day < 0)
{
retVal.Day += daysPerMonth;
DT1.Month -= 1;
}
retVal.Month = DT1.Month - DT2.Month;
if (retVal.Month < 0)
{
retVal.Month += monthsPerYear;
DT1.Year -= 1;
}
retVal.Year = DT1.Year - DT2.Year;
}
catch (Exception ex) { }
return retVal;
}
Then i get:
1981/0/3-10:9:0
This is pretty close to what i'm after except i shouldn't get 0 for month and year should be 1980. Any kind of help is appreciated.
Just to make things clear again; in this context I have to use a 30-day month and ignore leap-years, different numbers of months, etc. Its a weird thing to do, i know. So I'm pretty much after a 'wrong answer' as opposed to the exact answer given by the managed classes.
If you're estimating a month at 30 days, of course your math will be off. When you subtract 878 days from 5/1/1981, .Net is giving you the exact difference, not an estimate, and this difference accounts for leap years, if there are any. The error is not in the Subtract(...) method - it is in your own "manual" calculation.
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0);
TimeSpan t = new TimeSpan(878, 13, 51, 0);
dt.Ticks
624931668000000000
t.Ticks
759090600000000
dt.Ticks - t.Ticks
624172577400000000
new DateTime(dt2)
{12/4/1978 11:09:00 PM}
Date: {12/4/1978 12:00:00 AM}
Day: 4
DayOfWeek: Monday
DayOfYear: 338
Hour: 23
Kind: Unspecified
Millisecond: 0
Minute: 9
Month: 12
Second: 0
Ticks: 624172577400000000
TimeOfDay: {23:09:00}
Year: 1978
These are the total ticks since the epoch. Do this math, then convert back into a datetime.
Also: correct your math. 878 days is 2 years and 148 days. 5/1/1981 is the 121st day of the year, so subtract 120 to get Jan 1, 1979. This leaves 28 days. Start counting backwards from the end of 1978, and you get very close to the .Net answer. Your own answer isn't anywhere close.
EDIT based on feedback
// zh-Hans is a chinese culture
CultureInfo ci = CultureInfo.GetCultureInfo("zh-Hans");
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0, ci.Calendar);
TimeSpan t = new TimeSpan(878, 13, 51, 0);
Please note that you are still subtracting 878 days. The length of a month would be irrelevant in that case based on the Julian calendar. You will probably need to find the correct culture code for your particular calendar, then try this. However, with this calendar, I still arrive at the same answer above.
Beyond doing this, I am unsure how else to do the math. If you can provide a link to how you are doing it by hand, I can help code it for you.
EDIT 2
I understand now. Try this:
DateTime dt = new DateTime(1981, 5, 1, 13, 0, 0, ci.Calendar);
int years = 878 / 365;
int remainingDays = 878 % 365;
int months = remainingDays / 30;
remainingDays = remainingDays % 30;
TimeSpan t = new TimeSpan(years * 365 + months * 30 + remainingDays);
DateTime newdate = dt.Subtract(t);
You cannot assume a 30-day month. You are specifying that you want to subtract 878 days. The managed classes (I'm assuming you mean managed when you say native) are designed to factor in leap-years, different numbers of months, etc.
Using the managed classes will not give you a 0 for a month.

Categories