Five weekends

From Rosetta Code
Revision as of 17:12, 13 November 2010 by 69.114.215.82 (talk)
Task
Five weekends
You are encouraged to solve this task according to the task description, using any language you may know.

The month of October in 2010 has five Fridays, five Saturdays, and five Sundays.

The task

  1. Write a program to show all months that have this same characteristic of five full weekends from the year 1900 through 2100 (Gregorian calendar).
  2. Show the number of months with this property (there should be 201).
  3. Show at least the first and last five dates, in order.

Algorithm suggestions

  • Count the number of Fridays, Saturdays, and Sundays in every month.
  • Find all of the 31-day months that begin on Friday.

Extra credit

Count and/or show all of the years which do not have at least one five-weekend month (there should be 29).

D

<lang d>import std.gregorian ; // this module currently work in progress import std.stdio, std.algorithm, std.array, std.range ;

Date[] m5w(Date start = Date(1900,1,1), Date end = Date(2100,12,31) ) {

   Date[] res ;    
   for(Date when = Date(start.year, start.month, 1) ; // adjust to 1st day
       when < end ; 
       when = Date(when.year, 1 + when.month, 1)) {
               
       if(when.endOfMonthDay == 31)   // Such month must has 3 + 4*7 
           if(when.dayOfWeek == 5 )   // days and start at friday
               res ~= when ;          // for 5 FULL weekends.
   }
   return res ;

}

bool noM5wByYear(int year) {

   return (m5w(Date(year,1,1), Date(year,12,31)).length == 0 ) ;

}

void main() {

   auto m = m5w() ; // use default input       
   writefln("There are %d months of which the first and last five are:",
       m.length) ;     
   foreach(d;m[0..5]~m[$-5..$]) 
       writefln("%s", d.toSimpleString[0..$-2]) ;
   auto noM5w = filter!(noM5wByYear)(iota(1900,2101)) ;
   writefln("There are %d years in the range that do not have "
            "months with five weekends", array(noM5w).length) ;

}</lang>

output match python one.

J

