Date
Canonical source:
protos/lib/datetime/Date.protosat locked revisione99b712ecf658a527b1fe82c8beba2c1286d8dc9.
// THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE ADAPTIVE PUBLIC LICENSE// ("LICENSE") AS FIRST COMPLETED BY: Guillermo Adrián Molina. ANY USE, PUBLIC// DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION OF, OR PREPARATION OF// DERIVATIVE WORKS BASED ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE// OF THIS LICENSE AND ITS TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF// THE LICENSE. "LICENSED WORK" AND "RECIPIENT" ARE DEFINED IN THE LICENSE. A COPY// OF THE LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.TXT" ACCOMPANYING// THE CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS// FILE, A COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE:// https://github.com/guillermomolina/protos//// Software distributed under the License is distributed on an "AS IS" basis,// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for// the specific language governing rights and limitations under the License.
//! Civil dates in the proleptic Gregorian calendar with astronomical year//! numbering: year `0` and negative years exist, the Gregorian leap-year rule//! extends backward without a Julian cutover, and the supported years are//! `-9999` through `9999` inclusive. A date is a frozen ordinary object with//! Integer `year`, `month`, and `day` slots; it carries no time, offset, zone,//! or clock state.//!//! Every date's immediate delegation parent is this module, the canonical//! date factory and prototype. `recognizes` accepts exactly the dates of this//! family: objects whose immediate parent is this module and whose local slots//! are exactly the date state and behavior slots with a valid state. Matching//! slots under another parent are not a date.//!//! Dates answer semantic equality through `==`: two dates are equal when both//! are dates and their year, month, and day are equal. `hash` is coherent with that equality, so//! independently constructed equal dates work as the same Map key. `===`//! remains ordinary object identity. `compare` is the natural chronological//! order and answers `0` exactly for equal dates.
/// Answers a fresh frozen date for `year`, `month` (`1` through `12`), and/// `day` (`1` through the number of days of that month in that year). Every/// argument must be an Integer and `year` must lie in `-9999` through `9999`./// An invalid or out-of-range field signals an Error; no field is clamped,/// wrapped, or normalized.call: (year, month, day) => { Date: import("std:datetime/Date")
// Hashes are folded modulo a fixed prime so the accumulator stays bounded. mix: (accumulated, value) => { (accumulated * 31 + value) % 2305843009213693951 }
behavior: { equals: (other) => { date: this Date.recognizes(other) && (date.year == other.year) && (date.month == other.month) && (date.day == other.day) }
hash: () => { date: this mix(mix(mix(17, date.year.hash()), date.month.hash()), date.day.hash()) } }
date: (Date) { year: year month: month day: day ...behavior.alias("equals", "==") } date.freeze()
// Recognition owns field validation, so construction and recognition // accept exactly the same states. Date.recognizes(date).ifFalse(() => { Error().signal() }) date}
/// Answers `true` exactly when `value` is a date of this family: its immediate/// delegation parent is this module, its local slots are exactly `year`,/// `month`, `day`, `equals`, `==`, and `hash`, and its fields form a valid/// date. Answers `false` for any other value.recognizes: (value) => { Date: import("std:datetime/Date")
shaped: (value !== null) && (value.parent() === Date) && (value.slotNames().size() == 6) && value.hasSlot("year") && value.hasSlot("month") && value.hasSlot("day") && value.hasSlot("equals") && value.hasSlot("==") && value.hasSlot("hash") && Integer.recognizes(value.year) && Integer.recognizes(value.month) && Integer.recognizes(value.day) && (value.year >= -9999) && (value.year <= 9999) && (value.month >= 1) && (value.month <= 12)
valid: false shaped.ifTrue(() => { year: value.year month: value.month // Divisibility by 4, 100, and 400 is sign-independent, so the rule // applies unchanged to year zero and negative years. leap: ((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0)) length: 31 ((month == 4) || (month == 6) || (month == 9) || (month == 11)).ifTrue(() => { length = 30 }) (month == 2).ifTrue(() => { length = 28 leap.ifTrue(() => { length = 29 }) }) valid = (value.day >= 1) && (value.day <= length) }) valid}
/// Answers `-1`, `0`, or `1` as date `left` is chronologically before, equal/// to, or after date `right`, ordering by year, then month, then day. The/// result is `0` exactly when `left == right`. Arguments must be dates/// recognized by this module; otherwise an Error is signalled.compare: (left, right) => { Date: import("std:datetime/Date")
(Date.recognizes(left) && Date.recognizes(right)).ifFalse(() => { Error().signal() })
compareIntegers: (first, second) => { result: 0 (first < second).ifTrue(() => { result = -1 }) (first > second).ifTrue(() => { result = 1 }) result }
result: compareIntegers(left.year, right.year) (result == 0).ifTrue(() => { result = compareIntegers(left.month, right.month) }) (result == 0).ifTrue(() => { result = compareIntegers(left.day, right.day) }) result}