Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
build/dev/javascript/gleam_time/gleam.mjs
Normal file
1
build/dev/javascript/gleam_time/gleam.mjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "../prelude.mjs";
|
||||
391
build/dev/javascript/gleam_time/gleam/time/calendar.mjs
Normal file
391
build/dev/javascript/gleam_time/gleam/time/calendar.mjs
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
import * as $int from "../../../gleam_stdlib/gleam/int.mjs";
|
||||
import * as $order from "../../../gleam_stdlib/gleam/order.mjs";
|
||||
import { Ok, Error, CustomType as $CustomType } from "../../gleam.mjs";
|
||||
import * as $duration from "../../gleam/time/duration.mjs";
|
||||
import { local_time_offset_seconds } from "../../gleam_time_ffi.mjs";
|
||||
|
||||
export class Date extends $CustomType {
|
||||
constructor(year, month, day) {
|
||||
super();
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
}
|
||||
}
|
||||
export const Date$Date = (year, month, day) => new Date(year, month, day);
|
||||
export const Date$isDate = (value) => value instanceof Date;
|
||||
export const Date$Date$year = (value) => value.year;
|
||||
export const Date$Date$0 = (value) => value.year;
|
||||
export const Date$Date$month = (value) => value.month;
|
||||
export const Date$Date$1 = (value) => value.month;
|
||||
export const Date$Date$day = (value) => value.day;
|
||||
export const Date$Date$2 = (value) => value.day;
|
||||
|
||||
export class TimeOfDay extends $CustomType {
|
||||
constructor(hours, minutes, seconds, nanoseconds) {
|
||||
super();
|
||||
this.hours = hours;
|
||||
this.minutes = minutes;
|
||||
this.seconds = seconds;
|
||||
this.nanoseconds = nanoseconds;
|
||||
}
|
||||
}
|
||||
export const TimeOfDay$TimeOfDay = (hours, minutes, seconds, nanoseconds) =>
|
||||
new TimeOfDay(hours, minutes, seconds, nanoseconds);
|
||||
export const TimeOfDay$isTimeOfDay = (value) => value instanceof TimeOfDay;
|
||||
export const TimeOfDay$TimeOfDay$hours = (value) => value.hours;
|
||||
export const TimeOfDay$TimeOfDay$0 = (value) => value.hours;
|
||||
export const TimeOfDay$TimeOfDay$minutes = (value) => value.minutes;
|
||||
export const TimeOfDay$TimeOfDay$1 = (value) => value.minutes;
|
||||
export const TimeOfDay$TimeOfDay$seconds = (value) => value.seconds;
|
||||
export const TimeOfDay$TimeOfDay$2 = (value) => value.seconds;
|
||||
export const TimeOfDay$TimeOfDay$nanoseconds = (value) => value.nanoseconds;
|
||||
export const TimeOfDay$TimeOfDay$3 = (value) => value.nanoseconds;
|
||||
|
||||
export class January extends $CustomType {}
|
||||
export const Month$January = () => new January();
|
||||
export const Month$isJanuary = (value) => value instanceof January;
|
||||
|
||||
export class February extends $CustomType {}
|
||||
export const Month$February = () => new February();
|
||||
export const Month$isFebruary = (value) => value instanceof February;
|
||||
|
||||
export class March extends $CustomType {}
|
||||
export const Month$March = () => new March();
|
||||
export const Month$isMarch = (value) => value instanceof March;
|
||||
|
||||
export class April extends $CustomType {}
|
||||
export const Month$April = () => new April();
|
||||
export const Month$isApril = (value) => value instanceof April;
|
||||
|
||||
export class May extends $CustomType {}
|
||||
export const Month$May = () => new May();
|
||||
export const Month$isMay = (value) => value instanceof May;
|
||||
|
||||
export class June extends $CustomType {}
|
||||
export const Month$June = () => new June();
|
||||
export const Month$isJune = (value) => value instanceof June;
|
||||
|
||||
export class July extends $CustomType {}
|
||||
export const Month$July = () => new July();
|
||||
export const Month$isJuly = (value) => value instanceof July;
|
||||
|
||||
export class August extends $CustomType {}
|
||||
export const Month$August = () => new August();
|
||||
export const Month$isAugust = (value) => value instanceof August;
|
||||
|
||||
export class September extends $CustomType {}
|
||||
export const Month$September = () => new September();
|
||||
export const Month$isSeptember = (value) => value instanceof September;
|
||||
|
||||
export class October extends $CustomType {}
|
||||
export const Month$October = () => new October();
|
||||
export const Month$isOctober = (value) => value instanceof October;
|
||||
|
||||
export class November extends $CustomType {}
|
||||
export const Month$November = () => new November();
|
||||
export const Month$isNovember = (value) => value instanceof November;
|
||||
|
||||
export class December extends $CustomType {}
|
||||
export const Month$December = () => new December();
|
||||
export const Month$isDecember = (value) => value instanceof December;
|
||||
|
||||
/**
|
||||
* Get the offset for the computer's currently configured time zone.
|
||||
*
|
||||
* Note this may not be the time zone that is correct to use for your user.
|
||||
* For example, if you are making a web application that runs on a server you
|
||||
* want _their_ computer's time zone, not yours.
|
||||
*
|
||||
* This is the _current local_ offset, not the current local time zone. This
|
||||
* means that while it will result in the expected outcome for the current
|
||||
* time, it may result in unexpected output if used with other timestamps. For
|
||||
* example: a timestamp that would locally be during daylight savings time if
|
||||
* is it not currently daylight savings time when this function is called.
|
||||
*/
|
||||
export function local_offset() {
|
||||
return $duration.seconds(local_time_offset_seconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the English name for a month.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* month_to_string(April)
|
||||
* // -> "April"
|
||||
* ```
|
||||
*/
|
||||
export function month_to_string(month) {
|
||||
if (month instanceof January) {
|
||||
return "January";
|
||||
} else if (month instanceof February) {
|
||||
return "February";
|
||||
} else if (month instanceof March) {
|
||||
return "March";
|
||||
} else if (month instanceof April) {
|
||||
return "April";
|
||||
} else if (month instanceof May) {
|
||||
return "May";
|
||||
} else if (month instanceof June) {
|
||||
return "June";
|
||||
} else if (month instanceof July) {
|
||||
return "July";
|
||||
} else if (month instanceof August) {
|
||||
return "August";
|
||||
} else if (month instanceof September) {
|
||||
return "September";
|
||||
} else if (month instanceof October) {
|
||||
return "October";
|
||||
} else if (month instanceof November) {
|
||||
return "November";
|
||||
} else {
|
||||
return "December";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number for the month, where January is 1 and December is 12.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* month_to_int(January)
|
||||
* // -> 1
|
||||
* ```
|
||||
*/
|
||||
export function month_to_int(month) {
|
||||
if (month instanceof January) {
|
||||
return 1;
|
||||
} else if (month instanceof February) {
|
||||
return 2;
|
||||
} else if (month instanceof March) {
|
||||
return 3;
|
||||
} else if (month instanceof April) {
|
||||
return 4;
|
||||
} else if (month instanceof May) {
|
||||
return 5;
|
||||
} else if (month instanceof June) {
|
||||
return 6;
|
||||
} else if (month instanceof July) {
|
||||
return 7;
|
||||
} else if (month instanceof August) {
|
||||
return 8;
|
||||
} else if (month instanceof September) {
|
||||
return 9;
|
||||
} else if (month instanceof October) {
|
||||
return 10;
|
||||
} else if (month instanceof November) {
|
||||
return 11;
|
||||
} else {
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the month for a given number, where January is 1 and December is 12.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* month_from_int(1)
|
||||
* // -> Ok(January)
|
||||
* ```
|
||||
*/
|
||||
export function month_from_int(month) {
|
||||
if (month === 1) {
|
||||
return new Ok(new January());
|
||||
} else if (month === 2) {
|
||||
return new Ok(new February());
|
||||
} else if (month === 3) {
|
||||
return new Ok(new March());
|
||||
} else if (month === 4) {
|
||||
return new Ok(new April());
|
||||
} else if (month === 5) {
|
||||
return new Ok(new May());
|
||||
} else if (month === 6) {
|
||||
return new Ok(new June());
|
||||
} else if (month === 7) {
|
||||
return new Ok(new July());
|
||||
} else if (month === 8) {
|
||||
return new Ok(new August());
|
||||
} else if (month === 9) {
|
||||
return new Ok(new September());
|
||||
} else if (month === 10) {
|
||||
return new Ok(new October());
|
||||
} else if (month === 11) {
|
||||
return new Ok(new November());
|
||||
} else if (month === 12) {
|
||||
return new Ok(new December());
|
||||
} else {
|
||||
return new Error(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given year is a leap year.
|
||||
*
|
||||
* A leap year occurs every 4 years, except for years divisible by 100,
|
||||
* unless they are also divisible by 400.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* is_leap_year(2024)
|
||||
* // -> True
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* is_leap_year(2023)
|
||||
* // -> False
|
||||
* ```
|
||||
*/
|
||||
export function is_leap_year(year) {
|
||||
let $ = (year % 400) === 0;
|
||||
if ($) {
|
||||
return $;
|
||||
} else {
|
||||
let $1 = (year % 100) === 0;
|
||||
if ($1) {
|
||||
return false;
|
||||
} else {
|
||||
return (year % 4) === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given date is valid.
|
||||
*
|
||||
* This function properly accounts for leap years when validating February days.
|
||||
* A leap year occurs every 4 years, except for years divisible by 100,
|
||||
* unless they are also divisible by 400.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* is_valid_date(Date(2023, April, 15))
|
||||
* // -> True
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* is_valid_date(Date(2023, April, 31))
|
||||
* // -> False
|
||||
* ```
|
||||
*
|
||||
* ```gleam
|
||||
* is_valid_date(Date(2024, February, 29))
|
||||
* // -> True (2024 is a leap year)
|
||||
* ```
|
||||
*/
|
||||
export function is_valid_date(date) {
|
||||
let year;
|
||||
let month;
|
||||
let day;
|
||||
year = date.year;
|
||||
month = date.month;
|
||||
day = date.day;
|
||||
let $ = day < 1;
|
||||
if ($) {
|
||||
return false;
|
||||
} else {
|
||||
if (month instanceof January) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof February) {
|
||||
let _block;
|
||||
let $1 = is_leap_year(year);
|
||||
if ($1) {
|
||||
_block = 29;
|
||||
} else {
|
||||
_block = 28;
|
||||
}
|
||||
let max_february_days = _block;
|
||||
return day <= max_february_days;
|
||||
} else if (month instanceof March) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof April) {
|
||||
return day <= 30;
|
||||
} else if (month instanceof May) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof June) {
|
||||
return day <= 30;
|
||||
} else if (month instanceof July) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof August) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof September) {
|
||||
return day <= 30;
|
||||
} else if (month instanceof October) {
|
||||
return day <= 31;
|
||||
} else if (month instanceof November) {
|
||||
return day <= 30;
|
||||
} else {
|
||||
return day <= 31;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a time of day is valid.
|
||||
*
|
||||
* Validates that hours are 0-23, minutes are 0-59, seconds are 0-59,
|
||||
* and nanoseconds are 0-999,999,999.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789))
|
||||
* // -> True
|
||||
* ```
|
||||
*/
|
||||
export function is_valid_time_of_day(time) {
|
||||
let hours;
|
||||
let minutes;
|
||||
let seconds;
|
||||
let nanoseconds;
|
||||
hours = time.hours;
|
||||
minutes = time.minutes;
|
||||
seconds = time.seconds;
|
||||
nanoseconds = time.nanoseconds;
|
||||
return (((((((hours >= 0) && (hours <= 23)) && (minutes >= 0)) && (minutes <= 59)) && (seconds >= 0)) && (seconds <= 59)) && (nanoseconds >= 0)) && (nanoseconds <= 999_999_999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Naively compares two dates without any time zone information, returning an
|
||||
* order.
|
||||
*
|
||||
* ## Correctness
|
||||
*
|
||||
* This function compares dates without any time zone information, only using
|
||||
* the rules for the gregorian calendar. This is typically sufficient, but be
|
||||
* aware that in reality some time zones will change their calendar date
|
||||
* occasionally. This can result in days being skipped, out of order, or
|
||||
* happening multiple times.
|
||||
*
|
||||
* If you need real-world correct time ordering then use the
|
||||
* `gleam/time/timestamp` module instead.
|
||||
*/
|
||||
export function naive_date_compare(one, other) {
|
||||
let _pipe = $int.compare(one.year, other.year);
|
||||
let _pipe$1 = $order.lazy_break_tie(
|
||||
_pipe,
|
||||
() => {
|
||||
return $int.compare(month_to_int(one.month), month_to_int(other.month));
|
||||
},
|
||||
);
|
||||
return $order.lazy_break_tie(
|
||||
_pipe$1,
|
||||
() => { return $int.compare(one.day, other.day); },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset for the [Coordinated Universal Time (UTC)](https://en.wikipedia.org/wiki/Coordinated_Universal_Time)
|
||||
* time zone.
|
||||
*
|
||||
* The utc zone has no time adjustments, it is always zero. It never observes
|
||||
* daylight-saving time and it never shifts around based on political
|
||||
* restructuring.
|
||||
*/
|
||||
export const utc_offset = $duration.empty;
|
||||
382
build/dev/javascript/gleam_time/gleam/time/duration.mjs
Normal file
382
build/dev/javascript/gleam_time/gleam/time/duration.mjs
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
import * as $bool from "../../../gleam_stdlib/gleam/bool.mjs";
|
||||
import * as $int from "../../../gleam_stdlib/gleam/int.mjs";
|
||||
import * as $order from "../../../gleam_stdlib/gleam/order.mjs";
|
||||
import * as $string from "../../../gleam_stdlib/gleam/string.mjs";
|
||||
import { CustomType as $CustomType, remainderInt, divideInt, isEqual } from "../../gleam.mjs";
|
||||
|
||||
class Duration extends $CustomType {
|
||||
constructor(seconds, nanoseconds) {
|
||||
super();
|
||||
this.seconds = seconds;
|
||||
this.nanoseconds = nanoseconds;
|
||||
}
|
||||
}
|
||||
|
||||
export class Nanosecond extends $CustomType {}
|
||||
export const Unit$Nanosecond = () => new Nanosecond();
|
||||
export const Unit$isNanosecond = (value) => value instanceof Nanosecond;
|
||||
|
||||
export class Microsecond extends $CustomType {}
|
||||
export const Unit$Microsecond = () => new Microsecond();
|
||||
export const Unit$isMicrosecond = (value) => value instanceof Microsecond;
|
||||
|
||||
export class Millisecond extends $CustomType {}
|
||||
export const Unit$Millisecond = () => new Millisecond();
|
||||
export const Unit$isMillisecond = (value) => value instanceof Millisecond;
|
||||
|
||||
export class Second extends $CustomType {}
|
||||
export const Unit$Second = () => new Second();
|
||||
export const Unit$isSecond = (value) => value instanceof Second;
|
||||
|
||||
export class Minute extends $CustomType {}
|
||||
export const Unit$Minute = () => new Minute();
|
||||
export const Unit$isMinute = (value) => value instanceof Minute;
|
||||
|
||||
export class Hour extends $CustomType {}
|
||||
export const Unit$Hour = () => new Hour();
|
||||
export const Unit$isHour = (value) => value instanceof Hour;
|
||||
|
||||
export class Day extends $CustomType {}
|
||||
export const Unit$Day = () => new Day();
|
||||
export const Unit$isDay = (value) => value instanceof Day;
|
||||
|
||||
export class Week extends $CustomType {}
|
||||
export const Unit$Week = () => new Week();
|
||||
export const Unit$isWeek = (value) => value instanceof Week;
|
||||
|
||||
export class Month extends $CustomType {}
|
||||
export const Unit$Month = () => new Month();
|
||||
export const Unit$isMonth = (value) => value instanceof Month;
|
||||
|
||||
export class Year extends $CustomType {}
|
||||
export const Unit$Year = () => new Year();
|
||||
export const Unit$isYear = (value) => value instanceof Year;
|
||||
|
||||
/**
|
||||
* Ensure the duration is represented with `nanoseconds` being positive and
|
||||
* less than 1 second.
|
||||
*
|
||||
* This function does not change the amount of time that the duratoin refers
|
||||
* to, it only adjusts the values used to represent the time.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
function normalise(duration) {
|
||||
let multiplier = 1_000_000_000;
|
||||
let nanoseconds$1 = remainderInt(duration.nanoseconds, multiplier);
|
||||
let overflow = duration.nanoseconds - nanoseconds$1;
|
||||
let seconds$1 = duration.seconds + (divideInt(overflow, multiplier));
|
||||
let $ = nanoseconds$1 >= 0;
|
||||
if ($) {
|
||||
return new Duration(seconds$1, nanoseconds$1);
|
||||
} else {
|
||||
return new Duration(seconds$1 - 1, multiplier + nanoseconds$1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a duration to a number of the largest number of a unit, serving as
|
||||
* a rough description of the duration that a human can understand.
|
||||
*
|
||||
* The size used for each unit are described in the documentation for the
|
||||
* `Unit` type.
|
||||
*
|
||||
* ```gleam
|
||||
* seconds(125)
|
||||
* |> approximate
|
||||
* // -> #(2, Minute)
|
||||
* ```
|
||||
*
|
||||
* This function rounds _towards zero_. This means that if a duration is just
|
||||
* short of 2 days then it will approximate to 1 day.
|
||||
*
|
||||
* ```gleam
|
||||
* hours(47)
|
||||
* |> approximate
|
||||
* // -> #(1, Day)
|
||||
* ```
|
||||
*/
|
||||
export function approximate(duration) {
|
||||
let s;
|
||||
let ns;
|
||||
s = duration.seconds;
|
||||
ns = duration.nanoseconds;
|
||||
let minute = 60;
|
||||
let hour = minute * 60;
|
||||
let day = hour * 24;
|
||||
let week = day * 7;
|
||||
let year = day * 365 + hour * 6;
|
||||
let month = globalThis.Math.trunc(year / 12);
|
||||
let microsecond = 1000;
|
||||
let millisecond = microsecond * 1000;
|
||||
let $ = undefined;
|
||||
if (s < 0) {
|
||||
let _block;
|
||||
let _pipe = new Duration(- s, - ns);
|
||||
let _pipe$1 = normalise(_pipe);
|
||||
_block = approximate(_pipe$1);
|
||||
let $1 = _block;
|
||||
let amount;
|
||||
let unit;
|
||||
amount = $1[0];
|
||||
unit = $1[1];
|
||||
return [- amount, unit];
|
||||
} else if (s >= year) {
|
||||
return [divideInt(s, year), new Year()];
|
||||
} else if (s >= month) {
|
||||
return [divideInt(s, month), new Month()];
|
||||
} else if (s >= week) {
|
||||
return [divideInt(s, week), new Week()];
|
||||
} else if (s >= day) {
|
||||
return [divideInt(s, day), new Day()];
|
||||
} else if (s >= hour) {
|
||||
return [divideInt(s, hour), new Hour()];
|
||||
} else if (s >= minute) {
|
||||
return [divideInt(s, minute), new Minute()];
|
||||
} else if (s > 0) {
|
||||
return [s, new Second()];
|
||||
} else if (ns >= millisecond) {
|
||||
return [divideInt(ns, millisecond), new Millisecond()];
|
||||
} else if (ns >= microsecond) {
|
||||
return [divideInt(ns, microsecond), new Microsecond()];
|
||||
} else {
|
||||
return [ns, new Nanosecond()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare one duration to another, indicating whether the first spans a
|
||||
* larger amount of time (and so is greater) or smaller amount of time (and so
|
||||
* is lesser) than the second.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* compare(seconds(1), seconds(2))
|
||||
* // -> order.Lt
|
||||
* ```
|
||||
*
|
||||
* Whether a duration is negative or positive doesn't matter for comparing
|
||||
* them, only the amount of time spanned matters.
|
||||
*
|
||||
* ```gleam
|
||||
* compare(seconds(-2), seconds(1))
|
||||
* // -> order.Gt
|
||||
* ```
|
||||
*/
|
||||
export function compare(left, right) {
|
||||
let parts = (x) => {
|
||||
let $ = x.seconds >= 0;
|
||||
if ($) {
|
||||
return [x.seconds, x.nanoseconds];
|
||||
} else {
|
||||
return [x.seconds * -1 - 1, 1_000_000_000 - x.nanoseconds];
|
||||
}
|
||||
};
|
||||
let $ = parts(left);
|
||||
let ls;
|
||||
let lns;
|
||||
ls = $[0];
|
||||
lns = $[1];
|
||||
let $1 = parts(right);
|
||||
let rs;
|
||||
let rns;
|
||||
rs = $1[0];
|
||||
rns = $1[1];
|
||||
let _pipe = $int.compare(ls, rs);
|
||||
return $order.break_tie(_pipe, $int.compare(lns, rns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the difference between two durations.
|
||||
*
|
||||
* This is effectively substracting the first duration from the second.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* difference(seconds(1), seconds(5))
|
||||
* // -> seconds(4)
|
||||
* ```
|
||||
*/
|
||||
export function difference(left, right) {
|
||||
let _pipe = new Duration(
|
||||
right.seconds - left.seconds,
|
||||
right.nanoseconds - left.nanoseconds,
|
||||
);
|
||||
return normalise(_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two durations together.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* ```gleam
|
||||
* add(seconds(1), seconds(5))
|
||||
* // -> seconds(6)
|
||||
* ```
|
||||
*/
|
||||
export function add(left, right) {
|
||||
let _pipe = new Duration(
|
||||
left.seconds + right.seconds,
|
||||
left.nanoseconds + right.nanoseconds,
|
||||
);
|
||||
return normalise(_pipe);
|
||||
}
|
||||
|
||||
function nanosecond_digits(loop$n, loop$position, loop$acc) {
|
||||
while (true) {
|
||||
let n = loop$n;
|
||||
let position = loop$position;
|
||||
let acc = loop$acc;
|
||||
if (position === 9) {
|
||||
return acc;
|
||||
} else if ((acc === "") && ((remainderInt(n, 10)) === 0)) {
|
||||
loop$n = globalThis.Math.trunc(n / 10);
|
||||
loop$position = position + 1;
|
||||
loop$acc = acc;
|
||||
} else {
|
||||
let acc$1 = $int.to_string(n % 10) + acc;
|
||||
loop$n = globalThis.Math.trunc(n / 10);
|
||||
loop$position = position + 1;
|
||||
loop$acc = acc$1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duration of a number of seconds.
|
||||
*/
|
||||
export function seconds(amount) {
|
||||
return new Duration(amount, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duration of a number of minutes.
|
||||
*/
|
||||
export function minutes(amount) {
|
||||
return seconds(amount * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duration of a number of hours.
|
||||
*/
|
||||
export function hours(amount) {
|
||||
return seconds(amount * 60 * 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duration of a number of milliseconds.
|
||||
*/
|
||||
export function milliseconds(amount) {
|
||||
let remainder = amount % 1000;
|
||||
let overflow = amount - remainder;
|
||||
let nanoseconds$1 = remainder * 1_000_000;
|
||||
let seconds$1 = globalThis.Math.trunc(overflow / 1000);
|
||||
let _pipe = new Duration(seconds$1, nanoseconds$1);
|
||||
return normalise(_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duration of a number of nanoseconds.
|
||||
*
|
||||
* # JavaScript int limitations
|
||||
*
|
||||
* Remember that JavaScript can only perfectly represent ints between positive
|
||||
* and negative 9,007,199,254,740,991! If you use a single call to this
|
||||
* function to create durations larger than that number of nanoseconds then
|
||||
* you will likely not get exactly the value you expect. Use `seconds` and
|
||||
* `milliseconds` as much as possible for large durations.
|
||||
*/
|
||||
export function nanoseconds(amount) {
|
||||
let _pipe = new Duration(0, amount);
|
||||
return normalise(_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the duration to a number of seconds.
|
||||
*
|
||||
* There may be some small loss of precision due to `Duration` being
|
||||
* nanosecond accurate and `Float` not being able to represent this.
|
||||
*/
|
||||
export function to_seconds(duration) {
|
||||
let seconds$1 = $int.to_float(duration.seconds);
|
||||
let nanoseconds$1 = $int.to_float(duration.nanoseconds);
|
||||
return seconds$1 + (nanoseconds$1 / 1_000_000_000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the duration to a number of seconds and nanoseconds. There is no
|
||||
* loss of precision with this conversion on any target.
|
||||
*/
|
||||
export function to_seconds_and_nanoseconds(duration) {
|
||||
return [duration.seconds, duration.nanoseconds];
|
||||
}
|
||||
|
||||
export const empty = /* @__PURE__ */ new Duration(0, 0);
|
||||
|
||||
/**
|
||||
* Convert the duration to an [ISO8601][1] formatted duration string.
|
||||
*
|
||||
* The ISO8601 duration format is ambiguous without context due to months and
|
||||
* years having different lengths, and because of leap seconds. This function
|
||||
* encodes the duration as days, hours, and seconds without any leap seconds.
|
||||
* Be sure to take this into account when using the duration strings.
|
||||
*
|
||||
* [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations
|
||||
*/
|
||||
export function to_iso8601_string(duration) {
|
||||
return $bool.guard(
|
||||
isEqual(duration, empty),
|
||||
"PT0S",
|
||||
() => {
|
||||
let split = (total, limit) => {
|
||||
let amount = remainderInt(total, limit);
|
||||
let remainder = divideInt((total - amount), limit);
|
||||
return [amount, remainder];
|
||||
};
|
||||
let $ = split(duration.seconds, 60);
|
||||
let seconds$1;
|
||||
let rest;
|
||||
seconds$1 = $[0];
|
||||
rest = $[1];
|
||||
let $1 = split(rest, 60);
|
||||
let minutes$1;
|
||||
let rest$1;
|
||||
minutes$1 = $1[0];
|
||||
rest$1 = $1[1];
|
||||
let $2 = split(rest$1, 24);
|
||||
let hours$1;
|
||||
let rest$2;
|
||||
hours$1 = $2[0];
|
||||
rest$2 = $2[1];
|
||||
let days = rest$2;
|
||||
let add$1 = (out, value, unit) => {
|
||||
if (value === 0) {
|
||||
return out;
|
||||
} else {
|
||||
return (out + $int.to_string(value)) + unit;
|
||||
}
|
||||
};
|
||||
let _block;
|
||||
let _pipe = "P";
|
||||
let _pipe$1 = add$1(_pipe, days, "D");
|
||||
let _pipe$2 = $string.append(_pipe$1, "T");
|
||||
let _pipe$3 = add$1(_pipe$2, hours$1, "H");
|
||||
_block = add$1(_pipe$3, minutes$1, "M");
|
||||
let output = _block;
|
||||
let $3 = duration.nanoseconds;
|
||||
if ($3 === 0) {
|
||||
if (seconds$1 === 0) {
|
||||
return output;
|
||||
} else {
|
||||
return (output + $int.to_string(seconds$1)) + "S";
|
||||
}
|
||||
} else {
|
||||
let f = nanosecond_digits(duration.nanoseconds, 0, "");
|
||||
return (((output + $int.to_string(seconds$1)) + ".") + f) + "S";
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
1215
build/dev/javascript/gleam_time/gleam/time/timestamp.mjs
Normal file
1215
build/dev/javascript/gleam_time/gleam/time/timestamp.mjs
Normal file
File diff suppressed because it is too large
Load diff
468
build/dev/javascript/gleam_time/gleam@time@calendar.erl
Normal file
468
build/dev/javascript/gleam_time/gleam@time@calendar.erl
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
-module(gleam@time@calendar).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleam/time/calendar.gleam").
|
||||
-export([local_offset/0, month_to_string/1, month_to_int/1, month_from_int/1, is_leap_year/1, is_valid_date/1, is_valid_time_of_day/1, naive_date_compare/2]).
|
||||
-export_type([date/0, time_of_day/0, month/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
?MODULEDOC(
|
||||
" This module is for working with the Gregorian calendar, established by\n"
|
||||
" Pope Gregory XIII in 1582!\n"
|
||||
"\n"
|
||||
" ## When should you use this module?\n"
|
||||
"\n"
|
||||
" > **tldr:** You probably want to use [`gleam/time/timestamp`](./timestamp.html)\n"
|
||||
" > instead!\n"
|
||||
"\n"
|
||||
" Calendar time is difficult to work with programmatically, it is the source\n"
|
||||
" of most time-related bugs in software. Compared to _epoch time_, which the\n"
|
||||
" `gleam/time/timestamp` module uses, there are many disadvantages to\n"
|
||||
" calendar time:\n"
|
||||
"\n"
|
||||
" - They are ambiguous if you don't know what time-zone is being used.\n"
|
||||
"\n"
|
||||
" - A time-zone database is required to understand calendar time even when\n"
|
||||
" you have the time zone. These are large and your program has to\n"
|
||||
" continously be updated as new versions of the database are published.\n"
|
||||
"\n"
|
||||
" - The type permits invalid states. e.g. `days` could be set to the number\n"
|
||||
" 32, but this should not be possible!\n"
|
||||
"\n"
|
||||
" - There is not a single unique canonical value for each point in time,\n"
|
||||
" thanks to time zones. Two different `Date` + `TimeOfDay` value pairs\n"
|
||||
" could represent the same point in time. This means that you can't check\n"
|
||||
" for time equality with `==` when using calendar types.\n"
|
||||
"\n"
|
||||
" - They are computationally complex, using a more memory to represent and\n"
|
||||
" requiring a lot more CPU time to manipulate.\n"
|
||||
"\n"
|
||||
" There are also advantages to calendar time:\n"
|
||||
"\n"
|
||||
" - Calendar time is how human's talk about time, so if you want to show a\n"
|
||||
" time or take a time from a human user then calendar time will make it\n"
|
||||
" easier for them.\n"
|
||||
"\n"
|
||||
" - They can represent more abstract time periods such as \"New Year's Day\".\n"
|
||||
" This may seem like an exact window of time at first, but really the\n"
|
||||
" definition of \"New Year's Day\" is more fuzzy than that. When it starts\n"
|
||||
" and ends will depend where in the world you are, so if you want to refer\n"
|
||||
" to a day as a global concept instead of a fixed window of time for that\n"
|
||||
" day in a specific location, then calendar time can represent that.\n"
|
||||
"\n"
|
||||
" So when should you use calendar time? These are our recommendations:\n"
|
||||
"\n"
|
||||
" - Default to `gleam/time/timestamp`, which is epoch time. It is\n"
|
||||
" unambiguous, efficient, and significantly less likely to result in logic\n"
|
||||
" bugs.\n"
|
||||
"\n"
|
||||
" - When writing time to a database or other data storage use epoch time,\n"
|
||||
" using whatever epoch format it supports. For example, PostgreSQL\n"
|
||||
" `timestamp` and `timestampz` are both epoch time, and `timestamp` is\n"
|
||||
" preferred as it is more straightforward to use as your application is\n"
|
||||
" also using epoch time.\n"
|
||||
"\n"
|
||||
" - When communicating with other computer systems continue to use epoch\n"
|
||||
" time. For example, when sending times to another program you could\n"
|
||||
" encode time as UNIX timestamps (seconds since 00:00:00 UTC on 1 January\n"
|
||||
" 1970).\n"
|
||||
"\n"
|
||||
" - When communicating with humans use epoch time internally, and convert\n"
|
||||
" to-and-from calendar time at the last moment, when iteracting with the\n"
|
||||
" human user. It may also help the users to also show the time as a fuzzy\n"
|
||||
" duration from the present time, such as \"about 4 days ago\".\n"
|
||||
"\n"
|
||||
" - When representing \"fuzzy\" human time concepts that don't exact periods\n"
|
||||
" in time, such as \"one month\" (varies depending on which month, which\n"
|
||||
" year, and in which time zone) and \"Christmas Day\" (varies depending on\n"
|
||||
" which year and time zone) then use calendar time.\n"
|
||||
"\n"
|
||||
" Any time you do use calendar time you should be extra careful! It is very\n"
|
||||
" easy to make mistake with. Avoid it where possible.\n"
|
||||
"\n"
|
||||
" ## Time zone offsets\n"
|
||||
"\n"
|
||||
" This package includes the `utc_offset` value and the `local_offset`\n"
|
||||
" function, which are the offset for the UTC time zone and get the time\n"
|
||||
" offset the computer running the program is configured to respectively.\n"
|
||||
"\n"
|
||||
" If you need to use other offsets in your program then you will need to get\n"
|
||||
" them from somewhere else, such as from a package which loads the\n"
|
||||
" [IANA Time Zone Database](https://www.iana.org/time-zones), or from the\n"
|
||||
" website visitor's web browser, which your frontend can send for you.\n"
|
||||
"\n"
|
||||
" ## Use in APIs\n"
|
||||
"\n"
|
||||
" If you are making an API such as a HTTP JSON API you are encouraged to use\n"
|
||||
" Unix timestamps instead of calendar times.\n"
|
||||
).
|
||||
|
||||
-type date() :: {date, integer(), month(), integer()}.
|
||||
|
||||
-type time_of_day() :: {time_of_day, integer(), integer(), integer(), integer()}.
|
||||
|
||||
-type month() :: january |
|
||||
february |
|
||||
march |
|
||||
april |
|
||||
may |
|
||||
june |
|
||||
july |
|
||||
august |
|
||||
september |
|
||||
october |
|
||||
november |
|
||||
december.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 147).
|
||||
?DOC(
|
||||
" Get the offset for the computer's currently configured time zone.\n"
|
||||
"\n"
|
||||
" Note this may not be the time zone that is correct to use for your user.\n"
|
||||
" For example, if you are making a web application that runs on a server you\n"
|
||||
" want _their_ computer's time zone, not yours.\n"
|
||||
"\n"
|
||||
" This is the _current local_ offset, not the current local time zone. This\n"
|
||||
" means that while it will result in the expected outcome for the current\n"
|
||||
" time, it may result in unexpected output if used with other timestamps. For\n"
|
||||
" example: a timestamp that would locally be during daylight savings time if\n"
|
||||
" is it not currently daylight savings time when this function is called.\n"
|
||||
).
|
||||
-spec local_offset() -> gleam@time@duration:duration().
|
||||
local_offset() ->
|
||||
gleam@time@duration:seconds(gleam_time_ffi:local_time_offset_seconds()).
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 163).
|
||||
?DOC(
|
||||
" Returns the English name for a month.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" month_to_string(April)\n"
|
||||
" // -> \"April\"\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec month_to_string(month()) -> binary().
|
||||
month_to_string(Month) ->
|
||||
case Month of
|
||||
january ->
|
||||
<<"January"/utf8>>;
|
||||
|
||||
february ->
|
||||
<<"February"/utf8>>;
|
||||
|
||||
march ->
|
||||
<<"March"/utf8>>;
|
||||
|
||||
april ->
|
||||
<<"April"/utf8>>;
|
||||
|
||||
may ->
|
||||
<<"May"/utf8>>;
|
||||
|
||||
june ->
|
||||
<<"June"/utf8>>;
|
||||
|
||||
july ->
|
||||
<<"July"/utf8>>;
|
||||
|
||||
august ->
|
||||
<<"August"/utf8>>;
|
||||
|
||||
september ->
|
||||
<<"September"/utf8>>;
|
||||
|
||||
october ->
|
||||
<<"October"/utf8>>;
|
||||
|
||||
november ->
|
||||
<<"November"/utf8>>;
|
||||
|
||||
december ->
|
||||
<<"December"/utf8>>
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 188).
|
||||
?DOC(
|
||||
" Returns the number for the month, where January is 1 and December is 12.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" month_to_int(January)\n"
|
||||
" // -> 1\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec month_to_int(month()) -> integer().
|
||||
month_to_int(Month) ->
|
||||
case Month of
|
||||
january ->
|
||||
1;
|
||||
|
||||
february ->
|
||||
2;
|
||||
|
||||
march ->
|
||||
3;
|
||||
|
||||
april ->
|
||||
4;
|
||||
|
||||
may ->
|
||||
5;
|
||||
|
||||
june ->
|
||||
6;
|
||||
|
||||
july ->
|
||||
7;
|
||||
|
||||
august ->
|
||||
8;
|
||||
|
||||
september ->
|
||||
9;
|
||||
|
||||
october ->
|
||||
10;
|
||||
|
||||
november ->
|
||||
11;
|
||||
|
||||
december ->
|
||||
12
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 213).
|
||||
?DOC(
|
||||
" Returns the month for a given number, where January is 1 and December is 12.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" month_from_int(1)\n"
|
||||
" // -> Ok(January)\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec month_from_int(integer()) -> {ok, month()} | {error, nil}.
|
||||
month_from_int(Month) ->
|
||||
case Month of
|
||||
1 ->
|
||||
{ok, january};
|
||||
|
||||
2 ->
|
||||
{ok, february};
|
||||
|
||||
3 ->
|
||||
{ok, march};
|
||||
|
||||
4 ->
|
||||
{ok, april};
|
||||
|
||||
5 ->
|
||||
{ok, may};
|
||||
|
||||
6 ->
|
||||
{ok, june};
|
||||
|
||||
7 ->
|
||||
{ok, july};
|
||||
|
||||
8 ->
|
||||
{ok, august};
|
||||
|
||||
9 ->
|
||||
{ok, september};
|
||||
|
||||
10 ->
|
||||
{ok, october};
|
||||
|
||||
11 ->
|
||||
{ok, november};
|
||||
|
||||
12 ->
|
||||
{ok, december};
|
||||
|
||||
_ ->
|
||||
{error, nil}
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 290).
|
||||
?DOC(
|
||||
" Determines if a given year is a leap year.\n"
|
||||
"\n"
|
||||
" A leap year occurs every 4 years, except for years divisible by 100,\n"
|
||||
" unless they are also divisible by 400.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_leap_year(2024)\n"
|
||||
" // -> True\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_leap_year(2023)\n"
|
||||
" // -> False\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec is_leap_year(integer()) -> boolean().
|
||||
is_leap_year(Year) ->
|
||||
case (Year rem 400) =:= 0 of
|
||||
true ->
|
||||
true;
|
||||
|
||||
false ->
|
||||
case (Year rem 100) =:= 0 of
|
||||
true ->
|
||||
false;
|
||||
|
||||
false ->
|
||||
(Year rem 4) =:= 0
|
||||
end
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 254).
|
||||
?DOC(
|
||||
" Checks if a given date is valid.\n"
|
||||
"\n"
|
||||
" This function properly accounts for leap years when validating February days.\n"
|
||||
" A leap year occurs every 4 years, except for years divisible by 100,\n"
|
||||
" unless they are also divisible by 400.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_valid_date(Date(2023, April, 15))\n"
|
||||
" // -> True\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_valid_date(Date(2023, April, 31))\n"
|
||||
" // -> False\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_valid_date(Date(2024, February, 29))\n"
|
||||
" // -> True (2024 is a leap year)\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec is_valid_date(date()) -> boolean().
|
||||
is_valid_date(Date) ->
|
||||
{date, Year, Month, Day} = Date,
|
||||
case Day < 1 of
|
||||
true ->
|
||||
false;
|
||||
|
||||
false ->
|
||||
case Month of
|
||||
january ->
|
||||
Day =< 31;
|
||||
|
||||
march ->
|
||||
Day =< 31;
|
||||
|
||||
may ->
|
||||
Day =< 31;
|
||||
|
||||
july ->
|
||||
Day =< 31;
|
||||
|
||||
august ->
|
||||
Day =< 31;
|
||||
|
||||
october ->
|
||||
Day =< 31;
|
||||
|
||||
december ->
|
||||
Day =< 31;
|
||||
|
||||
april ->
|
||||
Day =< 30;
|
||||
|
||||
june ->
|
||||
Day =< 30;
|
||||
|
||||
september ->
|
||||
Day =< 30;
|
||||
|
||||
november ->
|
||||
Day =< 30;
|
||||
|
||||
february ->
|
||||
Max_february_days = case is_leap_year(Year) of
|
||||
true ->
|
||||
29;
|
||||
|
||||
false ->
|
||||
28
|
||||
end,
|
||||
Day =< Max_february_days
|
||||
end
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 313).
|
||||
?DOC(
|
||||
" Checks if a time of day is valid.\n"
|
||||
"\n"
|
||||
" Validates that hours are 0-23, minutes are 0-59, seconds are 0-59,\n"
|
||||
" and nanoseconds are 0-999,999,999.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789))\n"
|
||||
" // -> True\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec is_valid_time_of_day(time_of_day()) -> boolean().
|
||||
is_valid_time_of_day(Time) ->
|
||||
{time_of_day, Hours, Minutes, Seconds, Nanoseconds} = Time,
|
||||
(((((((Hours >= 0) andalso (Hours =< 23)) andalso (Minutes >= 0)) andalso (Minutes
|
||||
=< 59))
|
||||
andalso (Seconds >= 0))
|
||||
andalso (Seconds =< 59))
|
||||
andalso (Nanoseconds >= 0))
|
||||
andalso (Nanoseconds =< 999999999).
|
||||
|
||||
-file("src/gleam/time/calendar.gleam", 340).
|
||||
?DOC(
|
||||
" Naively compares two dates without any time zone information, returning an\n"
|
||||
" order.\n"
|
||||
"\n"
|
||||
" ## Correctness\n"
|
||||
"\n"
|
||||
" This function compares dates without any time zone information, only using\n"
|
||||
" the rules for the gregorian calendar. This is typically sufficient, but be\n"
|
||||
" aware that in reality some time zones will change their calendar date\n"
|
||||
" occasionally. This can result in days being skipped, out of order, or\n"
|
||||
" happening multiple times.\n"
|
||||
"\n"
|
||||
" If you need real-world correct time ordering then use the\n"
|
||||
" `gleam/time/timestamp` module instead.\n"
|
||||
).
|
||||
-spec naive_date_compare(date(), date()) -> gleam@order:order().
|
||||
naive_date_compare(One, Other) ->
|
||||
_pipe = gleam@int:compare(erlang:element(2, One), erlang:element(2, Other)),
|
||||
_pipe@1 = gleam@order:lazy_break_tie(
|
||||
_pipe,
|
||||
fun() ->
|
||||
gleam@int:compare(
|
||||
month_to_int(erlang:element(3, One)),
|
||||
month_to_int(erlang:element(3, Other))
|
||||
)
|
||||
end
|
||||
),
|
||||
gleam@order:lazy_break_tie(
|
||||
_pipe@1,
|
||||
fun() ->
|
||||
gleam@int:compare(erlang:element(4, One), erlang:element(4, Other))
|
||||
end
|
||||
).
|
||||
381
build/dev/javascript/gleam_time/gleam@time@duration.erl
Normal file
381
build/dev/javascript/gleam_time/gleam@time@duration.erl
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
-module(gleam@time@duration).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleam/time/duration.gleam").
|
||||
-export([approximate/1, compare/2, difference/2, add/2, seconds/1, minutes/1, hours/1, milliseconds/1, nanoseconds/1, to_seconds/1, to_seconds_and_nanoseconds/1, to_iso8601_string/1]).
|
||||
-export_type([duration/0, unit/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
-opaque duration() :: {duration, integer(), integer()}.
|
||||
|
||||
-type unit() :: nanosecond |
|
||||
microsecond |
|
||||
millisecond |
|
||||
second |
|
||||
minute |
|
||||
hour |
|
||||
day |
|
||||
week |
|
||||
month |
|
||||
year.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 110).
|
||||
?DOC(
|
||||
" Ensure the duration is represented with `nanoseconds` being positive and\n"
|
||||
" less than 1 second.\n"
|
||||
"\n"
|
||||
" This function does not change the amount of time that the duratoin refers\n"
|
||||
" to, it only adjusts the values used to represent the time.\n"
|
||||
).
|
||||
-spec normalise(duration()) -> duration().
|
||||
normalise(Duration) ->
|
||||
Multiplier = 1000000000,
|
||||
Nanoseconds = case Multiplier of
|
||||
0 -> 0;
|
||||
Gleam@denominator -> erlang:element(3, Duration) rem Gleam@denominator
|
||||
end,
|
||||
Overflow = erlang:element(3, Duration) - Nanoseconds,
|
||||
Seconds = erlang:element(2, Duration) + (case Multiplier of
|
||||
0 -> 0;
|
||||
Gleam@denominator@1 -> Overflow div Gleam@denominator@1
|
||||
end),
|
||||
case Nanoseconds >= 0 of
|
||||
true ->
|
||||
{duration, Seconds, Nanoseconds};
|
||||
|
||||
false ->
|
||||
{duration, Seconds - 1, Multiplier + Nanoseconds}
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 76).
|
||||
?DOC(
|
||||
" Convert a duration to a number of the largest number of a unit, serving as\n"
|
||||
" a rough description of the duration that a human can understand.\n"
|
||||
"\n"
|
||||
" The size used for each unit are described in the documentation for the\n"
|
||||
" `Unit` type.\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" seconds(125)\n"
|
||||
" |> approximate\n"
|
||||
" // -> #(2, Minute)\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" This function rounds _towards zero_. This means that if a duration is just\n"
|
||||
" short of 2 days then it will approximate to 1 day.\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" hours(47)\n"
|
||||
" |> approximate\n"
|
||||
" // -> #(1, Day)\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec approximate(duration()) -> {integer(), unit()}.
|
||||
approximate(Duration) ->
|
||||
{duration, S, Ns} = Duration,
|
||||
Minute = 60,
|
||||
Hour = Minute * 60,
|
||||
Day = Hour * 24,
|
||||
Week = Day * 7,
|
||||
Year = (Day * 365) + (Hour * 6),
|
||||
Month = Year div 12,
|
||||
Microsecond = 1000,
|
||||
Millisecond = Microsecond * 1000,
|
||||
case nil of
|
||||
_ when S < 0 ->
|
||||
{Amount, Unit} = begin
|
||||
_pipe = {duration, - S, - Ns},
|
||||
_pipe@1 = normalise(_pipe),
|
||||
approximate(_pipe@1)
|
||||
end,
|
||||
{- Amount, Unit};
|
||||
|
||||
_ when S >= Year ->
|
||||
{case Year of
|
||||
0 -> 0;
|
||||
Gleam@denominator -> S div Gleam@denominator
|
||||
end, year};
|
||||
|
||||
_ when S >= Month ->
|
||||
{case Month of
|
||||
0 -> 0;
|
||||
Gleam@denominator@1 -> S div Gleam@denominator@1
|
||||
end, month};
|
||||
|
||||
_ when S >= Week ->
|
||||
{case Week of
|
||||
0 -> 0;
|
||||
Gleam@denominator@2 -> S div Gleam@denominator@2
|
||||
end, week};
|
||||
|
||||
_ when S >= Day ->
|
||||
{case Day of
|
||||
0 -> 0;
|
||||
Gleam@denominator@3 -> S div Gleam@denominator@3
|
||||
end, day};
|
||||
|
||||
_ when S >= Hour ->
|
||||
{case Hour of
|
||||
0 -> 0;
|
||||
Gleam@denominator@4 -> S div Gleam@denominator@4
|
||||
end, hour};
|
||||
|
||||
_ when S >= Minute ->
|
||||
{case Minute of
|
||||
0 -> 0;
|
||||
Gleam@denominator@5 -> S div Gleam@denominator@5
|
||||
end, minute};
|
||||
|
||||
_ when S > 0 ->
|
||||
{S, second};
|
||||
|
||||
_ when Ns >= Millisecond ->
|
||||
{case Millisecond of
|
||||
0 -> 0;
|
||||
Gleam@denominator@6 -> Ns div Gleam@denominator@6
|
||||
end, millisecond};
|
||||
|
||||
_ when Ns >= Microsecond ->
|
||||
{case Microsecond of
|
||||
0 -> 0;
|
||||
Gleam@denominator@7 -> Ns div Gleam@denominator@7
|
||||
end, microsecond};
|
||||
|
||||
_ ->
|
||||
{Ns, nanosecond}
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 140).
|
||||
?DOC(
|
||||
" Compare one duration to another, indicating whether the first spans a\n"
|
||||
" larger amount of time (and so is greater) or smaller amount of time (and so\n"
|
||||
" is lesser) than the second.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" compare(seconds(1), seconds(2))\n"
|
||||
" // -> order.Lt\n"
|
||||
" ```\n"
|
||||
"\n"
|
||||
" Whether a duration is negative or positive doesn't matter for comparing\n"
|
||||
" them, only the amount of time spanned matters.\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" compare(seconds(-2), seconds(1))\n"
|
||||
" // -> order.Gt\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec compare(duration(), duration()) -> gleam@order:order().
|
||||
compare(Left, Right) ->
|
||||
Parts = fun(X) -> case erlang:element(2, X) >= 0 of
|
||||
true ->
|
||||
{erlang:element(2, X), erlang:element(3, X)};
|
||||
|
||||
false ->
|
||||
{(erlang:element(2, X) * -1) - 1,
|
||||
1000000000 - erlang:element(3, X)}
|
||||
end end,
|
||||
{Ls, Lns} = Parts(Left),
|
||||
{Rs, Rns} = Parts(Right),
|
||||
_pipe = gleam@int:compare(Ls, Rs),
|
||||
gleam@order:break_tie(_pipe, gleam@int:compare(Lns, Rns)).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 164).
|
||||
?DOC(
|
||||
" Calculate the difference between two durations.\n"
|
||||
"\n"
|
||||
" This is effectively substracting the first duration from the second.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" difference(seconds(1), seconds(5))\n"
|
||||
" // -> seconds(4)\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec difference(duration(), duration()) -> duration().
|
||||
difference(Left, Right) ->
|
||||
_pipe = {duration,
|
||||
erlang:element(2, Right) - erlang:element(2, Left),
|
||||
erlang:element(3, Right) - erlang:element(3, Left)},
|
||||
normalise(_pipe).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 178).
|
||||
?DOC(
|
||||
" Add two durations together.\n"
|
||||
"\n"
|
||||
" # Examples\n"
|
||||
"\n"
|
||||
" ```gleam\n"
|
||||
" add(seconds(1), seconds(5))\n"
|
||||
" // -> seconds(6)\n"
|
||||
" ```\n"
|
||||
).
|
||||
-spec add(duration(), duration()) -> duration().
|
||||
add(Left, Right) ->
|
||||
_pipe = {duration,
|
||||
erlang:element(2, Left) + erlang:element(2, Right),
|
||||
erlang:element(3, Left) + erlang:element(3, Right)},
|
||||
normalise(_pipe).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 225).
|
||||
-spec nanosecond_digits(integer(), integer(), binary()) -> binary().
|
||||
nanosecond_digits(N, Position, Acc) ->
|
||||
case Position of
|
||||
9 ->
|
||||
Acc;
|
||||
|
||||
_ when (Acc =:= <<""/utf8>>) andalso ((N rem 10) =:= 0) ->
|
||||
nanosecond_digits(N div 10, Position + 1, Acc);
|
||||
|
||||
_ ->
|
||||
Acc@1 = <<(erlang:integer_to_binary(N rem 10))/binary, Acc/binary>>,
|
||||
nanosecond_digits(N div 10, Position + 1, Acc@1)
|
||||
end.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 239).
|
||||
?DOC(" Create a duration of a number of seconds.\n").
|
||||
-spec seconds(integer()) -> duration().
|
||||
seconds(Amount) ->
|
||||
{duration, Amount, 0}.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 244).
|
||||
?DOC(" Create a duration of a number of minutes.\n").
|
||||
-spec minutes(integer()) -> duration().
|
||||
minutes(Amount) ->
|
||||
seconds(Amount * 60).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 249).
|
||||
?DOC(" Create a duration of a number of hours.\n").
|
||||
-spec hours(integer()) -> duration().
|
||||
hours(Amount) ->
|
||||
seconds((Amount * 60) * 60).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 254).
|
||||
?DOC(" Create a duration of a number of milliseconds.\n").
|
||||
-spec milliseconds(integer()) -> duration().
|
||||
milliseconds(Amount) ->
|
||||
Remainder = Amount rem 1000,
|
||||
Overflow = Amount - Remainder,
|
||||
Nanoseconds = Remainder * 1000000,
|
||||
Seconds = Overflow div 1000,
|
||||
_pipe = {duration, Seconds, Nanoseconds},
|
||||
normalise(_pipe).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 273).
|
||||
?DOC(
|
||||
" Create a duration of a number of nanoseconds.\n"
|
||||
"\n"
|
||||
" # JavaScript int limitations\n"
|
||||
"\n"
|
||||
" Remember that JavaScript can only perfectly represent ints between positive\n"
|
||||
" and negative 9,007,199,254,740,991! If you use a single call to this\n"
|
||||
" function to create durations larger than that number of nanoseconds then\n"
|
||||
" you will likely not get exactly the value you expect. Use `seconds` and\n"
|
||||
" `milliseconds` as much as possible for large durations.\n"
|
||||
).
|
||||
-spec nanoseconds(integer()) -> duration().
|
||||
nanoseconds(Amount) ->
|
||||
_pipe = {duration, 0, Amount},
|
||||
normalise(_pipe).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 283).
|
||||
?DOC(
|
||||
" Convert the duration to a number of seconds.\n"
|
||||
"\n"
|
||||
" There may be some small loss of precision due to `Duration` being\n"
|
||||
" nanosecond accurate and `Float` not being able to represent this.\n"
|
||||
).
|
||||
-spec to_seconds(duration()) -> float().
|
||||
to_seconds(Duration) ->
|
||||
Seconds = erlang:float(erlang:element(2, Duration)),
|
||||
Nanoseconds = erlang:float(erlang:element(3, Duration)),
|
||||
Seconds + (Nanoseconds / 1000000000.0).
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 292).
|
||||
?DOC(
|
||||
" Convert the duration to a number of seconds and nanoseconds. There is no\n"
|
||||
" loss of precision with this conversion on any target.\n"
|
||||
).
|
||||
-spec to_seconds_and_nanoseconds(duration()) -> {integer(), integer()}.
|
||||
to_seconds_and_nanoseconds(Duration) ->
|
||||
{erlang:element(2, Duration), erlang:element(3, Duration)}.
|
||||
|
||||
-file("src/gleam/time/duration.gleam", 192).
|
||||
?DOC(
|
||||
" Convert the duration to an [ISO8601][1] formatted duration string.\n"
|
||||
"\n"
|
||||
" The ISO8601 duration format is ambiguous without context due to months and\n"
|
||||
" years having different lengths, and because of leap seconds. This function\n"
|
||||
" encodes the duration as days, hours, and seconds without any leap seconds.\n"
|
||||
" Be sure to take this into account when using the duration strings.\n"
|
||||
"\n"
|
||||
" [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations\n"
|
||||
).
|
||||
-spec to_iso8601_string(duration()) -> binary().
|
||||
to_iso8601_string(Duration) ->
|
||||
gleam@bool:guard(
|
||||
Duration =:= {duration, 0, 0},
|
||||
<<"PT0S"/utf8>>,
|
||||
fun() ->
|
||||
Split = fun(Total, Limit) ->
|
||||
Amount = case Limit of
|
||||
0 -> 0;
|
||||
Gleam@denominator -> Total rem Gleam@denominator
|
||||
end,
|
||||
Remainder = case Limit of
|
||||
0 -> 0;
|
||||
Gleam@denominator@1 -> (Total - Amount) div Gleam@denominator@1
|
||||
end,
|
||||
{Amount, Remainder}
|
||||
end,
|
||||
{Seconds, Rest} = Split(erlang:element(2, Duration), 60),
|
||||
{Minutes, Rest@1} = Split(Rest, 60),
|
||||
{Hours, Rest@2} = Split(Rest@1, 24),
|
||||
Days = Rest@2,
|
||||
Add = fun(Out, Value, Unit) -> case Value of
|
||||
0 ->
|
||||
Out;
|
||||
|
||||
_ ->
|
||||
<<<<Out/binary,
|
||||
(erlang:integer_to_binary(Value))/binary>>/binary,
|
||||
Unit/binary>>
|
||||
end end,
|
||||
Output = begin
|
||||
_pipe = <<"P"/utf8>>,
|
||||
_pipe@1 = Add(_pipe, Days, <<"D"/utf8>>),
|
||||
_pipe@2 = gleam@string:append(_pipe@1, <<"T"/utf8>>),
|
||||
_pipe@3 = Add(_pipe@2, Hours, <<"H"/utf8>>),
|
||||
Add(_pipe@3, Minutes, <<"M"/utf8>>)
|
||||
end,
|
||||
case {Seconds, erlang:element(3, Duration)} of
|
||||
{0, 0} ->
|
||||
Output;
|
||||
|
||||
{_, 0} ->
|
||||
<<<<Output/binary,
|
||||
(erlang:integer_to_binary(Seconds))/binary>>/binary,
|
||||
"S"/utf8>>;
|
||||
|
||||
{_, _} ->
|
||||
F = nanosecond_digits(
|
||||
erlang:element(3, Duration),
|
||||
0,
|
||||
<<""/utf8>>
|
||||
),
|
||||
<<<<<<<<Output/binary,
|
||||
(erlang:integer_to_binary(Seconds))/binary>>/binary,
|
||||
"."/utf8>>/binary,
|
||||
F/binary>>/binary,
|
||||
"S"/utf8>>
|
||||
end
|
||||
end
|
||||
).
|
||||
1188
build/dev/javascript/gleam_time/gleam@time@timestamp.erl
Normal file
1188
build/dev/javascript/gleam_time/gleam@time@timestamp.erl
Normal file
File diff suppressed because it is too large
Load diff
12
build/dev/javascript/gleam_time/gleam_time_ffi.erl
Normal file
12
build/dev/javascript/gleam_time/gleam_time_ffi.erl
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-module(gleam_time_ffi).
|
||||
-export([system_time/0, local_time_offset_seconds/0]).
|
||||
|
||||
system_time() ->
|
||||
{0, erlang:system_time(nanosecond)}.
|
||||
|
||||
local_time_offset_seconds() ->
|
||||
Utc = calendar:universal_time(),
|
||||
Local = calendar:local_time(),
|
||||
UtcSeconds = calendar:datetime_to_gregorian_seconds(Utc),
|
||||
LocalSeconds = calendar:datetime_to_gregorian_seconds(Local),
|
||||
LocalSeconds - UtcSeconds.
|
||||
11
build/dev/javascript/gleam_time/gleam_time_ffi.mjs
Normal file
11
build/dev/javascript/gleam_time/gleam_time_ffi.mjs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export function system_time() {
|
||||
const now = Date.now();
|
||||
const milliseconds = now % 1_000;
|
||||
const nanoseconds = milliseconds * 1000_000;
|
||||
const seconds = (now - milliseconds) / 1_000;
|
||||
return [seconds, nanoseconds];
|
||||
}
|
||||
|
||||
export function local_time_offset_seconds() {
|
||||
return new Date().getTimezoneOffset() * -60;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue