Find the last Sunday of each month

Write a program or a script that returns the last Sundays of each month of a given year. The year may be given through any simple input method in your language (command line, std in, etc.).

Task
Find the last Sunday of each month
You are encouraged to solve this task according to the task description, using any language you may know.

Example of an expected output:


./last_sundays 2013
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
Cf.

Ada

The program from [[1]] solves this task, as well.

Output:
>./last_weekday_in_month sunday 2013
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

C++

<lang cpp>

  1. include <windows.h>
  2. include <iostream>
  3. include <string>

//-------------------------------------------------------------------------------------------------- using namespace std;

//-------------------------------------------------------------------------------------------------- class lastSunday { public:

   lastSunday()
   {

m[0] = "JANUARY: "; m[1] = "FEBRUARY: "; m[2] = "MARCH: "; m[3] = "APRIL: "; m[4] = "MAY: "; m[5] = "JUNE: "; m[6] = "JULY: "; m[7] = "AUGUST: "; m[8] = "SEPTEMBER: "; m[9] = "OCTOBER: "; m[10] = "NOVEMBER: "; m[11] = "DECEMBER: ";

   }
   void findLastSunday( int y )
   {

year = y; isleapyear();

int days[] = { 31, isleap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, d; for( int i = 0; i < 12; i++ ) { d = days[i]; while( true ) { if( !getWeekDay( i, d ) ) break; d--; } lastDay[i] = d; }

display();

   }

private:

   void isleapyear()
   {

isleap = false; if( !( year % 4 ) ) { if( year % 100 ) isleap = true; else if( !( year % 400 ) ) isleap = true; }

   }
   void display()
   {

system( "cls" ); cout << " YEAR " << year << endl << "=============" << endl; for( int x = 0; x < 12; x++ ) cout << m[x] << lastDay[x] << endl;

cout << endl << endl;

   }
   int getWeekDay( int m, int d )
   {

int y = year;

int f = y + d + 3 * m - 1; m++; if( m < 3 ) y--; else f -= int( .4 * m + 2.3 );

f += int( y / 4 ) - int( ( y / 100 + 1 ) * 0.75 ); f %= 7;

return f;

   }
   int lastDay[12], year;
   string m[12];
   bool isleap;

}; //-------------------------------------------------------------------------------------------------- int main( int argc, char* argv[] ) {

   int y;
   lastSunday ls;
   while( true )
   {

system( "cls" ); cout << "Enter the year( yyyy ) --- ( 0 to quit ): "; cin >> y; if( !y ) return 0;

ls.findLastSunday( y );

system( "pause" );

   }
   return 0;

} //-------------------------------------------------------------------------------------------------- </lang> Output:

  YEAR 2013
=============
JANUARY:   27
FEBRUARY:  24
MARCH:     31
APRIL:     28
MAY:       26
JUNE:      30
JULY:      28
AUGUST:    25
SEPTEMBER: 29
OCTOBER:   27
NOVEMBER:  24
DECEMBER:  29

Perl

<lang Perl>#!/usr/bin/perl use strict ; use warnings ; use DateTime ;

for my $i( 1..12 ) {

  my $date = DateTime->last_day_of_month( year => $ARGV[ 0 ] , 

month => $i ) ;

  while ( $date->dow != 7 ) {
     $date = $date->subtract( days => 1 ) ;
  }
  my $ymd = $date->ymd ;
  print "$ymd\n" ;

}</lang> 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

REXX

This REXX example is an exact replication of the Rosetta Code

  • find last Fridays of each month for any year

except for the innards of the first DO loop.

The   lastDOW   subroutine can be used for any day-of-the-week for any month for any year. <lang rexx>/*REXX program displays dates of last Sundays of each month for any year*/ parse arg yyyy

                  do j=1 for  12
                  _ = lastDOW('Sunday', j, yyyy)
                  say right(_,4)'-'right(j,2,0)"-"left(word(_,2),2)
                  end  /*j*/

