parent previous next question (Smalltalk Textbook 34)

EngiCalendar

In this section I'll describe the 'Date' class and show you a calendar program as an example of it's usage. The calendar program is in Appendix 19. Please set the time zone correctly before continuing.

To set time zone on UNIX do this ( in Japan in this case):


Program-34-1: (TimeZone; time, date, timezone, japan, sunday, month, day, year)
-------------------------------------------------------
TimeZone setDefaultTimeZone:
        (TimeZone
                timeDifference: 9
                DST: 0
                at: 0
                from: 0
                to: 0
                startDay: #Sunday) "for JAPAN"
-------------------------------------------------------

On Macintosh or DOS.


Program-34-2: (TimeZone; time, date, timezone, month, day, year, today)
-------------------------------------------------------
TimeZone setDefaultTimeZone: TimeZone null "for Mac or DOS"
-------------------------------------------------------

If you do not set the time zone correctly, programs in this section will not work well. Templates for doing this can be found in the 'Settings' option of the 'File' menu. Look under the 'Time Zones' book mark.

Let's start with 'Date'. Evaluate 'Date today' to get an instance of the Date class which contains today's date. 'Date' has two instance variables; 'day' and 'year' but not 'month'. Execute this program 34-3 to see why.


Program-34-3: (Date; time, timezone, month, day, year, today)
------------------------------------------------------
| aDate |
aDate := Date today.
Transcript cr; show: aDate year printString.
Transcript cr; show: aDate day printString
------------------------------------------------------

You see the number of this year and a number of days since January 1. So, the number of days will always be in the range of 1 to 365 or 1 to 366 for leap years.

How about a month.


Program-34-4: (Date; time, monthName, monthIndexday, year, today)
------------------------------------------------
| aDate |
aDate := Date today.
Transcript cr; show: aDate monthIndex printString.
Transcript cr; show: aDate monthName
------------------------------------------------

Now the day of the month.


Program-34-5: (Date; time, dayOfMonth, year, month, day)
------------------------------------------------
| aDate |
aDate := Date today.
Transcript cr; show: aDate dayOfMonth printString
------------------------------------------------

Next is the day of the week, starting with Monday being 1, etc.


Program-34-5: (Date; time, dayOfMonth, year, month, day)
-------------------------------------------------
| aDate |
aDate := Date today.
Transcript cr; show: aDate weekdayIndex printString.
Transcript cr; show: aDate weekday
-------------------------------------------------

Program 34-6 writes today's date on the transcript.

>
Program 34-6: (Date WriteStream; time, dayOfMonth, weekday, monthIndex,
year, month, day)
----------------------------------------------------
| aDate aStream |
aDate := Date today.
aStream := WriteStream on: String new.
aStream nextPutAll: aDate monthIndex printString.
aStream nextPutAll: '/'.
aStream nextPutAll: aDate dayOfMonth printString.
aStream nextPutAll: ' ('.
aStream nextPutAll: (aDate weekday copyFrom: 1 to: 3).
aStream nextPutAll: '), '.
aStream nextPutAll: aDate year printString.
Transcript cr; show: aStream contents
----------------------------------------------------

Send 'leapYear:' message to 'Date' to see whether it is a leap year. 1 means yes, 0 no.


Program-34-7: (Date; time, leapYear, year, month, day)
-----------------------------------------------------
Transcript cr; show: (Date leapYear: 1994) printString.
Transcript cr; show: (Date leapYear: 1996) printString.
-----------------------------------------------------

Program 34-8 prints the number of days in February and the number of days in a year.


Program-34-8: (Date, ReadStream; time, daysInMonth, daysInYear, year, 
month, day)
-------------------------------------------------------
| aDate |
aDate := Date readFrom: (ReadStream on: '2/1,1994').
Transcript cr; show: aDate daysInMonth printString.
Transcript cr; show: aDate daysInYear printString.
aDate := Date readFrom: (ReadStream on: '2/1,1996').
Transcript cr; show: aDate daysInMonth printString.
Transcript cr; show: aDate daysInYear printString.
-------------------------------------------------------

That covers most of the major messages for the 'Date' class. Appendix 19 is a calendar program which uses the 'Date' class. File it in. Then do it this program to print a calendar of this month on the transcript.


Program-34-9: (EngiCalendarModel; calendar, time, date, year, month, day)
-----------------------------------------------
| calendarModel |
calendarModel := EngiCalendarModel new.
Transcript cr; show: calendarModel printString
------------------------------------------------

Program 34-10 opens a calendar window for February 1996. 1996 is a leap year, so the calendar has 29 days.


Program-34-10: (EngiCalendarView, EngiCalendarModel; calendar, time, date,
year, month, day)
------------------------------------------------
| calendarModel |
calendarModel := EngiCalendarModel month: 2 year: 1996.
EngiCalendarView openOn: calendarModel
------------------------------------------------

You can see active areas on the calendar by clicking red button. The yellow button menu on the year or month allows you to change it; on a week day it allows you to enter a note associated with that day.

The background yellow button menu has 'new', 'open', and 'save'.

Now, let us examine the details of the calendar program. I think that a good Smalltalk will have an MVC which is (1) pluggable, (2) uses dependency well, and (3) has an effective controller. The calendar program is not pluggable but is an orthodox MVC program which uses dependency and delegates the appropriate roles to the various parts, i.e. Input events are handled exclusively by the controller, display events are handled by the view and model-changing events are handled only by the model. The inheritance trees are:

---------------------------------------------
Object ()
. Model ('dependents')
. . EngiCalendarModel ('monthIndex'
                       'yearIndex'
                       'calendarMatrix'
                       'memoDictionary')
---------------------------------------------
Object ()
. VisualComponent ()
. . VisualPart ('container')
. . . DependentPart ('model')
. . . . View ('controller')
. . . . . EngiCalendarView ('frames')
---------------------------------------------
Object ()
. Controller ('model' 'view' 'sensor')
. . ControllerWithMenu ('menuHolder' 'performer')
. . . EngiCalendarController ('today')
---------------------------------------------

The 'monthIndex' instance variable is an integer from 1 to 12. The 'yearIndex' has the year anno domino. 'calendarMatrix' is a 5 by 7 matrix with the days of a week horizontally, and weeks vertically. If a month has 6 weeks, the days of 6th week are stored at the beginning of the first week. 'memoDictionary' is a memo pad dictionary with day as key.

The 'frames' instance variable of the view holds active button areas, calculated when the view draws itself.

If you make the year and month this current month, then today will be highlighted. The 'today' instance variable of the controller stores today's date to provide for this highlighting.


parent previous next question
Copyright (C) 1994-1996 by Atsushi Aoki
Translated by Kaoru Rin Hayashi & Brent N. Reeves