A type Data

The following example shows how a type Date with the relevant operations can be defined. Note the redefinition of the type MyDate to change the printing method.

let rec
type Date <->
[ Day :int;
Month :int;
Year :int;
Print := meth() :string is
implode({stringofint(self.Day);"/";
stringofint(self.Month);"/";
stringofint(self.Year)});
private Leap := meth(Year :int) :bool is
use isdiv := fun(x :int, y:int) :bool
is (x mod y = 0)
in  if isdiv(Year, 100)
then isdiv(Year,400)
else isdiv(Year,4);
IsLeap := meth() :bool is self.Leap(self.Year);
private MonthToDays :=
meth(Month :int, Year :int) :int is
use MonthTable := {0;31;59;90;120;151;181;
212;243;273;304;334;365}
in
nth(MonthTable, Month) +
if (Month > 2) And (self.Leap(Year))
then 1 else 0;
DateToDays := meth() :int is
use PastYears := self.Year - 1
%complete years between 1/1/1 and d%
in  PastYears*365 +( PastYears div 4)  +
(PastYears div 400) +
self.MonthToDays(self.Month, self.Year) +
self.Day - (PastYears div 100);
LessEqDate := meth(d :Date) :bool is
self.DateToDays <= d.DateToDays;
private DaysToDate := meth(Days :int)  :Date is
use Days := var (Days - 1)
and E4Years := 365 * 4 + 1
ext E100Years := E4Years * 25 - 1
ext E400Years := E100Years * 4 + 1
ext year := var (((at Days) div E400Years) * 400 + 1)
in  (Days <- (at Days) mod E400Years;
year <- at year + ((at Days) div E100Years) * 100;
Days <- (at Days) mod E100Years;
year <- at year + ((at Days) div E4Years) * 4;
Days <- (at Days) mod E4Years;
year <- at year + ((at Days) div 365);
Days <- ((at Days) mod 365) + 1;
use month := var 0
in  (month <- ((at Days) div 30) + 1;
while at Days <= self.MonthToDays(at month, at year) do
month <- at month - 1;
Days <- at Days - self.MonthToDays(at month, at year);
mkDate([Year := at year;
Month := at month;
Day := at Days ])) );
AddDays := meth(Days :int) :Date is
self.DaysToDate(self.DateToDays + Days)
]
before mk(this)
if Not(
use y := this.Year
and m := this.Month
and d := this.Day
and within := fun(V :int, m :int, M :int) :bool is
if V >= m And V <= M
then true
else false
in y >= 0 And
within(m, 1, 12) And
within(d, 1, if m=2
then if y mod 4 = 0
then 28
else 29
else if m isin {4;6;9;11}
then 30
else 31) )
do failwith "wrong values for a data";

let mkDate :=
fun(G:int, M:int, A:int):Date is
mkDate( [Day := G; Month := M; Year := A] );

% Another Date type to print dates with the format "Month D, Y" %

let rec type
MyDate <->
is Date and
[Print := meth() :string is
use MonthName := {"January";"February";"March";"April";"May";
"June";"July";"August";"September";"October";
"November";"December"}
in
implode({nth(MonthName,self.Month);" ";
stringofint(self.Day);", ";
stringofint(self.Year)});
AddDays := meth(Days :int) :MyDate is
inMyDate( (self As Date)!AddDays(Days), [ ] )

];

let mkMyDate :=
fun(G:int, M:int, A:int):Date is
mkMyDate( [Day := G; Month := M; Year := A] );