JavaScript (Node.js), 166 bytes
d=>{w=(d-978336e6)/r+.5&7
for(D=0,e=new Date(d.getTime()+(7-w)*r);e.getMonth()!=0||e.getDate()!=4;D++)e.setTime(e.getTime()-r)
return[e.getYear(),8+D>>3,w+1]}
r=864e5
Takes input as a Javascript date object. Outputs as a 3-element list [week-year, week number, weekday number]
. The week-numbering year is expressed as a 2-digit year (year minus 1900). If this is not acceptable, change e.getYear()
to e.getFullYear()
for +4 bytes.
Huh?
r=864e5 // milliseconds in a day
d=>{ // Take d as a date object
w= // w is 1 less than the week number
(d-978336e6) // milliseconds since Jan 1, 2001
/r+ // Divide to get days
.5&7 // Round (up or down) to nearest integer, and take mod 8.
// This rounding smooths over DST and related variations
// Variations over 12 hours do not occur, as far as I know
for(
D=0, // D will be the number of days since the last Jan 4
e=new Date(d.getTime()+(7-w)*r); // Initialize e to be the end of this week
e.getMonth()!=0||e.getDate()!=4; // While e is not Jan 4 of any year:
D++ // Increment D
) e.setTime(e.getTime()-r) // Set e to the day before
return [
e.getYear(), // The week-numbering year of d is the same as the Gregorian year of the preceding Jan 4
8+D>>3, // Convert days elapsed into weeks since Jan 4
w+1 // the week number
]
}