Skip to content

Commit

Permalink
Fix edge case in strftime %V (ISO 8601 week number) (#16494)
Browse files Browse the repository at this point in the history
strftime() now returns correct week number *51* instead of *50* for
date 2018-12-17. Code is based on musl's ``strftime.c:week_num()``.

Signed-off-by: Christian Heimes <christian@python.org>
  • Loading branch information
tiran authored Mar 15, 2022
1 parent b674062 commit 43ad4ce
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 26 deletions.
46 changes: 20 additions & 26 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -862,34 +862,28 @@ LibraryManager.library = {
// or more days in the new year, then it is considered week 1.
// Otherwise, it is the last week of the previous year, and the next week is week 1.
// Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
var janFourthThisYear = new Date(date.tm_year+1900, 0, 4);
var janFourthNextYear = new Date(date.tm_year+1901, 0, 4);

var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);

var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);

if (compareByDay(endDate, firstWeekStartThisYear) < 0) {
// if given date is before this years first week, then it belongs to the 53rd week of last year
return '53';
var val = Math.floor((date.tm_yday + 7 - (date.tm_wday + 6) % 7 ) / 7);
// If 1 Jan is just 1-3 days past Monday, the previous week
// is also in this year.
if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) {
val++;
}

if (compareByDay(firstWeekStartNextYear, endDate) <= 0) {
// if given date is after next years first week, then it belongs to the 01th week of next year
return '01';
}

// given date is in between CW 01..53 of this calendar year
var daysDifference;
if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) {
// first CW of this year starts last year
daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate()
} else {
// first CW of this year starts this year
daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate();
if (!val) {
val = 52;
// If 31 December of prev year a Thursday, or Friday of a
// leap year, then the prev year has 53 weeks.
var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7;
if (dec31 == 4 || (dec31 == 5 && __isLeapYear(date.tm_year%400-1))) {
val++;
}
} else if (val == 53) {
// If 1 January is not a Thursday, and not a Wednesday of a
// leap year, then this year has only 52 weeks.
var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7;
if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year)))
val = 1;
}
return leadingNulls(Math.ceil(daysDifference/7), 2);
return leadingNulls(val, 2);
},
'%w': function(date) {
return date.tm_wday;
Expand Down
15 changes: 15 additions & 0 deletions tests/core/test_strftime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,21 @@ int main() {
size = strftime(s, sizeof(s), "%Y-%m-%d %W %U", &tm);
test(!cmp(s, "2013-01-07 01 01"), "strftime test #36a", s);

// strftime %V (ISO 8601 week number) edge cases
time_t dec17 = 1481932800;
gmtime_r(&dec17, &tm);
size = strftime(s, sizeof(s), "%Y-%m-%d %G %V %w", &tm);
test(!cmp(s, "2016-12-17 2016 50 6"), "strftime test #37", s);

dec17 = 1513468800;
gmtime_r(&dec17, &tm);
size = strftime(s, sizeof(s), "%Y-%m-%d %G %V %w", &tm);
test(!cmp(s, "2017-12-17 2017 50 0"), "strftime test #37a", s);

dec17 = 1545004800;
gmtime_r(&dec17, &tm);
size = strftime(s, sizeof(s), "%Y-%m-%d %G %V %w", &tm);
test(!cmp(s, "2018-12-17 2018 51 1"), "strftime test #37b", s);

return 0;
}
3 changes: 3 additions & 0 deletions tests/core/test_strftime.out
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ strftime test #34: 1
strftime test #35: 1
strftime test #36: 1
strftime test #36a: 1
strftime test #37: 1
strftime test #37a: 1
strftime test #37b: 1

0 comments on commit 43ad4ce

Please sign in to comment.