Code Golf Asked on December 10, 2021
Back in 1965, The Beatles released their hit song ‘Eight Days a Week’.
In this code-golf challenge we are going to reimagine dates of the 21st century as if there really were eight days a week.
A Gregorian calendar date between 1 January 2001 and 31 December 2100 inclusive. You may take input in any convenient format (including built-in date objects).
The weekday number, week number, and week-numbering year (all defined below) corresponding to the input date. You may use any format (to be specified in your answer) in which all three numbers are unambiguously identifiable.
The week date system, based on the ISO week date system but modified for eight-day weeks, works like this:
Given that the number of days in a Gregorian calendar year is never a multiple of 8, the above rules have two important consequences:
Input (Gregorian yyyy-mm-dd) -> Output (week-numbering year, week number, weekday number)
2001-01-01 -> 2001, 1,1
2001-01-08 -> 2001, 1,8
2001-01-09 -> 2001, 2,1
2001-12-31 -> 2001,46,5
2002-01-01 -> 2001,46,6
2002-01-04 -> 2002, 1,1
2002-12-29 -> 2002,45,8
2002-12-30 -> 2003, 1,1
2020-02-29 -> 2020, 8,7
2037-09-13 -> 2037,32,5
2061-04-23 -> 2061,15,4
2100-12-31 -> 2101, 1,4
Related, but that challenge involves standard 7-day weeks, has finicky I/O requirements, and bans date/time libraries and built-ins.
Saved 2 bytes by 0-indexing weekday.
Solved without looping
SELECT
year(z|7^1),(datepart(y,z|7^1)+7)/8,z%8FROM(SELECT
datediff(d,2,@)z)t
In order to show this method provides the correct results, I have included a link to compare all test cases from the question. This link is not 0 indexed to allow easy comparison
Answered by t-clausen.dk on December 10, 2021
def c(t):
y,w,d=2001,1,1;D=type(t);O=D.toordinal
for o in range(730487,O(t)+1):
y,w,d=(y,w,d+1)if d<8 else(y,w+1,1)if O(D(y+1,1,4))not in range(o,o+8)else(y+1,1,1)
return y,w,d
My approach counts years, weeks and days for every proleptic Gregorian ordinal between 2001-01-01
and the date.
c
expects a datetime.date
instance of a date on or after 2001-01-01
.
Every day the day counter is incremented unless a week has passed.
Every week the day counter is reset and the week counter is incremented, unless january 4th of the next year is in the next week.
Every year the week counter is reset and the year counter is incremented.
The magic number 730487
is the ordinal of 2001-01-02
:
730487 == datetime.date(2001, 1, 2).toordinal()
c returns a tuple of integers containing the year, week number and day of the week in that order.
week number and day of the week are >=1
.
Answered by steviestickman on December 10, 2021
Expects a Date object. Returns [year, week, weekday]
.
d=>(g=n=>(w=/an 0[^9]/.test(x=new Date(99,24,n))?++y/y:w+1,q=(d-x)/864e5)>3?g(n+8):[y,w,5+q])(-3,w=y=2e3)
We start a few days before January 1, 2001 and progressively advance in the future, adding 8 days at each iteration. We increment the year and reset the week number each time we reach the 4th of January. We stop as soon as the target date is passed.
The most important part in the code is:
/an 0[^9]/.test(x = new Date(99, 24, n))
When the year argument is less than 100, the Date()
constructor interprets it as 19xx. So, new Date(99, 24, n)
means 24 months and n-1 days after January 1, 1999, or n-1 days after January 1, 2001.
When passed to the .test()
method, the date is implicitly turned into a string. For instance, new Date(99, 24, 5)
is converted to:
"Fri Jan 05 2001 00:00:00 GMT+0000 (Coordinated Universal Time)"
January is the only month whose 3-letter abbreviation ends in -an
. So /an 0[^9]/
is used to test if the date is between January 1 and January 8 (both included).
What we really want to know is whether we are between January 4 and January 11, but the corresponding regular expression would be significantly longer. It's shorter to do it that way and use an offset of -3 days instead. This is why n is initialized to -3.
d => ( // d = input date
g = n => // g is a recursive function taking a number of days n
( //
w = // update w:
/an 0[^9]/ // if the following date x is between January 1 and
.test( // January 8 (meaning that x + 3 days is within the
x = // week including the 4th of January)
new Date( // where x is defined as ...
99, 24, n // ... n-1 days after January 1, 2001
) // (24 months and n-1 days after January 1, 1999)
) ? // then:
++y / y // increment the year y and set w to 1
: // else:
w + 1, // increment w
q = (d - x) // if x + 3 days is less than the target date d
/ 864e5 // i.e. the difference in days q between d and x
) > 3 ? // is greater than 3:
g(n + 8) // do a recursive call with n + 8,
// i.e. one '8-day week' later
: // else:
[ // return the result array:
y, // year
w, // week number
5 + q // weekday: 8 + (q - 3)
] // end of array
)(-3, w = y = 2e3) // initial call to g with n = -3 and y = 2000
Answered by Arnauld on December 10, 2021
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
]
}
Answered by fireflame241 on December 10, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP