Previous | Next | Table of Contents | Index | Program List | Copyright

3.6 System Structures: Using Ada's Calendar Package

In this section you will see how to use another standard Ada library package, Ada.Calendar.

In all Ada packages, the resources provided are listed in an Ada source file called the package specification. The package specification plays two roles: It describes the package to the compiler, and it serves as a "contract" with the programmer using it, telling this human user exactly what resources to expect. Some of the different kinds of resources provided by a package are

Ada's calendar package provides a number of useful resources relating to dates and times. Figure 3.2 shows a part of the specification for Ada.Calendar; for clarity we have listed only those services needed in this example. Appendix E gives the entire specification for Ada.Calendar.

Figure 3.2 Partial Specification of Package Ada.Calendar

PACKAGE Ada.Calendar IS

--  standard Ada package, must be supplied with compilers
--  provides useful services for dates and times

--  type definitions

  TYPE Time IS PRIVATE;

  SUBTYPE Year_Number  IS Integer RANGE 1901..2099;
  SUBTYPE Month_Number IS Integer RANGE 1..12;
  SUBTYPE Day_Number   IS Integer RANGE 1..31;

--  functions to get the current time and return its date components

  FUNCTION Clock RETURN Time;

  FUNCTION Year  (Date : Time) RETURN Year_Number;
  FUNCTION Month (Date : Time) RETURN Month_Number;
  FUNCTION Day   (Date : Time) RETURN Day_Number;

--  Ada.Calendar provides many other interesting facilities;
--  for clarity, these are omitted from this figure.

END Ada.Calendar;

After the first line,

    PACKAGE Ada.Calendar IS
which indicates the beginning of a package specification, four type declaration statements are given. The line
    TYPE Time IS PRIVATE;
specifies Time as a PRIVATE type, the details of whose values are not known to the package user. We do not know a Time value explicitly because it is an internal value produced by the computer's real-time clock; this internal value represents a year, calendar day, and time of day in a single bit pattern. We will discuss PRIVATE types in detail later, especially in Chapter 10. For now, you need to know that because Time is a PRIVATE type, the only way we can use Time values is to work with them according to the various operations provided by Calendar; you will see a few of these operations in a short while.

Given a program preceded by a context clause

    WITH Ada.Calendar;
the declaration
    Right_Now : Ada.Calendar.Time;
declares a variable capable of holding a time value. The form Ada.Calendar.Time is similar to the form Ada.Text_IO.New_Line in that the name of the package is used to qualify the use of the package resource: Time is a resource provided by Ada.Calendar; New_Line is a resource provided by Ada.Text_IO.

Returning to the specification of Ada.Calendar in Figure 3.2, the next three lines give declarations for years, months, and days:

    SUBTYPE Year_Number IS Integer RANGE 1901..2099;
    SUBTYPE Month_Number IS Integer RANGE 1..12;
    SUBTYPE Day_Number IS Integer RANGE 1..31;

Recall from Chapter 2 that a type consists of a set of values and a set of operations on these values and that a subtype is a subset of the original set of values, together with the full original set of operations. For example, in declaring Month_Number to be a subtype of Integer and giving its range as 1..12, we are saying that any variables of type Month_Number can hold integer values only in the range 1 through 12, inclusive. Similarly, variables of subtype Day_Number can hold integer values in the range 1 through 31, inclusive. All of the operations on integers apply to values of these subtypes, but if an operation attempts to store a value that is outside the declared range, this operation is improper and a Constraint_Error exception will be raised at run time.

In this text we will make frequent use of subtypes; they are a convenient way to inform the compiler--and the reader of a program--that certain variables have ranges that are restricted according to their intended use. Ada can then help us to avoid and recover from errors by checking that variables store numbers only of appropriate size.

The declarations

    This_Year  : Ada.Calendar.Year_Number;
    This_Month : Ada.Calendar.Month_Number;
    This_Day   : Ada.Calendar.Day_Number;

