13
\$\begingroup\$

Your task is to output a single number; the number of ISO weeks that a given date range intersects. To quote Wikipedia: An average year is exactly 52.1775 weeks long, but this is not about the average.

Input consists of two space-separated ISO dates:

0047-12-24 2013-06-01

The end date is never before the start date. We'll use the extrapolated Gregorian calendar for simplicity.

Test cases:

Format: input -> output
2015-12-31 2016-01-01 -> 1    (both are within week 53 of 2015)
2016-01-03 2016-01-04 -> 2    (the 3rd is within week 53, and the 4th is in week 1)
2015-12-24 2015-12-24 -> 1    (this single day is of course within a single week)

Your solution should handle dates between 0001-01-01 and 9999-12-31.

\$\endgroup\$
  • 2
    \$\begingroup\$ Can the input be an array of strings instead of a single space-separated string? Alternatively, can the two dates be two arguments to a function? \$\endgroup\$ – Doorknob Dec 17 '15 at 13:20
  • \$\begingroup\$ I assume you mean Proleptic Gregorian Calendar. \$\endgroup\$ – Brad Gilbert b2gills Dec 17 '15 at 13:23
  • \$\begingroup\$ Also, do weeks start on Sunday or Monday? According to your second test case, it's Monday, but just making sure. \$\endgroup\$ – Doorknob Dec 17 '15 at 13:23
  • \$\begingroup\$ @Doorknob冰 Wikipedia quote: "A date is specified by the ISO week-numbering year in the format YYYY, a week number in the format ww prefixed by the letter 'W', and the weekday number, a digit d from 1 through 7, beginning with Monday and ending with Sunday" So starting on Monday as per ISO 8601 \$\endgroup\$ – Tensibai Dec 17 '15 at 13:26
  • 2
    \$\begingroup\$ According to the ISO standard, Weeks start with Monday. \$\endgroup\$ – Filip Haglund Dec 17 '15 at 13:26
2
\$\begingroup\$

Ruby, 89 88 86 bytes

->s{a,b=s.split.map{|x|Time.gm *x.split(?-)}
n=1
(a+=86400).wday==1&&n+=1until a==b
n}

A very primitive solution: given the first date a and the second date b, increment a, check if it's a Monday (if so, we've "rolled over" to a new week), and stop once a is b.

Takes input in the exact format specified in the question: a single space-separated string. (The way it parses the input is fancy-ish: it uses Ruby's "splat" operator, *. This operator "expands" an array or enumerable out, so Time.gm *[2013, 06, 01] becomes Time.gm 2013, 06, 01.)

Imported from a comment discussion:

We don't care about "weeks in a year;" we only care about "weeks overall." So whether a year has 52 or 53 weeks doesn't make any difference.

Just to avoid future confusion with whether this handles year boundaries correctly—according to Wikipedia, all weeks always start on Monday and end on Sunday.

For some more fancy tricks, these are all equivalent:

until a==b;a+=86400;n+=a.wday==1?1:0;end
[a+=86400,n+=a.wday==1?1:0]until a==b
n+=(a+=86400).wday==1?1:0 until a==b
(a+=86400).wday==1&&n+=1 until a==b
(a+=86400).wday==1&&n+=1until a==b
\$\endgroup\$
  • \$\begingroup\$ Can't you can save 1 char replacing a==b by a<b ? \$\endgroup\$ – Tensibai Dec 17 '15 at 15:50
1
\$\begingroup\$

VBA , 125 Bytes

Expecting 2 function inputs as strings

Function u(k,j)
f=DateValue(k)
For i=0 To (DateValue(j)-f)
q=DatePart("ww",f+i,2,2)
If q<>j Then u=u+1
j=q
Next
End Function

Pretty sure This can be golfed. This is looping though everyday between the 2 dates and increments the counter every time the ISO date changes.

DatePart("ww",f+i,2,2) is the ISO week number in VBA the first 2 means Monday (complies with ISO standard 8601, section 3.17). then second 2 means Week that has at least four days in the new year (complies with ISO standard 8601, section 3.17)

140 bytes w/single input

Function g(k)
y=Split(k)
f=DateValue(y(0))
For i=0 To (DateValue(y(1))-f)
q=DatePart("ww",f+i,2,2)
If q<>j Then g=g+1
j=q
Next
End Function
\$\endgroup\$
1
\$\begingroup\$

R, 76 bytes

d=as.Date(scan("","raw"));sum(strftime(seq(d[1],d[2],"day")[-1],"%w")==1)+1

After the talk with @Doorknob, it's finally as easy as counting the Mondays.

seq allow to create a vector of dates (of each day between two dates) and strftime transform it to the 'number of the day in the week', now counts the times it's 1 to get the number of time you changed week and add 1 to take in account previous week.

Another approach looping:

R, 85 bytes

n=1;d=as.Date(scan("","raw"));z=d[1];while(z<d[2]){n=n+(strftime(z<-z+1,"%w")==1)};n
\$\endgroup\$
  • 1
    \$\begingroup\$ Regarding your second approach, you should only add 1 to the total number of Mondays if the first date does not fall on a Monday. \$\endgroup\$ – DavidC Dec 18 '15 at 3:09
  • \$\begingroup\$ @DavidCarraher Thanks, reading your answer made me think I could remove the first day of range, thus fixing the problem :) \$\endgroup\$ – Tensibai Dec 18 '15 at 15:39
0
\$\begingroup\$

Mathematica 135 79 96 84 74 bytes

Consistent with @Doorknob's suggestion, this

  • generates all the dates in the range

  • counts the number of Mondays (ISOWeekDay =="1") in the DateRange(excluding d), and

  • adds 1 to the total.


Count[DateString[#,"ISOWeekDay"]&/@Rest@(DateRange@@StringSplit@#),"1"]+1&

Count[DateString[#,"ISOWeekDay"]&/@Rest@(DateRange@@StringSplit@#),"1"]+1&
/@ {"2015-12-31 2016-01-01", "2016-01-04 2016-01-05","2016-01-03 2016-01-04", 
"2015-12-24 2015-12-24", "1876-12-24 2016-01-01"}

{1, 1, 2, 1, 7255}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.