CS 3721
Programming Languages  
Spring 2013
Recitation 11. 
 Ruby: Classes
Week 12-13: Apr 10-19

Submit following directions at: submissions and rules at: rules. Deadlines are:
  • 2013-04-19  23:59:59 (that's Fri, 19 Apr 2013, 11:59:59 pm) for full credit.
  • 2013-04-22  23:59:59 (that's Mon, 22 Apr 2013, 11:59:59 pm) for 75% credit.


Program involving classes: [See the Chapter on Classes in the PPG for much of the notation here.]

Note that even though this problem also involves dates, it is independent of the date problem in Recitation 10. You should not have any common code between the two problems. This problem has you working with dates and times. Ruby, like most programming languages, has library functions for treating times and dates, and in normal applications, you should use such library functions. This problem has you creating classes from scratch in order to learn how to do it. You are not allowed to use Ruby time/date library classes for this assignment.

Consider the following Date1 class in Ruby. (Here is the whole file, Date1 class plus test code: date.rb. I named this class Date1 because Ruby has a library class Date. It actually worked to redefine the library class, but there were other problems.)

Date1 class in Ruby, file: date.rb
The class Date1 Test code and a run
#!/usr/bin/ruby
class Date1
  attr_reader :year, :month, :day
  def initialize(y, m, d)
    if check_date(y, m, d)
      @year, @month, @day = y, m, d
    else
      @year, @month, @day = 2000, 1, 1
    end
  end

  @@mon = [nil,"Jan","Feb","Mar","Apr",
               "May","Jun","Jul","Aug",
               "Sep","Oct","Nov","Dec"]
  @@days = [0, 31, 28, 31, 30, 31, 30,
               31, 31, 30, 31, 30, 31]

  def check_date(y, m, d)
    if m < 1 or m > 12 or 
       d < 0 or d > 31 or y < 0
      false
    elsif d > 0 and d <= @@days[m]
      true
    elsif m == 2 and d == 29 and
         is_leap(y)
      true
    else
      false
    end
  end
  
  def is_leap(y)
    if (y%400 == 0 or
        (y%4 == 0 and y%100 != 0) )
      true
    else
      false
    end
  end
    
  def to_s
    @@mon[@month] + " " + day.to_s +
      ", " + year.to_s
  end
  
  def incr_day
    @day +=1
    if not (@month == 2 and 
            is_leap(@year) and
            @day == 29) and
            @day > @@days[@month]
      @day = 1
      incr_month
    end
  end
  
  def incr_month
    @month +=1
    if @month > 12
      @month = 1
      incr_year
    end
  end
  
  def incr_year
    @year += 1
  end

end
def test_date(y, m, d)
  date = Date1.new(y, m, d)
  print   "Start Date: ", date," "
  date.incr_day
  print "\n  Next day: ", date  
  date.incr_day
  print "\n  Next day: ", date  
  date.incr_day
  print "\n  Next day: ", date,
    "\n"
end
print "TEST ORDINARY DATES\n"
test_date(2004, 4, 22)
test_date(2013, 3, 30)
test_date(2013, 4, 29)
print "\nTEST YEAR END\n"
test_date(2001, 12, 30)
test_date(1999, 12, 30)
print "\nTEST LEAP YEARS\n"
test_date(2004, 2, 28)
test_date(2001, 2, 27)
test_date(2000, 2, 28)
test_date(1900, 2, 27)
test_date(2100, 2, 27)
% ruby date.rb TEST ORDINARY DATES Start Date: Apr 22, 2004 Next day: Apr 23, 2004 Next day: Apr 24, 2004 Next day: Apr 25, 2004 Start Date: Mar 30, 2013 Next day: Mar 31, 2013 Next day: Apr 1, 2013 Next day: Apr 2, 2013 Start Date: Apr 29, 2013 Next day: Apr 30, 2013 Next day: May 1, 2013 Next day: May 2, 2013 TEST YEAR END Start Date: Dec 30, 2001 Next day: Dec 31, 2001 Next day: Jan 1, 2002 Next day: Jan 2, 2002 Start Date: Dec 30, 1999 Next day: Dec 31, 1999 Next day: Jan 1, 2000 Next day: Jan 2, 2000 TEST LEAP YEARS Start Date: Feb 28, 2004 Next day: Feb 29, 2004 Next day: Mar 1, 2004 Next day: Mar 2, 2004 Start Date: Feb 27, 2001 Next day: Feb 28, 2001 Next day: Mar 1, 2001 Next day: Mar 2, 2001 Start Date: Feb 28, 2000 Next day: Feb 29, 2000 Next day: Mar 1, 2000 Next day: Mar 2, 2000 Start Date: Feb 27, 1900 Next day: Feb 28, 1900 Next day: Mar 1, 1900 Next day: Mar 2, 1900 Start Date: Feb 27, 2100 Next day: Feb 28, 2100 Next day: Mar 1, 2100 Next day: Mar 2, 2100

  1. Answer the following questions about the Ruby program above. (These are not necessarily obvious. You will need to consult the reference above, or some other reference.)

    1. In code outside the class (at the top of the right hand column above), the statement on the third line is (truncated) "print date". This statement causes things to be printed. How is the output determined?

    2. Does the class Date1 have a constructor? What is it?

    3. What is the meaning (or purpose) of the character '@' in front of year in the code?

    4. What is the meaning (or purpose) of the characters '@@' in front of days in the code?

    5. What is the purpose (or effect) of the line attr_reader :year, :month, :day ?

  2. In the same file add another class Time1 that will keep track of hours, minutes, and seconds. This new class should be similar to the Date1 class. (Again, it is named Time1 because Ruby has a library class named Time.) In particular, it should have methods: initialize, check_time, to_s, incr_sec, incr_min, and incr_hour. Test this new class with code to instantiate time objects and to test incrementing by seconds, including showing that it handles the end of a minute and the end of an hour correctly.

  3. Change your Time1 class so that it inherits the Date1 class, that is Date1 is a superclass of Time1, and Time1 is a subclass of Date1. (In Java terms, Time1 extends Date1, and in place of the Java "Time1 extends Date1", Ruby writes "Time1 < Date1". The initializer for Time1 should now have 6 parameters, including the ones before, along with year, month, and day. These last three will be passed on into Date1 using the super method. [Again see the Chapter on Classes in the PPG.]

  4. Alter the incr_hour method of Time1, so that in case an addition goes past hour 23, it will wrap around to hour 0 and increment to the next day.

    It is permissible to change the code in the class Date1, although I didn't need to make any changes. There are many different ways to write this code. You should test your program with the following initial data, and your output should look something like the following:

    Possible output for this question
    Start Time:      Apr 22, 2004 13:15:30 
        Next second: Apr 22, 2004 13:15:31
        Next second: Apr 22, 2004 13:15:32
        
    Start Time:      Feb 28, 2000 13:59:59 
        Next second: Feb 28, 2000 14:00:00
        Next second: Feb 28, 2000 14:00:01
        
    Start Time:      Feb 28, 2000 23:59:59 
        Next second: Feb 29, 2000 00:00:00
        Next second: Feb 29, 2000 00:00:01
        
    Start Time:      Feb 29, 2000 23:59:59 
        Next second: Mar 1, 2000 00:00:00
        Next second: Mar 1, 2000 00:00:01
        
    Start Time:      Feb 28, 2001 23:59:59 
        Next second: Mar 1, 2001 00:00:00
        Next second: Mar 1, 2001 00:00:01
        
    Start Time:      Dec 31, 2001 23:59:58 
        Next second: Dec 31, 2001 23:59:59
        Next second: Jan 1, 2002 00:00:00
        
    Start Time:      Apr 30, 2001 23:59:58 
        Next second: Apr 30, 2001 23:59:59
        Next second: May 1, 2001 00:00:00
    


What you should submit: Refer to the submissions directions and to deadlines at the top of this page. If two people are working together, be sure that both signed in as a pair and that both names appear at the top of the submission.

 Contents of submission for Recitation 14:

Last Name, First Name; Course Number; Recitation Number.

1. Give short answers to the questions here.

2. For your own benefit and for partial credit, you should first create the class Time1 as a completely separate class (in a separate file). This is very similar to Date1, but simpler, so you should really try this one. You should test this separately and show your test.

3. and 4. These parts just add extra features. For full credit, it is enough to have a single Ruby program in a single file that does everything, producing roughly the output shown above for the 7 given inputs. (The two classes could also be in separate files, but in that case you would need a "require" statement.)


Revision date: 2013-03-23. (Please use ISO 8601, the International Standard Date and Time Notation.)