Last Friday of each month

From Rosetta Code
Revision as of 04:25, 10 November 2011 by rosettacode>Kernigh (→‎{{header|UNIX Shell}}: GNU date???)
Last Friday of each month is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Write a program or a script that returns the last Fridays of each month of a given year provided as argument on the command line.

Example of an expected output:

./last_fridays 2012
2012-01-27
2012-02-24
2012-03-30
2012-04-27
2012-05-25
2012-06-29
2012-07-27
2012-08-31
2012-09-28
2012-10-26
2012-11-30
2012-12-28
Cf.

C

Doesn't work with Julian calendar (then again, you probably don't need to plan your weekends for middle ages).

<lang c>#define _XOPEN_SOURCE

  1. include <stdio.h>
  2. include <stdlib.h>

int main(int c, char *v[]) { int days[] = {31,29,31,30,31,30,31,31,30,31,30,31}; int m, y, w;

if (c < 2 || (y = atoi(v[1])) <= 1700) return 1;

	days[1] -= (y % 4) || (!(y % 100) && (y % 400));

w = y * 365 + (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + 6;

for(m = 0; m < 12; m++) { w = (w + days[m]) % 7; printf("%d-%02d-%d\n", y, m + 1, days[m] + (w < 5 ? -2 : 5) - w); }

return 0; }</lang>

C++

Library: Boost

called with ./last_fridays 2012 <lang cpp>#include <boost/date_time/gregorian/gregorian.hpp>

  1. include <iostream>
  2. include <cstdlib>

int main( int argc , char* argv[ ] ) {

  using namespace boost::gregorian ;
  greg_month months[ ] = { Jan , Feb , Mar , Apr , May , Jun , Jul ,
     Aug , Sep , Oct , Nov , Dec } ;
  greg_year gy = atoi( argv[ 1 ] ) ;
  for ( int i = 0 ; i < 12 ; i++ ) {
     last_day_of_the_week_in_month lwdm ( Friday , months[ i ] ) ;
     date d = lwdm.get_date( gy ) ;
     std::cout << d << std::endl ;
  }
  return 0 ;

}</lang> Output:

2012-Jan-27
2012-Feb-24
2012-Mar-30
2012-Apr-27
2012-May-25
2012-Jun-29
2012-Jul-27
2012-Aug-31
2012-Sep-28
2012-Oct-26
2012-Nov-30
2012-Dec-28

Icon and Unicon

This will write the last fridays for every year given as an argument. There is no error checking on the year.

<lang Icon>procedure main(A) every write(lastfridays(!A)) end

procedure lastfridays(year) every m := 1 to 12 do {

  d := case m of {
     2        : if IsLeapYear(year) then 29 else 28
     4|6|9|11 : 30
     default  : 31
     }                          # last day of month
      
  z := 0  
  j := julian(m,d,year) + 1     # first day of next month
  until (j-:=1)%7 = 4 do z -:=1 # backup to last friday=4
  suspend sprintf("%d-%d-%d",year,m,d+z)
  }

end

link datetime, printf</lang>

printf.icn provides formatting datetime.icn provides julian and IsLeapYear

Output:

last_fridays.exe 2012
2012-1-27
2012-2-24
2012-3-30
2012-4-27
2012-5-25
2012-6-29
2012-7-27
2012-8-31
2012-9-28
2012-10-26
2012-11-30
2012-12-28

J

<lang j>require'dates' last_fridays=: 12 {. [:({:/.~ }:"1)@(#~ 5 = weekday)@todate (i.366) + todayno@,&1 1</lang>

In other words, start from January 1 of the given year, and count forward for 366 days, keeping the fridays. Then pick the last friday within each represented month. Then pick the first 12 (since on a non-leap year which ends on a thursday we would get an extra friday).

Example use:

<lang j> last_fridays 2012 2012 1 27 2012 2 24 2012 3 30 2012 4 27 2012 5 25 2012 6 29 2012 7 27 2012 8 31 2012 9 28 2012 10 26 2012 11 30 2012 12 28</lang>

Java

Works with: Java version 1.5+

<lang java5>import java.text.DateFormatSymbols; import java.util.Calendar; import java.util.GregorianCalendar;

public class LastFriday { private static int[] months = {Calendar.JANUARY, Calendar.FEBRUARY, Calendar.MARCH, Calendar.APRIL, Calendar.MAY, Calendar.JUNE, Calendar.JULY, Calendar.AUGUST, Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER, Calendar.DECEMBER}; public static void main(String[] args){ int year = Integer.parseInt(args[0]); boolean leapYear = new GregorianCalendar().isLeapYear(year); for(int month:months){ int days = 31; switch(month){ case Calendar.SEPTEMBER: case Calendar.APRIL: case Calendar.JUNE: case Calendar.NOVEMBER: days = 30; break; case Calendar.FEBRUARY: days = leapYear ? 29 : 28; default: } GregorianCalendar date = new GregorianCalendar(year, month, days); while(date.get(Calendar.DAY_OF_WEEK) != Calendar.FRIDAY){ date.add(Calendar.DAY_OF_MONTH, -1); } String monthStr = new DateFormatSymbols().getShortMonths()[month]; System.out.println(monthStr +" "+ date.get(Calendar.DAY_OF_MONTH)); } } }</lang> Output (for java LastFriday 2012):

Jan 27
Feb 24
Mar 30
Apr 27
May 25
Jun 29
Jul 27
Aug 31
Sep 28
Oct 26
Nov 30
Dec 28

OCaml

Using the module Unix from the standard OCaml library:

<lang ocaml>#load "unix.cma" open Unix

let usage() =

 Printf.eprintf "%s <year>\n" Sys.argv.(0);
 exit 1

let print_date t =

 Printf.printf "%d-%02d-%02d\n" (t.tm_year + 1900) (t.tm_mon + 1) t.tm_mday

let is_date_ok tm t =

 (tm.tm_year = t.tm_year &&
  tm.tm_mon  = t.tm_mon  &&
  tm.tm_mday = t.tm_mday)

let () =

 let _year =
   try int_of_string Sys.argv.(1)
   with _ -> usage()
 in
 let year = _year - 1900 in
 let fridays = Array.make 12 (Unix.gmtime 0.0) in
 for month = 0 to 11 do
   for day_of_month = 1 to 31 do
     let tm = { (Unix.gmtime 0.0) with 
       tm_year = year;
       tm_mon = month;
       tm_mday = day_of_month;
     } in
     let _, t = Unix.mktime tm in
     if is_date_ok tm t  (* check for months that have less than 31 days *)
     && t.tm_wday = 5  (* is a friday *)
     then fridays.(month) <- t
   done;
 done;
 Array.iter print_date fridays</lang>

Output:

$ ocaml last_fridays.ml 2012
2012-01-27
2012-02-24
2012-03-30
2012-04-27
2012-05-25
2012-06-29
2012-07-27
2012-08-31
2012-09-28
2012-10-26
2012-11-30
2012-12-28

With a dedicated library

<lang ocaml>open CalendarLib

let usage() =

 Printf.eprintf "%s <year>\n" Sys.argv.(0);
 exit 1

let print_date (year, month, day) =

 Printf.printf "%d-%02d-%02d\n" year month day

let () =

 let year =
   try int_of_string Sys.argv.(1)
   with _ -> usage()
 in
 let fridays = ref [] in
 for month = 1 to 12 do
   let num_days = Date.days_in_month (Date.make_year_month year month) in
   let rec aux day =
     if Date.day_of_week (Date.make year month day) = Date.Fri
     then fridays := (year, month, day) :: !fridays
     else aux (pred day)
   in
   aux num_days
 done;
 List.iter print_date (List.rev !fridays)</lang>

Run this script with the command:

ocaml unix.cma str.cma -I +calendar calendarLib.cma last_fridays.ml 2012

Perl

<lang Perl>#!/usr/bin/perl -w use strict ; use DateTime ; use feature qw( say ) ;

foreach my $month ( 1..12 ) {

  my $dt = DateTime->last_day_of_month( year => $ARGV[ 0 ] , month => $month ) ;
  while ( $dt->day_of_week != 5 ) {
     $dt->subtract( days => 1 ) ;
  }
  say $dt->ymd ;

}</lang> Output:

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

Pike

<lang Pike>int(0..1) last_friday(object day) {

  return day->week_day() == 5 && 
         day->month_day() > day->month()->number_of_days()-7; 

}

int main(int argc, array argv) {

   array days = filter(Calendar.Year((int)argv[1])->months()->days()[*], last_friday);
   write("%{%s\n%}", days->format_ymd());
   return 0;

}</lang>

Ruby

<lang ruby>require 'date'

def last_friday(year, month)

 if month == 12
   d = Date.new(year+1, 1, 1)
 else
   d = Date.new(year, month+1, 1)
 end
 begin
   d -= 1
 end until d.wday == 5 # Ruby 1.9: d.friday?
 d

end

year = Integer(ARGV.shift) (1..12).each {|month| puts last_friday(year, month)}</lang>

Library: ActiveSupport

Using the ActiveSupport library for some convenience methods

<lang ruby>require 'rubygems' require 'activesupport'

def last_friday(year, month)

 d = Date.new(year, month, 1).end_of_month
 until d.wday == 5
   d = d.yesterday
 end
 d

end</lang>

Tcl

<lang tcl>package require Tcl 8.5 set year [lindex $argv 0] foreach dm {02/1 03/1 04/1 05/1 06/1 07/1 08/1 09/1 10/1 11/1 12/1 12/32} {

   # The [clock scan] code is unhealthily clever; use it for our own evil purposes
   set t [clock scan "last friday" -base [clock scan $dm/$year -gmt 1] -gmt 1]
   # Print the interesting part
   puts [clock format $t -format "%Y-%m-%d" -gmt 1]

}</lang> Sample execution:

$ tclsh8.5 lastfri.tcl 2012
2012-01-27
2012-02-24
2012-03-30
2012-04-27
2012-05-25
2012-06-29
2012-07-27
2012-08-31
2012-09-28
2012-10-26
2012-11-30
2012-12-28

UNIX Shell

Using ncal. Will switch to Julian calender as ncal sees fit, and will not calculate past year 9999 (chances are you'll be too dead by then to worry about weekends anyway). <lang bash>#!/bin/sh

if [ -z $1 ]; then exit 1; fi

  1. weed out multiple erros due to bad year

ncal 1 $1 > /dev/null && \ for m in 01 02 03 04 05 06 07 08 09 10 11 12; do echo $1-$m-`ncal $m $1 | grep Fr | sed 's/.* \([0-9]\)/\1/'` done</lang>

Using date --date from GNU date??? This code is not portable.

<lang bash>#!/bin/sh

  1. Free code, no limit work
  2. $Id: lastfridays,v 1.1 2011/11/10 00:48:16 gilles Exp gilles $
  1. usage :
  2. lastfridays 2012 # prints last fridays of months of year 2012

debug=${debug:-false}

  1. debug=true

epoch_year_day() { #set -x x_epoch=`expr ${2:-0} '*' 86400 + 43200` date --date="${1:-1970}-01-01 UTC $x_epoch seconds" +%s }

year_of_epoch() { date --date="1970-01-01 UTC ${1:-0} seconds" +%Y } day_of_epoch() { LC_ALL=C date --date="1970-01-01 UTC ${1:-0} seconds" +%A } date_of_epoch() { date --date="1970-01-01 UTC ${1:-0} seconds" "+%Y-%m-%d" } month_of_epoch() { date --date="1970-01-01 UTC ${1:-0} seconds" "+%m" }

last_fridays() { year=${1:-2012}

       next_year=`expr $year + 1`
       $debug && echo "next_year $next_year"
       current_year=$year
       day=0
       previous_month=01
       while test $current_year != $next_year; do
       	$debug && echo "day $day"
       	current_epoch=`epoch_year_day $year $day`
       	$debug && echo "current_epoch $current_epoch"
       	current_year=`year_of_epoch $current_epoch`
       	current_day=`day_of_epoch $current_epoch`
       	$debug && echo "current_day $current_day"
       	test $current_day = 'Friday' && current_friday=`date_of_epoch $current_epoch`
       	$debug && echo "current_friday $current_friday"
       	current_month=`month_of_epoch $current_epoch`
       	$debug && echo "current_month $current_month"
       	# Change of month => previous friday is the last of month
       	test "$previous_month" != "$current_month" \
       		&& echo $previous_friday
       	
       	previous_month=$current_month
       	previous_friday=$current_friday
       	day=`expr $day + 1`
       done

}

  1. main

last_fridays ${1:-2012}</lang>

Sample execution:

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