declare variables of the three subtypes provided by Calendar. Again we have used qualified references; this is done to remind both the compiler and the human reader of the package in which the resources are defined.

Next we consider how to determine the current time of day in Ada. Returning to the Calendar specification in Fig. 3.2, the next line

    FUNCTION Clock RETURN Time;

specifies a function called Clock. Given the declaration

    Right_Now: Ada.Calendar.Time;
then an assignment statement such as
    Right_Now := Ada.Calendar.Clock;
will be compiled into machine instructions that read the computer's internal clock, which delivers the current time of day and stores this time value in the variable Right_Now. The expression Ada.Calendar.Clock is a function call; we will see other function calls shortly.

This value is not very useful to us in this form; for example, we cannot display a time value because its precise form is not available to us. But as the next three lines,

 
    FUNCTION Year (Date: Time) RETURN Year_Number;
    FUNCTION Month (Date: Time) RETURN Month_Number;
    FUNCTION Day (Date: Time) RETURN Day_Number;

of the specification show, the package gives us operations to extract the year, month, and day from the internal time value. Each of these operations is a function with a single parameter Date, which is of type Time. For example, if we declare a variable

    This_Year : Ada.Calendar.Year_Number;
the assignment statement
    This_Year := Ada.Calendar.Year(Date => Right_Now);
will store the current calendar year in This_Year. Since Ada.Calendar.Year_Number is an ordinary integer subtype, ordinary integer operations can be performed on the value in This_Year; specifically, its value can be displayed. This function call is analogous to an Ada.Text_IO procedure call such as
    Text_IO.Put(Item => FirstInitial);

in the sense that a value is being supplied to correspond to the formal parameter. The formal parameter of Put is called Item; the formal parameter of Year is called Date.

In using the operations of Ada.Calendar, we have no knowledge of the details of how they perform. This is of no concern to us; the "contract" embodied in the specification tells us what to expect, and this is all we need to know.

SYNTAX DISPLAY
Simple Function Call Statement

Form:
variable := fname (actual parameters);

Example:
This_Month := 
  Ada.Calendar.Month(Date => Right_Now);
Right_Now := Ada.Calendar.Clock;

Interpretation:
The list of parameters (if any) is enclosed in parentheses; each actual parameter value is preceded by the name of that formal parameter. The variable must be of the same type as the return type of the function fname. The function fname is called, and its returned value is stored in variable. During the function execution, the named actual parameters are associated with the corresponding formal parameters.

Note 1:
Multiple parameters are separated by commas. Be careful here: The formal parameters are separated by semicolons, but the actual parameters are separated by commas.

Note 2:
The number of actual and formal parameters must be the same. Each actual parameter that is an expression is evaluated when fname is called; this value is assigned to the corresponding formal parameter.

Note 3:
The type of each actual parameter must agree with the type of the corresponding formal parameter. Ada does not allow, for example, an integer-valued actual parameter to be associated with a float-valued formal parameter.

Note 4:
In this book, each actual parameter is listed with the name of the corresponding formal parameter (the two are separated by =>). Therefore, strictly speaking the order of the actual parameters does not have to match that of the formal parameters. It is nevertheless good practice to list the actual parameters in an order corresponding to the order of the formal parameters.

Note that as the second example shows, functions can be defined to have no parameters at all. The number, order, and type of the parameters is, of course, determined by the writer of the function, not its user.


Case Study: Displaying Today's Date in "mm/dd/yy" Form

Let's use the knowledge gained in this chapter to solve the problem of displaying today's date.

PROBLEM SPECIFICATION

Display today's date in the usual American form mm/dd/yy; for example, if today is October 21, 1995, we display 10/21/95. If today is July 8, 1996, we display 7/8/96.

ANALYSIS

Today's date can be obtained from the computer's internal clock using theappropriate Ada calendar facilities to get a time value and then to extract the month, day, and year. These three values can then be formatted to give the desired display.

Data Requirements

Problem Data Types:

We need only the type Time and the subtypes Year_Number, Month_Number, and Day_Number provided by the standard package Ada.Calendar.