<lang j>require 'types/datetime numeric' find5wkdMonths=: verb define

 years=. range 2{. y
 months=. 1 3 5 7 8 10 12
 m5w=. (#~ 0 = weekday) >,{years;months;31   NB. 5 full weekends iff 31st is Sunday(0)
 >'MMM YYYY' fmtDate toDayNo m5w

)</lang> Usage: <lang j> # find5wkdMonths 1900 2100 NB. number of months found 201

  (5&{. , '...' , _5&{.) find5wkdMonths 1900 2100   NB. First and last 5 months found

Mar 1901 Aug 1902 May 1903 Jan 1904 Jul 1904 ... Mar 2097 Aug 2098 May 2099 Jan 2100 Oct 2100

  # (range -. {:"1@(_ ". find5wkdMonths)) 1900 2100   NB. number of years without 5 weekend months

29</lang>

Java

<lang java>import java.util.Calendar; import java.util.GregorianCalendar;

public class FiveFSS {

   private static boolean[] years = new boolean[201];
   //dreizig tage habt september...
   private static int[] month31 = {Calendar.JANUARY, Calendar.MARCH, Calendar.MAY,
       Calendar.JULY, Calendar.AUGUST, Calendar.OCTOBER, Calendar.DECEMBER};
   public static void main(String[] args) {
       StringBuilder months = new StringBuilder();
       int numMonths = 0;
       for (int year = 1900; year <= 2100; year++) {
           for (int month : month31) {
               Calendar date = new GregorianCalendar(year, month, 1);
               if (date.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {
                   years[year - 1900] = true;
                   numMonths++;
                   //months are 0-indexed in Calendar
                   months.append((date.get(Calendar.MONTH) + 1) + "-" + year +"\n");
               }
           }
       }
       System.out.println("There are "+numMonths+" months with five weekends from 1900 through 2100:");
       System.out.println(months);
       System.out.println("Years with no five-weekend months:");
       for (int year = 1900; year <= 2100; year++) {
           if(!years[year - 1900]){
               System.out.println(year);
           }
       }
   }

}</lang> Output (middle results cut out):

 There are 201 months with five weekends from 1900 through 2100:
3-1901
8-1902
5-1903
1-1904
7-1904
12-1905
3-1907
5-1908
1-1909
10-1909
7-1910
...
12-2090
8-2092
5-2093
1-2094
10-2094
7-2095
3-2097
8-2098
5-2099
1-2100
10-2100

Years with no five-weekend months:
1900
1906
1917
1923
1928
1934
1945
1951
1956
1962
1973
1979
1984
1990
2001
2007
2012
2018
2029
2035
2040
2046
2057
2063
2068
2074
2085
2091
2096

k

<lang k> cal_j:(_jd[19000101]+!(-/_jd 21010101 19000101)) / enumerate the calendar is_we:(cal_j!7) _lin 4 5 6 / identify friday saturdays and sundays m:__dj[cal_j]%100 / label the months mi:&15=+/'is_we[=m] / group by month and sum the weekend days `0:,"There are ",($#mi)," months with five weekends" m5:(?m)[mi] `0:$5#m5 `0:,"..." `0:$-5#m5 y:1900+!201 / enumerate the years in the range y5:?_ m5%100 / label the years of the months yn5:y@&~y _lin y5 / find any years not in the 5 weekend month list `0:,"There are ",($#yn5)," years without any five-weekend months" `0:,1_,/",",/:$yn5</lang> Output:

There are 201 months with five weekends
190103
190208
190305
190401
190407
...
209703
209808
209905
210001
210010
There are 29 years without any five-weekend months
1900,1906,1917,1923,1928,1934,1945,1951,1956,1962,1973,1979,1984,1990,2001,2007,2012,2018,2029,2035,2040,2046,2057,2063,2068,2074,2085,2091,2096

Perl 6

Works with: Rakudo version 2010-10

<lang perl6># A month has 5 weekends iff it has 31 days and starts on Friday.

  1. Note: [@happy.end X- ^5].reverse is a workaround; Rakudo does not yet allow [*-5..*].

my @years = 1900 .. 2100; my @ym = @years X < 01 03 05 07 08 10 12 >; # Months with 31 days

my @happy = @ym.map({ Date.new: "$^a-$^b-01" }).grep: { .day-of-week == 5 };

say 'Happy month count: ', +@happy; say 'First happy months: '~ @happy[^5]; say 'Last happy months: '~ @happy[@happy.end X- ^5].reverse; say 'Dreary years count: ', @years - @happy».year.uniq; </lang> Output:

Happy month count:  201
First happy months: 1901-03-01 1902-08-01 1903-05-01 1904-01-01 1904-07-01
Last  happy months: 2097-03-01 2098-08-01 2099-05-01 2100-01-01 2100-10-01
Dreary years count: 29

PicoLisp

<lang PicoLisp>(setq Lst

  (make
     (for Y (range 1900 2100)
        (for M (range 1 12)
           (and
              (date Y M 31)
              (= "Friday" (day (date Y M 1)))
              (link (list (get *Mon M) Y)) ) ) ) ) )

(prinl "There are " (length Lst) " months with five weekends:") (mapc println (head 5 Lst)) (prinl "...") (mapc println (tail 5 Lst)) (prinl) (setq Lst (diff (range 1900 2100) (uniq (mapcar cadr Lst)))) (prinl "There are " (length Lst) " years with no five-weekend months:") (println Lst)</lang> Output:

There are 201 months with five weekends:
(Mar 1901)
(Aug 1902)
(May 1903)
(Jan 1904)
(Jul 1904)
...
(Mar 2097)
(Aug 2098)
(May 2099)
(Jan 2100)
(Oct 2100)

There are 29 years with no five-weekend months:
(1900 1906 1917 1923 1928 1934 1945 1951 1956 1962 1973 1979 1984 1990 2001 2007
2012 2018 2029 2035 2040 2046 2057 2063 2068 2074 2085 2091 2096)

Python

<lang python>from datetime import timedelta, date

DAY = timedelta(days=1) START, STOP = date(1900, 1, 1), date(2101, 1, 1) WEEKEND = {6, 5, 4} # Sunday is day 6 FMT = '%Y %m(%B)'

def fiveweekendspermonth(start=START, stop=STOP):

   'Compute months with five weekends between dates'
   
   when = start
   lastmonth = weekenddays = 0
   fiveweekends = []
   while when < stop:
       year, mon, _mday, _h, _m, _s, wday, _yday, _isdst = when.timetuple()
       if mon != lastmonth:
           if weekenddays >= 15:
               fiveweekends.append(when - DAY)
           weekenddays = 0
           lastmonth = mon
       if wday in WEEKEND:
           weekenddays += 1
       when += DAY
   return fiveweekends

dates = fiveweekendspermonth() indent = ' ' print('There are %s months of which the first and last five are:' % len(dates)) print(indent +('\n'+indent).join(d.strftime(FMT) for d in dates[:5])) print(indent +'...') print(indent +('\n'+indent).join(d.strftime(FMT) for d in dates[-5:]))

print('\nThere are %i years in the range that do not have months with five weekends'

     % len(set(range(START.year, STOP.year)) - {d.year for d in dates}))</lang>

Alternate Algorithm

The condition is equivalent to having a thirty-one day month in which the last day of the month is a Sunday. <lang python>LONGMONTHS = (1, 3, 5, 7, 8, 10, 12) # Jan Mar May Jul Aug Oct Dec def fiveweekendspermonth2(start=START, stop=STOP):

   return [date(yr, month, 31)
           for yr in range(START.year, STOP.year)
           for month in LONGMONTHS
           if date(yr, month, 31).timetuple()[6] == 6 # Sunday
           ]

dates2 = fiveweekendspermonth2() assert dates2 == dates</lang>

Sample Output

There are 201 months of which the first and last five are:
  1901 03(March)
  1902 08(August)
  1903 05(May)
  1904 01(January)
  1904 07(July)
  ...
  2097 03(March)
  2098 08(August)
  2099 05(May)
  2100 01(January)
  2100 10(October)

There are 29 years in the range that do not have months with five weekends

Ruby

<lang ruby># if the last day of the month falls on a Sunday and the month has 31 days,

  1. this is the only case where the month has 5 weekends.

start = Date.parse("1900-01-01") stop = Date.parse("2100-12-31") dates = (start..stop).find_all do |day|

 day.mday == 31 and day.wday == 0

end

puts "There are #{dates.size} months with 5 weekends from 1900 to 2100:" puts dates[0, 5].map { |d| d.strftime("%b %Y") }.join("\n") puts "..." puts dates[-5, 5].map { |d| d.strftime("%b %Y") }.join("\n")

years_with_5w = dates.map(&:year)

years = (1900...2100).to_a - years_with_5w

puts "There are #{years.size} years without months with 5 weekends:" puts years.join(", ")</lang>

Output

There are 201 months with 5 weekends from 1900 to 2100:
Mar 1901
Aug 1902
May 1903
Jan 1904
Jul 1904
...
Mar 2097
Aug 2098
May 2099
Jan 2100
Oct 2100
There are 29 years without months with 5 weekends:
1900, 1906, 1917, 1923, 1928, 1934, 1945, 1951, 1956, 1962, 1973, 1979, 1984,
1990, 2001, 2007, 2012, 2018, 2029, 2035, 2040, 2046, 2057, 2063, 2068, 2074,
2085, 2091, 2096

Tcl

<lang tcl>package require Tcl 8.5

set months {} set years {} for {set year 1900} {$year <= 2100} {incr year} {

   set count [llength $months]
   foreach month {Jan Mar May Jul Aug Oct Dec} {

set date [clock scan "$month/01/$year" -format "%b/%d/%Y" -locale en_US] if {[clock format $date -format %u] == 5} { # Month with 31 days that starts on a Friday => has 5 weekends lappend months "$month $year" }

   }
   if {$count == [llength $months]} {

# No change to number of months; year must've been without lappend years $year

   }

} puts "There are [llength $months] months with five weekends" puts [join [list {*}[lrange $months 0 4] ... {*}[lrange $months end-4 end]] \n] puts "There are [llength $years] years without any five-weekend months" puts [join $years ","]</lang> Output:

There are 201 months with five weekends
Mar 1901
Aug 1902
May 1903
Jan 1904
Jul 1904
...
Mar 2097
Aug 2098
May 2099
Jan 2100
Oct 2100
There are 29 years without any five-weekend months
1900,1906,1917,1923,1928,1934,1945,1951,1956,1962,1973,1979,1984,1990,2001,2007,2012,2018,2029,2035,2040,2046,2057,2063,2068,2074,2085,2091,2096