Find the last Sunday in every month of a given year

21

1

F# solutions known inside 140 chars, and this is a Rosetta Code problem.

Required result on stdout or in a string variable for input year 2014:

2014-01-26 2014-02-23 2014-03-30 2014-04-27 2014-05-25 2014-06-29 2014-07-27 2014-08-31 2014-09-28 2014-10-26 2014-11-30 2014-12-28

As it was requested, for 1900:

1900-01-28 1900-02-25 1900-03-25 1900-04-29 1900-05-27 1900-06-24 1900-07-29 1900-08-26 1900-09-30 1900-10-28 1900-11-25 1900-12-30

And 2000:

2000-01-30 2000-02-27 2000-03-26 2000-04-30 2000-05-28 2000-06-25 2000-07-30 2000-08-27 2000-09-24 2000-10-29 2000-11-26 2000-12-31

Posed because dates seem to bring out awkwardness in most languages. More than permitting date libraries, I hope to see them! But if external to the base language, declare in the name of the post (e.g. C# + Jon Skeet's NodaTime).

Clarifications:

  • Year range 1900 to 3015
  • Gregorian calendar
  • If it otherwise matters, whatever is conventional for UK/London.
  • A program taking a commandline switch or stdin is fine, producing the result to stdout
  • A function taking a value for the year and returning a string is also fine.

Standard loopholes excluded. Looking forward to the APL, J, K solutions, and seeing some new date libraries.

Phil H

Posted 2014-12-16T11:15:16.877

Reputation: 1 376

@Sp3000 - 1752 could be particularly awkward :-) – r3mainer – 2014-12-16T12:14:52.580

@MartinBüttner: Please do use date libraries, have edited the question to ask people to declare those they use with the language. – Phil H – 2014-12-16T14:43:54.817

1You should specify the range of years which are valid input, and considerations about Gregorian adoption. (I.e. if the range of years includes any before 1930 then you should specify either that the Gregorian calendar should be used for the entire range, regardless of locale; or that the output can vary by locale; or you should give a cutoff date before which the Julian calendar should be used and test cases for the three years around the changeover). – Peter Taylor – 2014-12-16T14:45:37.267

@MartinBüttner: leading zeroes are there in the example output, so yes please. – Phil H – 2014-12-16T14:53:41.493

1@squeamishossifrage: I've limited it to 1900 and Gregorian, because I'd rather avoid a research project to establish the spec... – Phil H – 2014-12-16T14:58:36.020

I didn't see the requirement, so before I just go ahead with it: Do the dates need to be in ascending order or can they be from Dec 31->Jan 1 for the given year? :) – Brandon – 2014-12-17T19:50:02.830

@PhilH Sorry for letting you wait so long for your desired answer. Is it too late to get a check-mark?

– Adám – 2016-12-14T19:45:45.543

1@Adám: Sorry to make you wait so long for a reply :) Yes indeed. – Phil H – 2016-12-20T11:34:26.817

Answers

1

Dyalog APL with cal from dfns, 19 bytes

Better late than never!

Prompts for year, returns list of dates in yyyy m d format.

⎕{⍺⍵,2↑⊢⌿cal⍺⍵}¨⍳12

prompt for numeric input and let that be the left argument to the