Problem Inputs:

No inputs need to be entered by the user.

Problem Outputs:

Today's date, in the form mm/dd/yy.

DESIGN

Initial Algorithm

1. Get the current time value from the computer's clock.

2. Extract the current month, day, and year from the time value.

3. Format and display the date.

Algorithm Refinements

Step 2 Refinement

2.1. Extract the current month from the time value.

2.2. Extract the current day from the time value.

2.3. Extract the current year from the time value.

In step 3, we note that because the year is in the form yyyy (for example, 1989), we need to select the last two digits for formatting.

Step 3 Refinement

3.1. Find the last two digits of the year.

3.2. Format and display the current month, day, and year.

We can illustrate the steps in the refinement process with a diagram that shows the algorithm subproblems and their interdependencies. An example of such a diagram, called a structure chart, is shown in Figure 3.3.

Figure 3.3 Structure Chart for Formatting and Displaying Today's Date

Figure 3.3

As we trace down this diagram, we go from an abstract problem to a more detailed subproblem. The original problem is shown at the top, or level 0, of the structure chart. The major subproblems appear at level 1. The different subproblems resulting from the refinement of each level-1 step are shown at level 2 and are connected to their respective level-1 subproblem. This diagram shows that the subproblem Extract date values from time value is dependent on the solutions to the subproblems Extract month, extract day, and extract year. Because the subproblem Get current time is not refined further, there are no level-2 subproblems connected to it.

Structure charts are intended to show the structural relationship between the subproblems. The algorithm (not the structure chart) shows the order in which each step must be carried out to solve the problem.

TEST PLAN

In this case, no user inputs are provided to the program. The only thing to test is the correct extraction and formatting of the month, day, and year. It is easy to check whether the program produced the correct date; just look at an ordinary calendar.

IMPLEMENTATION

Program3.6 shows the Ada program for this problem.

Program 3.6
Displaying Today's Date

WITH Ada.Text_IO;
WITH Ada.Calendar;
WITH Ada.Integer_Text_IO;
PROCEDURE Todays_Date IS
------------------------------------------------------------------------
--| Finds and displays today's date in the form mm/dd/yy
--| The date is gotten from PACKAGE Ada.Calendar
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------
 
  Right_Now  : Ada.Calendar.Time;           -- holds internal clock value
  This_Year  : Ada.Calendar.Year_Number;    -- holds current year
  This_Month : Ada.Calendar.Month_Number;   -- holds current month 
  This_Day   : Ada.Calendar.Day_Number;     -- holds current day
 
  Last_Two_Digits : Natural;
  This_century    : CONSTANT Integer := 1900; 
   
BEGIN -- Todays_Date 
 
  -- Get the current time value from the computer's clock
  Right_Now := Ada.Calendar.Clock;
 
  -- Extract the current month, day, and year from the time value 
  This_Month := Ada.Calendar.Month(Date => Right_Now);
  This_Day   := Ada.Calendar.Day  (Date => Right_Now);
  This_Year  := Ada.Calendar.Year (Date => Right_Now);
 
  -- Format and display the date
  Last_Two_Digits := This_Year - This_Century;
 
  Ada.Text_IO.Put (Item => "Today's date is ");
  Ada.Integer_Text_IO.Put (Item => This_Month, Width => 1);
  Ada.Text_IO.Put (Item => '/');
  Ada.Integer_Text_IO.Put (Item => This_Day, Width => 1);
  Ada.Text_IO.Put (Item => '/');
  Ada.Integer_Text_IO.Put (Item => Last_Two_Digits, Width => 1); 
  Ada.Text_IO.New_Line;
 
END Todays_Date;
Sample Run
Today's date is 7/24/95

The program begins with the appropriate context clauses, including one for Calendar. Variables for the time, month, day, and year are declared. We need two more declarations:

    Last_Two_Digits : Natural;
    This_Century : CONSTANT Natural := 1900;

