Calculating week numbers including week 53 when necessary - c#

I am try to find a nice way to work out the week number from a non standard starting date. Week 1 shall contain the first Sunday in April. To calculate this, I just loop through the first 7 days in April til I find the first Sunday. Weeks will start on Sunday.
Normally I would attempt to solve this doing something like this:
numberOfDaysDifferenceBetweenEpoch / 7 % 52 + 1;
However about every 5 years it works out as there are 53 weeks in a year. Obviously the function above will not work if it happens to be a 53 week year. An easy solution would be just to make two functions, which take the modulus of 52 or 53 however I'm hoping there is a cleaner way of doing this. What would the best way to approach this problem?

Here is one way that should work. You may want to optimise the GetEpochInYear method if you are using it frequently.
private static DateTime GetEpochInYear(int year)
{
DateTime currentYearEpoch = new DateTime(year, 4, 1);
while (currentYearEpoch.DayOfWeek != DayOfWeek.Sunday)
{
currentYearEpoch = currentYearEpoch.AddDays(1);
}
return currentYearEpoch;
}
private static int GetWeekNumber(DateTime dateOfInterest)
{
DateTime currentYearEpoch = GetEpochInYear(dateOfInterest.Year);
if (dateOfInterest < currentYearEpoch)
{
currentYearEpoch = GetEpochInYear(dateOfInterest.Year - 1);
}
int days = (int)(dateOfInterest - currentYearEpoch).TotalDays;
return (days / 7) +1;
}

Related

C# - Get the first day and last day of the week knowing the week number, month number and year

so the problem is this: I managed to get the number of weeks that make up a month (for example: May 2022 is made up of 6 weeks counting from day 1 to day 31 without exception). So having the number of weeks that make up a month I need to know, knowing also the number of the month and the year, which will be the first and last day of the selected week.
I tried to do some research but I only find solutions that use the Calendar.GetWeekOfYear method but, as I said before, I have the week number for the single month not the total for the year.
I hope you can help me. Thanks in advance.
If I understood correctly it should look something like this
var week = 2;
var month = 5;
var year = 2022;
var firstDayOfMonth = new DateTime(year, month, 1);
var dayOfWeek = firstDayOfMonth.DayOfWeek;
var diffToMonday = (7 + (dayOfWeek - DayOfWeek.Monday)) % 7;
var firstMonday = firstDayOfMonth.AddDays(-diffToMonday);
var requestedMonday = firstMonday.AddDays(7 * (week - 1));
var requestedSunday = firstMonday.AddDays(7 * week - 1);

Adding two periods on nodatime

Where listObjAge is a list with multiple periods;
Period objTotalPeriod = listObjAge[0].period;
for (int i = 1; i < listObjAge.Count; i++) {
objTotalPeriod += listObjAge[i].period;
}
In-short:
What i am getting:
listObjAge[0].period + listObjAge[1].period = ????.
2 yr 1 mnth 28 days + 0 yr 8 mnth 30 days = 2 yr 9 mnth 58 days
// this result is not wrong but is there any way to correctly add days for the above code.
What i am expecting:
2 yr 1 mnth 28 days + 0 yr 8 mnth 30 days = 2 yr 10 mnth 28 days
As you can see i want to add results of two period. Is there any way we can achieve it using nodatime.
Solved:
I know its not correct theoretically. But it worked for me.
int intDays = 0;
for (int i = 0; i < listObjAge.Count; i++) {
intDays += listObjAge[i].period.Days; // adding all the days for every period
}
strYear = (intDays / 365).ToString();
strMonth = ((intDays % 365) / 30).ToString();
strDays = ((intDays % 365) % 30).ToString();
You should look at the user guide for Noda Time that describes arithmetic http://nodatime.org/2.0.x/userguide/arithmetic - look under the section "Adding a Period" for more information.
It's easiest to think about where this can be confusing with an example. Suppose we add "one month minus three days" to January 30th 2011:
Period period = Period.FromMonths(1) - Period.FromDays(3);
LocalDate date = new LocalDate(2011, 1, 30);
date = date + period;
If you give this puzzle to a real person, they may well come up with an answer of "February 27th" by waiting until the last moment to check the validity. Noda Time will give an answer of February 25th, as the above code is effectively evaluated as:
Period period = Period.FromMonths(1) - Period.FromDays(3);
LocalDate date = new LocalDate(2011, 1, 30);
date = date + Period.FromMonths(1); // February 28th (truncated)
date = date - Period.FromDays(3); // February 25th
The benefit of this approach is simplicity and predictability: when you know the rules, it's very easy to work out what Noda Time will do. The downside is that if you don't know the rules, it looks like it's broken.
With your code
According to the documentation, the result you are getting is the expected behavior based on the "rules". Simply, your addition operation on two two periods will evaluate to:
Years (2 + 0) = 2
Months(1 + 8) = 9
Days (28 + 30) = 58
Your comment:
this result is not wrong but is there any way to correctly add days for the above code.
What do you mean as "correct"? Are you saying that 28 + 30 = 58 is incorrect?
Alternatives
int days = 28 + 30; // carry over your days and +1 month or whatever logic you had in mind
int months = 1 + 8;
Period p1 = new PeriodBuilder { Days = days, Months = months }.Build();
It sounds like you're looking for something to normalize the months and days (and weeks?). The existing Normalize method deals with everything from "days downwards" (e.g. hours) so you can use that to start with:
public static Period NormalizeIncludingMonths(this Period period, int daysPerMonth)
{
period = period.Normalize();
int extraMonths = days / daysPerMonth;
int months = period.Months + extraMonths;
int extraYears = months / 12;
// Simplest way of changing just a few parts...
var builder = period.ToBuilder();
builder.Years += extraYears;
builder.Months = months % 12;
builder.Days = days % daysPerMonth;
return builder.Build();
}
So in your case, it sounds like you might want:
objTotalPeriod = objTotalPeriod.NormalizeIncludingMonths(31);
Note that arithmetic using this may well produce "odd" results, just as part of the nature of calendrical arithmetic.
Use this
public static Period add(Period b, Period c)
{
int years = b.year + c.year;
int months = b.month + c.month;
int days= b.days + c.days;
return new PeriodBuilder { Days = days, Months = months , Years = years}.Build();
}

