Reputation: 7805
I know how to get a week number for a ISO format. There are some nice questions and answers about this.
I would like now to know how to find the week number in the US format. The US week number rules says the week 1 is always the week containing the 1st of january, so I'm safe in January. But how to know the week number of 1st of march of a given year?
Teoretically I imagine it has to take into account which day in the week the year starts, find out the length of that years februari and iterate untill I reach the right day, adding weeks to it.
In my logic should be something like:
function getUSweekNumber(y, m, d) {
var firstOfJanuary = new Date(y, 0, 1);
var dayOfWeek = firstOfJanuary.getDay() || 7;
var weekNr = 1;
for (var i = 0; i < m; i++) {
var daysInMonth = new Date(y, i + 1, 0).getDate();
weekNr += parseInt(daysInMonth / 7, 10);
dayOfWeek += daysInMonth % 7;
}
weekNr += parseInt((dayOfWeek + d) / 7, 10);
return weekNr;
}
but I get wrong results:
console.log(getUSweekNumber(2015, 0, 1)); // 1
console.log(getUSweekNumber(2015, 0, 15)); // 3
console.log(getUSweekNumber(2015, 7, 1)); // 32 <- wrong, whould be 31
What am I missing? Is there a better way?
Upvotes: 1
Views: 441
Reputation: 20737
Your approach with a loop seems somewhat complicated. Your initial thinking that you need the day of the week is good, but you do not use it correctly.
The US week starts on a sunday, and the first week is the week of January 1st. Let's take a closer look of the first week of 2015:
| 28 | 29 | 30 | 31 | 1 | 2 | 3 |
| Su | Mo | Tu | We | Th | Fr | Sa |
For ease of computing, we want to shift the week, so that the first 7 days are week 1. How do we do that? Well, by using the day of the week. Because Sunday is already day 0, we don't need to alter it in any way. If January 1st is on a Sunday, we don't need to shift the week.
We can easily compute the amount of days between two dates. Because of daylight saving, we have to round the number we get, and since we don't shift the clock with more than 12 hours, we are fine normalizing that way. Then we just need to divide the number by 7 to get the week number.
function getUSweekNumber(y, m, d) {
//January 1st this year
var beginOfThisYear = new Date(y, 0, 1);
var dayOfWeek = beginOfThisYear.getDay();
//January 1st next year
var beginOfNextYear = new Date(y+1, 0, 1);
var dayOfWeekNextYear = beginOfNextYear.getDay();
//Provided date
var currentDay = new Date(y, m, d);
var oneDay = 1000 * 60 * 60 * 24;
var numberOfDays = 1 + Math.round((currentDay.getTime() - beginOfThisYear.getTime()) / oneDay);
var weekNr;
if( currentDay.getTime() >= beginOfNextYear.getTime() - (oneDay * dayOfWeekNextYear) ) {
//First week of next year
weekNr = 1;
} else {
//Shift week so 1-7 are week 1, then get week number
weekNr = Math.ceil((numberOfDays + dayOfWeek) / 7);
}
return weekNr;
}
console.log(getUSweekNumber(2015, 0, 1)); // 1
console.log(getUSweekNumber(2015, 0, 15)); // 3
console.log(getUSweekNumber(2015, 7, 1)); // 31
console.log(getUSweekNumber(2015, 11, 27)); //1
Upvotes: 1
Reputation: 7204
A one line solution
It appears that the selected answer might have a bug. For example, when given the date 30-DEC-2015 it should return 1 because that week contains 1-JAN-2016. Yet, the function instead returns 53.
So the problem is how to handle the final days from the previous year that fall within the first week of the current year?
One possible way to deal with it is to adjust the target date so that it always starts on the last day of the week rather than any day of the week. And this can be done by using the day value (0-6) as an offset like so:
var endDate = targetDate.getUTCDate() + 6 - targetDate.getUTCDay();
This works for USA week numbering and could also be made to work with ISO-8601 week numbering with minor modifications (not shown). We can then plug this snippet into a one line solution like so:
var weeks=Math.ceil((((d.setUTCDate(d.getUTCDate()+6-d.getUTCDay())-d.setUTCMonth(0,1))/86400000)+d.getUTCDay()+1)/7);
And our function simply becomes:
function getWeekUS( date ) {
var d = new Date( date );
return Math.ceil(((( d.setUTCDate( d.getUTCDate() + 6 - d.getUTCDay()) - d.setUTCMonth( 0, 1 )) / 86400000 ) + d.getUTCDay() + 1 ) / 7 );
}
Explained:
We first make a copy of the date to avoid altering the original. We then add an offset so the target date falls on the last day of the week. Then we set the date to 1-JAN and find the difference between that and the target date. We divide that number by 8640000 (24*60*60*1000) to convert milliseconds to days. Finally, we add the starting date offset and then divide by 7 to get weeks.
And now our function returns the correct week for edge cases like 30-DEC-2015.
Upvotes: 1
Reputation: 1706
use http://momentjs.com. They define the method week
:
function getUSweekNumber(y, m, d) {
var date = moment({ year: y, month: m, day: d });
return date.week();
}
Upvotes: 0