to allow us to find the last two digits of the year, as you will see in a moment. (The constant This_Century will have to be changed, and the program recompiled, on January 1, 2000.)

In the program body, the statements that get the clock value and extract the month and day were just discussed. Finally, the statement

    Last_Two_Digits := This_Year - This_Century;

finds the last two digits of the year: If the current year is 1990, This_Year gets the value 1990-1900, or 90, as desired.

Finally, the results are formatted and displayed using a sequence of Put statements from Text_IO and Ada.Integer_Text_IO. Notice how the integer values are displayed using a width of 1 to keep them "up against" the slashes.

TESTING

The sample run shows the correct date, correctly formatted.


Case Study: Displaying Today's Date in "MONTH dd, yyyy" Form

PROBLEM SPECIFICATION

Display today's date in the form MONTH dd, yyyy.

ANALYSIS

This problem is similar to the previous one. In fact, it can be solved just bymodifying the previous algorithm. Package Ada.Calendar gives us only the number of the current month from 1 to 12, so we need to specify the names of the months. We can do this with an enumeration type:

    TYPE Months IS
        (January, February, March, April, May, June,
        July, August, September, October, November, December);

The current month can be displayed using an instance of Enumeration_IO, as in the colors program: PACKAGE Month_IO IS NEW Ada.Text_IO.Enumeration_IO(Enum => Months);

DESIGN

All steps of the algorithm are the same, except for step 3.1. We do not need toselect the last two digits of the year because all four digits will be printed. However, we need to find the name corresponding to the number of the current month. Because the month is given from 1 to 12, and the positions of the names are 0 to 11, subtracting 1 from the month will give us the right position in Months, from which we can find the month name using the Val attribute. If the month name is stored in a variable Month_Name of type Months, we have

    Month_Name := Months'Val(This_Month - 1);

The solution to this problem is shown in Program 3.7.

Program 3.7
Displaying Today's Date in Another Format

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
WITH Ada.Calendar; 
PROCEDURE Todays_Date_2 IS
------------------------------------------------------------------------
--| Finds today's date and displays it in the form MONTH dd, yyyy
--| An enumeration type is used for months
--| The date is gotten from PACKAGE Ada.Calendar
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------
 
  TYPE Months IS 
     (January, February, March, April, May, June, 
      July, August, September, October, November, December);

  PACKAGE Months_IO IS NEW Ada.Text_IO.Enumeration_IO(Enum => Months);
 
  Right_Now  : Ada.Calendar.Time;           -- holds internal clock value
  This_Year  : Ada.Calendar.Year_Number;    -- holds current year
  This_Month : Ada.Calendar.Month_Number;   -- holds current month 
  This_Day   : Ada.Calendar.Day_Number;     -- holds current day

  Month_Name: Months;
   
BEGIN -- Todays_Date_2
 
  -- Get the current time value from the computer's clock
  Right_Now := Ada.Calendar.Clock;
 
  -- Extract the current month, day, and year from the time value 
  This_Month := Ada.Calendar.Month(Date => Right_Now);
  This_Day   := Ada.Calendar.Day  (Date => Right_Now);
  This_Year  := Ada.Calendar.Year (Date => Right_Now);
 
  -- Format and display the date
  Month_Name := Months'Val(This_Month - 1); 
 
  Ada.Text_IO.Put (Item => "Today's date is ");
  Months_IO.Put (Item => Month_Name, Set => Ada.Text_IO.Upper_Case);
  Ada.Text_IO.Put (Item => ' ');
  Ada.Integer_Text_IO.Put (Item => This_Day, Width => 1);
  Ada.Text_IO.Put (Item => ',');
  Ada.Integer_Text_IO.Put (Item => This_Year, Width => 5);         
  Ada.Text_IO.New_Line;
 
END Todays_Date_2;
Sample Run
Today's date is JULY 24, 1995


Previous | Next | Table of Contents | Index | Program List | Copyright

Copyright © 1996 by Addison-Wesley Publishing Company, Inc.