Get Date Range by week number c# [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
In .net, knowing the week number how can I get the weekdays date?
Hello,
I've got a question for ya'll.
How do i get the date range of a given week number.
For example:
If I enter week 12 the output should be:
21-03-2011
22-03-2011
23-03-2011
24-03-2011
25-03-2011
26-03-2011
27-03-2011
I really hope you guys can help me out, i just cant find the awnser anywhere!
Thanks in advance.
Note
I appear to have missed bug. The current code have been updated as of 2012-01-30 to account for this fact and we now derive the daysOffset based on Tuesday which according to Mikael Svenson appears to solve the problem.
These ISO8601 week date calculations are a bit wonky, but this is how you do it:
DateTime jan1 = new DateTime(yyyy, 1, 1);
int daysOffset = DayOfWeek.Tuesday - jan1.DayOfWeek;
DateTime firstMonday = jan1.AddDays(daysOffset);
var cal = CultureInfo.CurrentCulture.Calendar;
int firstWeek = cal.GetWeekOfYear(jan1, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
var weekNum = ww;
if (firstWeek <= 1)
{
weekNum -= 1;
}
var result = firstMonday.AddDays(weekNum * 7 + d - 1);
return result;
Basically calculate a reference point, then add days, the hard stuff has to do with the fact that week 53 can sometimes occur in January and week 1 can sometimes occur in December. You need to adjust for that and this is one way to do that.
The above code calculates the date off a year (yyyy) and week number (ww) and day of week (d).
Find out which day of the week was the first January of the year (e.g. in 2011 it was Saturday)
Add the necessary count of days to become the next monday (2 days)
From this day on, add (Number of weeks - 1) * 7 days to get the first day of the week you are interested in
-Display this day plus the next days to get the whole week
Something like this should do the trick
DateTime d = new DateTime(someYear, 1, 1);
d.AddDays(numWeeks * 7);
for (int x = 0; x < 7; x++)
{
Console.WriteLine(d.ToShortDateString());
d.AddDays(1);
}

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.

Calculate DateTime Weeks into Rows

I am currently writing a small calendar in ASP.Net C#. Currently to produce the rows of the weeks I do the following for loop:
var iWeeks = 6;
for (int w = 0; w < iWeeks; w++) {
This works fine, however, some month will only have 5 weeks and in some rare cases, 4.
How can I calculate the number of rows that will be required for a particular month?
This is an example of what I am creating:
As you can see for the above month, there are only 5 rows required, however. Take the this month (August 2008) which started on a Saturday and ends on a Monday on the 6th Week/Row.
Image found on google
This is an example of what I am creating:
As you can see for the above month, there are only 5 rows required, however. Take the this month (August 2008) which started on a Saturday and ends on a Monday on the 6th Week/Row.
Image found on google
Here is the method that does it:
public int GetWeekRows(int year, int month)
{
DateTime firstDayOfMonth = new DateTime(year, month, 1);
DateTime lastDayOfMonth = new DateTime(year, month, 1).AddMonths(1).AddDays(-1);
System.Globalization.Calendar calendar = System.Threading.Thread.CurrentThread.CurrentCulture.Calendar;
int lastWeek = calendar.GetWeekOfYear(lastDayOfMonth, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
int firstWeek = calendar.GetWeekOfYear(firstDayOfMonth, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return lastWeek - firstWeek + 1;
}
You can customize the calendar week rule by modifying the System.Globalization.CalendarWeekRule.FirstFourDayWeek part. I hope the code is self explanatory.
Well, it depends on the culture you're using, but let's assume you can use Thread.CurrentThread.CurrentCulture, then the code to get the week of today would be:
Culture culture = Thread.CurrentThread.CurrentCulture;
Calendar cal = culture.Calendar;
Int32 week = cal.GetWeekOfYear(DateTime.Today,
culture.DateTimeFormat.CalendarWeekRule,
culture.DateTimeFormat.FirstDayOfWeek);
How about checking which week the first and last days will be in?
you can get the days of a month by using DateTime.DaysInMonth(int WhichYear,int WhichMonth);
The months in the Julian / Gregorian calendar have the same number of days each year, except February who can have 28 or 29 days depending on the leapness of the year. You can find the number of days in the Description section at http://en.wikipedia.org/wiki/Gregorian_calendar.
As #darkdog mentioned you have DateTime.DaysInMonth. Just do this:
var days = DateTime.DaysInMonth(year, month) +
WhatDayOfWeekTheMonthStarts(year, month);
int rows = (days / 7);
if (0 < days % 7)
{
++rows;
}
Take into consideration the fact that for globalization / localization purposes, some parts of the world use different calendars / methods of organization of the year.
The problem isn't the number of days in the month, it's how many weeks it spans over.
February in a non-leap year will have 28 days, and if the first day of the month is a monday, february will span exactly 4 week numbers.
However, if the first day of the month is a tuesday, or any other day of the week, february will span 5 week numbers.
A 31 day month can span 5 or 6 weeks the same way. If the month starts on a monday, the 31 days gives you 5 week numbers. If the month starts on saturday or sunday, it will span 6 week numbers.
So the right way to obtain this number is to find the week number of the first and last days of the month.
Edit #1: Here's how to calculate the number of weeks a given month spans:
Edit #2: Fixed bugs in code
public static Int32 GetWeekForDateCurrentCulture(DateTime dt)
{
CultureInfo culture = Thread.CurrentThread.CurrentCulture;
Calendar cal = culture.Calendar;
return cal.GetWeekOfYear(dt,
culture.DateTimeFormat.CalendarWeekRule,
culture.DateTimeFormat.FirstDayOfWeek);
}
public static Int32 GetWeekSpanCountForMonth(DateTime dt)
{
DateTime firstDayInMonth = new DateTime(dt.Year, dt.Month, 1);
DateTime lastDayInMonth = firstDayInMonth.AddMonths(1).AddDays(-1);
return
GetWeekForDateCurrentCulture(lastDayInMonth)
- GetWeekForDateCurrentCulture(firstDayInMonth)
+ 1;
}
Try this,
DateTime.DaysInMonth
First Find out which weekday the first day of the month is in. Just new up a datetime with the first day, always 1, and the year and month in question, there is a day of week property on it.
Then from here, you can use the number of days in the month, DateTime.DaysInMonth, in order to determine how many weeks when you divide by seven and then add the number of days from 1 that your first day falls on. For instance,
public static int RowsForMonth(int month, int year)
{
DateTime first = new DateTime(year, month, 1);
//number of days pushed beyond monday this one sits
int offset = ((int)first.DayOfWeek) - 1;
int actualdays = DateTime.DaysInMonth(month, year) + offset;
decimal rows = (actualdays / 7);
if ((rows - ((int)rows)) > .1)
{
rows++;
}
return rows;
}
Check Calendar.GetWeekOfYear. It should do the trick.
There is a problem with it, it does not follow the 4 day rule by ISO 8601, but otherwise it is neat.

Categories

Resources