Is sqeak 2.3 beta ready for Y2K?
Alan Lovejoy
sourcery at pacbell.net
Fri Jul 2 09:40:08 UTC 1999
Stephen Pair wrote:
> How old will you be on that birthday? :)
>
> Interestingly, to verify those dates, you would need to rely on some
> algorithm, unless you happen to have calendars for the next mellinium handy
> (and a lot of spare time). So you would need to verify the algorithm in
> Squeak with the algorithm accepted as an international standard.
>
> Assuming that the arithmetic and algorithms in the Date class are correct
> and that you have a reliable source (your OS, accessing primitives, human
> entry) for dates and times, you should be safe. Storage of the dates should
> also be a non issue as long as there are no flaws in the internal
> representation and external conversions.
I decided to actually check the code, and believe I have found some
flaws:
Date class>fromDays: dayCount
"Answer an instance of me which is dayCount days after January 1,
1901."
^self
newDay: 1 + (dayCount asInteger rem: 1461)
"There are 1461 days in a 4-year cycle.
2000 is a leap year, so no extra correction is necessary. "
year: 1901 + ((dayCount asInteger quo: 1461) * 4)
The problem with the above is that the algorithm deals only with every-4-year
leap days, but not with every-100-year anti-leap days nor every-400 year
super-leap days. Of course, as the comment notes, this lapse just so happens
to give the correct result for the year 2000. The algorithm above is valid
only for years in the range 1901 to 2099.
subtractDate: aDate
"Answer the number of days between the receiver and aDate."
year = aDate year
ifTrue: [^day - aDate day]
ifFalse: [^year - 1 // 4 - (aDate year // 4) + day
+ aDate daysLeftInYear + (year - 1 - aDate year * 365)]
The same flaw is exhibited with the algorithm above: it only accounts for
the leap days defined by the Julian calendar, and fails to consider the
Gregorian leap days (which fail to occur in 3 out of every 4 century
years).
Date class>leapYear: yearInteger
"Answer 1 if the year yearInteger is a leap year; answer 0 if it is not."
(yearInteger \\ 4 ~= 0 or: [yearInteger \\ 100 = 0 and:
[yearInteger \\ 400 ~= 0]])
ifTrue: [^0]
ifFalse: [^1]
And finally, the algorithm (above) that determines whether a year is
a leap year
does not properly handle dates B.C. This can easily be fixed with
the following
version:
Date class>leapYear: yearInteger
"Answer 1 if the year yearInteger is a leap year; answer 0 if it is not."
| adjustedYear |
adjustedYear := yearInteger > 0
ifTrue: [yearInteger]
ifFalse: [(yearInteger + 1) negated "There is no year 0!!"].
(adjustedYear \\ 4 ~= 0 or: [adjustedYear \\ 100 = 0 and:
[adjustedYear \\ 400 ~= 0]])
ifTrue: [^0]
ifFalse: [^1]
Below is an alogorithm that computes the Gregorian year, and the day of
the year, from the number of days since (or until) January 1 of the
year 1 A.D.:
Date class>gregorianYearAndDaysInYearFromDays: days into: aTwoArgBlock
| quadCentury centuryquadYear year daysInMonth value isInADEra |
value := days.
isInADEra := days >= 0.
isInADEra
ifTrue: [year := 0]
ifFalse:
[value := value abs.
value >= DaysPerLeapYear
ifTrue:
[year := 1;
value := value - DaysPerLeapYear. "Subtract the
year 1 B.C." ]
ifFalse: [year := 0]]
quadCentury := value // DaysPerQuadCentury.
value := value \\ DaysPerQuadCentury.
century := value // DaysPerCentury.
value := value \\ DaysPerCentury.
quadYear := value // DaysPerQuadYear.
value := value \\ DaysPerQuadYear.
value >= DaysPerStandardYear
ifTrue:
["e.g., 1 AD or 2 BC"
value := value - DaysPerStandardYear.
year := year + 1.
value >= DaysPerStandardYear
ifTrue:
["e.g., 2 AD or 3 BC"
value := value - DaysPerStandardYear.
year := year + 1.
value >= DaysPerStandardYear
ifTrue:
["e.g., 3 AD or 4 BC"
value := value - DaysPerStandardYear.
year := year + 1.
value >= DaysPerLeapYear
ifTrue:
["e.g., 4 AD or 5 BC (although
this won't occur in the AD case)"
value := value - DaysPerLeapYear.
year := year + 1]]]].
year := year + (quadCentury * YearsPerQuadCentury) +
(century * YearsPerCentury) +
(quadYear * YearsPerQuadYear) + 1;
isInADEra
ifTrue: [year := year + 1 "Adjust for fact that years are ordinals"]
ifFalse:
[year := year negated.
value > 0
ifTrue:
[(Date leapYear: year) = 1
ifTrue: [value := DaysPerLeapYear - value]
ifFalse: [value := DaysPerStandardYear - value]]].
^aTwoArgBlock value: year value: value
And here is an algorithm that computes the number of days from (or until)
January 1 of the year 1 A.D. upto (or since) January 1 of a given year:
Date class>daysForYear: gregorianYear
| days yearDelta quadCenturies centuries quadYears years isInADEra |
days := 0.
isInADEra := gregorianYear > 0.
gregorianYear = 0 ifTrue: [gregorianYear = -1]. "There is no year 0"
isInADEra
ifTrue: [yearDelta := gregorianYear - 1]
ifFalse: [yearDelta := (gregorianYear + 1) negated].
quadCenturies := yearDelta // yearsPerQuadCentury.
yearDelta := yearDelta rem: yearsPerQuadCentury.
centuries := yearDelta // yearsPerCentury.
yearDelta := yearDelta rem: yearsPerCentury.
quadYears := yearDelta // yearsPerQuadYear.
years := yearDelta rem: yearsPerQuadYear.
days :=
(quadCenturies * DaysPerQuadCentury) +
(centuries * DaysPerCentury) +
(quadYears * DaysPerQuadYear) +
(years * DaysPerStandardYear).
isInADEra
ifFalse:
[days := days + DaysPerLeapYear. "1 B.C. is a leap year"
days := days negated].
^days
These algorithms depend on the following constants:
DaysPerStandardYear := 365.
DaysPerLeapYear := DaysPerStandardYear + 1.
YearsPerQuadYear := 4.
YearsPerCentury := 100.
YearsPerQuadCentury := 400.
QuadYearsPerCentury = 25.
DaysPerQuadYear := DaysPerLeapYear + (3 * DaysPerStandardYear).
DaysPerCentury := QuadYearsPerCentury * DaysPerQuadYear - 1.
CenturiesPerQuadCentury := 4.
DaysPerQuadCentury := CenturiesPerQuadCentury * DaysPerCentury + 1.
Some useful/interesting values:
Number of days (Gregorian, not Julian) from 1/1/1 to 15 Oct 1582 (the date
on which the Gregorian Calendar became the official calendar of the Roman
Catholic world): 577735.
Number of days (Gregorian, not Julian) from 1/1/1 to 1 Jan 1900: 693,595.
Number of days (ditto) from 1/1/1 to 1 Jan 1901: 693,960
(Squeak's & VW's base date).
Number of days (ditto) from 1/1/1 to 1 Jan 1970: 719,162
(Unix/VMS/MVS/VM base date).
Number of days (ditto) from 1/1/1 to 1 Jan 1980: 722,814
(WinDos/Mac base date).
Number of days (ditto) from 1/1/1 to: 1 Jan 2000: 730,119 (a
topical value).
--Alan ("Do I have too much ``time`` on my hands, or what?")
Content-Type: text/x-vcard; charset=us-ascii;
name="sourcery.vcf"
Content-Transfer-Encoding: 7bit
Content-Description: Card for Alan Lovejoy
Content-Disposition: attachment;
filename="sourcery.vcf"
Attachment converted: Anon:sourcery.vcf 4 (TEXT/R*ch) (0000AD6E)
More information about the Squeak-dev
mailing list
|