exit /*stick a fork in it, we're done.*/ /*┌────────────────────────────────────────────────────────────────────┐

 │ lastDOW:  procedure to return the date of the  last day-of-week of │
 │           any particular month  of any particular year.            │
 │                                                                    │
 │ The  day-of-week  must be specified (it can be in any case,        │
 │ (lower-/mixed-/upper-case)  as an English name of the spelled day  │
 │ of the week,   with a minimum length that causes no ambiguity.     │
 │ I.E.:   W  for Wednesday,   Sa  for Saturday,   Su  for Sunday ... │
 │                                                                    │
 │ The month can be specified as an integer   1 ──► 12                │
 │    1=January     2=February     3=March     ...     12=December    │
 │ or the English  name  of the month,  with a minimum length that    │
 │ causes no ambiguity.    I.E.:  Jun  for June,   D  for December.   │
 │ If omitted  [or an asterisk(*)],  the current month is used.       │
 │                                                                    │
 │ The year is specified as an integer or just the last two digits    │
 │ (two digit years are assumed to be in the current century,  and    │
 │ there is no windowing for a two-digit year).                       │
 │ If omitted  [or an asterisk(*)],  the current year is used.        │
 │ Years < 100   must be specified with  (at least 2)  leading zeroes.│
 │                                                                    │
 │ Method used: find the "day number" of the 1st of the next month,   │
 │ then subtract one  (this gives the "day number" of the last day of │
 │ the month,  bypassing the leapday mess).   The last day-of-week is │
 │ then obtained straightforwardly,   or  via subtraction.            │
 └────────────────────────────────────────────────────────────────────┘*/

lastdow: procedure; arg dow .,mm .,yy . /*DOW = day of week*/ parse arg a.1,a.2,a.3 /*orig args, errmsg*/ if mm== | mm=='*' then mm=left(date('U'),2) /*use default month*/ if yy== | yy=='*' then yy=left(date('S'),4) /*use default year */ if length(yy)==2 then yy=left(date('S'),2)yy /*append century. */

                  /*Note mandatory leading blank in strings below.*/

$=" Monday TUesday Wednesday THursday Friday SAturday SUnday" !=" JAnuary February MARch APril MAY JUNe JULy AUgust September",

 " October November December"

upper $ ! /*uppercase strings*/ if dow== then call .er "wasn't specified",1 if arg()>3 then call .er 'arguments specified',4

 do j=1 for 3                                       /*any plural args ?*/
 if words(arg(j))>1       then call .er 'is illegal:',j
 end

dw=pos(' 'dow,$) /*find day-of-week*/ if dw==0 then call .er 'is invalid:',1 if dw\==lastpos(' 'dow,$) then call .er 'is ambigious:',1

if datatype(mm,'month') then /*if MM is alpha...*/

 do
 m=pos(' 'mm,!)                                     /*maybe its good...*/
 if m==0                  then call .er 'is invalid:',1
 if m\==lastpos(' 'mm,!)  then call .er 'is ambigious:',2
 mm=wordpos(word(substr(!,m),1),!)-1                /*now, use true Mon*/
 end

if \datatype(mm,'W') then call .er "isn't an integer:",2 if \datatype(yy,'W') then call .er "isn't an integer:",3 if mm<1 | mm>12 then call .er "isn't in range 1──►12:",2 if yy=0 then call .er "can't be 0 (zero):",3 if yy<0 then call .er "can't be negative:",3 if yy>9999 then call .er "can't be > 9999:",3

tdow=wordpos(word(substr($,dw),1),$)-1 /*target DOW, 0──►6*/

                                                    /*day# of last dom.*/

_=date('B',right(yy+(mm=12),4)right(mm//12+1,2,0)"01",'S')-1 ?=_//7 /*calc. DOW, 0──►6*/ if ?\==tdow then _=_-?-7+tdow+7*(?>tdow) /*not DOW? Adjust.*/ return date('weekday',_,"B") date(,_,'B') /*return the answer*/

.er: arg ,_;say; say '***error!*** (in LASTDOW)';say /*tell error, and */

 say word('day-of-week month year excess',arg(2)) arg(1) a._
 say; exit 13                                       /*... then exit.   */</lang>

output when using the default input (the current year, 2013):

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