{ ... anonymous function (found below) applied to each of

⍳12 the numbers from 1 to 12 (the months)

The above anonymous function is as follows:

⍺⍵, prepend the left and right arguments (i.e. year and month) to

2↑ the first two characters of

⊢⌿ the bottom-most row of

cal the calendar for

⍺⍵ left argument and right argument (year and month)

TryAPL online:

  1. Return to this page after clicking here to import cal and its dependencies.

  2. Click here to run the test cases.

Adám

Posted 2014-12-16T11:15:16.877

Reputation: 37 779

Very good. Had hoped for an APL library that understood what the dates mean, but cal is reasonable! – Phil H – 2016-12-20T11:36:15.727

@PhilH date and days?

– Adám – 2016-12-20T11:51:07.973

1

@PhilH There's also the Dates namespace from MiServer.

– Adám – 2016-12-20T11:53:22.613

1

@PhilH And Dyalog APL's dyadic primitives + - < = and work with .Net date objects.

– Adám – 2016-12-20T19:01:13.167

7

Ruby, 91+6=97

#!ruby -prdate
$_=(Date.new(y=$_.to_i)...Date.new(y+1)).select(&:sunday?).chunk(&:mon).map{|k,v|v[-1]}*' '

Works out pretty well. select(&:sunday?) is pretty, and surprisingly, *' ' does all of the formatting by itself.

histocrat

Posted 2014-12-16T11:15:16.877

Reputation: 20 600

Nice tricks! You can save three more chars by using chunk instead of group_by. – Cristian Lupascu – 2014-12-16T20:53:25.310

So I can, nice. – histocrat – 2014-12-16T21:01:35.303

6

Bash 4.x + ncal, 57

If newline separators are OK instead of spaces, then we can remove the -n switch and trailing space from the echo statement. And I guess it will still work without the shebang, so I removed that too:

for i in {01..12};{ echo "$1-$i-`ncal $i $1|tail -c-3`";}

Original script (73 bytes):

#!/bin/bash
for i in {01..12};{ echo -n "$1-$i-`ncal $i $1|tail -c-3` ";}

Usage:

$ bash sundays.sh 2014
2014-01-26
2014-02-23
2014-03-30
2014-04-27
2014-05-25
2014-06-29
2014-07-27
2014-08-31
2014-09-28
2014-10-26
2014-11-30
2014-12-28
$

Note: Bash versions prior to 4.0 will omit the leading zeroes from the months. This can be fixed with the addition of 5 characters by changing {01..12} to `seq -w 1 12)`. Also, tail -c-3 might cause problems on some systems where the output of ncal includes trailing spaces, but I'm not aware of any that do.

r3mainer

Posted 2014-12-16T11:15:16.877

Reputation: 19 135

1Is the difference really related to Darwin, not just the Bash version? Was added in Bash 4.0 (though there were some bugs in it later). Anyway, 1 character can still be spared by using \…`` instead of good habit $(…). – manatwork – 2014-12-16T12:25:49.207

Ah, could be. Darwin says it's using version 3.2.53; Debian is using 4.1.5. – r3mainer – 2014-12-16T12:28:54.320

@manatwork P.S. Just noticed your comment about the back-ticks. Good catch, thanks! – r3mainer – 2014-12-16T15:02:18.987

I don't think you need to count the #!/bin/bash for the purposes of golfing. – Digital Trauma – 2014-12-17T05:45:52.713

@DigitalTrauma That's good. It looks like I can use line breaks instead of spaces too. Down to 57 bytes now :-) – r3mainer – 2014-12-17T10:11:02.653

6

IBM DFSORT, 11 3 lines of 71, 72 or 80 characters

 OPTION COPY 
 OUTFIL REPEAT=12,OVERLAY=(5:SEQNUM,2,ZD,5,2,1,8,Y4T,LASTDAYM,TOJUL=Y4T*
 ,9,7,Y4T,ADDDAYS,+1,TOJUL=Y4T,1:16,7,Y4T,PREVDSUN,TOGREG=Y4T(-),12X) 

The two answers with columnar output format have stood the test of time. That gives me a "loop", sort of, in that on OUTFIL REPEAT= copies the current record that many times.

Different technique to get to the value, which seems longer but is shorter as I can't work out any unconditional way to deal with the 12th record being in the following year, and making it conditional means including IFTHEN=(WHEN=, twice, and some other stuff. Gain on the swings (first of month is simplest way to do it) lose heavily on the roundabouts (particular syntax requirements).

This uses an inbuilt function (all functions in DFSORT are inbuilt) to find the last day of the month. Then adds one day (function) to get to first of following month and uses the PREVDSUN function to get the previous Sunday (which will always be the last Sunday in the previous month, as before).

When turning the year (input) into a valid date, a two-digit sequence number is used for the month, and that value is copied for the day as well, since the starting point does not matter as long as valid, as we are after the last day of the month initially: 5,2 is shorter than C'01'.

Here's the detail:

OPTION COPY - copy input file to output

OUTFIL - to allow multiple output files, with different selection and formatting, produce formatted reports. Used in preference to the shorter INREC because of the use of REPEAT=.

REPEAT=12 - produce 12 copies of each record. In this example, there can be only one input record (unlike the previous version) because of the SEQNUM.

5: - start at column 5 on the record.

SEQNUM,2,ZD - sequence number, defaults to start at one, two digits, "zoned decimal" (for unsigned, which they will be, same as character).

1,8 - copy bytes 1 for length 8 to current location (9). This is because the Y4T need to see that 8, else a different date format will be used.

Y4T - ccyymmdd-format date (due to the 8 immediately in front of it).

LASTDAYM - Last day of Month (also possible of Week, Quarter and Year).

TOJUL= - output date-conversion for date functions (TOJUL is one character less than TOGREG)

9,7 - now that it is 7 long, Y4T is going to be CCYYDDD.

ADDDAYS - adds a number of days, adjusting automatically if goes into following month/year (could also be ADDMONS and ADDYEARS)

PREVDSUN - the Julian date comes in, previous Sunday is located, TOGREG to get the correct output format, with the "-" seperator (could be anything you like as separator)

12X - blanks to clear up the mess which has allowed us to do it in such a short way

The output from the above, for 2014, is:

2014-01-26
2014-02-23
2014-03-30
2014-04-27
2014-05-25
2014-06-29
2014-07-27
2014-08-31
2014-09-28
2014-10-26
2014-11-23
2014-12-28

Something is required to tell the SORT what to do. There is no default. OPTION COPY is the shortest, SORT FIELDS=COPY is equivalent but longer.

The work itself it done this time in OUTFIL (to allow the use of REPEAT). The working code is arguably any of 160 (2 * 80), 144 (2 * 72), 140 (72 + 69), or 138 (70 + 68) (excluding the leading blanks, forced continuation and trailing blanks).

Given that the receiver would have to know what they are doing, I think I can say that the DFSORT code to list the last Sunday of each month for any year from 1900 (will run from year 0001, but I'm avoiding the research as well) up to 9999 (although DFSORT supports years up to 9999, previous solution would not work in year 9999 since the 12th date goes into the following year) can be Tweeted.

Why is the code so long, if there are especially apt inbuilt-functions?

Field-definitions are ephemeral. A field is only defined as a particular location within the data (which is a record) for its immediate use. To put it another way, fields are not defined as such, but are defined for each use and only for use. Date functions need to know which (of many) formats of date are used for the source, and the output must be in a date format, so that has to be specified.

Now that we have a Julian date.... TBC?


 OPTION COPY 
 INREC OVERLAY=(1,4,C'0201',1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8*
 ,94:C'1',89:1,4,ZD,ADD,+1,ZD,LENGTH=4,14:C'3',22:C'4',30:C'5',38:C'6',*
 46:C'7',54:C'8',62:C'9',69:C'10',77:C'11',85:C'12',127:X,89,8,Y4T,PREV*
 DSUN,TOGREG=Y4T(-),116:X,81,8,Y4T,PREVDSUN,TOGREG=Y4T(-),105:X,73,8,Y4*
 T,PREVDSUN,TOGREG=Y4T(-),94:X,65,8,Y4T,PREVDSUN,TOGREG=Y4T(-),83:X,57,*
 8,Y4T,PREVDSUN,TOGREG=Y4T(-),72:X,49,8,Y4T,PREVDSUN,TOGREG=Y4T(-),61:X*
 ,41,8,Y4T,PREVDSUN,TOGREG=Y4T(-),50:X,33,8,Y4T,PREVDSUN,TOGREG=Y4T(-),*
 39:X,25,8,Y4T,PREVDSUN,TOGREG=Y4T(-),28:X,17,8,Y4T,PREVDSUN,TOGREG=Y4T*
 (-),17:X,09,8,Y4T,PREVDSUN,TOGREG=Y4T(-),1:1,8,Y4T,PREVDSUN,TOGREG=Y4T*
 (-),11:X,18,120,6X) 

Needs some JCL

//LASTSUNG EXEC PGM=SORT 
//SYSOUT   DD SYSOUT=* 
//SORTOUT  DD SYSOUT=* 
//SYSIN    DD * 

And an input file (another line of JCL and three instream items of data):

//SORTIN DD *
2014 
1900 
2000 

Produces:

2014-01-26 2014-02-23 2014-03-30 2014-04-27 2014-05-25 2014-06-29 2014-07-27 2014-08-31 2014-09-28 2014-10-26 2014-11-30 2014-12-28
1900-01-28 1900-02-25 1900-03-25 1900-04-29 1900-05-27 1900-06-24 1900-07-29 1900-08-26 1900-09-30 1900-10-28 1900-11-25 1900-12-30
2000-01-30 2000-02-27 2000-03-26 2000-04-30 2000-05-28 2000-06-25 2000-07-30 2000-08-27 2000-09-24 2000-10-29 2000-11-26 2000-12-31

Will actually work up to the year 9999.

DFSORT is IBM's Mainframe sorting product. Data can be manipulated, but since sorting is key and sorts are often large and long-running, the DFSORT control cards have no looping constructs, so we can't put a SORT into a loop. Makes things a bit long-winded for tasks like Golf.

Why to post the answer, is because DFSORT has a PREVDday function. So last Sunday in a month is easy. It is the Sunday previous (PREVDSUN) to the first day of the following month.

It was also fun to do it within one "operand" (OVERLAY), a bit like doing it all within sprintf or similar.

Here it is ungolfed:

 OPTION COPY 

 INREC OVERLAY=(1,4,C'0201',1,8,1,8,1,8,1,8,1,8,1,8, 
         1,8,1,8,1,8,1,8, 
         1,8,94:C'1',89:1,4,ZD,ADD,+1,ZD,LENGTH=4, 
         14:C'3',22:C'4',30:C'5',38:C'6',46:C'7',54:C'8',
         62:C'9',69:C'10',77:C'11',85:C'12', 
        127:X,89,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
        116:X,81,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
        105:X,73,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         94:X,65,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         83:X,57,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         72:X,49,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         61:X,41,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         50:X,33,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         39:X,25,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         28:X,17,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         17:X,09,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
          1:1,8,Y4T,PREVDSUN,TOGREG=Y4T(-), 
         11:X,18,120,6X) 

Whilst not quite abuse, it wouldn't be usual to attempt to cram all this into one OVERLAY, and there is some seemingly unnecessary stuff which is needed to allow it all to go into one OVERLAY. There is some room for golfing, but since it would only remove one line at most, I'm not tempted.

The INREC is processed for each record.

OVERLAY allows the content of an existing record to be changed. If the record is extended beyond its length in the process, that is not a problem.

1,4 is the year coming in. It has a literal of 0201 appended to it, and then the successive 1,8s repeat it 11 times to give one long chuck of 96 bytes,

The 12th year on the extended current record gets 1 added to it, and its month made to 1 (January).

The remaining 10 months are changed to 3 through 11.

Then there are 12, in reverse order (due to OVERLAY) of these type of thing:

127:X,89,8,Y4T,PREVDSUN,TOGREG=Y4T(-),

The n: is a column-number on the record. The X inserts a blank. 89,8 takes the data from that column/length, Y4T treats it as a CCYYMMDD date, PREVDSUM works out the previous Sunday,TOGREG=Y4T(-) outputs it as a Gregorian CCYY-MM-DD date.

Because you get rubbish if the source and target of a particular part of an OVERLAY overlap destructively, the final 11:X,18,120,6X) rearranges and masks a bit of mess.

Manuals and papers can be found at: http://www-01.ibm.com/support/docview.wss?uid=isg3T7000080, and includes the 900+ page DFSORT Application Programming Guide.

As with all IBM products all manuals are available for free (except an excruciatingly small amount of very expensive ones which only a very small number of people in the world would even pretend to understand).

All DFSORT Control Cards must start with a blank. Column 72 is only used for continuation (any non-blank will do, but * is conventional). Column 72 is followed by a sequence number area which is ignored, making the each record 80 bytes.

Another couple of solutions to come, maybe.

Bill Woodger

Posted 2014-12-16T11:15:16.877

Reputation: 1 391

5

Bash, 63 bytes

for i in {01..12};{  date -v30d -v${i}m  -v2014y  -vsun +%Y-%m-%d;}

Output:

2014-01-26
2014-02-23
2014-03-30
2014-04-27
2014-05-25
2014-06-29
2014-07-27
2014-08-24
2014-09-28
2014-10-26 
2014-11-30
2014-12-28

michael501

Posted 2014-12-16T11:15:16.877

Reputation: 191

for i in {1..12};{ date -v30d -v${i}m -v$1y -v0w +%Y-%m-%d;} - 60 bytes – Digital Trauma – 2014-12-17T05:35:45.247

-v param to date is specific to BSD date. So this works on OSX, but not on most Linux - perhaps that should be stated in the answer. – Digital Trauma – 2014-12-17T05:36:49.497

@DigitalTrauma , works on mac and my Linux. – michael501 – 2014-12-17T12:29:32.983

4

Python 2 - 189 bytes

y=input()
for m in range(12):
 d=[31-(1322>>m&1),28+(y%400<1or 1>y%4<y%100)][m==1];Y=y-(m<2);Z=Y%100;C=Y/100
 while(d+13*((m-2)%12+4)/5+Z+Z/4+C/4-2*C)%7-1:d-=1
 print"%d-%02d-%d"%(y,m+1,d),

Enter the date via STDIN.

Still a lot more golfing that can be done. The program goes a little overboard, just for fun:

  • No imports, in particular not using any built-in date functions
  • Uses Zeller's congruence for calculating days of the week

Notes

  • 1322 is a magic lookup table for determining whether a non-February month has 30 or 31 days
  • No zfill is needed for years due to the input range, nor days as they will always be over 20

Python 2 - 106 bytes

The not-as-fun solution:

from calendar import*
y=input();m=1
while m<13:w,n=monthrange(y,m);print"%d-%02d-%d"%(y,m,n-(n+w)%7),;m+=1

calendar.monthrange returns two numbers: the weekday the month starts on (w) and the number of days in the month (n). The solution is a little counterintuitive due to a catch — the weekday returned starts at 0 for Monday, not Sunday! However, this is offset by the fact that n is 1-based.

Sp3000

Posted 2014-12-16T11:15:16.877

Reputation: 58 729

1A very silly Pyth answer: $from calendar import monthrange as gt$V12AGH>QhN%"%d-%02d-%d"(QhN-H%+GH7 – FryAmTheEggman – 2014-12-16T17:58:57.167

3

JavaScript, ES6, 222 219 199 bytes

I did not see any JavaScript answer in the rosetta wiki.

Here we go:

S=Y=>{for(l=[],p=new Date(Y,i=0);i<365+!(Y%4);)if(!(d=new Date(Y,0,++i,9)).getDay()){p.getMonth()-d.getMonth()&&l.push(p);p=new Date(d)}return[...l,p].map(x=>x.toISOString().split("T")[0]).join(" ")}

This creates a function S which returns a string with the desired output. the function also takes care of leap years.

Due to ES6, this only works in a latest Firefox.

Thanks to apsillers for the tip which got it reduced to 200 bytes

Find the ungolfed version below as a stack snippet which you can run here itself:

S=Y=>{
  for(l=[],p=new Date(Y,i=0);i<365+!(Y%4);)
    if(!(d=new Date(Y,0,++i,9)).getDay()){
      p.getMonth()-d.getMonth()&&l.push(p);
      p=new Date(d)
    }
  return[...l,p].map(x=>x.toISOString().split("T")[0]).join(" ")
}

alert(S(parseInt(prompt())))

Optimizer

Posted 2014-12-16T11:15:16.877

Reputation: 25 836

You can use +prompt() rather than parseInt and shave off some bytes – Jacob – 2014-12-16T12:51:10.433

@Jacob That prompt is not added to the byte count. – Optimizer – 2014-12-16T12:54:54.973

OIC. I should've read the question... – Jacob – 2014-12-16T12:56:22.190

@apsillers Thanks a tonne! Fixed the issue and reduced it a lot based on your tip. – Optimizer – 2014-12-16T17:56:00.507

Input 2100 output 2100-01-31 2100-02-28 2100-03-28 2100-04-25 2100-05-30 2100-06-27 2100-07-25 2100-08-29 2100-09-26 2100-10-31 2100-11-28 2100-12-26 2101-01-02 is wrong. – Qwertiy – 2014-12-16T22:12:38.960

@Qwertiy Fixed. – Optimizer – 2014-12-17T08:54:25.893

Input 2100 output 2099-12-31 2100-01-31 2100-02-28 2100-03-28 2100-04-25 2100-05-30 2100-06-27 2100-07-25 2100-08-29 2100-09-26 2100-10-31 2100-11-28 2100-12-26from the snippet – Bill Woodger – 2014-12-17T12:51:25.760

@BillWoodger I don't get that. I get 2100-01-31 2100-02-28 2100-03-28 2100-04-25 2100-05-30 2100-06-27 2100-07-25 2100-08-29 2100-09-26 2100-10-31 2100-11-28 2100-12-26 – Optimizer – 2014-12-17T12:55:41.507

I click on Run code snippet, enter 2100 in the box that arrives, and then get the result as previously pasted. – Bill Woodger – 2014-12-17T12:59:49.547

2101 looks OK at a glance. 2500 same as 2100, 2200, 2300, 2400 all OK. – Bill Woodger – 2014-12-17T13:02:30.927

@BillWoodger Must be timezone related. What timezone are you in ? – Optimizer – 2014-12-17T13:09:39.567

I'm all WET/GMT – Bill Woodger – 2014-12-17T13:17:05.437

new Date(Y,0,++i,9) - I expect it works only with UTC to UTC+9? – Qwertiy – 2014-12-17T23:50:05.470

3

JavaScript (ES6) 155 145

Edit Fixed timezone problems Could be shorter if made recursive. Maybe.

F=y=>{
  for(n=i=o=[];!o[11];)
    d=new Date(Date.UTC(y,0,++i)),m=d.getMonth(),
    d.getDay()||(m!=n&&o.push(p.toISOString().slice(0,10)),p=d,n=m);
  return o.join(' ')
}

edc65

Posted 2014-12-16T11:15:16.877

Reputation: 31 086

You can use new Date(y,0,++i,9) . Also, this fails for 2100 and above leap years as JS does not have info about those leap years and thus, does not have Feb 29 at all in leap years for 2100 and above. – Optimizer – 2014-12-17T05:42:32.450

@Optimizer it's not JS: 2100,2200,2300 are not leap years. 2014 is a leap year and JS knows. As for using hour 9, I can't verify but I think that doesn'to work if you are for instance in Melbourne... – edc65 – 2014-12-17T08:35:08.300

Ah.. Never knew we drop 3 days per 400 yrs. About the 9 - I changed my timezone from -1000 (Hawaii) to +1100 (Melbourne) and new Date(2014,0,26,9) was a Sunday giving correct ISO string as well as getDay() as 0. – Optimizer – 2014-12-17T08:53:39.713

3

Rebol - 120 116 80 79 76

d: do join"1-1-"input print collect[for m 2 13 1[d/2: m keep d - d/weekday]]


Ungolfed + some annotations:

d: do join "1-1-" input         ;; golfy way to create Rebol date! datatype 1-Jan-(year)

print collect [
    for m 2 13 1 [              ;; loop thru months 2 to 13!
        d/2: m                  ;; move to (1st of) next month
        keep d - d/weekday      ;; collect/keep last sunday of month
    ]
]

Example of the sunday calculation in Rebol console:

>> ; get last sunday of Jan 2014

>> d: 1-1-2014
== 1-Jan-2014

>> d/month: d/month + 1
== 2

>> d
== 1-Feb-2014

>> d/weekday
== 6

>> d - d/weekday
== 26-Jan-2014

>> ; above is last sunday of Jan 2014
>> ; and when pass end of year (ie. month 13!)

>> d/month: 13
== 13

>> d
== 1-Jan-2015

draegtun

Posted 2014-12-16T11:15:16.877

Reputation: 1 592

Potential 87: d: 1-1-1 d/year: do input print collect[repeat m 12[d/month: m + 1 keep d - d/weekday]] – rgchris – 2015-01-06T06:03:43.253

@rgchris Thanks Chris. Was able to shave another 7 chars of it to. – draegtun – 2015-01-06T09:23:08.930

Nice!! It's bad, but never really think of FOR as being a shortcut. – rgchris – 2015-01-06T20:57:16.453

2

CJam, 122 102 bytes

30li:X400%){[1387Yb30f+~28I!I4%!I100%e&|g+\]W%{\_2$>7*-\7%7\m1$+}/}fI;]12/W=12,{101+s1>}%]z{X+W%'-*S}/

This does not use any form of date library. It can be also still be golfed a lot, I think.

Test it here.

Martin Ender

Posted 2014-12-16T11:15:16.877

Reputation: 184 808

3I'm so spectacularly stoked that a CJam answer isn't the top answer in a code-golf challenge for once. I can die happy.. Today is a good day (until it's golfed down to 6 bytes, obviously) – Brandon – 2014-12-17T19:25:26.440

@Brandon: That was the kind of reason I thought it would be interesting. Really hoping to see some wondrous library that makes this easy, but disappointed so far. – Phil H – 2015-01-08T17:01:34.677

1

R, 128 chars

P=paste;f=format;a=strptime(P(1:366,scan()),"%j %Y");cat(sort(sapply(split(a,f(a,"%B")),function(x)P(tail(x[f(x,"%u")==7],1)))))

With line breaks:

P=paste
f=format
a=strptime(P(1:366,scan()),"%j %Y")
cat(sort(sapply(split(a,f(a,"%B")),function(x)P(tail(x[f(x,"%u")==7],1)))))

plannapus

Posted 2014-12-16T11:15:16.877

Reputation: 8 610

1

C# 255

Ungolfed

static void Main(string[] a)
    {
        int y = Int32.Parse(Console.ReadLine());
        DateTime d = new DateTime(y, 1, 1);
        while (d.Year == y)
        {
            if (d.DayOfWeek == DayOfWeek.Sunday && d.Day>(DateTime.DaysInMonth(y,d.Month)-7))
                Console.WriteLine(d.ToShortDateString());
            d = d.AddDays(1);
        }
        Console.ReadKey();
    }

Edit: modified to print only the last Sunday :)

bacchusbeale

Posted 2014-12-16T11:15:16.877

Reputation: 1 235

Not the required output format. + This is code golf – edc65 – 2015-01-02T12:51:08.307

1

"Oh, no, him again!"

Java - 259 246 bytes

void g(int y){for(int i=;i<12;i++){GregorianCalendar c=new GregorianCalendar(y,i,0);c.set(c.DAY_OF_WEEK,c.SUNDAY);c.set(c.DAY_OF_WEEK_IN_MONTH,-1);System.out.print(y+"-"+String.format("%02d",(c.get(c.MONTH)+1))+"-"+(c.get(c.DAY_OF_MONTH))+" ");}}

Ungolfed version:

void g(int y){
    for (int i = 0; i < 12;i++) {
        GregorianCalendar c = new GregorianCalendar(y, i, 0);
        c.set(c.DAY_OF_WEEK, c.SUNDAY);
        c.set(c.DAY_OF_WEEK_IN_MONTH, -1);
        System.out.print(y+"-"+String.format("%02d",(c.get(c.MONTH)+1))+"-"+(c.get(c.DAY_OF_MONTH))+" ");
    }
}

Usage:

import java.util.GregorianCalendar;
import java.util.Scanner;

public class LastSundayInYear {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Year?");
        int year = scanner.nextInt();
        LastSundayInYear sunday = new LastSundayInYear();
        sunday.g(year); 
    }

    void g(int y){
        for (int i = -1; ++i < 12;) {
            GregorianCalendar c = new GregorianCalendar(y, i, 0);
            c.set(c.DAY_OF_WEEK, c.SUNDAY);
            c.set(c.DAY_OF_WEEK_IN_MONTH, -1);
            System.out.print(y+"-"+String.format("%02d",(c.get(c.MONTH)+1))+"-"+(c.get(c.DAY_OF_MONTH))+" ");
        }
    }
}

