mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
[time/datetime]: Document package datetime
This commit is contained in:
@@ -1,16 +1,46 @@
|
|||||||
package datetime
|
package datetime
|
||||||
|
|
||||||
// Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
|
/*
|
||||||
// | Midnight Monday, January 3, 1 A.D. (Julian)
|
Type representing a mononotic day number corresponding to a date.
|
||||||
|
|
||||||
|
Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
|
||||||
|
| Midnight Monday, January 3, 1 A.D. (Julian)
|
||||||
|
*/
|
||||||
Ordinal :: i64
|
Ordinal :: i64
|
||||||
|
|
||||||
|
/*
|
||||||
|
*/
|
||||||
EPOCH :: Ordinal(1)
|
EPOCH :: Ordinal(1)
|
||||||
|
|
||||||
// Minimum and maximum dates and ordinals. Chosen for safe roundtripping.
|
/*
|
||||||
|
Minimum valid value for date.
|
||||||
|
|
||||||
|
The value is chosen such that a conversion `date -> ordinal -> date` is always
|
||||||
|
safe.
|
||||||
|
*/
|
||||||
MIN_DATE :: Date{year = -25_252_734_927_766_552, month = 1, day = 1}
|
MIN_DATE :: Date{year = -25_252_734_927_766_552, month = 1, day = 1}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Maximum valid value for date
|
||||||
|
|
||||||
|
The value is chosen such that a conversion `date -> ordinal -> date` is always
|
||||||
|
safe.
|
||||||
|
*/
|
||||||
MAX_DATE :: Date{year = 25_252_734_927_766_552, month = 12, day = 31}
|
MAX_DATE :: Date{year = 25_252_734_927_766_552, month = 12, day = 31}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Minimum value for an ordinal
|
||||||
|
*/
|
||||||
MIN_ORD :: Ordinal(-9_223_372_036_854_775_234)
|
MIN_ORD :: Ordinal(-9_223_372_036_854_775_234)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Maximum value for an ordinal
|
||||||
|
*/
|
||||||
MAX_ORD :: Ordinal( 9_223_372_036_854_774_869)
|
MAX_ORD :: Ordinal( 9_223_372_036_854_774_869)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Possible errors returned by datetime functions.
|
||||||
|
*/
|
||||||
Error :: enum {
|
Error :: enum {
|
||||||
None,
|
None,
|
||||||
Invalid_Year,
|
Invalid_Year,
|
||||||
@@ -24,12 +54,22 @@ Error :: enum {
|
|||||||
Invalid_Delta,
|
Invalid_Delta,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A type representing a date.
|
||||||
|
|
||||||
|
The minimum and maximum values for a year can be found in `MIN_DATE` and
|
||||||
|
`MAX_DATE` constants. The `month` field can range from 1 to 12, and the day
|
||||||
|
ranges from 1 to however many days there are in the specified month.
|
||||||
|
*/
|
||||||
Date :: struct {
|
Date :: struct {
|
||||||
year: i64,
|
year: i64,
|
||||||
month: i8,
|
month: i8,
|
||||||
day: i8,
|
day: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A type representing a time within a single day within a nanosecond precision.
|
||||||
|
*/
|
||||||
Time :: struct {
|
Time :: struct {
|
||||||
hour: i8,
|
hour: i8,
|
||||||
minute: i8,
|
minute: i8,
|
||||||
@@ -37,17 +77,30 @@ Time :: struct {
|
|||||||
nano: i32,
|
nano: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A type representing datetime.
|
||||||
|
*/
|
||||||
DateTime :: struct {
|
DateTime :: struct {
|
||||||
using date: Date,
|
using date: Date,
|
||||||
using time: Time,
|
using time: Time,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A type representing a difference between two instances of datetime.
|
||||||
|
|
||||||
|
**Note**: All fields are i64 because we can also use it to add a number of
|
||||||
|
seconds or nanos to a moment, that are then normalized within their respective
|
||||||
|
ranges.
|
||||||
|
*/
|
||||||
Delta :: struct {
|
Delta :: struct {
|
||||||
days: i64, // These are all i64 because we can also use it to add a number of seconds or nanos to a moment,
|
days: i64,
|
||||||
seconds: i64, // that are then normalized within their respective ranges.
|
seconds: i64,
|
||||||
nanos: i64,
|
nanos: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Type representing one of the months.
|
||||||
|
*/
|
||||||
Month :: enum i8 {
|
Month :: enum i8 {
|
||||||
January = 1,
|
January = 1,
|
||||||
February,
|
February,
|
||||||
@@ -63,6 +116,9 @@ Month :: enum i8 {
|
|||||||
December,
|
December,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Type representing one of the weekdays.
|
||||||
|
*/
|
||||||
Weekday :: enum i8 {
|
Weekday :: enum i8 {
|
||||||
Sunday = 0,
|
Sunday = 0,
|
||||||
Monday,
|
Monday,
|
||||||
|
|||||||
@@ -1,56 +1,113 @@
|
|||||||
/*
|
/*
|
||||||
Calendrical conversions using a proleptic Gregorian calendar.
|
Calendrical conversions using a proleptic Gregorian calendar.
|
||||||
|
|
||||||
Implemented using formulas from: Calendrical Calculations Ultimate Edition, Reingold & Dershowitz
|
Implemented using formulas from: Calendrical Calculations Ultimate Edition,
|
||||||
|
Reingold & Dershowitz
|
||||||
*/
|
*/
|
||||||
package datetime
|
package datetime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// Procedures that return an Ordinal
|
/*
|
||||||
|
Obtain an ordinal from a date.
|
||||||
|
|
||||||
|
This procedure converts the specified date into an ordinal. If the specified
|
||||||
|
date is not a valid date, an error is returned.
|
||||||
|
*/
|
||||||
date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal, err: Error) {
|
date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal, err: Error) {
|
||||||
validate(date) or_return
|
validate(date) or_return
|
||||||
return unsafe_date_to_ordinal(date), .None
|
return unsafe_date_to_ordinal(date), .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain an ordinal from date components.
|
||||||
|
|
||||||
|
This procedure converts the specified date, provided by its individual
|
||||||
|
components, into an ordinal. If the specified date is not a valid date, an error
|
||||||
|
is returned.
|
||||||
|
*/
|
||||||
components_to_ordinal :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (ordinal: Ordinal, err: Error) {
|
components_to_ordinal :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (ordinal: Ordinal, err: Error) {
|
||||||
validate(year, month, day) or_return
|
validate(year, month, day) or_return
|
||||||
return unsafe_date_to_ordinal({year, i8(month), i8(day)}), .None
|
return unsafe_date_to_ordinal({year, i8(month), i8(day)}), .None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procedures that return a Date
|
/*
|
||||||
|
Obtain date using an Ordinal.
|
||||||
|
|
||||||
|
This provedure converts the specified ordinal into a date. If the ordinal is not
|
||||||
|
a valid ordinal, an error is returned.
|
||||||
|
*/
|
||||||
ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date, err: Error) {
|
ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date, err: Error) {
|
||||||
validate(ordinal) or_return
|
validate(ordinal) or_return
|
||||||
return unsafe_ordinal_to_date(ordinal), .None
|
return unsafe_ordinal_to_date(ordinal), .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain a date from date components.
|
||||||
|
|
||||||
|
This procedure converts date components, specified by a year, a month and a day,
|
||||||
|
into a date object. If the provided date components don't represent a valid
|
||||||
|
date, an error is returned.
|
||||||
|
*/
|
||||||
components_to_date :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (date: Date, err: Error) {
|
components_to_date :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (date: Date, err: Error) {
|
||||||
validate(year, month, day) or_return
|
validate(year, month, day) or_return
|
||||||
return Date{i64(year), i8(month), i8(day)}, .None
|
return Date{i64(year), i8(month), i8(day)}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain time from time components.
|
||||||
|
|
||||||
|
This procedure converts time components, specified by an hour, a minute, a second
|
||||||
|
and nanoseconds, into a time object. If the provided time components don't
|
||||||
|
represent a valid time, an error is returned.
|
||||||
|
*/
|
||||||
components_to_time :: proc "contextless" (#any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (time: Time, err: Error) {
|
components_to_time :: proc "contextless" (#any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (time: Time, err: Error) {
|
||||||
validate(hour, minute, second, nanos) or_return
|
validate(hour, minute, second, nanos) or_return
|
||||||
return Time{i8(hour), i8(minute), i8(second), i32(nanos)}, .None
|
return Time{i8(hour), i8(minute), i8(second), i32(nanos)}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain datetime from components.
|
||||||
|
|
||||||
|
This procedure converts date components and time components into a datetime object.
|
||||||
|
If the provided date components or time components don't represent a valid
|
||||||
|
datetime, an error is returned.
|
||||||
|
*/
|
||||||
components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) {
|
components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) {
|
||||||
date := components_to_date(year, month, day) or_return
|
date := components_to_date(year, month, day) or_return
|
||||||
time := components_to_time(hour, minute, second, nanos) or_return
|
time := components_to_time(hour, minute, second, nanos) or_return
|
||||||
return {date, time}, .None
|
return {date, time}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain an datetime from an ordinal.
|
||||||
|
|
||||||
|
This procedure converts the value of an ordinal into a datetime. Since the
|
||||||
|
ordinal only has the amount of days, the resulting time in the datetime
|
||||||
|
object will always have the time equal to `00:00:00.000`.
|
||||||
|
*/
|
||||||
ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) {
|
ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) {
|
||||||
d := ordinal_to_date(ordinal) or_return
|
d := ordinal_to_date(ordinal) or_return
|
||||||
return {Date(d), {}}, .None
|
return {Date(d), {}}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate the weekday from an ordinal.
|
||||||
|
|
||||||
|
This procedure takes the value of an ordinal and returns the day of week for
|
||||||
|
that ordinal.
|
||||||
|
*/
|
||||||
day_of_week :: proc "contextless" (ordinal: Ordinal) -> (day: Weekday) {
|
day_of_week :: proc "contextless" (ordinal: Ordinal) -> (day: Weekday) {
|
||||||
return Weekday((ordinal - EPOCH + 1) %% 7)
|
return Weekday((ordinal - EPOCH + 1) %% 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate the difference between two dates.
|
||||||
|
|
||||||
|
This procedure calculates the difference between two dates `a - b`, and returns
|
||||||
|
a delta between the two dates in `days`. If either `a` or `b` is not a valid
|
||||||
|
date, an error is returned.
|
||||||
|
*/
|
||||||
subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error) {
|
subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error) {
|
||||||
ord_a := date_to_ordinal(a) or_return
|
ord_a := date_to_ordinal(a) or_return
|
||||||
ord_b := date_to_ordinal(b) or_return
|
ord_b := date_to_ordinal(b) or_return
|
||||||
@@ -59,6 +116,16 @@ subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate the difference between two datetimes.
|
||||||
|
|
||||||
|
This procedure calculates the difference between two datetimes, `a - b`, and
|
||||||
|
returns a delta between the two dates. The difference is returned in all three
|
||||||
|
fields of the `Delta` struct: the difference in days, the difference in seconds
|
||||||
|
and the difference in nanoseconds.
|
||||||
|
|
||||||
|
If either `a` or `b` is not a valid datetime, an error is returned.
|
||||||
|
*/
|
||||||
subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err: Error) {
|
subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err: Error) {
|
||||||
ord_a := date_to_ordinal(a) or_return
|
ord_a := date_to_ordinal(a) or_return
|
||||||
ord_b := date_to_ordinal(b) or_return
|
ord_b := date_to_ordinal(b) or_return
|
||||||
@@ -73,19 +140,42 @@ subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate a difference between two deltas.
|
||||||
|
*/
|
||||||
subtract_deltas :: proc "contextless" (a, b: Delta) -> (delta: Delta, err: Error) {
|
subtract_deltas :: proc "contextless" (a, b: Delta) -> (delta: Delta, err: Error) {
|
||||||
delta = Delta{a.days - b.days, a.seconds - b.seconds, a.nanos - b.nanos}
|
delta = Delta{a.days - b.days, a.seconds - b.seconds, a.nanos - b.nanos}
|
||||||
delta = normalize_delta(delta) or_return
|
delta = normalize_delta(delta) or_return
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculate a difference between two datetimes, dates or deltas.
|
||||||
|
*/
|
||||||
sub :: proc{subtract_datetimes, subtract_dates, subtract_deltas}
|
sub :: proc{subtract_datetimes, subtract_dates, subtract_deltas}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add certain amount of days to a date.
|
||||||
|
|
||||||
|
This procedure adds the specified amount of days to a date and returns a new
|
||||||
|
date. The new date would have happened the specified amount of days after the
|
||||||
|
specified date.
|
||||||
|
*/
|
||||||
add_days_to_date :: proc "contextless" (a: Date, days: i64) -> (date: Date, err: Error) {
|
add_days_to_date :: proc "contextless" (a: Date, days: i64) -> (date: Date, err: Error) {
|
||||||
ord := date_to_ordinal(a) or_return
|
ord := date_to_ordinal(a) or_return
|
||||||
ord += days
|
ord += days
|
||||||
return ordinal_to_date(ord)
|
return ordinal_to_date(ord)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add delta to a date.
|
||||||
|
|
||||||
|
This procedure adds a delta to a date, and returns a new date. The new date
|
||||||
|
would have happened the time specified by `delta` after the specified date.
|
||||||
|
|
||||||
|
**Note**: The delta is assumed to be normalized. That is, if it contains seconds
|
||||||
|
or milliseconds, regardless of the amount only the days will be added.
|
||||||
|
*/
|
||||||
add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date, err: Error) {
|
add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date, err: Error) {
|
||||||
ord := date_to_ordinal(a) or_return
|
ord := date_to_ordinal(a) or_return
|
||||||
// Because the input is a Date, we add only the days from the Delta.
|
// Because the input is a Date, we add only the days from the Delta.
|
||||||
@@ -93,6 +183,13 @@ add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date,
|
|||||||
return ordinal_to_date(ord)
|
return ordinal_to_date(ord)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add delta to datetime.
|
||||||
|
|
||||||
|
This procedure adds a delta to a datetime, and returns a new datetime. The new
|
||||||
|
datetime would have happened the time specified by `delta` after the specified
|
||||||
|
datetime.
|
||||||
|
*/
|
||||||
add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (datetime: DateTime, err: Error) {
|
add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (datetime: DateTime, err: Error) {
|
||||||
days := date_to_ordinal(a) or_return
|
days := date_to_ordinal(a) or_return
|
||||||
|
|
||||||
@@ -110,8 +207,18 @@ add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (date
|
|||||||
datetime.time = components_to_time(hour, minute, second, sum_delta.nanos) or_return
|
datetime.time = components_to_time(hour, minute, second, sum_delta.nanos) or_return
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add days to a date, delta to a date or delta to datetime.
|
||||||
|
*/
|
||||||
add :: proc{add_days_to_date, add_delta_to_date, add_delta_to_datetime}
|
add :: proc{add_days_to_date, add_delta_to_date, add_delta_to_datetime}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the day number in a year
|
||||||
|
|
||||||
|
This procedure returns the number of the day in a year, starting from 1. If
|
||||||
|
the date is not a valid date, an error is returned.
|
||||||
|
*/
|
||||||
day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
||||||
validate(date) or_return
|
validate(date) or_return
|
||||||
|
|
||||||
@@ -120,6 +227,13 @@ day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the remaining number of days in a year.
|
||||||
|
|
||||||
|
This procedure returns the number of days between the specified date and
|
||||||
|
December 31 of the same year. If the date is not a valid date, an error is
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err: Error) {
|
days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err: Error) {
|
||||||
// Alternative formulation `day_number` subtracted from 365 or 366 depending on leap year
|
// Alternative formulation `day_number` subtracted from 365 or 366 depending on leap year
|
||||||
validate(date) or_return
|
validate(date) or_return
|
||||||
@@ -127,6 +241,12 @@ days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err:
|
|||||||
return delta.days, .None
|
return delta.days, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the last day of a given month on a given year.
|
||||||
|
|
||||||
|
This procedure returns the amount of days in a specified month on a specified
|
||||||
|
date. If the specified year or month is not valid, an error is returned.
|
||||||
|
*/
|
||||||
last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i8, err: Error) {
|
last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i8, err: Error) {
|
||||||
// Not using formula 2.27 from the book. This is far simpler and gives the same answer.
|
// Not using formula 2.27 from the book. This is far simpler and gives the same answer.
|
||||||
|
|
||||||
@@ -140,16 +260,33 @@ last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the new year date of a given year.
|
||||||
|
|
||||||
|
This procedure returns the January 1st date of the specified year. If the year
|
||||||
|
is not valid, an error is returned.
|
||||||
|
*/
|
||||||
new_year :: proc "contextless" (#any_int year: i64) -> (new_year: Date, err: Error) {
|
new_year :: proc "contextless" (#any_int year: i64) -> (new_year: Date, err: Error) {
|
||||||
validate(year, 1, 1) or_return
|
validate(year, 1, 1) or_return
|
||||||
return {year, 1, 1}, .None
|
return {year, 1, 1}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the end year of a given date.
|
||||||
|
|
||||||
|
This procedure returns the December 31st date of the specified year. If the year
|
||||||
|
is not valid, an error is returned.
|
||||||
|
*/
|
||||||
year_end :: proc "contextless" (#any_int year: i64) -> (year_end: Date, err: Error) {
|
year_end :: proc "contextless" (#any_int year: i64) -> (year_end: Date, err: Error) {
|
||||||
validate(year, 12, 31) or_return
|
validate(year, 12, 31) or_return
|
||||||
return {year, 12, 31}, .None
|
return {year, 12, 31}, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain the range of dates for a given year.
|
||||||
|
|
||||||
|
This procedure returns dates, for every day of a given year in a slice.
|
||||||
|
*/
|
||||||
year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (range: []Date) {
|
year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (range: []Date) {
|
||||||
is_leap := is_leap_year(year)
|
is_leap := is_leap_year(year)
|
||||||
|
|
||||||
@@ -171,6 +308,15 @@ year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (rang
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Normalize the delta.
|
||||||
|
|
||||||
|
This procedure normalizes the delta in such a way that the number of seconds
|
||||||
|
is between 0 and the number of seconds in the day and nanoseconds is between
|
||||||
|
0 and 10^9.
|
||||||
|
|
||||||
|
If the value for `days` overflows during this operation, an error is returned.
|
||||||
|
*/
|
||||||
normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err: Error) {
|
normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err: Error) {
|
||||||
// Distribute nanos into seconds and remainder
|
// Distribute nanos into seconds and remainder
|
||||||
seconds, nanos := divmod(delta.nanos, 1e9)
|
seconds, nanos := divmod(delta.nanos, 1e9)
|
||||||
@@ -194,6 +340,12 @@ normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err:
|
|||||||
// The following procedures don't check whether their inputs are in a valid range.
|
// The following procedures don't check whether their inputs are in a valid range.
|
||||||
// They're still exported for those who know their inputs have been validated.
|
// They're still exported for those who know their inputs have been validated.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain an ordinal from a date.
|
||||||
|
|
||||||
|
This procedure converts a date into an ordinal. If the date is not a valid date,
|
||||||
|
the result is unspecified.
|
||||||
|
*/
|
||||||
unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal) {
|
unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal) {
|
||||||
year_minus_one := date.year - 1
|
year_minus_one := date.year - 1
|
||||||
|
|
||||||
@@ -223,6 +375,12 @@ unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain a year and a day of the year from an ordinal.
|
||||||
|
|
||||||
|
This procedure returns the year and the day of the year of a given ordinal.
|
||||||
|
Of the ordinal is outside of its valid range, the result is unspecified.
|
||||||
|
*/
|
||||||
unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, day_ordinal: i64) {
|
unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, day_ordinal: i64) {
|
||||||
// Days after epoch
|
// Days after epoch
|
||||||
d0 := ordinal - EPOCH
|
d0 := ordinal - EPOCH
|
||||||
@@ -253,6 +411,12 @@ unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, d
|
|||||||
return year + 1, day_ordinal
|
return year + 1, day_ordinal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain a date from an ordinal.
|
||||||
|
|
||||||
|
This procedure converts an ordinal into a date. If the ordinal is outside of
|
||||||
|
its valid range, the result is unspecified.
|
||||||
|
*/
|
||||||
unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date) {
|
unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date) {
|
||||||
year, _ := unsafe_ordinal_to_year(ordinal)
|
year, _ := unsafe_ordinal_to_year(ordinal)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//+private
|
||||||
package datetime
|
package datetime
|
||||||
|
|
||||||
// Internal helper functions for calendrical conversions
|
// Internal helper functions for calendrical conversions
|
||||||
|
|||||||
@@ -1,14 +1,29 @@
|
|||||||
package datetime
|
package datetime
|
||||||
|
|
||||||
// Validation helpers
|
// Validation helpers
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check if a year is a leap year.
|
||||||
|
*/
|
||||||
is_leap_year :: proc "contextless" (#any_int year: i64) -> (leap: bool) {
|
is_leap_year :: proc "contextless" (#any_int year: i64) -> (leap: bool) {
|
||||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in date formation.
|
||||||
|
|
||||||
|
This procedure validates all fields of a date, and if any of the fields is
|
||||||
|
outside of allowed range, an error is returned.
|
||||||
|
*/
|
||||||
validate_date :: proc "contextless" (date: Date) -> (err: Error) {
|
validate_date :: proc "contextless" (date: Date) -> (err: Error) {
|
||||||
return validate(date.year, date.month, date.day)
|
return validate(date.year, date.month, date.day)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in date formation given date components.
|
||||||
|
|
||||||
|
This procedure checks whether a date formed by the specified year month and a
|
||||||
|
day is a valid date. If not, an error is returned.
|
||||||
|
*/
|
||||||
validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (err: Error) {
|
validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (err: Error) {
|
||||||
if year < MIN_DATE.year || year > MAX_DATE.year {
|
if year < MIN_DATE.year || year > MAX_DATE.year {
|
||||||
return .Invalid_Year
|
return .Invalid_Year
|
||||||
@@ -29,6 +44,12 @@ validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #a
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in Ordinal
|
||||||
|
|
||||||
|
This procedure checks if the ordinal is in a valid range for roundtrip
|
||||||
|
conversions with the dates. If not, an error is returned.
|
||||||
|
*/
|
||||||
validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
||||||
if ordinal < MIN_ORD || ordinal > MAX_ORD {
|
if ordinal < MIN_ORD || ordinal > MAX_ORD {
|
||||||
return .Invalid_Ordinal
|
return .Invalid_Ordinal
|
||||||
@@ -36,10 +57,22 @@ validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in time formation
|
||||||
|
|
||||||
|
This procedure checks whether time has all fields in valid ranges, and if not
|
||||||
|
an error is returned.
|
||||||
|
*/
|
||||||
validate_time :: proc "contextless" (time: Time) -> (err: Error) {
|
validate_time :: proc "contextless" (time: Time) -> (err: Error) {
|
||||||
return validate(time.hour, time.minute, time.second, time.nano)
|
return validate(time.hour, time.minute, time.second, time.nano)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in time formed by its components.
|
||||||
|
|
||||||
|
This procedure checks whether the time formed by its components is valid, and
|
||||||
|
if not an error is returned.
|
||||||
|
*/
|
||||||
validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minute, #any_int second, #any_int nano: i64) -> (err: Error) {
|
validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minute, #any_int second, #any_int nano: i64) -> (err: Error) {
|
||||||
if hour < 0 || hour > 23 {
|
if hour < 0 || hour > 23 {
|
||||||
return .Invalid_Hour
|
return .Invalid_Hour
|
||||||
@@ -56,12 +89,21 @@ validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minut
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in datetime formation.
|
||||||
|
|
||||||
|
This procedure checks whether all fields of date and time in the specified
|
||||||
|
datetime are valid, and if not, an error is returned.
|
||||||
|
*/
|
||||||
validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) {
|
validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) {
|
||||||
validate(datetime.date) or_return
|
validate(datetime.date) or_return
|
||||||
validate(datetime.time) or_return
|
validate(datetime.time) or_return
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check for errors in date, time or datetime.
|
||||||
|
*/
|
||||||
validate :: proc{
|
validate :: proc{
|
||||||
validate_date,
|
validate_date,
|
||||||
validate_year_month_day,
|
validate_year_month_day,
|
||||||
|
|||||||
Reference in New Issue
Block a user