Count number of Mondays in a given date range - c#

Given a date range, I need to know how many Mondays (or Tuesdays, Wednesdays, etc) are in that range.
I am currently working in C#.

Try this:
static int CountDays(DayOfWeek day, DateTime start, DateTime end)
{
TimeSpan ts = end - start; // Total duration
int count = (int)Math.Floor(ts.TotalDays / 7); // Number of whole weeks
int remainder = (int)(ts.TotalDays % 7); // Number of remaining days
int sinceLastDay = (int)(end.DayOfWeek - day); // Number of days since last [day]
if (sinceLastDay < 0) sinceLastDay += 7; // Adjust for negative days since last [day]
// If the days in excess of an even week are greater than or equal to the number days since the last [day], then count this one, too.
if (remainder >= sinceLastDay) count++;
return count;
}

Since you're using C#, if you're using C#3.0, you can use LINQ.
Assuming you have an Array/List/IQueryable etc that contains your dates as DateTime types:
DateTime[] dates = { new DateTime(2008,10,6), new DateTime(2008,10,7)}; //etc....
var mondays = dates.Where(d => d.DayOfWeek == DayOfWeek.Monday); // = {10/6/2008}
Added:
Not sure if you meant grouping them and counting them, but here's how to do that in LINQ as well:
var datesgrouped = from d in dates
group d by d.DayOfWeek into grouped
select new { WeekDay = grouped.Key, Days = grouped };
foreach (var g in datesgrouped)
{
Console.Write (String.Format("{0} : {1}", g.WeekDay,g.Days.Count());
}

It's fun to look at different algorithms for calculating day of week, and #Gabe Hollombe's pointing to WP on the subject was a great idea (and I remember implementing Zeller's Congruence in COBOL about twenty years ago), but it was rather along the line of handing someone a blueprint of a clock when all they asked what time it was.
In C#:
private int CountMondays(DateTime startDate, DateTime endDate)
{
int mondayCount = 0;
for (DateTime dt = startDate; dt < endDate; dt = dt.AddDays(1.0))
{
if (dt.DayOfWeek == DayOfWeek.Monday)
{
mondayCount++;
}
}
return mondayCount;
}
This of course does not evaluate the end date for "Mondayness", so if this was desired, make the for loop evaluate
dt < endDate.AddDays(1.0)

Here's some pseudocode:
DifferenceInDays(Start, End) / 7 // Integer division discarding remainder
+ 1 if DayOfWeek(Start) <= DayImLookingFor
+ 1 if DayOfWeek(End) >= DayImLookingFor
- 1
Where DifferenceInDays returns End - Start in days, and DayOfWeek returns the day of the week as an integer. It doesn't really matter what mapping DayOfWeek uses, as long as it is increasing and matches up with DayImLookingFor.
Note that this algorithm assumes the date range is inclusive. If End should not be part of the range, you'll have to adjust the algorithm slightly.
Translating to C# is left as an exercise for the reader.

Any particular language and therefore date format?
If dates are represented as a count of days, then the difference between two values plus one (day), and divide by 7, is most of the answer. If both end dates are the day in question, add one.
Edited: corrected 'modulo 7' to 'divide by 7' - thanks. And that is integer division.

You could try this, if you want to get specific week days between two dates
public List<DateTime> GetSelectedDaysInPeriod(DateTime startDate, DateTime endDate, List<DayOfWeek> daysToCheck)
{
var selectedDates = new List<DateTime>();
if (startDate >= endDate)
return selectedDates; //No days to return
if (daysToCheck == null || daysToCheck.Count == 0)
return selectedDates; //No days to select
try
{
//Get the total number of days between the two dates
var totalDays = (int)endDate.Subtract(startDate).TotalDays;
//So.. we're creating a list of all dates between the two dates:
var allDatesQry = from d in Enumerable.Range(1, totalDays)
select new DateTime(
startDate.AddDays(d).Year,
startDate.AddDays(d).Month,
startDate.AddDays(d).Day);
//And extracting those weekdays we explicitly wanted to return
var selectedDatesQry = from d in allDatesQry
where daysToCheck.Contains(d.DayOfWeek)
select d;
//Copying the IEnumerable to a List
selectedDates = selectedDatesQry.ToList();
}
catch (Exception ex)
{
//Log error
//...
//And re-throw
throw;
}
return selectedDates;
}

Add the smallest possible number to make the first day a Monday. Subtract the smallest possible number to make the last day a Monday. Calculate the difference in days and divide by 7.

Convert the dates to Julian Day Number, then do a little bit of math. Since Mondays are zero mod 7, you could do the calculation like this:
JD1=JulianDayOf(the_first_date)
JD2=JulianDayOf(the_second_date)
Round JD1 up to nearest multiple of 7
Round JD2 up to nearest multiple of 7
d = JD2-JD1
nMondays = (JD2-JD1+7)/7 # integer divide

I have had the same need today. I started with the cjm function since I don't understand the JonB function and since the Cyberherbalist function is not linear.
I had have to correct
DifferenceInDays(Start, End) / 7 // Integer division discarding remainder
+ 1 if DayOfWeek(Start) <= DayImLookingFor
+ 1 if DayOfWeek(End) >= DayImLookingFor
- 1
to
DifferenceInDays(Start, End) / 7 // Integer division discarding remainder
+ 1 if DayImLookingFor is between Start.Day and End.Day
With the between function that return true if, starting from the start day, we meet first the dayImLookingFor before the endDay.
I have done the between function by computing the number of day from startDay to the other two days:
private int CountDays(DateTime start, DateTime end, DayOfWeek selectedDay)
{
if (start.Date > end.Date)
{
return 0;
}
int totalDays = (int)end.Date.Subtract(start.Date).TotalDays;
DayOfWeek startDay = start.DayOfWeek;
DayOfWeek endDay = end.DayOfWeek;
///look if endDay appears before or after the selectedDay when we start from startDay.
int startToEnd = (int)endDay - (int)startDay;
if (startToEnd < 0)
{
startToEnd += 7;
}
int startToSelected = (int)selectedDay - (int)startDay;
if (startToSelected < 0)
{
startToSelected += 7;
}
bool isSelectedBetweenStartAndEnd = startToEnd >= startToSelected;
if (isSelectedBetweenStartAndEnd)
{
return totalDays / 7 + 1;
}
else
{
return totalDays / 7;
}
}

This will return a collection of integers showing how many times each day of the week occurs within a date range
int[] CountDays(DateTime firstDate, DateTime lastDate)
{
var totalDays = lastDate.Date.Subtract(firstDate.Date).TotalDays + 1;
var weeks = (int)Math.Floor(totalDays / 7);
var result = Enumerable.Repeat<int>(weeks, 7).ToArray();
if (totalDays % 7 != 0)
{
int firstDayOfWeek = (int)firstDate.DayOfWeek;
int lastDayOfWeek = (int)lastDate.DayOfWeek;
if (lastDayOfWeek < firstDayOfWeek)
lastDayOfWeek += 7;
for (int dayOfWeek = firstDayOfWeek; dayOfWeek <= lastDayOfWeek; dayOfWeek++)
result[dayOfWeek % 7]++;
}
return result;
}
Or a slight variation which lets you do FirstDate.TotalDaysOfWeeks(SecondDate) and returns a Dictionary
public static Dictionary<DayOfWeek, int> TotalDaysOfWeeks(this DateTime firstDate, DateTime lastDate)
{
var totalDays = lastDate.Date.Subtract(firstDate.Date).TotalDays + 1;
var weeks = (int)Math.Floor(totalDays / 7);
var resultArray = Enumerable.Repeat<int>(weeks, 7).ToArray();
if (totalDays % 7 != 0)
{
int firstDayOfWeek = (int)firstDate.DayOfWeek;
int lastDayOfWeek = (int)lastDate.DayOfWeek;
if (lastDayOfWeek < firstDayOfWeek)
lastDayOfWeek += 7;
for (int dayOfWeek = firstDayOfWeek; dayOfWeek <= lastDayOfWeek; dayOfWeek++)
resultArray[dayOfWeek % 7]++;
}
var result = new Dictionary<DayOfWeek, int>();
for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++)
result[(DayOfWeek)dayOfWeek] = resultArray[dayOfWeek];
return result;
}

A bit Modified Code is here works and Tested by me
private int CountDays(DayOfWeek day, DateTime startDate, DateTime endDate)
{
int dayCount = 0;
for (DateTime dt = startDate; dt < endDate; dt = dt.AddDays(1.0))
{
if (dt.DayOfWeek == day)
{
dayCount++;
}
}
return dayCount;
}
Example:
int Days = CountDays(DayOfWeek.Friday, Convert.ToDateTime("2019-07-04"),
Convert.ToDateTime("2019-07-27")).ToString();

I had a similar problem for a report. I needed the number of workdays between two dates.
I could have cycled through the dates and counted but my discrete math training wouldn't let me. Here is a function I wrote in VBA to get the number of workdays between two dates. I'm sure .net has a similar WeekDay function.
1
2 ' WorkDays
3 ' returns the number of working days between two dates
4 Public Function WorkDays(ByVal dtBegin As Date, ByVal dtEnd As Date) As Long
5
6 Dim dtFirstSunday As Date
7 Dim dtLastSaturday As Date
8 Dim lngWorkDays As Long
9
10 ' get first sunday in range
11 dtFirstSunday = dtBegin + ((8 - Weekday(dtBegin)) Mod 7)
12
13 ' get last saturday in range
14 dtLastSaturday = dtEnd - (Weekday(dtEnd) Mod 7)
15
16 ' get work days between first sunday and last saturday
17 lngWorkDays = (((dtLastSaturday - dtFirstSunday) + 1) / 7) * 5
18
19 ' if first sunday is not begin date
20 If dtFirstSunday <> dtBegin Then
21
22 ' assume first sunday is after begin date
23 ' add workdays from begin date to first sunday
24 lngWorkDays = lngWorkDays + (7 - Weekday(dtBegin))
25
26 End If
27
28 ' if last saturday is not end date
29 If dtLastSaturday <> dtEnd Then
30
31 ' assume last saturday is before end date
32 ' add workdays from last saturday to end date
33 lngWorkDays = lngWorkDays + (Weekday(dtEnd) - 1)
34
35 End If
36
37 ' return working days
38 WorkDays = lngWorkDays
39
40 End Function

private System.Int32 CountDaysOfWeek(System.DayOfWeek dayOfWeek, System.DateTime date1, System.DateTime date2)
{
System.DateTime EndDate;
System.DateTime StartDate;
if (date1 > date2)
{
StartDate = date2;
EndDate = date1;
}
else
{
StartDate = date1;
EndDate = date2;
}
while (StartDate.DayOfWeek != dayOfWeek)
StartDate = StartDate.AddDays(1);
return EndDate.Subtract(StartDate).Days / 7 + 1;
}

Four years later, I thought I'd run a test:
[TestMethod]
public void ShouldFindFridaysInTimeSpan()
{
//reference: http://stackoverflow.com/questions/248273/count-number-of-mondays-in-a-given-date-range
var spanOfSixtyDays = new TimeSpan(60, 0, 0, 0);
var setOfDates = new List<DateTime>(spanOfSixtyDays.Days);
var now = DateTime.Now;
for(int i = 0; i < spanOfSixtyDays.Days; i++)
{
setOfDates.Add(now.AddDays(i));
}
Assert.IsTrue(setOfDates.Count == 60,
"The expected number of days is not here.");
var fridays = setOfDates.Where(i => i.DayOfWeek == DayOfWeek.Friday);
Assert.IsTrue(fridays.Count() > 0,
"The expected Friday days are not here.");
Assert.IsTrue(fridays.First() == setOfDates.First(i => i.DayOfWeek == DayOfWeek.Friday),
"The expected first Friday day is not here.");
Assert.IsTrue(fridays.Last() == setOfDates.Last(i => i.DayOfWeek == DayOfWeek.Friday),
"The expected last Friday day is not here.");
}
My use of TimeSpan is a bit of overkill---actually I wanted to query TimeSpan directly.

Related

How to delete a particular time slot, from the difference between two dates

string StartDateString = "2020-04-20 18:05:07.6187";
DateTime StartDate = DateTime.Parse(StartDateString);
string EndDateString = "2020-04-22 14:10:00.6187";
DateTime EndDate = DateTime.Parse(EndDateString);
TimeSpan DifferenceStartDateEndDate = StartDate - EndDate;
Now I want to delete time between 10 pm to 8am from DifferenceStartDateEndDate (1 day 20 hours four minutes). I.e. I want to remove time between 10 pm to 8 am which will occur from StartDate to EndDate.
As I understand it, you need to add to your calculation only hours in a day during which your business is active. (08:00 - 22:00)
Here are two helpers:
// For the first day in our calculation
// Start at 08:00 if the given date has an hour component that is earlier
static TimeSpan GetTill2200(DateTime date)
{
if (date.Hour >= 22) return TimeSpan.Zero;
if (date.Hour < 8) date = date.Date.AddHours(8);
return date.Date.AddHours(22) - date;
}
// For the last day in our calculation
// End at 22:00 if the given date has an hour component that is later
static TimeSpan GetFrom0800(DateTime date)
{
if (date.Hour < 8) return TimeSpan.Zero;
if (date.Hour >= 22) date = date.Date.AddHours(22);
return date - date.Date.AddHours(8);
}
And here is the code flow to combine start and end dates and the dates in the middle
// StartDate and EndDate variables as in your question.
TimeSpan result = GetTill2200(StartDate);
DateTime currentDate = StartDate.AddDays(1);
while (currentDate.Date < EndDate.Date)
{
// Add 14 hours
// Total: 17:54:52:3813
result = result.Add(TimeSpan.FromHours(14));
currentDate = currentDate.AddDays(1);
}
// returns 06:10:00.6187
// Total = 1.00:04:53 ( 1 days, 4 minutes, 53 seconds )
result = result.Add(GetFrom0800(EndDate));
// Prints 1.00:04:53
Console.WriteLine(result);

LINQ DateTimes between hours with duration

I'm trying to write a query that return dates that occur within a certain hour of the day plus duration. For example, any DateTimes that occur after (8PM + 8 hours), the hour and duration are variable. It is possible that hour + duration can be in the next day. A spike:
[Test]
public void should_find_dates_between_beginhour_plus_duration()
{
var dates = new []
{
new DateTime(2017, 1, 3, 12,0,0),
new DateTime(2017, 1, 4, 21,0,0),
new DateTime(2017, 1, 5, 2,0,0)
};
var beginHour = 20; //8pm
var duration = 8; //hours
var results = dates.Where(x => x.Hour >= beginHour && x <= x.???? + duration);
//should contain the last 2 dates
foreach (var date in results)
{
Console.WriteLine(date);
}
}
Thus winter/summer time shift does not important here, then you can calculate end hour before running your query. Filtering will be simple - you pick dates which have hour either bigger than begin hour (later in the evening), or smaller than end hour (i.e. earlier in the morning):
var endHour = DateTime.Today.AddHours(beginHour + duration).Hour;
var results = dates.Where(x => beginHour < endHour
? (beginHour <= x.Hour && x.Hour <= endHour)
: (beginHour <= x.Hour || x.Hour <= endHour));

Shift date by months but keep the same day

I am trying to shift a date by a number of given months, but also to keep the same day (for example, if the day of the date is Monday, and after shifting with x months the day is Thursday, I want also to subtract 3 days from the new obtained date. This algorithm should add/subtract days depending on the new obtained date, basically providing the closest date that represents the same day of week). As an example, if I have the start date 10.08.2016(Wednesday), and I add 3 months, I will get 10.11.2016(Thursday), so the closest Wednesday to that day is 09.11.2016.
What I managed to make till now looks something like this:
int startDayOfWeek = ((int)startDay.DayOfWeek) == 0 ? 7 : (int)startDay.DayOfWeek;
int newStartDayOfWeek = ((int)startDay.AddMonths(period).DayOfWeek) == 0 ? 7 : (int)startDay.AddMonths(period).DayOfWeek;
int shiftingDays = startDayOfWeek - newStartDayOfWeek;
if (shiftingDays > 3)
shiftingDays -= 7;
where startDay is the start date, and period is the number of months I want to shift to.
But this still fails some times, so any tips would be greately appreciated.
An example when this code fails would be:
startDate = 01.08.2016 (Monday) and period would be 5
After adding 5 months, I get 01.01.2017, which is Sunday, and the closest Monday would be on 02.01.2017, but I get -6 days.
Use this code. The idea is to divide the days by 7, round it, and multiply by 7.
DateTime endDate = startDay.AddMonths((int)period);
endDate = startDay.AddDays((int)Math.Round((double)(endDate - startDay ).Days / 7)*7);
Is that what you are looking for ?
static void Main( string[] args )
{
//DateTime startDay = DateTime.Now;
DateTime startDay = new DateTime( 2016, 8, 1 );
//DateTime startDay = new DateTime( 2016, 8, 10 );
DateTime newDay = startDay.AddMonths( 5 );
int startDayOfWeek = (int)startDay.DayOfWeek;
int newDayOfWeek = (int)newDay.DayOfWeek;
int shift1 = (7 + startDayOfWeek - newDayOfWeek) % 7;
int shift2 = (7 + newDayOfWeek - startDayOfWeek) % 7;
DateTime test = newDay + ((shift1 > shift2) ? - TimeSpan.FromDays( shift2 ) : TimeSpan.FromDays( shift1 ));
}

Get the Date of Months Week Number

I have a code 2014P07W4 which means:
2014 = year
P07 = 7th month of the year
W4 = 4th week of the month.
I would like to work out the date of the First day of the 4th week in July 2014. In this example I would expect to see a date of 21/7/2014.
July 2014 weeks
Week 1 - 1st to 6th
Week 2 - 7th to 13th
Week 3 - 14th to 20th
Week 4 - 21st to 27th
Week 5 - 28th to 31st
From the code I know the week no = 4 then I want to be able to calculate the date 21/7/2014. I am assuming the first day of the week is a Monday
I am asking how to read that code and get the first day of the week specified
Hope this is clearer it has been a long day
You need to parse the code and extract the year, month, and weekNo an numbers.
Then, you can use this method to get the start day of the week:
int WeekStartDay(int year, int month, int weekNo)
{
DateTime monthStart = new DateTime(year, month, 1);
int monthStart_DayOfWeek = ((int)monthStart.DayOfWeek + 6) % 7;
int weekStart_DayOfMonth = 1;
if (1 < weekNo) {
weekStart_DayOfMonth += 7 - monthStart_DayOfWeek;
}
if (2 < weekNo) {
weekStart_DayOfMonth += 7 * (weekNo - 2);
}
return weekStart_DayOfMonth;
}
Take first day of month (2014/07/01), find next Monday (first day of 2nd week), add 14 days (first day of 4th week).
DateTime date = new DateTime(2014, 7, 1);
int daysToFirstDayOf2ndWeek = date.DayOfWeek == DayOfWeek.Monday
? 7
: ((int)DayOfWeek.Monday - (int)date.DayOfWeek + 7) % 7;
DateTime firstDayOf2ndWeek = date.AddDays(daysToFirstDayOf2ndWeek);
DateTime firstDayOf4thWeek = firstDayOf2ndWeek.AddDays(14);
may be this will works
string code = "2014P07W4";
int yr = int.Parse(code.Substring(0, 4));
int mnth = int.Parse(code.Substring(5, 2));
int week = int.Parse(code.Substring(8));
DateTime dt = new DateTime(yr, mnth, 1);
if (dt.DayOfWeek == DayOfWeek.Monday)
{
DateTime newdate = dt.AddDays((week - 1) * 7);
}
else
{
DateTime newdate = dt.AddDays((8 - (int)dt.DayOfWeek) % 7 + ((week - 2) * 7));
}

Getting year and week from date

I need to return year and week of a given date. Sounds simple. But to be right 2012-01-01 have to return 2011-52, because week 1 in 2012 starts January 2th.
To find the week, I use:
GregorianCalendar calw = new GregorianCalendar(GregorianCalendarTypes.Localized);
return calw.GetWeekOfYear(DateTime.Parse("2012-01-01"), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday).ToString();
this return 52. (correct)
But how do I get the Year?
edit:
With the help from here: http://codebetter.com/petervanooijen/2005/09/26/iso-weeknumbers-of-a-date-a-c-implementation/
This seems to work:
private int weekYear(DateTime fromDate)
{
GregorianCalendar cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
int week = weekNumber(fromDate);
int month = cal.GetMonth(fromDate);
int year = cal.GetYear(fromDate);
//week starts after 31st december
if (week > 50 && month == 1)
year = year - 1;
//week starts before 1st January
if (week < 5 && month == 12)
year = year + 1;
return year;
}
private int weekNumber(DateTime fromDate)
{
// Get jan 1st of the year
DateTime startOfYear = fromDate.AddDays(-fromDate.Day + 1).AddMonths(-fromDate.Month + 1);
// Get dec 31st of the year
DateTime endOfYear = startOfYear.AddYears(1).AddDays(-1);
// ISO 8601 weeks start with Monday
// The first week of a year includes the first Thursday
// DayOfWeek returns 0 for sunday up to 6 for saterday
int[] iso8601Correction = { 6, 7, 8, 9, 10, 4, 5 };
int nds = fromDate.Subtract(startOfYear).Days + iso8601Correction[(int)startOfYear.DayOfWeek];
int wk = nds / 7;
switch (wk)
{
case 0:
// Return weeknumber of dec 31st of the previous year
return weekNumber(startOfYear.AddDays(-1));
case 53:
// If dec 31st falls before thursday it is week 01 of next year
if (endOfYear.DayOfWeek < DayOfWeek.Thursday)
return 1;
else
return wk;
default: return wk;
}
}
Noda Time handles this for you very easily:
Noda Time v1.x
using System;
using NodaTime;
public class Test
{
static void Main()
{
LocalDate date = new LocalDate(2012, 1, 1);
Console.WriteLine($"WeekYear: {date.WeekYear}"); // 2011
Console.WriteLine($"WeekOfWeekYear: {date.WeekOfWeekYear}"); // 52
}
}
Noda Time v2.x
using System;
using NodaTime;
using NodaTime.Calendars;
public class Test
{
static void Main()
{
LocalDate date = new LocalDate(2012, 1, 1);
IWeekYearRule rule = WeekYearRules.Iso;
Console.WriteLine($"WeekYear: {rule.GetWeekYear(date)}"); // 2011
Console.WriteLine($"WeekOfWeekYear: {rule.GetWeekOfWeekYear(date)}"); // 52
}
}
That's using the ISO calendar system where the week year starts in the first week with at least 4 days in that year. (Like CalendarWeekRule.FirstFourDayWeek.) If you want a different calendar system, specify it in the LocalDate constructor. Week year rules are handled slightly differently between 1.x and 2.x.
EDIT: Note that this gives the right value for both this situation (where the week-year is less than the calendar year) and the situation at the other end of the year, where the week-year can be more than the calendar year. For example, December 31st 2012 is in week 1 of week-year 2013.
That's the beauty of having a library do this for you: its job is to understand this sort of thing. Your code shouldn't have to worry about it. You should just be able to ask for what you want.
You can get the weeknumber according to the CalendarWeekRule in this way:
var d = new DateTime(2012, 01, 01);
System.Globalization.CultureInfo cul = System.Globalization.CultureInfo.CurrentCulture;
var firstDayWeek = cul.Calendar.GetWeekOfYear(
d,
System.Globalization.CalendarWeekRule.FirstDay,
DayOfWeek.Monday);
int weekNum = cul.Calendar.GetWeekOfYear(
d,
System.Globalization.CalendarWeekRule.FirstFourDayWeek,
DayOfWeek.Monday);
int year = weekNum >= 52 && d.Month == 1 ? d.Year - 1 : d.Year;
You probably want to compare CalendarWeekRule.FirstDay with CalendarWeekRule.FirstFourDayWeek. On this way you get the weeknumber and the year (DateTime.Year-1 if they differ).
CultureInfo.Calendar Property
Calendar.GetWeekOfYear Method
CalendarWeekRule Enumeration
That is just an edge case which you will have to add special code for. Get the year from the date string and then if the week = 52 and the month = 1 then subtract one from the year.
I have solving similar problem where the result should be in "YYYYWW" format. I wanted avoid hardcoded dates and using 3rd party libraries.
My test case was date 1.1.2017 which should return week 201652 (Iso YearWeek)
To get week number I have used thread: Get the correct week number of a given date which returns week number without the year.
Finally the correct year I got from Monday(first day of iso week) of required date:
// returns only week number
// from [Get the correct week number of a given date] thread
public static int GetIso8601WeekOfYear(DateTime time)
{
// Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll
// be the same week# as whatever Thursday, Friday or Saturday are,
// and we always get those right
DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
{
time = time.AddDays(3);
}
// Return the week of our adjusted day
var week = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
return week;
}
// returns int YearWeek in format "YYYYWW"
public static int GetIso8601YearWeekOfYear(DateTime time)
{
var delta = (-((time.DayOfWeek - CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek + 7) % 7));
var firstDayofWeek = time.AddDays(delta); // takeMonday
var week = GetIso8601WeekOfYear(time);
var yearWeek = (firstDayofWeek.Year * 100) + week;
return yearWeek;
}
In my approach I'm taking advantage of the fact, that GetWeekOfYear() displays a correct ISO-8601 week number for days with the same year as Thursday of the same week. So I look for Thursday that belongs to the same week as a given date, and then call GetWeekOfYear() on it.
I can't do that trick to get a correct year, as there's no iso8601-compliant method for this, so I make a year adjustment if Thursday belongs to a different year than a given date.
The solution is basically a three-liner:
using System.Globalization;
namespace TESTS
{
class Program
{
static void Main(string[] args)
{
//sample dates with correct week numbers in comments:
string[] dats = new string[] {
"2011-12-31","2012-01-01" //1152
,"2012-12-31","2013-01-01" //1301
,"2013-12-31","2014-01-01" //1401
,"2014-12-31","2015-01-01" //1501
,"2015-12-31", "2016-01-01" //1553
};
foreach (string str in dats)
{
Console.WriteLine("{0} {1}", str, GetCalendarWeek(DateTime.Parse(str)));
}
Console.ReadKey();
}
public static int GetCalendarWeek(DateTime dat)
{
CultureInfo cult = System.Globalization.CultureInfo.CurrentCulture;
// thursday of the same week as dat.
// value__ for Sunday is 0, so I need (true, not division remainder %) mod function to have values 0..6 for monday..sunday
// If you don't like casting Days to int, use some other method of getting that thursday
DateTime thursday = dat.AddDays(mod((int)DayOfWeek.Thursday-1,7) - mod((int)dat.DayOfWeek-1,7));
//week number for thursday:
int wk = cult.Calendar.GetWeekOfYear(thursday, cult.DateTimeFormat.CalendarWeekRule, cult.DateTimeFormat.FirstDayOfWeek);
// year adjustment - if thursday is in different year than dat, there'll be -1 or +1:
int yr = dat.AddYears(thursday.Year-dat.Year).Year;
// return in yyww format:
return 100 * (yr%100) + wk;
}
// true mod - helper function (-1%7=-1, I need -1 mod 7 = 6):
public static int mod(int x, int m)
{
return (x % m + m) % m;
}
}

Categories

Resources