Output:

2014-01-26 2014-02-23 2014-03-30 2014-04-27 2014-05-25 2014-06-29 2014-07-27 2014-08-31 2014-09-28 2014-10-26 2014-11-30 2014-12-28

Yet another "let's put a Java reply just for kicks" answer. Oh well. But, at least, since you bothered to reach this point of my answer, I'll try to bore you some more and explain my reasoning.

The method g receives the desired year and, for each month, creates a GregorianCalendar object (months go from 0 to 11). Then, the first c.set sets the day of the week as a Sunday and the second declares that we want the last week of the month - as seen on the official documentation. The System.out.println prints out the date of that Sunday (if we were doing this right, the year would be printed as c.get(c.YEAR), but using y again shaves off 13 characters), the month has to be formatted to add a leading zero from January to September (and the value is incremented because, well, months in here are represented 0-11) and the day of last Sunday is printed. And this procedure is repeated for the other eleven months.

Rodolfo Dias

Posted 2014-12-16T11:15:16.877

Reputation: 3 940

1

q, 67

{({{1<>x mod 7}-[;1]/x}')14h$1_til[13]+13h$"D"$(($)x),".01.01"}

tmartin

Posted 2014-12-16T11:15:16.877

Reputation: 3 917

Are there no date libraries usable for this in q? – Phil H – 2016-12-20T11:32:59.213

0

C#, 212, 237

string x(int y){var s="";var t="";var d=new DateTime(y,1,1);for(;;){if(d.Year!=y){return s;}t=(d.DayOfWeek==DayOfWeek.Sunday)?t=string.Format("{0}-{1}-{2} ",d.Year,d.Month,d.Day):t;s=(d.AddDays(1).Month!=d.Month)?s+=t:s;d=d.AddDays(1);}}

With line breaks

string x(int y)
    {
        var s = "";
        var t = "";
        var d = new DateTime(y,1,1);
        for (;;)
        {
            if (d.Year != y) {
                return s;
            }
            t = (d.DayOfWeek == DayOfWeek.Sunday) ? t = string.Format("{0}-{1}-{2} ", d.Year, d.Month, d.Day) : t;
            s=(d.AddDays(1).Month!=d.Month)?s+=t:s;
            d=d.AddDays(1);
        }
    }

Output for 2014

"2015-1-25 2015-2-22 2015-3-29 2015-4-26 2015-5-31 2015-6-28 2015-7-26 2015-8-30 2015-9-27 2015-10-25 2015-11-29 2015-12-27"

Darren Breen

Posted 2014-12-16T11:15:16.877

Reputation: 31

Not the required output format – edc65 – 2015-01-02T12:46:34.423

There, fixed. Better? – Darren Breen – 2015-01-02T14:02:18.260

0

C# 171

Function returning a string.

string S(int y){var r="";for(int m=1;m<13;++m){var d=new System.DateTime(y,1,1).AddMonths(m).AddDays(-1);r+=y+string.Format("-{0:00}-{1} ",m,d.Day-d.DayOfWeek);}return r;}

Ungolfed

string S(int y)
{
    var r="";
    for (int m=1;m<13;++m)
    {
        var d = new System.DateTime(y, 1, 1).AddMonths(m).AddDays(-1);
        r += y + string.Format("-{0:00}-{1} ", m, d.Day - d.DayOfWeek);
    }
    return r;
}

edc65

Posted 2014-12-16T11:15:16.877

Reputation: 31 086

0

VB-192

Function z(y)
For i = 1 To 11
a = 0
s = IIf(i <> 11, DateSerial(y, i + 1, 1), DateSerial(y + 1, 1, 1))
While Weekday(s - a) <> 1
a = a + 1
Wend
r = r + Str(s - a) + " "
Next
z = r
End Function

Could be worse ^^

My second and last entry (don't think i can get it smaller)

142

Function z(y)
Dim m(12)
For i = 1 To 366
s = DateSerial(y, 1, 1) + i
If Weekday(s) = 1 Then m(Month(s)) = s
Next
z = Join(m, " ")
End Function

dwana

Posted 2014-12-16T11:15:16.877

Reputation: 531

0

C# 194

using Linq:

string d(int y){return string.Join(" ",Enumerable.Range(1,12).Select(m=>new DateTime(y,m,DateTime.DaysInMonth(y,m))).Select(d=>d.AddDays(-(int)d.DayOfWeek)).Select(d=>d.ToString("yyy-MM-dd")));}

Ungolfed

string d(int y)
{
    return string.Join(" ",Enumerable.Range(1,12)
        .Select(m => new DateTime(y, m, DateTime.DaysInMonth(y, m)))
        .Select(d => d.AddDays(-(int)d.DayOfWeek))
        .Select(d => d.ToString("yyy-MM-dd")));
}

Output

2013-01-27 2013-02-24 2013-03-31 2013-04-28 2013-05-26 2013-06-30 2013-07-28 2013-08-25 2013-09-29 2013-10-27 2013-11-24 2013-12-29

Manuel Schweigert

Posted 2014-12-16T11:15:16.877

Reputation: 121

0

Mathematica - 171

Wrapped in an anonymous function, returns the string

StringJoin[Last@#~DateString~{"Year","-","Month","-","Day"," "}&/@GatherBy[Select[DateRange[DateObject[{#}],DateObject[{#+1}]],DayName@#==Sunday&],DateValue[#,"Month"]&]]&

First mathematica golf. I feel like it could be substantially reduced.

globby

Posted 2014-12-16T11:15:16.877

Reputation: 1 132

0

Ruby 76

Uses a commandline parameter ruby sundays.rb 1900. Uses the Date library.

require'date';puts (1..12).map{|m|d=Date.new($*[0].to_i,m,-1);d-d.wday}*" "

steenslag

Posted 2014-12-16T11:15:16.877

Reputation: 2 070