mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
feat: add date and time functionality (#4904)
This PR introduces date and time functionality to the Lean 4 Std. Breaking Changes: - `Lean.Data.Rat` is now `Std.Internal.Rat` because it's used by the DateTime library. --------- Co-authored-by: Markus Himmel <markus@himmel-villmar.de> Co-authored-by: Mac Malone <tydeu@hatpress.net>
This commit is contained in:
@@ -170,7 +170,7 @@ lib.warn "The Nix-based build is deprecated" rec {
|
||||
ln -sf ${lean-all}/* .
|
||||
'';
|
||||
buildPhase = ''
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi' -j$NIX_BUILD_CORES
|
||||
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi|leanruntest_timeIO' -j$NIX_BUILD_CORES
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
|
||||
@@ -29,5 +29,4 @@ import Lean.Data.Xml
|
||||
import Lean.Data.NameTrie
|
||||
import Lean.Data.RBTree
|
||||
import Lean.Data.RBMap
|
||||
import Lean.Data.Rat
|
||||
import Lean.Data.RArray
|
||||
|
||||
@@ -6,9 +6,10 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.Ord
|
||||
import Init.Data.Array.DecidableEq
|
||||
import Lean.Data.Rat
|
||||
import Std.Internal.Rat
|
||||
|
||||
namespace Lean.Meta.Linear
|
||||
open Std.Internal
|
||||
|
||||
structure Var where
|
||||
id : Nat
|
||||
|
||||
@@ -6,5 +6,6 @@ Authors: Sebastian Ullrich
|
||||
prelude
|
||||
import Std.Data
|
||||
import Std.Sat
|
||||
import Std.Time
|
||||
import Std.Tactic
|
||||
import Std.Internal
|
||||
|
||||
@@ -8,7 +8,8 @@ import Init.NotationExtra
|
||||
import Init.Data.ToString.Macro
|
||||
import Init.Data.Int.DivMod
|
||||
import Init.Data.Nat.Gcd
|
||||
namespace Lean
|
||||
namespace Std
|
||||
namespace Internal
|
||||
|
||||
/-!
|
||||
Rational numbers for implementing decision procedures.
|
||||
@@ -144,4 +145,5 @@ instance : Coe Int Rat where
|
||||
coe num := { num }
|
||||
|
||||
end Rat
|
||||
end Lean
|
||||
end Internal
|
||||
end Std
|
||||
231
src/Std/Time.lean
Normal file
231
src/Std/Time.lean
Normal file
@@ -0,0 +1,231 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time
|
||||
import Std.Time.Date
|
||||
import Std.Time.Zoned
|
||||
import Std.Time.Format
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Notation
|
||||
import Std.Time.Duration
|
||||
import Std.Time.Zoned.Database
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
/-!
|
||||
# Time
|
||||
|
||||
The Lean API for date, time, and duration functionalities.
|
||||
|
||||
# Overview
|
||||
|
||||
This module of the standard library defines various concepts related to time, such as dates, times,
|
||||
time zones and durations. These types are designed to be strongly-typed and to avoid problems with
|
||||
conversion. It offers both unbounded and bounded variants to suit different use cases, like
|
||||
adding days to a date or representing ordinal values.
|
||||
|
||||
# Date and Time Components
|
||||
|
||||
Date and time components are classified into different types based how you SHOULD use them. These
|
||||
components are categorized as:
|
||||
|
||||
## Offset
|
||||
|
||||
Offsets represent unbounded shifts in specific date or time units. They are typically used in operations
|
||||
like `date.addDays` where a `Day.Offset` is the parameter. Some offsets, such as `Month.Offset`, do not
|
||||
correspond directly to a specific duration in seconds, as their value depends on the context (if
|
||||
the year is leap or the size of the month). Offsets with a clear correspondent to seconds can be
|
||||
converted because they use an internal type called `UnitVal`.
|
||||
|
||||
- Types with a correspondence to seconds:
|
||||
- `Day.Offset`
|
||||
- `Hour.Offset`
|
||||
- `Week.Offset`
|
||||
- `Millisecond.Offset`
|
||||
- `Nanosecond.Offset`
|
||||
- `Second.Offset`
|
||||
|
||||
- Types without a correspondence to seconds:
|
||||
- `Month.Offset`
|
||||
- `Year.Offset`
|
||||
|
||||
## Ordinal
|
||||
|
||||
Ordinal types represent specific bounded values in reference to another unit, e.g., `Day.Ordinal`
|
||||
represents a day in a month, ranging from 1 to 31. Some ordinal types like `Hour.Ordinal` and `Second.Ordinal`,
|
||||
allow for values beyond the normal range (e.g, 60 seconds) to accommodate special cases with leap seconds
|
||||
like `23:59:60` that is valid in ISO 8601.
|
||||
|
||||
- Ordinal types:
|
||||
- `Day.Ordinal`: Ranges from 1 to 31.
|
||||
- `Day.Ordinal.OfYear`: Ranges from 1 to (365 or 366).
|
||||
- `Month.Ordinal`: Ranges from 1 to 12.
|
||||
- `WeekOfYear.Ordinal`: Ranges from 1 to 53.
|
||||
- `Hour.Ordinal`: Ranges from 0 to 23.
|
||||
- `Millisecond.Ordinal`: Ranges from 0 to 999.
|
||||
- `Minute.Ordinal`: Ranges from 0 to 59.
|
||||
- `Nanosecond.Ordinal`: Ranges from 0 to 999,999,999.
|
||||
- `Second.Ordinal`: Ranges from 0 to 60.
|
||||
- `Weekday`: That is a inductive type with all the seven days.
|
||||
|
||||
## Span
|
||||
|
||||
Span types are used as subcomponents of other types. They represent a range of values in the limits
|
||||
of the parent type, e.g, `Nanosecond.Span` ranges from -999,999,999 to 999,999,999, as 1,000,000,000
|
||||
nanoseconds corresponds to one second.
|
||||
|
||||
- Span types:
|
||||
- `Nanosecond.Span`: Ranges from -999,999,999 to 999,999,999.
|
||||
|
||||
# Date and Time Types
|
||||
|
||||
Dates and times are made up of different parts. An `Ordinal` is an absolute value, like a specific day in a month,
|
||||
while an `Offset` is a shift forward or backward in time, used in arithmetic operations to add or subtract days, months or years.
|
||||
Dates use components like `Year.Ordinal`, `Month.Ordinal`, and `Day.Ordinal` to ensure they represent
|
||||
valid points in time.
|
||||
|
||||
Some types, like `Duration`, include a `Span` to represent ranges over other types, such as `Second.Offset`.
|
||||
This type can have a fractional nanosecond part that can be negative or positive that is represented as a `Nanosecond.Span`.
|
||||
|
||||
## Date
|
||||
These types provide precision down to the day level, useful for representing and manipulating dates.
|
||||
|
||||
- **`PlainDate`:** Represents a calendar date in the format `YYYY-MM-DD`.
|
||||
|
||||
## Time
|
||||
These types offer precision down to the nanosecond level, useful for representing and manipulating time of day.
|
||||
|
||||
- **`PlainTime`**: Represents a time of day in the format `HH:mm:ss,sssssssss`.
|
||||
|
||||
## Date and time
|
||||
Combines date and time into a single representation, useful for precise timestamps and scheduling.
|
||||
|
||||
- **`PlainDateTime`**: Represents both date and time in the format `YYYY-MM-DDTHH:mm:ss,sssssssss`.
|
||||
- **`Timestamp`**: Represents a specific point in time with nanosecond precision. Its zero value corresponds
|
||||
to the UNIX epoch. This type should be used when sending or receiving timestamps between systems.
|
||||
|
||||
## Zoned date and times.
|
||||
Combines date, time and time zones.
|
||||
|
||||
- **`DateTime`**: Represents both date and time but with a time zone in the type constructor.
|
||||
- **`ZonedDateTime`**: Is a way to represent date and time that includes `ZoneRules`, which consider
|
||||
Daylight Saving Time (DST). This means it can handle local time changes throughout the year better
|
||||
than a regular `DateTime`. If you want to use a specific time zone without worrying about DST, you can
|
||||
use the `ofTimestampWithZone` function, which gives you a `ZonedDateTime` based only on that time zone,
|
||||
without considering the zone rules, otherwise you can use `ofTimestamp` or `ofTimestampWithIdentifier`.
|
||||
|
||||
## Duration
|
||||
Represents spans of time and the difference between two points in time.
|
||||
|
||||
- **`Duration`**: Represents the time span or difference between two `Timestamp`s values with nanosecond precision.
|
||||
|
||||
# Formats
|
||||
|
||||
Format strings are used to convert between `String` representations and date/time types, like `yyyy-MM-dd'T'HH:mm:ss.sssZ`.
|
||||
The table below shows the available format specifiers. Some specifiers can be repeated to control truncation or offsets.
|
||||
When a character is repeated `n` times, it usually truncates the value to `n` characters.
|
||||
|
||||
The supported formats include:
|
||||
- `G`: Represents the era, such as AD (Anno Domini) or BC (Before Christ).
|
||||
- `G`, `GG`, `GGG` (short): Displays the era in a short format (e.g., "AD").
|
||||
- `GGGG` (full): Displays the era in a full format (e.g., "Anno Domini").
|
||||
- `GGGGG` (narrow): Displays the era in a narrow format (e.g., "A").
|
||||
- `y`: Represents the year of the era.
|
||||
- `yy`: Displays the year in a two-digit format, showing the last two digits (e.g., "04" for 2004).
|
||||
- `yyyy`: Displays the year in a four-digit format (e.g., "2004").
|
||||
- `yyyy+`: Extended format for years with more than four digits.
|
||||
- `u`: Represents the year.
|
||||
- `uu`: Two-digit year format, showing the last two digits (e.g., "04" for 2004).
|
||||
- `uuuu`: Displays the year in a four-digit format (e.g., "2004" or "-1000").
|
||||
- `uuuu+`: Extended format for handling years with more than four digits (e.g., "12345" or "-12345"). Useful for historical dates far into the past or future!
|
||||
- `D`: Represents the day of the year.
|
||||
- `M`: Represents the month of the year, displayed as either a number or text.
|
||||
- `M`, `MM`: Displays the month as a number, with `MM` zero-padded (e.g., "7" for July, "07" for July with padding).
|
||||
- `MMM`: Displays the abbreviated month name (e.g., "Jul").
|
||||
- `MMMM`: Displays the full month name (e.g., "July").
|
||||
- `MMMMM`: Displays the month in a narrow form (e.g., "J" for July).
|
||||
- `d`: Represents the day of the month.
|
||||
- `Q`: Represents the quarter of the year.
|
||||
- `Q`, `QQ`: Displays the quarter as a number (e.g., "3", "03").
|
||||
- `QQQ` (short): Displays the quarter as an abbreviated text (e.g., "Q3").
|
||||
- `QQQQ` (full): Displays the full quarter text (e.g., "3rd quarter").
|
||||
- `QQQQQ` (narrow): Displays the quarter as a short number (e.g., "3").
|
||||
- `w`: Represents the week of the week-based year, each week starts on Monday (e.g., "27").
|
||||
- `W`: Represents the week of the month, each week starts on Monday (e.g., "4").
|
||||
- `E`: Represents the day of the week as text.
|
||||
- `E`, `EE`, `EEE`: Displays the abbreviated weekday name (e.g., "Tue").
|
||||
- `EEEE`: Displays the full day name (e.g., "Tuesday").
|
||||
- `EEEEE`: Displays the narrow day name (e.g., "T" for Tuesday).
|
||||
- `e`: Represents the weekday as number or text.
|
||||
- `e`, `ee`: Displays the the as a number, starting from 1 (Monday) to 7 (Sunday).
|
||||
- `eee`, `eeee`, `eeeee`: Displays the weekday as text (same format as `E`).
|
||||
- `F`: Represents the week of the month that the first week starts on the first day of the month (e.g., "3").
|
||||
- `a`: Represents the AM or PM designation of the day.
|
||||
- `a`, `aa`, `aaa`: Displays AM or PM in a concise format (e.g., "PM").
|
||||
- `aaaa`: Displays the full AM/PM designation (e.g., "Post Meridium").
|
||||
- `h`: Represents the hour of the AM/PM clock (1-12) (e.g., "12").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `K`: Represents the hour of the AM/PM clock (0-11) (e.g., "0").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `k`: Represents the hour of the day in a 1-24 format (e.g., "24").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `H`: Represents the hour of the day in a 0-23 format (e.g., "0").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `m`: Represents the minute of the hour (e.g., "30").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `s`: Represents the second of the minute (e.g., "55").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `S`: Represents a fraction of a second, typically displayed as a decimal number (e.g., "978" for milliseconds).
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `A`: Represents the millisecond of the day (e.g., "1234").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `n`: Represents the nanosecond of the second (e.g., "987654321").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `N`: Represents the nanosecond of the day (e.g., "1234000000").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `VV`: Represents the time zone ID, which could be a city-based zone (e.g., "America/Los_Angeles"), a UTC marker (`"Z"`), or a specific offset (e.g., "-08:30").
|
||||
- One or more repetitions of the character indicates the truncation of the value to the specified number of characters.
|
||||
- `z`: Represents the time zone name.
|
||||
- `z`, `zz`, `zzz`: Shows an abbreviated time zone name (e.g., "PST" for Pacific Standard Time).
|
||||
- `zzzz`: Displays the full time zone name (e.g., "Pacific Standard Time").
|
||||
- `O`: Represents the localized zone offset in the format "GMT" followed by the time difference from UTC.
|
||||
- `O`: Displays the GMT offset in a simple format (e.g., "GMT+8").
|
||||
- `OOOO`: Displays the full GMT offset, including hours and minutes (e.g., "GMT+08:00").
|
||||
- `X`: Represents the zone offset. It uses 'Z' for UTC and can represent any offset (positive or negative).
|
||||
- `X`: Displays the hour offset (e.g., "-08").
|
||||
- `XX`: Displays the hour and minute offset without a colon (e.g., "-0830").
|
||||
- `XXX`: Displays the hour and minute offset with a colon (e.g., "-08:30").
|
||||
- `XXXX`: Displays the hour, minute, and second offset without a colon (e.g., "-083045").
|
||||
- `XXXXX`: Displays the hour, minute, and second offset with a colon (e.g., "-08:30:45").
|
||||
- `x`: Represents the zone offset. Similar to X, but does not display 'Z' for UTC and focuses only on positive offsets.
|
||||
- `x`: Displays the hour offset (e.g., "+08").
|
||||
- `xx`: Displays the hour and minute offset without a colon (e.g., "+0830").
|
||||
- `xxx`: Displays the hour and minute offset with a colon (e.g., "+08:30").
|
||||
- `xxxx`: Displays the hour, minute, and second offset without a colon (e.g., "+083045").
|
||||
- `xxxxx`: Displays the hour, minute, and second offset with a colon (e.g., "+08:30:45").
|
||||
- `Z`: Always includes an hour and minute offset and may use 'Z' for UTC, providing clear differentiation between UTC and other time zones.
|
||||
- `Z`: Displays the hour and minute offset without a colon (e.g., "+0800").
|
||||
- `ZZ`: Displays "GMT" followed by the time offset (e.g., "GMT+08:00" or "Z").
|
||||
- `ZZZ`: Displays the full hour, minute, and second offset with a colon (e.g., "+08:30:45" or "Z").
|
||||
|
||||
# Macros
|
||||
|
||||
In order to help the user build dates easily, there are a lot of macros available for creating dates.
|
||||
The `.sssssssss` can be ommited in most cases.
|
||||
|
||||
|
||||
- **`date("uuuu-MM-dd")`**: Represents a date in the `uuuu-MM-dd` format, where `uuuu` refers to the year.
|
||||
- **`time("HH:mm:ss.sssssssss")`**: Represents a time in the format `HH:mm:ss.sssssssss`, including optional support for nanoseconds.
|
||||
- **`datetime("uuuu-MM-ddTHH:mm:ss.sssssssss")`**: Represents a datetime value in the `uuuu-MM-ddTHH:mm:ss.sssssssss` format, with optional nanoseconds.
|
||||
- **`offset("+HH:mm")`**: Represents a timezone offset in the format `+HH:mm`, where `+` or `-` indicates the direction from UTC.
|
||||
- **`timezone("NAME/ID ZZZ")`**: Specifies a timezone using a region-based name or ID, along with its associated offset.
|
||||
- **`datespec("FORMAT")`**: Defines a compile-time date format based on the provided string.
|
||||
- **`zoned("uuuu-MM-ddTHH:mm:ss.sssssssssZZZ")`**: Represents a `ZonedDateTime` with a fixed timezone and optional nanosecond precision.
|
||||
- **`zoned("uuuu-MM-ddTHH:mm:ss.sssssssss[IDENTIFIER]")`**: Defines an `IO ZonedDateTime`, where the timezone identifier is dynamically retrieved from the default timezone database.
|
||||
- **`zoned("uuuu-MM-ddTHH:mm:ss.sssssssss, timezone")`**: Represents an `IO ZonedDateTime`, using a specified `timezone` term and allowing optional nanoseconds.
|
||||
|
||||
-/
|
||||
8
src/Std/Time/Date.lean
Normal file
8
src/Std/Time/Date.lean
Normal file
@@ -0,0 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date.Basic
|
||||
import Std.Time.Date.PlainDate
|
||||
476
src/Std/Time/Date/Basic.lean
Normal file
476
src/Std/Time/Date/Basic.lean
Normal file
@@ -0,0 +1,476 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date.Unit.Basic
|
||||
import Std.Time.Date.ValidDate
|
||||
import Std.Time.Time.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
namespace Nanosecond
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Convert `Nanosecond.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (nanoseconds : Nanosecond.Offset) : Day.Offset :=
|
||||
nanoseconds.div 86400000000000
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Nanosecond.Offset :=
|
||||
days.mul 86400000000000
|
||||
|
||||
/--
|
||||
Convert `Nanosecond.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (nanoseconds : Nanosecond.Offset) : Week.Offset :=
|
||||
nanoseconds.div 604800000000000
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (weeks : Week.Offset) : Nanosecond.Offset :=
|
||||
weeks.mul 604800000000000
|
||||
|
||||
end Offset
|
||||
end Nanosecond
|
||||
|
||||
namespace Millisecond
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Convert `Millisecond.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (milliseconds : Millisecond.Offset) : Day.Offset :=
|
||||
milliseconds.div 86400000
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Millisecond.Offset :=
|
||||
days.mul 86400000
|
||||
|
||||
/--
|
||||
Convert `Millisecond.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (milliseconds : Millisecond.Offset) : Week.Offset :=
|
||||
milliseconds.div 604800000
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (weeks : Week.Offset) : Millisecond.Offset :=
|
||||
weeks.mul 604800000
|
||||
|
||||
end Offset
|
||||
end Millisecond
|
||||
|
||||
namespace Second
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Convert `Second.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (seconds : Second.Offset) : Day.Offset :=
|
||||
seconds.div 86400
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Second.Offset :=
|
||||
days.mul 86400
|
||||
|
||||
/--
|
||||
Convert `Second.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (seconds : Second.Offset) : Week.Offset :=
|
||||
seconds.div 604800
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (weeks : Week.Offset) : Second.Offset :=
|
||||
weeks.mul 604800
|
||||
|
||||
end Offset
|
||||
end Second
|
||||
|
||||
namespace Minute
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Convert `Minute.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (minutes : Minute.Offset) : Day.Offset :=
|
||||
minutes.div 1440
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Minute.Offset :=
|
||||
days.mul 1440
|
||||
|
||||
/--
|
||||
Convert `Minute.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (minutes : Minute.Offset) : Week.Offset :=
|
||||
minutes.div 10080
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (weeks : Week.Offset) : Minute.Offset :=
|
||||
weeks.mul 10080
|
||||
|
||||
end Offset
|
||||
end Minute
|
||||
|
||||
namespace Hour
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Convert `Hour.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (hours : Hour.Offset) : Day.Offset :=
|
||||
hours.div 24
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Hour.Offset :=
|
||||
days.mul 24
|
||||
|
||||
/--
|
||||
Convert `Hour.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (hours : Hour.Offset) : Week.Offset :=
|
||||
hours.div 168
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (weeks : Week.Offset) : Hour.Offset :=
|
||||
weeks.mul 168
|
||||
|
||||
end Offset
|
||||
end Hour
|
||||
|
||||
instance : HAdd Nanosecond.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Nanosecond.Offset Millisecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Nanosecond.Offset Second.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Nanosecond.Offset Minute.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Nanosecond.Offset Hour.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Nanosecond.Offset Day.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Nanosecond.Offset Week.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.add y.toNanoseconds
|
||||
|
||||
instance : HAdd Millisecond.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Millisecond.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Millisecond.Offset Second.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y.toMilliseconds
|
||||
|
||||
instance : HAdd Millisecond.Offset Minute.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y.toMilliseconds
|
||||
|
||||
instance : HAdd Millisecond.Offset Hour.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y.toMilliseconds
|
||||
|
||||
instance : HAdd Millisecond.Offset Day.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y.toMilliseconds
|
||||
|
||||
instance : HAdd Millisecond.Offset Week.Offset Millisecond.Offset where
|
||||
hAdd x y := x.add y.toMilliseconds
|
||||
|
||||
instance : HAdd Second.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Second.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.toMilliseconds.add y
|
||||
|
||||
instance : HAdd Second.Offset Second.Offset Second.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Second.Offset Minute.Offset Second.Offset where
|
||||
hAdd x y := x.add y.toSeconds
|
||||
|
||||
instance : HAdd Second.Offset Hour.Offset Second.Offset where
|
||||
hAdd x y := x.add y.toSeconds
|
||||
|
||||
instance : HAdd Second.Offset Day.Offset Second.Offset where
|
||||
hAdd x y := x.add y.toSeconds
|
||||
|
||||
instance : HAdd Second.Offset Week.Offset Second.Offset where
|
||||
hAdd x y := x.add y.toSeconds
|
||||
|
||||
instance : HAdd Minute.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Minute.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.toMilliseconds.add y
|
||||
|
||||
instance : HAdd Minute.Offset Second.Offset Second.Offset where
|
||||
hAdd x y := x.toSeconds.add y
|
||||
|
||||
instance : HAdd Minute.Offset Minute.Offset Minute.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Minute.Offset Hour.Offset Minute.Offset where
|
||||
hAdd x y := x.add y.toMinutes
|
||||
|
||||
instance : HAdd Minute.Offset Day.Offset Minute.Offset where
|
||||
hAdd x y := x.add y.toMinutes
|
||||
|
||||
instance : HAdd Minute.Offset Week.Offset Minute.Offset where
|
||||
hAdd x y := x.add y.toMinutes
|
||||
|
||||
instance : HAdd Hour.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Hour.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.toMilliseconds.add y
|
||||
|
||||
instance : HAdd Hour.Offset Second.Offset Second.Offset where
|
||||
hAdd x y := x.toSeconds.add y
|
||||
|
||||
instance : HAdd Hour.Offset Minute.Offset Minute.Offset where
|
||||
hAdd x y := x.toMinutes.add y
|
||||
|
||||
instance : HAdd Hour.Offset Hour.Offset Hour.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Hour.Offset Day.Offset Hour.Offset where
|
||||
hAdd x y := x.add y.toHours
|
||||
|
||||
instance : HAdd Hour.Offset Week.Offset Hour.Offset where
|
||||
hAdd x y := x.add y.toHours
|
||||
|
||||
instance : HAdd Day.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Day.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.toMilliseconds.add y
|
||||
|
||||
instance : HAdd Day.Offset Second.Offset Second.Offset where
|
||||
hAdd x y := x.toSeconds.add y
|
||||
|
||||
instance : HAdd Day.Offset Minute.Offset Minute.Offset where
|
||||
hAdd x y := x.toMinutes.add y
|
||||
|
||||
instance : HAdd Day.Offset Hour.Offset Hour.Offset where
|
||||
hAdd x y := x.toHours.add y
|
||||
|
||||
instance : HAdd Day.Offset Day.Offset Day.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HAdd Day.Offset Week.Offset Day.Offset where
|
||||
hAdd x y := x.add y.toDays
|
||||
|
||||
instance : HAdd Week.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hAdd x y := x.toNanoseconds.add y
|
||||
|
||||
instance : HAdd Week.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hAdd x y := x.toMilliseconds.add y
|
||||
|
||||
instance : HAdd Week.Offset Second.Offset Second.Offset where
|
||||
hAdd x y := x.toSeconds.add y
|
||||
|
||||
instance : HAdd Week.Offset Minute.Offset Minute.Offset where
|
||||
hAdd x y := x.toMinutes.add y
|
||||
|
||||
instance : HAdd Week.Offset Hour.Offset Hour.Offset where
|
||||
hAdd x y := x.toHours.add y
|
||||
|
||||
instance : HAdd Week.Offset Day.Offset Day.Offset where
|
||||
hAdd x y := x.toDays.add y
|
||||
|
||||
instance : HAdd Week.Offset Week.Offset Week.Offset where
|
||||
hAdd x y := x.add y
|
||||
|
||||
instance : HSub Nanosecond.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Nanosecond.Offset Millisecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Nanosecond.Offset Second.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Nanosecond.Offset Minute.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Nanosecond.Offset Hour.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Nanosecond.Offset Day.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Nanosecond.Offset Week.Offset Nanosecond.Offset where
|
||||
hSub x y := x.sub y.toNanoseconds
|
||||
|
||||
instance : HSub Millisecond.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Millisecond.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Millisecond.Offset Second.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y.toMilliseconds
|
||||
|
||||
instance : HSub Millisecond.Offset Minute.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y.toMilliseconds
|
||||
|
||||
instance : HSub Millisecond.Offset Hour.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y.toMilliseconds
|
||||
|
||||
instance : HSub Millisecond.Offset Day.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y.toMilliseconds
|
||||
|
||||
instance : HSub Millisecond.Offset Week.Offset Millisecond.Offset where
|
||||
hSub x y := x.sub y.toMilliseconds
|
||||
|
||||
instance : HSub Second.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Second.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.toMilliseconds.sub y
|
||||
|
||||
instance : HSub Second.Offset Second.Offset Second.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Second.Offset Minute.Offset Second.Offset where
|
||||
hSub x y := x.sub y.toSeconds
|
||||
|
||||
instance : HSub Second.Offset Hour.Offset Second.Offset where
|
||||
hSub x y := x.sub y.toSeconds
|
||||
|
||||
instance : HSub Second.Offset Day.Offset Second.Offset where
|
||||
hSub x y := x.sub y.toSeconds
|
||||
|
||||
instance : HSub Second.Offset Week.Offset Second.Offset where
|
||||
hSub x y := x.sub y.toSeconds
|
||||
|
||||
instance : HSub Minute.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Minute.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.toMilliseconds.sub y
|
||||
|
||||
instance : HSub Minute.Offset Second.Offset Second.Offset where
|
||||
hSub x y := x.toSeconds.sub y
|
||||
|
||||
instance : HSub Minute.Offset Minute.Offset Minute.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Minute.Offset Hour.Offset Minute.Offset where
|
||||
hSub x y := x.sub y.toMinutes
|
||||
|
||||
instance : HSub Minute.Offset Day.Offset Minute.Offset where
|
||||
hSub x y := x.sub y.toMinutes
|
||||
|
||||
instance : HSub Minute.Offset Week.Offset Minute.Offset where
|
||||
hSub x y := x.sub y.toMinutes
|
||||
|
||||
instance : HSub Hour.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Hour.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.toMilliseconds.sub y
|
||||
|
||||
instance : HSub Hour.Offset Second.Offset Second.Offset where
|
||||
hSub x y := x.toSeconds.sub y
|
||||
|
||||
instance : HSub Hour.Offset Minute.Offset Minute.Offset where
|
||||
hSub x y := x.toMinutes.sub y
|
||||
|
||||
instance : HSub Hour.Offset Hour.Offset Hour.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Hour.Offset Day.Offset Hour.Offset where
|
||||
hSub x y := x.sub y.toHours
|
||||
|
||||
instance : HSub Hour.Offset Week.Offset Hour.Offset where
|
||||
hSub x y := x.sub y.toHours
|
||||
|
||||
instance : HSub Day.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Day.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.toMilliseconds.sub y
|
||||
|
||||
instance : HSub Day.Offset Second.Offset Second.Offset where
|
||||
hSub x y := x.toSeconds.sub y
|
||||
|
||||
instance : HSub Day.Offset Minute.Offset Minute.Offset where
|
||||
hSub x y := x.toMinutes.sub y
|
||||
|
||||
instance : HSub Day.Offset Hour.Offset Hour.Offset where
|
||||
hSub x y := x.toHours.sub y
|
||||
|
||||
instance : HSub Day.Offset Day.Offset Day.Offset where
|
||||
hSub x y := x.sub y
|
||||
|
||||
instance : HSub Day.Offset Week.Offset Day.Offset where
|
||||
hSub x y := x.sub y.toDays
|
||||
|
||||
instance : HSub Week.Offset Nanosecond.Offset Nanosecond.Offset where
|
||||
hSub x y := x.toNanoseconds.sub y
|
||||
|
||||
instance : HSub Week.Offset Millisecond.Offset Millisecond.Offset where
|
||||
hSub x y := x.toMilliseconds.sub y
|
||||
|
||||
instance : HSub Week.Offset Second.Offset Second.Offset where
|
||||
hSub x y := x.toSeconds.sub y
|
||||
|
||||
instance : HSub Week.Offset Minute.Offset Minute.Offset where
|
||||
hSub x y := x.toMinutes.sub y
|
||||
|
||||
instance : HSub Week.Offset Hour.Offset Hour.Offset where
|
||||
hSub x y := x.toHours.sub y
|
||||
|
||||
instance : HSub Week.Offset Day.Offset Day.Offset where
|
||||
hSub x y := x.toDays.sub y
|
||||
|
||||
instance : HSub Week.Offset Week.Offset Week.Offset where
|
||||
hSub x y := x.sub y
|
||||
354
src/Std/Time/Date/PlainDate.lean
Normal file
354
src/Std/Time/Date/PlainDate.lean
Normal file
@@ -0,0 +1,354 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Internal
|
||||
import Std.Time.Date.Basic
|
||||
import Std.Internal.Rat
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Std.Internal
|
||||
open Std.Time
|
||||
open Internal
|
||||
open Lean
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`PlainDate` represents a date in the Year-Month-Day (YMD) format. It encapsulates the year, month,
|
||||
and day components, with validation to ensure the date is valid.
|
||||
-/
|
||||
structure PlainDate where
|
||||
|
||||
/-- The year component of the date. It is represented as an `Offset` type from `Year`. -/
|
||||
year : Year.Offset
|
||||
|
||||
/-- The month component of the date. It is represented as an `Ordinal` type from `Month`. -/
|
||||
month : Month.Ordinal
|
||||
|
||||
/-- The day component of the date. It is represented as an `Ordinal` type from `Day`. -/
|
||||
day : Day.Ordinal
|
||||
|
||||
/-- Validates the date by ensuring that the year, month, and day form a correct and valid date. -/
|
||||
valid : year.Valid month day
|
||||
deriving Repr
|
||||
|
||||
instance : Inhabited PlainDate where
|
||||
default := ⟨1, 1, 1, by decide⟩
|
||||
|
||||
instance : BEq PlainDate where
|
||||
beq x y := x.day == y.day && x.month == y.month && x.year == y.year
|
||||
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Creates a `PlainDate` by clipping the day to ensure validity. This function forces the date to be
|
||||
valid by adjusting the day to fit within the valid range to fit the given month and year.
|
||||
-/
|
||||
@[inline]
|
||||
def ofYearMonthDayClip (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : PlainDate :=
|
||||
let day := month.clipDay year.isLeap day
|
||||
PlainDate.mk year month day Month.Ordinal.valid_clipDay
|
||||
|
||||
instance : Inhabited PlainDate where
|
||||
default := mk 0 1 1 (by decide)
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` from year, month, and day components.
|
||||
-/
|
||||
@[inline]
|
||||
def ofYearMonthDay? (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : Option PlainDate :=
|
||||
if valid : year.Valid month day
|
||||
then some (PlainDate.mk year month day valid)
|
||||
else none
|
||||
|
||||
/--
|
||||
Creates a `PlainDate` from a year and a day ordinal within that year.
|
||||
-/
|
||||
@[inline]
|
||||
def ofYearOrdinal (year : Year.Offset) (ordinal : Day.Ordinal.OfYear year.isLeap) : PlainDate :=
|
||||
let ⟨⟨month, day⟩, proof⟩ := ValidDate.ofOrdinal ordinal
|
||||
⟨year, month, day, proof⟩
|
||||
|
||||
/--
|
||||
Creates a `PlainDate` from the number of days since the UNIX epoch (January 1st, 1970).
|
||||
-/
|
||||
def ofDaysSinceUNIXEpoch (day : Day.Offset) : PlainDate :=
|
||||
let z := day.toInt + 719468
|
||||
let era := (if z ≥ 0 then z else z - 146096).tdiv 146097
|
||||
let doe := z - era * 146097
|
||||
let yoe := (doe - doe.tdiv 1460 + doe.tdiv 36524 - doe.tdiv 146096).tdiv 365
|
||||
let y := yoe + era * 400
|
||||
let doy := doe - (365 * yoe + yoe.tdiv 4 - yoe.tdiv 100)
|
||||
let mp := (5 * doy + 2).tdiv 153
|
||||
let d := doy - (153 * mp + 2).tdiv 5 + 1
|
||||
let m := mp + (if mp < 10 then 3 else -9)
|
||||
let y := y + (if m <= 2 then 1 else 0)
|
||||
.ofYearMonthDayClip y (.clip m (by decide)) (.clip d (by decide))
|
||||
|
||||
/--
|
||||
Returns the unaligned week of the month for a `PlainDate` (day divided by 7, plus 1).
|
||||
-/
|
||||
def weekOfMonth (date : PlainDate) : Bounded.LE 1 5 :=
|
||||
date.day.sub 1 |>.ediv 7 (by decide) |>.add 1
|
||||
|
||||
/--
|
||||
Determines the quarter of the year for the given `PlainDate`.
|
||||
-/
|
||||
def quarter (date : PlainDate) : Bounded.LE 1 4 :=
|
||||
date.month.sub 1 |>.ediv 3 (by decide) |>.add 1
|
||||
|
||||
/--
|
||||
Transforms a `PlainDate` into a `Day.Ordinal.OfYear`.
|
||||
-/
|
||||
def dayOfYear (date : PlainDate) : Day.Ordinal.OfYear date.year.isLeap :=
|
||||
ValidDate.dayOfYear ⟨(date.month, date.day), date.valid⟩
|
||||
|
||||
/--
|
||||
Determines the era of the given `PlainDate` based on its year.
|
||||
-/
|
||||
@[inline]
|
||||
def era (date : PlainDate) : Year.Era :=
|
||||
date.year.era
|
||||
|
||||
/--
|
||||
Checks if the `PlainDate` is in a leap year.
|
||||
-/
|
||||
@[inline]
|
||||
def inLeapYear (date : PlainDate) : Bool :=
|
||||
date.year.isLeap
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
def toDaysSinceUNIXEpoch (date : PlainDate) : Day.Offset :=
|
||||
let y : Int := if date.month.toInt > 2 then date.year else date.year.toInt - 1
|
||||
let era : Int := (if y ≥ 0 then y else y - 399).tdiv 400
|
||||
let yoe : Int := y - era * 400
|
||||
let m : Int := date.month.toInt
|
||||
let d : Int := date.day.toInt
|
||||
let doy := (153 * (m + (if m > 2 then -3 else 9)) + 2).tdiv 5 + d - 1
|
||||
let doe := yoe * 365 + yoe.tdiv 4 - yoe.tdiv 100 + doy
|
||||
|
||||
.ofInt (era * 146097 + doe - 719468)
|
||||
|
||||
/--
|
||||
Adds a given number of days to a `PlainDate`.
|
||||
-/
|
||||
@[inline]
|
||||
def addDays (date : PlainDate) (days : Day.Offset) : PlainDate :=
|
||||
let dateDays := date.toDaysSinceUNIXEpoch
|
||||
ofDaysSinceUNIXEpoch (dateDays + days)
|
||||
|
||||
/--
|
||||
Subtracts a given number of days from a `PlainDate`.
|
||||
-/
|
||||
@[inline]
|
||||
def subDays (date : PlainDate) (days : Day.Offset) : PlainDate :=
|
||||
addDays date (-days)
|
||||
|
||||
/--
|
||||
Adds a given number of weeks to a `PlainDate`.
|
||||
-/
|
||||
@[inline]
|
||||
def addWeeks (date : PlainDate) (weeks : Week.Offset) : PlainDate :=
|
||||
let dateDays := date.toDaysSinceUNIXEpoch
|
||||
let daysToAdd := weeks.toDays
|
||||
ofDaysSinceUNIXEpoch (dateDays + daysToAdd)
|
||||
|
||||
/--
|
||||
Subtracts a given number of weeks from a `PlainDate`.
|
||||
-/
|
||||
@[inline]
|
||||
def subWeeks (date : PlainDate) (weeks : Week.Offset) : PlainDate :=
|
||||
addWeeks date (-weeks)
|
||||
|
||||
/--
|
||||
Adds a given number of months to a `PlainDate`, clipping the day to the last valid day of the month.
|
||||
-/
|
||||
def addMonthsClip (date : PlainDate) (months : Month.Offset) : PlainDate :=
|
||||
let totalMonths := (date.month.toOffset - 1) + months
|
||||
let totalMonths : Int := totalMonths
|
||||
let wrappedMonths := Bounded.LE.byEmod totalMonths 12 (by decide) |>.add 1
|
||||
let yearsOffset := totalMonths / 12
|
||||
PlainDate.ofYearMonthDayClip (date.year.add yearsOffset) wrappedMonths date.day
|
||||
|
||||
/--
|
||||
Subtracts `Month.Offset` from a `PlainDate`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsClip (date : PlainDate) (months : Month.Offset) : PlainDate :=
|
||||
addMonthsClip date (-months)
|
||||
|
||||
/--
|
||||
Creates a `PlainDate` by rolling over the extra days to the next month.
|
||||
-/
|
||||
def rollOver (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : PlainDate :=
|
||||
ofYearMonthDayClip year month 1 |>.addDays (day.toOffset - 1)
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the year to the given `year` value. The month and day remain unchanged,
|
||||
and any invalid days for the new year will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearClip (dt : PlainDate) (year : Year.Offset) : PlainDate :=
|
||||
ofYearMonthDayClip year dt.month dt.day
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the year to the given `year` value. The month and day are rolled
|
||||
over to the next valid month and day if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearRollOver (dt : PlainDate) (year : Year.Offset) : PlainDate :=
|
||||
rollOver year dt.month dt.day
|
||||
|
||||
/--
|
||||
Adds a given number of months to a `PlainDate`, rolling over any excess days into the following month.
|
||||
-/
|
||||
def addMonthsRollOver (date : PlainDate) (months : Month.Offset) : PlainDate :=
|
||||
addMonthsClip (ofYearMonthDayClip date.year date.month 1) months
|
||||
|>.addDays (date.day.toOffset - 1)
|
||||
|
||||
/--
|
||||
Subtracts `Month.Offset` from a `PlainDate`, rolling over excess days as needed.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsRollOver (date : PlainDate) (months : Month.Offset) : PlainDate :=
|
||||
addMonthsRollOver date (-months)
|
||||
|
||||
/--
|
||||
Adds `Year.Offset` to a `PlainDate`, rolling over excess days to the next month, or next year.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsRollOver (date : PlainDate) (years : Year.Offset) : PlainDate :=
|
||||
addMonthsRollOver date (years.mul 12)
|
||||
|
||||
/--
|
||||
Subtracts `Year.Offset` from a `PlainDate`, rolling over excess days to the next month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsRollOver (date : PlainDate) (years : Year.Offset) : PlainDate :=
|
||||
addMonthsRollOver date (- years.mul 12)
|
||||
|
||||
/--
|
||||
Adds `Year.Offset` to a `PlainDate`, clipping the day to the last valid day of the month.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsClip (date : PlainDate) (years : Year.Offset) : PlainDate :=
|
||||
addMonthsClip date (years.mul 12)
|
||||
|
||||
/--
|
||||
Subtracts `Year.Offset` from a `PlainDate`, clipping the day to the last valid day of the month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsClip (date : PlainDate) (years : Year.Offset) : PlainDate :=
|
||||
addMonthsClip date (- years.mul 12)
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysClip (dt : PlainDate) (days : Day.Ordinal) : PlainDate :=
|
||||
ofYearMonthDayClip dt.year dt.month days
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days rolled over to the next month or year as needed.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysRollOver (dt : PlainDate) (days : Day.Ordinal) : PlainDate :=
|
||||
rollOver dt.year dt.month days
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the month to the given `month` value.
|
||||
The day remains unchanged, and any invalid days for the new month will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthClip (dt : PlainDate) (month : Month.Ordinal) : PlainDate :=
|
||||
ofYearMonthDayClip dt.year month dt.day
|
||||
|
||||
/--
|
||||
Creates a new `PlainDate` by adjusting the month to the given `month` value.
|
||||
The day is rolled over to the next valid month if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthRollOver (dt : PlainDate) (month : Month.Ordinal) : PlainDate :=
|
||||
rollOver dt.year month dt.day
|
||||
|
||||
/--
|
||||
Calculates the `Weekday` of a given `PlainDate` using Zeller's Congruence for the Gregorian calendar.
|
||||
-/
|
||||
def weekday (date : PlainDate) : Weekday :=
|
||||
let days := date.toDaysSinceUNIXEpoch.val
|
||||
let res := if days ≥ -4 then (days + 4) % 7 else (days + 5) % 7 + 6
|
||||
.ofOrdinal (Bounded.LE.ofNatWrapping res (by decide))
|
||||
|
||||
/--
|
||||
Determines the week of the month for the given `PlainDate`. The week of the month is calculated based
|
||||
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
||||
based on the Gregorian Calendar.
|
||||
-/
|
||||
def alignedWeekOfMonth (date : PlainDate) : Week.Ordinal.OfMonth :=
|
||||
let weekday := date.withDaysClip 1 |>.weekday |>.toOrdinal |>.sub 1
|
||||
let days := date.day |>.sub 1 |>.addBounds weekday
|
||||
days |>.ediv 7 (by decide) |>.add 1
|
||||
|
||||
/--
|
||||
Sets the date to the specified `desiredWeekday`. If the `desiredWeekday` is the same as the current weekday,
|
||||
the original `date` is returned without modification. If the `desiredWeekday` is in the future, the
|
||||
function adjusts the date forward to the next occurrence of that weekday.
|
||||
-/
|
||||
def withWeekday (date : PlainDate) (desiredWeekday : Weekday) : PlainDate :=
|
||||
let weekday := date |>.weekday |>.toOrdinal
|
||||
let offset := desiredWeekday.toOrdinal |>.subBounds weekday
|
||||
|
||||
let offset : Bounded.LE 0 6 :=
|
||||
if h : offset.val < 0 then
|
||||
offset.truncateTop (Int.le_sub_one_of_lt h) |>.addBounds (.exact 7)
|
||||
|>.expandBottom (by decide)
|
||||
else
|
||||
offset.truncateBottom (Int.not_lt.mp h)
|
||||
|>.expandTop (by decide)
|
||||
|
||||
date.addDays (Day.Offset.ofInt offset.toInt)
|
||||
|
||||
/--
|
||||
Calculates the week of the year starting Monday for a given year.
|
||||
-/
|
||||
def weekOfYear (date : PlainDate) : Week.Ordinal :=
|
||||
let y := date.year
|
||||
|
||||
let w := Bounded.LE.exact 10
|
||||
|>.addBounds date.dayOfYear
|
||||
|>.subBounds date.weekday.toOrdinal
|
||||
|>.ediv 7 (by decide)
|
||||
|
||||
if h : w.val < 1 then
|
||||
(y-1).weeks |>.expandBottom (by decide)
|
||||
else if h₁ : w.val > y.weeks.val then
|
||||
.ofNat' 1 (by decide)
|
||||
else
|
||||
let h := Int.not_lt.mp h
|
||||
let h₁ := Int.not_lt.mp h₁
|
||||
let w := w.truncateBottom h |>.truncateTop (Int.le_trans h₁ y.weeks.property.right)
|
||||
w
|
||||
|
||||
instance : HAdd PlainDate Day.Offset PlainDate where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub PlainDate Day.Offset PlainDate where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd PlainDate Week.Offset PlainDate where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub PlainDate Week.Offset PlainDate where
|
||||
hSub := subWeeks
|
||||
|
||||
end PlainDate
|
||||
end Time
|
||||
end Std
|
||||
41
src/Std/Time/Date/Unit/Basic.lean
Normal file
41
src/Std/Time/Date/Unit/Basic.lean
Normal file
@@ -0,0 +1,41 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date.Unit.Day
|
||||
import Std.Time.Date.Unit.Month
|
||||
import Std.Time.Date.Unit.Year
|
||||
import Std.Time.Date.Unit.Weekday
|
||||
import Std.Time.Date.Unit.Week
|
||||
|
||||
/-!
|
||||
This module defines various units used for measuring, counting, and converting between days, months,
|
||||
years, weekdays, and weeks of the year.
|
||||
|
||||
The units are organized into types representing these time-related concepts, with operations provided
|
||||
to facilitate conversions and manipulations between them.
|
||||
-/
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
namespace Day.Offset
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofWeeks (week : Week.Offset) : Day.Offset :=
|
||||
week.mul 7
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toWeeks (day : Day.Offset) : Week.Offset :=
|
||||
day.ediv 7
|
||||
|
||||
end Day.Offset
|
||||
221
src/Std/Time/Date/Unit/Day.lean
Normal file
221
src/Std/Time/Date/Unit/Day.lean
Normal file
@@ -0,0 +1,221 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Day
|
||||
open Lean Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for days, which ranges between 1 and 31.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 1 31
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 1 (1 + (30 : Nat))) n)
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
instance : Inhabited Ordinal where default := 1
|
||||
|
||||
/--
|
||||
`Offset` represents an offset in days. It is defined as an `Int` with a base unit of 86400
|
||||
(the number of seconds in a day).
|
||||
-/
|
||||
def Offset : Type := UnitVal 86400
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance : OfNat Offset n := ⟨UnitVal.ofNat n⟩
|
||||
|
||||
instance {x y : Offset} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Offset} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 1 ≤ data ∧ data ≤ 31) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
`OfYear` represents the day ordinal within a year, which can be bounded between 1 and 365 or 366,
|
||||
depending on whether it's a leap year.
|
||||
-/
|
||||
def OfYear (leap : Bool) := Bounded.LE 1 (.ofNat (if leap then 366 else 365))
|
||||
|
||||
instance : Repr (OfYear leap) where
|
||||
reprPrec r p := reprPrec r.val p
|
||||
|
||||
instance : ToString (OfYear leap) where
|
||||
toString r := toString r.val
|
||||
|
||||
namespace OfYear
|
||||
|
||||
/--
|
||||
Creates an ordinal for a specific day within the year, ensuring that the provided day (`data`)
|
||||
is within the valid range for the year, which can be 1 to 365 or 366 for leap years.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≥ 1 ∧ data ≤ (if leap then 366 else 365) := by decide) : OfYear leap :=
|
||||
Bounded.LE.ofNat' data h
|
||||
|
||||
end OfYear
|
||||
|
||||
instance : OfNat (Ordinal.OfYear leap) n :=
|
||||
match leap with
|
||||
| true => inferInstanceAs (OfNat (Bounded.LE 1 (1 + (365 : Nat))) n)
|
||||
| false => inferInstanceAs (OfNat (Bounded.LE 1 (1 + (364 : Nat))) n)
|
||||
|
||||
instance : Inhabited (Ordinal.OfYear leap) where
|
||||
default := by
|
||||
refine ⟨1, And.intro (by decide) ?_⟩
|
||||
split <;> simp
|
||||
|
||||
/--
|
||||
Creates an ordinal from a natural number, ensuring the number is within the valid range
|
||||
for days of a month (1 to 31).
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≥ 1 ∧ data ≤ 31 := by decide) : Ordinal :=
|
||||
Bounded.LE.ofNat' data h
|
||||
|
||||
/--
|
||||
Creates an ordinal from a `Fin` value, ensuring it is within the valid range for days of the month (1 to 31).
|
||||
If the `Fin` value is 0, it is converted to 1.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 32) : Ordinal :=
|
||||
Bounded.LE.ofFin' data (by decide)
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
namespace OfYear
|
||||
|
||||
/--
|
||||
Converts an `OfYear` ordinal to a `Offset`.
|
||||
-/
|
||||
def toOffset (ofYear : OfYear leap) : Offset :=
|
||||
UnitVal.ofInt ofYear.val
|
||||
|
||||
end OfYear
|
||||
end Ordinal
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Converts an `Offset` to an `Ordinal`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOrdinal (off : Day.Offset) (h : off.val ≥ 1 ∧ off.val ≤ 31) : Ordinal :=
|
||||
Bounded.LE.mk off.val h
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Day.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Day.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (days : Day.Offset) : Nanosecond.Offset :=
|
||||
days.mul 86400000000000
|
||||
|
||||
/--
|
||||
Convert `Nanosecond.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (ns : Nanosecond.Offset) : Day.Offset :=
|
||||
ns.ediv 86400000000000
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (days : Day.Offset) : Millisecond.Offset :=
|
||||
days.mul 86400000
|
||||
|
||||
/--
|
||||
Convert `Millisecond.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (ms : Millisecond.Offset) : Day.Offset :=
|
||||
ms.ediv 86400000
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (days : Day.Offset) : Second.Offset :=
|
||||
days.mul 86400
|
||||
|
||||
/--
|
||||
Convert `Second.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (secs : Second.Offset) : Day.Offset :=
|
||||
secs.ediv 86400
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (days : Day.Offset) : Minute.Offset :=
|
||||
days.mul 1440
|
||||
|
||||
/--
|
||||
Convert `Minute.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (minutes : Minute.Offset) : Day.Offset :=
|
||||
minutes.ediv 1440
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (days : Day.Offset) : Hour.Offset :=
|
||||
days.mul 24
|
||||
|
||||
/--
|
||||
Convert `Hour.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (hours : Hour.Offset) : Day.Offset :=
|
||||
hours.ediv 24
|
||||
|
||||
end Offset
|
||||
end Day
|
||||
end Time
|
||||
end Std
|
||||
308
src/Std/Time/Date/Unit/Month.lean
Normal file
308
src/Std/Time/Date/Unit/Month.lean
Normal file
@@ -0,0 +1,308 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Date.Unit.Day
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Month
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for months, which ranges between 1 and 12.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 1 12
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 1 (1 + (11 : Nat))) n)
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 1
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents an offset in months. It is defined as an `Int`.
|
||||
-/
|
||||
def Offset : Type := Int
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Mul, Div, Neg, ToString, LT, LE, DecidableEq
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨Int.ofNat n⟩
|
||||
|
||||
/--
|
||||
`Quarter` represents a value between 1 and 4, inclusive, corresponding to the four quarters of a year.
|
||||
-/
|
||||
def Quarter := Bounded.LE 1 4
|
||||
|
||||
namespace Quarter
|
||||
|
||||
/--
|
||||
Determine the `Quarter` by the month.
|
||||
-/
|
||||
def ofMonth (month : Month.Ordinal) : Quarter :=
|
||||
month
|
||||
|>.sub 1
|
||||
|>.ediv 3 (by decide)
|
||||
|>.add 1
|
||||
|
||||
end Quarter
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
.ofNat data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
data
|
||||
|
||||
end Offset
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of January.
|
||||
-/
|
||||
@[inline] def january : Ordinal := 1
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of February.
|
||||
-/
|
||||
@[inline] def february : Ordinal := 2
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of March.
|
||||
-/
|
||||
@[inline] def march : Ordinal := 3
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of April.
|
||||
-/
|
||||
@[inline] def april : Ordinal := 4
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of May.
|
||||
-/
|
||||
@[inline] def may : Ordinal := 5
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of June.
|
||||
-/
|
||||
@[inline] def june : Ordinal := 6
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of July.
|
||||
-/
|
||||
@[inline] def july : Ordinal := 7
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of August.
|
||||
-/
|
||||
@[inline] def august : Ordinal := 8
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of September.
|
||||
-/
|
||||
@[inline] def september : Ordinal := 9
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of October.
|
||||
-/
|
||||
@[inline] def october : Ordinal := 10
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of November.
|
||||
-/
|
||||
@[inline] def november : Ordinal := 11
|
||||
|
||||
/--
|
||||
The ordinal value representing the month of December.
|
||||
-/
|
||||
@[inline] def december : Ordinal := 12
|
||||
|
||||
/--
|
||||
Converts a `Ordinal` into a `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (month : Ordinal) : Offset :=
|
||||
month.val
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 1 ≤ data ∧ data ≤ 12) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Nat`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≥ 1 ∧ data ≤ 12 := by decide) : Ordinal :=
|
||||
Bounded.LE.ofNat' data h
|
||||
|
||||
/--
|
||||
Converts a `Ordinal` into a `Nat`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNat (month : Ordinal) : Nat := by
|
||||
match month with
|
||||
| ⟨.ofNat s, _⟩ => exact s
|
||||
| ⟨.negSucc s, h⟩ => nomatch h.left
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds, if its 0 then its converted
|
||||
to 1.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 13) : Ordinal :=
|
||||
Bounded.LE.ofFin' data (by decide)
|
||||
|
||||
/--
|
||||
Transforms `Month.Ordinal` into `Second.Offset`.
|
||||
-/
|
||||
def toSeconds (leap : Bool) (month : Ordinal) : Second.Offset :=
|
||||
let daysAcc := #[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
|
||||
let days : Day.Offset := daysAcc[month.toNat]!
|
||||
let time := days.toSeconds
|
||||
if leap && month.toNat ≥ 2
|
||||
then time + 86400
|
||||
else time
|
||||
|
||||
/--
|
||||
Transforms `Month.Ordinal` into `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (leap : Bool) (month : Ordinal) : Minute.Offset :=
|
||||
toSeconds leap month
|
||||
|>.ediv 60
|
||||
|
||||
/--
|
||||
Transforms `Month.Ordinal` into `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (leap : Bool) (month : Ordinal) : Hour.Offset :=
|
||||
toMinutes leap month
|
||||
|>.ediv 60
|
||||
|
||||
/--
|
||||
Transforms `Month.Ordinal` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (leap : Bool) (month : Ordinal) : Day.Offset :=
|
||||
toSeconds leap month
|
||||
|>.convert
|
||||
|
||||
/--
|
||||
Size in days of each month if the year is not a leap year.
|
||||
-/
|
||||
@[inline]
|
||||
private def monthSizesNonLeap : { val : Array Day.Ordinal // val.size = 12 } :=
|
||||
⟨#[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], by decide⟩
|
||||
|
||||
/--
|
||||
Returns the cumulative size in days of each month for a non-leap year.
|
||||
-/
|
||||
@[inline]
|
||||
private def cumulativeSizes : { val : Array Day.Offset // val.size = 12 } :=
|
||||
⟨#[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], by decide⟩
|
||||
|
||||
/--
|
||||
Gets the number of days in a month.
|
||||
-/
|
||||
def days (leap : Bool) (month : Ordinal) : Day.Ordinal :=
|
||||
if month.val = 2 then
|
||||
if leap then 29 else 28
|
||||
else
|
||||
let ⟨months, p⟩ := monthSizesNonLeap
|
||||
let index : Fin 12 := (month.sub 1).toFin (by decide)
|
||||
let idx := (index.cast (by rw [p]))
|
||||
months.get idx.val idx.isLt
|
||||
|
||||
theorem days_gt_27 (leap : Bool) (i : Month.Ordinal) : days leap i > 27 := by
|
||||
match i with
|
||||
| ⟨2, _⟩ =>
|
||||
simp [days]
|
||||
split <;> decide
|
||||
| ⟨1, _⟩ | ⟨3, _⟩ | ⟨4, _⟩ | ⟨5, _⟩ | ⟨6, _⟩ | ⟨7, _⟩
|
||||
| ⟨8, _⟩ | ⟨9, _⟩ | ⟨10, _⟩ | ⟨11, _⟩ | ⟨12, _⟩ =>
|
||||
simp [days, monthSizesNonLeap]
|
||||
decide +revert
|
||||
|
||||
/--
|
||||
Returns the number of days until the `month`.
|
||||
-/
|
||||
def cumulativeDays (leap : Bool) (month : Ordinal) : Day.Offset := by
|
||||
let ⟨months, p⟩ := cumulativeSizes
|
||||
let index : Fin 12 := (month.sub 1).toFin (by decide)
|
||||
rw [← p] at index
|
||||
let res := months.get index.val index.isLt
|
||||
exact res + (if leap ∧ month.val > 2 then 1 else 0)
|
||||
|
||||
theorem cumulativeDays_le (leap : Bool) (month : Month.Ordinal) : cumulativeDays leap month ≥ 0 ∧ cumulativeDays leap month ≤ 334 + (if leap then 1 else 0) := by
|
||||
match month with
|
||||
| ⟨1, _⟩ | ⟨2, _⟩ | ⟨3, _⟩ | ⟨4, _⟩ | ⟨5, _⟩ | ⟨6, _⟩ | ⟨7, _⟩ | ⟨8, _⟩ | ⟨9, _⟩ | ⟨10, _⟩ | ⟨11, _⟩ | ⟨12, _⟩ =>
|
||||
simp [cumulativeSizes, Bounded.LE.sub, Bounded.LE.add, Bounded.LE.toFin, cumulativeDays]
|
||||
try split
|
||||
all_goals decide +revert
|
||||
|
||||
theorem difference_eq (p : month.val ≤ 11) :
|
||||
let next := month.truncateTop p |>.addTop 1 (by decide)
|
||||
(cumulativeDays leap next).val = (cumulativeDays leap month).val + (days leap month).val := by
|
||||
match month with
|
||||
| ⟨1, _⟩ | ⟨2, _⟩ | ⟨3, _⟩ | ⟨4, _⟩ | ⟨5, _⟩ | ⟨6, _⟩ | ⟨7, _⟩ | ⟨8, _⟩ | ⟨9, _⟩ | ⟨10, _⟩ | ⟨11, _⟩ =>
|
||||
simp [cumulativeDays, Bounded.LE.addTop, days, monthSizesNonLeap];
|
||||
try split <;> rfl
|
||||
try rfl
|
||||
| ⟨12, _⟩ => contradiction
|
||||
|
||||
/--
|
||||
Checks if a given day is valid for the specified month and year. For example, `29/02` is valid only
|
||||
if the year is a leap year.
|
||||
-/
|
||||
abbrev Valid (leap : Bool) (month : Month.Ordinal) (day : Day.Ordinal) : Prop :=
|
||||
day.val ≤ (days leap month).val
|
||||
|
||||
/--
|
||||
Clips the day to be within the valid range.
|
||||
-/
|
||||
@[inline]
|
||||
def clipDay (leap : Bool) (month : Month.Ordinal) (day : Day.Ordinal) : Day.Ordinal :=
|
||||
let max : Day.Ordinal := month.days leap
|
||||
if day.val > max.val
|
||||
then max
|
||||
else day
|
||||
|
||||
/--
|
||||
Proves that every value provided by a clipDay is a valid day in a year.
|
||||
-/
|
||||
theorem valid_clipDay : Valid leap month (clipDay leap month day) := by
|
||||
simp [Valid, clipDay]
|
||||
split
|
||||
exact Int.le_refl (days leap month).val
|
||||
next h => exact Int.not_lt.mp h
|
||||
|
||||
end Ordinal
|
||||
end Month
|
||||
end Time
|
||||
end Std
|
||||
185
src/Std/Time/Date/Unit/Week.lean
Normal file
185
src/Std/Time/Date/Unit/Week.lean
Normal file
@@ -0,0 +1,185 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Date.Unit.Day
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Week
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for weeks, which ranges between 1 and 53.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 1 53
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 1 (1 + (52 : Nat))) n)
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 1
|
||||
|
||||
/--
|
||||
`Offset` represents an offset in weeks.
|
||||
-/
|
||||
def Offset : Type := UnitVal (86400 * 7)
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance : OfNat Offset n := ⟨UnitVal.ofNat n⟩
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 1 ≤ data ∧ data ≤ 53) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
`OfMonth` represents the number of weeks within a month. It ensures that the week is within the
|
||||
correct bounds—either 1 to 6, representing the possible weeks in a month.
|
||||
-/
|
||||
def OfMonth := Bounded.LE 1 6
|
||||
deriving Repr
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≥ 1 ∧ data ≤ 53 := by decide) : Ordinal :=
|
||||
Bounded.LE.ofNat' data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 54) : Ordinal :=
|
||||
Bounded.LE.ofFin' data (by decide)
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Week.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Week.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (weeks : Week.Offset) : Millisecond.Offset :=
|
||||
weeks.mul 604800000
|
||||
|
||||
/--
|
||||
Convert `Millisecond.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (millis : Millisecond.Offset) : Week.Offset :=
|
||||
millis.ediv 604800000
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (weeks : Week.Offset) : Nanosecond.Offset :=
|
||||
weeks.mul 604800000000000
|
||||
|
||||
/--
|
||||
Convert `Nanosecond.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (nanos : Nanosecond.Offset) : Week.Offset :=
|
||||
nanos.ediv 604800000000000
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (weeks : Week.Offset) : Second.Offset :=
|
||||
weeks.mul 604800
|
||||
|
||||
/--
|
||||
Convert `Second.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (secs : Second.Offset) : Week.Offset :=
|
||||
secs.ediv 604800
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (weeks : Week.Offset) : Minute.Offset :=
|
||||
weeks.mul 10080
|
||||
|
||||
/--
|
||||
Convert `Minute.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (minutes : Minute.Offset) : Week.Offset :=
|
||||
minutes.ediv 10080
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (weeks : Week.Offset) : Hour.Offset :=
|
||||
weeks.mul 168
|
||||
|
||||
/--
|
||||
Convert `Hour.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (hours : Hour.Offset) : Week.Offset :=
|
||||
hours.ediv 168
|
||||
|
||||
/--
|
||||
Convert `Week.Offset` into `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (weeks : Week.Offset) : Day.Offset :=
|
||||
weeks.mul 7
|
||||
|
||||
/--
|
||||
Convert `Day.Offset` into `Week.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDays (days : Day.Offset) : Week.Offset :=
|
||||
days.ediv 7
|
||||
|
||||
end Offset
|
||||
end Week
|
||||
end Time
|
||||
end Std
|
||||
134
src/Std/Time/Date/Unit/Weekday.lean
Normal file
134
src/Std/Time/Date/Unit/Weekday.lean
Normal file
@@ -0,0 +1,134 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Date.Unit.Day
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Defines the enumeration for days of the week. Each variant corresponds to a day of the week.
|
||||
-/
|
||||
inductive Weekday
|
||||
/-- Monday. -/
|
||||
| monday
|
||||
|
||||
/-- Tuesday. -/
|
||||
| tuesday
|
||||
|
||||
/-- Wednesday. -/
|
||||
| wednesday
|
||||
|
||||
/-- Thursday. -/
|
||||
| thursday
|
||||
|
||||
/-- Friday. -/
|
||||
| friday
|
||||
|
||||
/-- Saturday. -/
|
||||
| saturday
|
||||
|
||||
/-- Sunday. -/
|
||||
| sunday
|
||||
deriving Repr, Inhabited, BEq
|
||||
|
||||
namespace Weekday
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for weekdays, which ranges between 1 and 7.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 1 7
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 1 (1 + (6 : Nat))) n)
|
||||
|
||||
/--
|
||||
Converts a `Ordinal` representing a day index into a corresponding `Weekday`. This function is useful
|
||||
for mapping numerical representations to days of the week.
|
||||
-/
|
||||
def ofOrdinal : Ordinal → Weekday
|
||||
| 1 => .monday
|
||||
| 2 => .tuesday
|
||||
| 3 => .wednesday
|
||||
| 4 => .thursday
|
||||
| 5 => .friday
|
||||
| 6 => .saturday
|
||||
| 7 => .sunday
|
||||
|
||||
/--
|
||||
Converts a `Weekday` to a `Ordinal`.
|
||||
-/
|
||||
def toOrdinal : Weekday → Ordinal
|
||||
| .monday => 1
|
||||
| .tuesday => 2
|
||||
| .wednesday => 3
|
||||
| .thursday => 4
|
||||
| .friday => 5
|
||||
| .saturday => 6
|
||||
| .sunday => 7
|
||||
|
||||
/--
|
||||
Converts a `Weekday` to a `Nat`.
|
||||
-/
|
||||
def toNat : Weekday → Nat
|
||||
| .monday => 1
|
||||
| .tuesday => 2
|
||||
| .wednesday => 3
|
||||
| .thursday => 4
|
||||
| .friday => 5
|
||||
| .saturday => 6
|
||||
| .sunday => 7
|
||||
|
||||
/--
|
||||
Converts a `Nat` to an `Option Weekday`.
|
||||
-/
|
||||
def ofNat? : Nat → Option Weekday
|
||||
| 1 => some .monday
|
||||
| 2 => some .tuesday
|
||||
| 3 => some .wednesday
|
||||
| 4 => some .thursday
|
||||
| 5 => some .friday
|
||||
| 6 => some .saturday
|
||||
| 7 => some .sunday
|
||||
| _ => none
|
||||
|
||||
/--
|
||||
Converts a `Nat` to a `Weekday`. Panics if the value provided is invalid.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat! (n : Nat) : Weekday :=
|
||||
match ofNat? n with
|
||||
| some res => res
|
||||
| none => panic! "invalid weekday"
|
||||
|
||||
/--
|
||||
Gets the next `Weekday`.
|
||||
-/
|
||||
def next : Weekday → Weekday
|
||||
| .monday => .tuesday
|
||||
| .tuesday => .wednesday
|
||||
| .wednesday => .thursday
|
||||
| .thursday => .friday
|
||||
| .friday => .saturday
|
||||
| .saturday => .sunday
|
||||
| .sunday => .monday
|
||||
|
||||
/--
|
||||
Check if it's a weekend.
|
||||
-/
|
||||
def isWeekend : Weekday → Bool
|
||||
| .saturday => true
|
||||
| .sunday => true
|
||||
| _ => false
|
||||
|
||||
end Weekday
|
||||
end Time
|
||||
end Std
|
||||
128
src/Std/Time/Date/Unit/Year.lean
Normal file
128
src/Std/Time/Date/Unit/Year.lean
Normal file
@@ -0,0 +1,128 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Internal
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Date.Unit.Day
|
||||
import Std.Time.Date.Unit.Month
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Year
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Defines the different eras.
|
||||
-/
|
||||
inductive Era
|
||||
/-- The era before the Common Era (BCE), always represents a date before year 0. -/
|
||||
| bce
|
||||
|
||||
/-- The Common Era (CE), represents dates from year 0 onwards. -/
|
||||
| ce
|
||||
deriving Repr, Inhabited
|
||||
|
||||
instance : ToString Era where
|
||||
toString
|
||||
| .bce => "BCE"
|
||||
| .ce => "CE"
|
||||
|
||||
/--
|
||||
`Offset` represents a year offset, defined as an `Int`.
|
||||
-/
|
||||
def Offset : Type := Int
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance {x y : Offset} : Decidable (x ≤ y) :=
|
||||
let x : Int := x
|
||||
inferInstanceAs (Decidable (x ≤ y))
|
||||
|
||||
instance {x y : Offset} : Decidable (x < y) :=
|
||||
let x : Int := x
|
||||
inferInstanceAs (Decidable (x < y))
|
||||
|
||||
instance : OfNat Offset n := ⟨Int.ofNat n⟩
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
.ofNat data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
data
|
||||
|
||||
/--
|
||||
Converts the `Year` offset to an `Int`.
|
||||
-/
|
||||
@[inline]
|
||||
def toInt (offset : Offset) : Int :=
|
||||
offset
|
||||
|
||||
/--
|
||||
Converts the `Year` offset to a `Month` offset.
|
||||
-/
|
||||
@[inline]
|
||||
def toMonths (val : Offset) : Month.Offset :=
|
||||
val.mul 12
|
||||
|
||||
/--
|
||||
Determines if a year is a leap year in the proleptic Gregorian calendar.
|
||||
-/
|
||||
@[inline]
|
||||
def isLeap (y : Offset) : Bool :=
|
||||
y.toInt.tmod 4 = 0 ∧ (y.toInt.tmod 100 ≠ 0 ∨ y.toInt.tmod 400 = 0)
|
||||
|
||||
/--
|
||||
Returns the `Era` of the `Year`.
|
||||
-/
|
||||
def era (year : Offset) : Era :=
|
||||
if year.toInt ≥ 1
|
||||
then .ce
|
||||
else .bce
|
||||
|
||||
/--
|
||||
Calculates the number of days in the specified `year`.
|
||||
-/
|
||||
def days (year : Offset) : Bounded.LE 365 366 :=
|
||||
if year.isLeap
|
||||
then .ofNatWrapping 366 (by decide)
|
||||
else .ofNatWrapping 355 (by decide)
|
||||
|
||||
/--
|
||||
Calculates the number of weeks in the specified `year`.
|
||||
-/
|
||||
def weeks (year : Offset) : Bounded.LE 52 53 :=
|
||||
let p (year : Offset) := Bounded.LE.byEmod (year.toInt + year.toInt/4 - year.toInt/100 + year.toInt/400) 7 (by decide)
|
||||
|
||||
let add : Bounded.LE 0 1 :=
|
||||
if (p year).val = 4 ∨ (p (year - 1)).val = 3
|
||||
then Bounded.LE.ofNat 1 (by decide)
|
||||
else Bounded.LE.ofNat 0 (by decide)
|
||||
|
||||
Bounded.LE.exact 52 |>.addBounds add
|
||||
|
||||
/--
|
||||
Checks if the given date is valid for the specified year, month, and day.
|
||||
-/
|
||||
@[inline]
|
||||
abbrev Valid (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : Prop :=
|
||||
day ≤ month.days year.isLeap
|
||||
|
||||
end Offset
|
||||
end Year
|
||||
end Time
|
||||
end Std
|
||||
88
src/Std/Time/Date/ValidDate.lean
Normal file
88
src/Std/Time/Date/ValidDate.lean
Normal file
@@ -0,0 +1,88 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Date.Unit.Day
|
||||
import Std.Time.Date.Unit.Month
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Std.Internal
|
||||
open Internal
|
||||
open Month.Ordinal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a valid date for a given year, considering whether it is a leap year. Example: `(2, 29)`
|
||||
is valid only if `leap` is `true`.
|
||||
-/
|
||||
def ValidDate (leap : Bool) := { val : Month.Ordinal × Day.Ordinal // Valid leap (Prod.fst val) (Prod.snd val) }
|
||||
|
||||
instance : Inhabited (ValidDate l) where
|
||||
default := ⟨⟨1, 1⟩, (by cases l <;> decide)⟩
|
||||
|
||||
namespace ValidDate
|
||||
|
||||
/--
|
||||
Transforms a tuple of a `Month` and a `Day` into a `Day.Ordinal.OfYear`.
|
||||
-/
|
||||
def dayOfYear (ordinal : ValidDate leap) : Day.Ordinal.OfYear leap :=
|
||||
let days := cumulativeDays leap ordinal.val.fst
|
||||
let proof := cumulativeDays_le leap ordinal.val.fst
|
||||
let bounded := Bounded.LE.mk days.toInt proof |>.addBounds ordinal.val.snd
|
||||
match leap, bounded with
|
||||
| true, bounded => bounded
|
||||
| false, bounded => bounded
|
||||
|
||||
/--
|
||||
Transforms a `Day.Ordinal.OfYear` into a tuple of a `Month` and a `Day`.
|
||||
-/
|
||||
def ofOrdinal (ordinal : Day.Ordinal.OfYear leap) : ValidDate leap :=
|
||||
let rec go (idx : Month.Ordinal) (acc : Int) (h : ordinal.val > acc) (p : acc = (cumulativeDays leap idx).val) : ValidDate leap :=
|
||||
let monthDays := days leap idx
|
||||
if h₁ : ordinal.val ≤ acc + monthDays.val then
|
||||
let bounded := Bounded.LE.mk ordinal.val (And.intro h h₁) |>.sub acc
|
||||
let bounded : Bounded.LE 1 monthDays.val := bounded.cast (by omega) (by omega)
|
||||
let days₁ : Day.Ordinal := ⟨bounded.val, And.intro bounded.property.left (Int.le_trans bounded.property.right monthDays.property.right)⟩
|
||||
⟨⟨idx, days₁⟩, Int.le_trans bounded.property.right (by simp)⟩
|
||||
else by
|
||||
let h₂ := Int.not_le.mp h₁
|
||||
|
||||
have h₃ : idx.val < 12 := Int.not_le.mp <| λh₃ => by
|
||||
have h₅ := ordinal.property.right
|
||||
let eq := Int.eq_iff_le_and_ge.mpr (And.intro idx.property.right h₃)
|
||||
simp [monthDays, days, eq] at h₂
|
||||
simp [cumulativeDays, eq] at p
|
||||
simp [p] at h₂
|
||||
cases leap
|
||||
all_goals (simp at h₂; simp_all)
|
||||
· have h₂ : 365 < ordinal.val := h₂
|
||||
omega
|
||||
· have h₂ : 366 < ordinal.val := h₂
|
||||
omega
|
||||
|
||||
let idx₂ := idx.truncateTop (Int.le_sub_one_of_lt h₃) |>.addTop 1 (by decide)
|
||||
refine go idx₂ (acc + monthDays.val) h₂ ?_
|
||||
simp [monthDays, p]
|
||||
rw [difference_eq (Int.le_of_lt_add_one h₃)]
|
||||
|
||||
termination_by 12 - idx.val.toNat
|
||||
decreasing_by
|
||||
simp_wf
|
||||
simp [Bounded.LE.addTop]
|
||||
let gt0 : idx.val ≥ 0 := Int.le_trans (by decide) idx.property.left
|
||||
refine Nat.sub_lt_sub_left (Int.toNat_lt gt0 |>.mpr h₃) ?_
|
||||
let toNat_lt_lt {n z : Int} (h : 0 ≤ z) (h₁ : 0 ≤ n) : z.toNat < n.toNat ↔ z < n := by
|
||||
rw [← Int.not_le, ← Nat.not_le, ← Int.ofNat_le, Int.toNat_of_nonneg h, Int.toNat_of_nonneg h₁]
|
||||
rw [toNat_lt_lt (by omega) (by omega)]
|
||||
omega
|
||||
|
||||
go 1 0 (Int.le_trans (by decide) ordinal.property.left) (by cases leap <;> decide)
|
||||
|
||||
end ValidDate
|
||||
end Time
|
||||
end Std
|
||||
104
src/Std/Time/DateTime.lean
Normal file
104
src/Std/Time/DateTime.lean
Normal file
@@ -0,0 +1,104 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.DateTime.Timestamp
|
||||
import Std.Time.DateTime.PlainDateTime
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
namespace Timestamp
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `Timestamp`
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTimeAssumingUTC (pdt : PlainDateTime) : Timestamp :=
|
||||
pdt.toTimestampAssumingUTC
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to a `PlainDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDateTimeAssumingUTC (timestamp : Timestamp) : PlainDateTime :=
|
||||
PlainDateTime.ofTimestampAssumingUTC timestamp
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `Timestamp`
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateAssumingUTC (pd : PlainDate) : Timestamp :=
|
||||
let days := pd.toDaysSinceUNIXEpoch
|
||||
let secs := days.toSeconds
|
||||
Timestamp.ofSecondsSinceUnixEpoch secs
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to a `PlainDate`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDateAssumingUTC (timestamp : Timestamp) : PlainDate :=
|
||||
let secs := timestamp.toSecondsSinceUnixEpoch
|
||||
let days := Day.Offset.ofSeconds secs
|
||||
PlainDate.ofDaysSinceUNIXEpoch days
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to a `PlainTime`
|
||||
-/
|
||||
@[inline]
|
||||
def getTimeAssumingUTC (timestamp : Timestamp) : PlainTime :=
|
||||
let nanos := timestamp.toNanosecondsSinceUnixEpoch
|
||||
PlainTime.ofNanoseconds nanos
|
||||
|
||||
end Timestamp
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `Timestamp`
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestampAssumingUTC (pdt : PlainDate) : Timestamp :=
|
||||
Timestamp.ofPlainDateAssumingUTC pdt
|
||||
|
||||
instance : HSub PlainDate PlainDate Duration where
|
||||
hSub x y := x.toTimestampAssumingUTC - y.toTimestampAssumingUTC
|
||||
|
||||
end PlainDate
|
||||
namespace PlainDateTime
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `Timestamp`
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDate (date : PlainDate) : PlainDateTime :=
|
||||
{ date, time := PlainTime.midnight }
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `PlainDate`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDate (pdt : PlainDateTime) : PlainDate :=
|
||||
pdt.date
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` to a `PlainDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainTime (time : PlainTime) : PlainDateTime :=
|
||||
{ date := ⟨1, 1, 1, by decide⟩, time }
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `PlainTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainTime (pdt : PlainDateTime) : PlainTime :=
|
||||
pdt.time
|
||||
|
||||
instance : HSub PlainDateTime PlainDateTime Duration where
|
||||
hSub x y := x.toTimestampAssumingUTC - y.toTimestampAssumingUTC
|
||||
|
||||
end PlainDateTime
|
||||
601
src/Std/Time/DateTime/PlainDateTime.lean
Normal file
601
src/Std/Time/DateTime/PlainDateTime.lean
Normal file
@@ -0,0 +1,601 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date
|
||||
import Std.Time.Time
|
||||
import Std.Time.Internal
|
||||
import Std.Time.DateTime.Timestamp
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a date and time with components for Year, Month, Day, Hour, Minute, Second, and Nanosecond.
|
||||
-/
|
||||
structure PlainDateTime where
|
||||
|
||||
/--
|
||||
The `Date` component of a `PlainDate`
|
||||
-/
|
||||
date : PlainDate
|
||||
|
||||
/--
|
||||
The `Time` component of a `PlainTime`
|
||||
-/
|
||||
time : PlainTime
|
||||
|
||||
deriving Inhabited, BEq, Repr
|
||||
|
||||
namespace PlainDateTime
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `Timestamp`
|
||||
-/
|
||||
def toTimestampAssumingUTC (dt : PlainDateTime) : Timestamp :=
|
||||
let days := dt.date.toDaysSinceUNIXEpoch
|
||||
let nanos := days.toSeconds + dt.time.toSeconds |>.mul 1000000000
|
||||
let nanos := nanos.val + dt.time.nanosecond.val
|
||||
Timestamp.ofNanosecondsSinceUnixEpoch (Nanosecond.Offset.ofInt nanos)
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to a `PlainDateTime`.
|
||||
-/
|
||||
def ofTimestampAssumingUTC (stamp : Timestamp) : PlainDateTime := Id.run do
|
||||
let leapYearEpoch := 11017
|
||||
let daysPer400Y := 365 * 400 + 97
|
||||
let daysPer100Y := 365 * 100 + 24
|
||||
let daysPer4Y := 365 * 4 + 1
|
||||
|
||||
let nanos := stamp.toNanosecondsSinceUnixEpoch
|
||||
let secs : Second.Offset := nanos.ediv 1000000000
|
||||
let daysSinceEpoch : Day.Offset := secs.ediv 86400
|
||||
let boundedDaysSinceEpoch := daysSinceEpoch
|
||||
|
||||
let mut rawDays := boundedDaysSinceEpoch - leapYearEpoch
|
||||
let mut rem := Bounded.LE.byMod secs.val 86400 (by decide)
|
||||
|
||||
let ⟨remSecs, days⟩ :=
|
||||
if h : rem.val ≤ -1 then
|
||||
let remSecs := rem.truncateTop h
|
||||
let remSecs : Bounded.LE 1 86399 := remSecs.add 86400
|
||||
let rawDays := rawDays - 1
|
||||
(remSecs.expandBottom (by decide), rawDays)
|
||||
else
|
||||
let h := rem.truncateBottom (Int.not_le.mp h)
|
||||
(h, rawDays)
|
||||
|
||||
let mut quadracentennialCycles := days.val / daysPer400Y;
|
||||
let mut remDays := days.val % daysPer400Y;
|
||||
|
||||
if remDays < 0 then
|
||||
remDays := remDays + daysPer400Y
|
||||
quadracentennialCycles := quadracentennialCycles - 1
|
||||
|
||||
let mut centenialCycles := remDays / daysPer100Y;
|
||||
|
||||
if centenialCycles = 4 then
|
||||
centenialCycles := centenialCycles - 1
|
||||
|
||||
remDays := remDays - centenialCycles * daysPer100Y
|
||||
let mut quadrennialCycles := remDays / daysPer4Y;
|
||||
|
||||
if quadrennialCycles = 25 then
|
||||
quadrennialCycles := quadrennialCycles - 1
|
||||
|
||||
remDays := remDays - quadrennialCycles * daysPer4Y
|
||||
let mut remYears := remDays / 365;
|
||||
|
||||
if remYears = 4 then
|
||||
remYears := remYears - 1
|
||||
|
||||
remDays := remDays - remYears * 365
|
||||
|
||||
let mut year := 2000 + remYears + 4 * quadrennialCycles + 100 * centenialCycles + 400 * quadracentennialCycles
|
||||
let months := [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];
|
||||
let mut mon : Fin 13 := 0;
|
||||
|
||||
for monLen in months do
|
||||
mon := mon + 1;
|
||||
if remDays < monLen then
|
||||
break
|
||||
remDays := remDays - monLen
|
||||
|
||||
let mday : Fin 31 := Fin.ofNat (Int.toNat remDays)
|
||||
|
||||
let hmon ←
|
||||
if h₁ : mon.val > 10
|
||||
then do
|
||||
year := year + 1
|
||||
pure (Month.Ordinal.ofNat (mon.val - 10) (by omega))
|
||||
else
|
||||
pure (Month.Ordinal.ofNat (mon.val + 2) (by omega))
|
||||
|
||||
let second : Bounded.LE 0 59 := remSecs.emod 60 (by decide)
|
||||
let minute : Bounded.LE 0 59 := (remSecs.ediv 60 (by decide)).emod 60 (by decide)
|
||||
let hour : Bounded.LE 0 23 := remSecs.ediv 3600 (by decide)
|
||||
let nano : Bounded.LE 0 999999999 := Bounded.LE.byEmod nanos.val 1000000000 (by decide)
|
||||
|
||||
return {
|
||||
date := PlainDate.ofYearMonthDayClip year hmon (Day.Ordinal.ofFin (Fin.succ mday))
|
||||
time := PlainTime.ofHourMinuteSecondsNano (leap := false) (hour.expandTop (by decide)) minute second nano
|
||||
}
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def toDaysSinceUNIXEpoch (pdt : PlainDateTime) : Day.Offset :=
|
||||
pdt.date.toDaysSinceUNIXEpoch
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDaysSinceUNIXEpoch (days : Day.Offset) (time : PlainTime) : PlainDateTime :=
|
||||
PlainDateTime.mk (PlainDate.ofDaysSinceUNIXEpoch days) time
|
||||
|
||||
/--
|
||||
Sets the `PlainDateTime` to the specified `desiredWeekday`.
|
||||
-/
|
||||
def withWeekday (dt : PlainDateTime) (desiredWeekday : Weekday) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.withWeekday dt.date desiredWeekday }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysClip (dt : PlainDateTime) (days : Day.Ordinal) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.ofYearMonthDayClip dt.date.year dt.date.month days }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days rolled over to the next month or year as needed.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysRollOver (dt : PlainDateTime) (days : Day.Ordinal) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.rollOver dt.date.year dt.date.month days }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the month to the given `month` value, with any
|
||||
out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthClip (dt : PlainDateTime) (month : Month.Ordinal) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.ofYearMonthDayClip dt.date.year month dt.date.day }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the month to the given `month` value.
|
||||
The day is rolled over to the next valid month if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthRollOver (dt : PlainDateTime) (month : Month.Ordinal) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.rollOver dt.date.year month dt.date.day }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the year to the given `year` value. The month and day
|
||||
remain unchanged, with any out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearClip (dt : PlainDateTime) (year : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.ofYearMonthDayClip year dt.date.month dt.date.day }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the year to the given `year` value. The month and day are rolled
|
||||
over to the next valid month and day if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearRollOver (dt : PlainDateTime) (year : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := PlainDate.rollOver year dt.date.month dt.date.day }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the `hour` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withHours (dt : PlainDateTime) (hour : Hour.Ordinal) : PlainDateTime :=
|
||||
{ dt with time := { dt.time with hour := hour } }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the `minute` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withMinutes (dt : PlainDateTime) (minute : Minute.Ordinal) : PlainDateTime :=
|
||||
{ dt with time := { dt.time with minute := minute } }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the `second` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withSeconds (dt : PlainDateTime) (second : Sigma Second.Ordinal) : PlainDateTime :=
|
||||
{ dt with time := { dt.time with second := second } }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the milliseconds component inside the `nano` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withMilliseconds (dt : PlainDateTime) (millis : Millisecond.Ordinal) : PlainDateTime :=
|
||||
{ dt with time := dt.time.withMilliseconds millis }
|
||||
|
||||
/--
|
||||
Creates a new `PlainDateTime` by adjusting the `nano` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withNanoseconds (dt : PlainDateTime) (nano : Nanosecond.Ordinal) : PlainDateTime :=
|
||||
{ dt with time := dt.time.withNanoseconds nano }
|
||||
|
||||
/--
|
||||
Adds a `Day.Offset` to a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addDays (dt : PlainDateTime) (days : Day.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addDays days }
|
||||
|
||||
/--
|
||||
Subtracts a `Day.Offset` from a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subDays (dt : PlainDateTime) (days : Day.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subDays days }
|
||||
|
||||
/--
|
||||
Adds a `Week.Offset` to a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addWeeks (dt : PlainDateTime) (weeks : Week.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addWeeks weeks }
|
||||
|
||||
/--
|
||||
Subtracts a `Week.Offset` from a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subWeeks (dt : PlainDateTime) (weeks : Week.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subWeeks weeks }
|
||||
|
||||
/--
|
||||
Adds a `Month.Offset` to a `PlainDateTime`, adjusting the day to the last valid day of the resulting
|
||||
month.
|
||||
-/
|
||||
def addMonthsClip (dt : PlainDateTime) (months : Month.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addMonthsClip months }
|
||||
|
||||
/--
|
||||
Subtracts `Month.Offset` from a `PlainDateTime`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsClip (dt : PlainDateTime) (months : Month.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subMonthsClip months }
|
||||
|
||||
/--
|
||||
Adds a `Month.Offset` to a `PlainDateTime`, rolling over excess days to the following month if needed.
|
||||
-/
|
||||
def addMonthsRollOver (dt : PlainDateTime) (months : Month.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addMonthsRollOver months }
|
||||
|
||||
/--
|
||||
Subtracts a `Month.Offset` from a `PlainDateTime`, adjusting the day to the last valid day of the
|
||||
resulting month.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsRollOver (dt : PlainDateTime) (months : Month.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subMonthsRollOver months }
|
||||
|
||||
/--
|
||||
Adds a `Month.Offset` to a `PlainDateTime`, rolling over excess days to the following month if needed.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsRollOver (dt : PlainDateTime) (years : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addYearsRollOver years }
|
||||
|
||||
/--
|
||||
Subtracts a `Month.Offset` from a `PlainDateTime`, rolling over excess days to the following month if
|
||||
needed.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsClip (dt : PlainDateTime) (years : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.addYearsClip years }
|
||||
|
||||
/--
|
||||
Subtracts a `Year.Offset` from a `PlainDateTime`, this function rolls over any excess days into the
|
||||
following month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsRollOver (dt : PlainDateTime) (years : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subYearsRollOver years }
|
||||
|
||||
/--
|
||||
Subtracts a `Year.Offset` from a `PlainDateTime`, adjusting the day to the last valid day of the
|
||||
resulting month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsClip (dt : PlainDateTime) (years : Year.Offset) : PlainDateTime :=
|
||||
{ dt with date := dt.date.subYearsClip years }
|
||||
|
||||
|
||||
/--
|
||||
Adds an `Hour.Offset` to a `PlainDateTime`, adjusting the date if the hour overflows.
|
||||
-/
|
||||
@[inline]
|
||||
def addHours (dt : PlainDateTime) (hours : Hour.Offset) : PlainDateTime :=
|
||||
let totalSeconds := dt.time.toSeconds + hours.toSeconds
|
||||
let days := totalSeconds.ediv 86400
|
||||
let newTime := dt.time.addSeconds (hours.toSeconds)
|
||||
{ dt with date := dt.date.addDays days, time := newTime }
|
||||
|
||||
/--
|
||||
Subtracts an `Hour.Offset` from a `PlainDateTime`, adjusting the date if the hour underflows.
|
||||
-/
|
||||
@[inline]
|
||||
def subHours (dt : PlainDateTime) (hours : Hour.Offset) : PlainDateTime :=
|
||||
addHours dt (-hours)
|
||||
|
||||
/--
|
||||
Adds a `Minute.Offset` to a `PlainDateTime`, adjusting the hour and date if the minutes overflow.
|
||||
-/
|
||||
@[inline]
|
||||
def addMinutes (dt : PlainDateTime) (minutes : Minute.Offset) : PlainDateTime :=
|
||||
let totalSeconds := dt.time.toSeconds + minutes.toSeconds
|
||||
let days := totalSeconds.ediv 86400
|
||||
let newTime := dt.time.addSeconds (minutes.toSeconds)
|
||||
{ dt with date := dt.date.addDays days, time := newTime }
|
||||
|
||||
/--
|
||||
Subtracts a `Minute.Offset` from a `PlainDateTime`, adjusting the hour and date if the minutes underflow.
|
||||
-/
|
||||
@[inline]
|
||||
def subMinutes (dt : PlainDateTime) (minutes : Minute.Offset) : PlainDateTime :=
|
||||
addMinutes dt (-minutes)
|
||||
|
||||
/--
|
||||
Adds a `Second.Offset` to a `PlainDateTime`, adjusting the minute, hour, and date if the seconds overflow.
|
||||
-/
|
||||
@[inline]
|
||||
def addSeconds (dt : PlainDateTime) (seconds : Second.Offset) : PlainDateTime :=
|
||||
let totalSeconds := dt.time.toSeconds + seconds
|
||||
let days := totalSeconds.ediv 86400
|
||||
let newTime := dt.time.addSeconds seconds
|
||||
{ dt with date := dt.date.addDays days, time := newTime }
|
||||
|
||||
/--
|
||||
Subtracts a `Second.Offset` from a `PlainDateTime`, adjusting the minute, hour, and date if the seconds underflow.
|
||||
-/
|
||||
@[inline]
|
||||
def subSeconds (dt : PlainDateTime) (seconds : Second.Offset) : PlainDateTime :=
|
||||
addSeconds dt (-seconds)
|
||||
|
||||
/--
|
||||
Adds a `Millisecond.Offset` to a `PlainDateTime`, adjusting the second, minute, hour, and date if the milliseconds overflow.
|
||||
-/
|
||||
@[inline]
|
||||
def addMilliseconds (dt : PlainDateTime) (milliseconds : Millisecond.Offset) : PlainDateTime :=
|
||||
let totalMilliseconds := dt.time.toMilliseconds + milliseconds
|
||||
let days := totalMilliseconds.ediv 86400000 -- 86400000 ms in a day
|
||||
let newTime := dt.time.addMilliseconds milliseconds
|
||||
{ dt with date := dt.date.addDays days, time := newTime }
|
||||
|
||||
/--
|
||||
Subtracts a `Millisecond.Offset` from a `PlainDateTime`, adjusting the second, minute, hour, and date if the milliseconds underflow.
|
||||
-/
|
||||
@[inline]
|
||||
def subMilliseconds (dt : PlainDateTime) (milliseconds : Millisecond.Offset) : PlainDateTime :=
|
||||
addMilliseconds dt (-milliseconds)
|
||||
|
||||
/--
|
||||
Adds a `Nanosecond.Offset` to a `PlainDateTime`, adjusting the seconds, minutes, hours, and date if the nanoseconds overflow.
|
||||
-/
|
||||
@[inline]
|
||||
def addNanoseconds (dt : PlainDateTime) (nanos : Nanosecond.Offset) : PlainDateTime :=
|
||||
let nano := Nanosecond.Offset.ofInt dt.time.nanosecond.val
|
||||
let totalNanos := nano + nanos
|
||||
let extraSeconds := totalNanos.ediv 1000000000
|
||||
let nanosecond := Bounded.LE.byEmod totalNanos.val 1000000000 (by decide)
|
||||
let newTime := dt.time.addSeconds extraSeconds
|
||||
{ dt with time := { newTime with nanosecond } }
|
||||
|
||||
/--
|
||||
Subtracts a `Nanosecond.Offset` from a `PlainDateTime`, adjusting the seconds, minutes, hours, and date if the nanoseconds underflow.
|
||||
-/
|
||||
@[inline]
|
||||
def subNanoseconds (dt : PlainDateTime) (nanos : Nanosecond.Offset) : PlainDateTime :=
|
||||
addNanoseconds dt (-nanos)
|
||||
|
||||
/--
|
||||
Getter for the `Year` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def year (dt : PlainDateTime) : Year.Offset :=
|
||||
dt.date.year
|
||||
|
||||
/--
|
||||
Getter for the `Month` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def month (dt : PlainDateTime) : Month.Ordinal :=
|
||||
dt.date.month
|
||||
|
||||
/--
|
||||
Getter for the `Day` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def day (dt : PlainDateTime) : Day.Ordinal :=
|
||||
dt.date.day
|
||||
|
||||
/--
|
||||
Getter for the `Weekday` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def weekday (dt : PlainDateTime) : Weekday :=
|
||||
dt.date.weekday
|
||||
|
||||
/--
|
||||
Getter for the `Hour` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def hour (dt : PlainDateTime) : Hour.Ordinal :=
|
||||
dt.time.hour
|
||||
|
||||
/--
|
||||
Getter for the `Minute` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def minute (dt : PlainDateTime) : Minute.Ordinal :=
|
||||
dt.time.minute
|
||||
|
||||
/--
|
||||
Getter for the `Millisecond` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def millisecond (dt : PlainDateTime) : Millisecond.Ordinal :=
|
||||
dt.time.millisecond
|
||||
|
||||
/--
|
||||
Getter for the `Second` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def second (dt : PlainDateTime) : Second.Ordinal dt.time.second.fst :=
|
||||
dt.time.second.snd
|
||||
|
||||
/--
|
||||
Getter for the `Nanosecond.Ordinal` inside of a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def nanosecond (dt : PlainDateTime) : Nanosecond.Ordinal :=
|
||||
dt.time.nanosecond
|
||||
|
||||
/--
|
||||
Determines the era of the given `PlainDateTime` based on its year.
|
||||
-/
|
||||
@[inline]
|
||||
def era (date : PlainDateTime) : Year.Era :=
|
||||
date.date.era
|
||||
|
||||
/--
|
||||
Checks if the `PlainDateTime` is in a leap year.
|
||||
-/
|
||||
@[inline]
|
||||
def inLeapYear (date : PlainDateTime) : Bool :=
|
||||
date.year.isLeap
|
||||
|
||||
/--
|
||||
Determines the week of the year for the given `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def weekOfYear (date : PlainDateTime) : Week.Ordinal :=
|
||||
date.date.weekOfYear
|
||||
|
||||
/--
|
||||
Returns the unaligned week of the month for a `PlainDateTime` (day divided by 7, plus 1).
|
||||
-/
|
||||
def weekOfMonth (date : PlainDateTime) : Bounded.LE 1 5 :=
|
||||
date.date.weekOfMonth
|
||||
|
||||
/--
|
||||
Determines the week of the month for the given `PlainDateTime`. The week of the month is calculated based
|
||||
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
||||
based on the Gregorian Calendar.
|
||||
-/
|
||||
@[inline]
|
||||
def alignedWeekOfMonth (date : PlainDateTime) : Week.Ordinal.OfMonth :=
|
||||
date.date.alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
Transforms a tuple of a `PlainDateTime` into a `Day.Ordinal.OfYear`.
|
||||
-/
|
||||
@[inline]
|
||||
def dayOfYear (date : PlainDateTime) : Day.Ordinal.OfYear date.year.isLeap :=
|
||||
ValidDate.dayOfYear ⟨(date.month, date.day), date.date.valid⟩
|
||||
|
||||
/--
|
||||
Determines the quarter of the year for the given `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def quarter (date : PlainDateTime) : Bounded.LE 1 4 :=
|
||||
date.date.quarter
|
||||
|
||||
/--
|
||||
Combines a `PlainDate` and `PlainTime` into a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def atTime : PlainDate → PlainTime → PlainDateTime :=
|
||||
PlainDateTime.mk
|
||||
|
||||
/--
|
||||
Combines a `PlainTime` and `PlainDate` into a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def atDate (time: PlainTime) (date: PlainDate) : PlainDateTime :=
|
||||
PlainDateTime.mk date time
|
||||
|
||||
instance : HAdd PlainDateTime Day.Offset PlainDateTime where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub PlainDateTime Day.Offset PlainDateTime where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd PlainDateTime Week.Offset PlainDateTime where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub PlainDateTime Week.Offset PlainDateTime where
|
||||
hSub := subWeeks
|
||||
|
||||
instance : HAdd PlainDateTime Hour.Offset PlainDateTime where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub PlainDateTime Hour.Offset PlainDateTime where
|
||||
hSub := subHours
|
||||
|
||||
instance : HAdd PlainDateTime Minute.Offset PlainDateTime where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub PlainDateTime Minute.Offset PlainDateTime where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd PlainDateTime Millisecond.Offset PlainDateTime where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub PlainDateTime Millisecond.Offset PlainDateTime where
|
||||
hSub := addMilliseconds
|
||||
|
||||
instance : HAdd PlainDateTime Second.Offset PlainDateTime where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub PlainDateTime Second.Offset PlainDateTime where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd PlainDateTime Nanosecond.Offset PlainDateTime where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub PlainDateTime Nanosecond.Offset PlainDateTime where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HAdd PlainDateTime Duration PlainDateTime where
|
||||
hAdd x y := addNanoseconds x y.toNanoseconds
|
||||
|
||||
end PlainDateTime
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Combines a `PlainDate` and `PlainTime` into a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def atTime : PlainDate → PlainTime → PlainDateTime :=
|
||||
PlainDateTime.mk
|
||||
|
||||
end PlainDate
|
||||
namespace PlainTime
|
||||
|
||||
/--
|
||||
Combines a `PlainTime` and `PlainDate` into a `PlainDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def atDate (time: PlainTime) (date: PlainDate) : PlainDateTime :=
|
||||
PlainDateTime.mk date time
|
||||
|
||||
end PlainTime
|
||||
end Time
|
||||
end Std
|
||||
289
src/Std/Time/DateTime/Timestamp.lean
Normal file
289
src/Std/Time/DateTime/Timestamp.lean
Normal file
@@ -0,0 +1,289 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Internal
|
||||
import Init.Data.Int
|
||||
import Std.Time.Time
|
||||
import Std.Time.Date
|
||||
import Std.Time.Duration
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents an exact point in time as a UNIX Epoch timestamp.
|
||||
-/
|
||||
structure Timestamp where
|
||||
|
||||
/--
|
||||
Duration since the unix epoch.
|
||||
-/
|
||||
val : Duration
|
||||
deriving Repr, BEq, Inhabited
|
||||
|
||||
instance : LE Timestamp where
|
||||
le x y := x.val ≤ y.val
|
||||
|
||||
instance { x y : Timestamp } : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance : OfNat Timestamp n where
|
||||
ofNat := ⟨OfNat.ofNat n⟩
|
||||
|
||||
instance : ToString Timestamp where
|
||||
toString s := toString s.val.toMilliseconds
|
||||
|
||||
instance : Repr Timestamp where
|
||||
reprPrec s := reprPrec (toString s)
|
||||
|
||||
namespace Timestamp
|
||||
|
||||
/--
|
||||
Fetches the current duration from the system.
|
||||
-/
|
||||
@[extern "lean_get_current_time"]
|
||||
opaque now : IO Timestamp
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to minutes as `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (tm : Timestamp) : Minute.Offset :=
|
||||
tm.val.second.ediv 60
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to days as `Day.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (tm : Timestamp) : Day.Offset :=
|
||||
tm.val.second.ediv 86400
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` from a `Second.Offset` since the Unix epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSecondsSinceUnixEpoch (secs : Second.Offset) : Timestamp :=
|
||||
⟨Duration.ofSeconds secs⟩
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` from a `Nanosecond.Offset` since the Unix epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanosecondsSinceUnixEpoch (nanos : Nanosecond.Offset) : Timestamp :=
|
||||
⟨Duration.ofNanoseconds nanos⟩
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` from a `Millisecond.Offset` since the Unix epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMillisecondsSinceUnixEpoch (milli : Millisecond.Offset) : Timestamp :=
|
||||
⟨Duration.ofNanoseconds milli.toNanoseconds⟩
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to seconds as `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSecondsSinceUnixEpoch (t : Timestamp) : Second.Offset :=
|
||||
t.val.second
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to nanoseconds as `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanosecondsSinceUnixEpoch (tm : Timestamp) : Nanosecond.Offset :=
|
||||
let nanos := tm.toSecondsSinceUnixEpoch.mul 1000000000
|
||||
let nanos := nanos + (.ofInt tm.val.nano.val)
|
||||
nanos
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to nanoseconds as `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMillisecondsSinceUnixEpoch (tm : Timestamp) : Millisecond.Offset :=
|
||||
tm.toNanosecondsSinceUnixEpoch.toMilliseconds
|
||||
|
||||
/--
|
||||
Calculates the duration from the given `Timestamp` to the current time.
|
||||
-/
|
||||
@[inline]
|
||||
def since (f : Timestamp) : IO Duration := do
|
||||
let cur ← Timestamp.now
|
||||
return Std.Time.Duration.sub cur.val f.val
|
||||
|
||||
/--
|
||||
Returns the `Duration` represented by the `Timestamp` since the Unix epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def toDurationSinceUnixEpoch (tm : Timestamp) : Duration :=
|
||||
tm.val
|
||||
|
||||
/--
|
||||
Adds a `Millisecond.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMilliseconds (t : Timestamp) (s : Millisecond.Offset) : Timestamp :=
|
||||
⟨t.val + s⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Millisecond.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMilliseconds (t : Timestamp) (s : Millisecond.Offset) : Timestamp :=
|
||||
⟨t.val - s⟩
|
||||
|
||||
/--
|
||||
Adds a `Nanosecond.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addNanoseconds (t : Timestamp) (s : Nanosecond.Offset) : Timestamp :=
|
||||
⟨t.val + s⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Nanosecond.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subNanoseconds (t : Timestamp) (s : Nanosecond.Offset) : Timestamp :=
|
||||
⟨t.val - s⟩
|
||||
|
||||
/--
|
||||
Adds a `Second.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addSeconds (t : Timestamp) (s : Second.Offset) : Timestamp :=
|
||||
⟨t.val + s⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Second.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subSeconds (t : Timestamp) (s : Second.Offset) : Timestamp :=
|
||||
⟨t.val - s⟩
|
||||
|
||||
/--
|
||||
Adds a `Minute.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMinutes (t : Timestamp) (m : Minute.Offset) : Timestamp :=
|
||||
⟨t.val + m⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Minute.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMinutes (t : Timestamp) (m : Minute.Offset) : Timestamp :=
|
||||
⟨t.val - m⟩
|
||||
|
||||
/--
|
||||
Adds an `Hour.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addHours (t : Timestamp) (h : Hour.Offset) : Timestamp :=
|
||||
⟨t.val + h⟩
|
||||
|
||||
/--
|
||||
Subtracts an `Hour.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subHours (t : Timestamp) (h : Hour.Offset) : Timestamp :=
|
||||
⟨t.val - h⟩
|
||||
|
||||
/--
|
||||
Adds a `Day.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addDays (t : Timestamp) (d : Day.Offset) : Timestamp :=
|
||||
⟨t.val + d⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Day.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subDays (t : Timestamp) (d : Day.Offset) : Timestamp :=
|
||||
⟨t.val - d⟩
|
||||
|
||||
/--
|
||||
Adds a `Week.Offset` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addWeeks (t : Timestamp) (d : Week.Offset) : Timestamp :=
|
||||
⟨t.val + d⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Week.Offset` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subWeeks (t : Timestamp) (d : Week.Offset) : Timestamp :=
|
||||
⟨t.val - d⟩
|
||||
|
||||
/--
|
||||
Adds a `Duration` to the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def addDuration (t : Timestamp) (d : Duration) : Timestamp :=
|
||||
⟨t.val + d⟩
|
||||
|
||||
/--
|
||||
Subtracts a `Duration` from the given `Timestamp`.
|
||||
-/
|
||||
@[inline]
|
||||
def subDuration (t : Timestamp) (d : Duration) : Timestamp :=
|
||||
⟨t.val - d⟩
|
||||
|
||||
instance : HAdd Timestamp Duration Timestamp where
|
||||
hAdd := addDuration
|
||||
|
||||
instance : HSub Timestamp Duration Timestamp where
|
||||
hSub := subDuration
|
||||
|
||||
instance : HAdd Timestamp Day.Offset Timestamp where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub Timestamp Day.Offset Timestamp where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd Timestamp Week.Offset Timestamp where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub Timestamp Week.Offset Timestamp where
|
||||
hSub := subWeeks
|
||||
|
||||
instance : HAdd Timestamp Hour.Offset Timestamp where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub Timestamp Hour.Offset Timestamp where
|
||||
hSub := subHours
|
||||
|
||||
instance : HAdd Timestamp Minute.Offset Timestamp where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub Timestamp Minute.Offset Timestamp where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd Timestamp Second.Offset Timestamp where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub Timestamp Second.Offset Timestamp where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd Timestamp Millisecond.Offset Timestamp where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub Timestamp Millisecond.Offset Timestamp where
|
||||
hSub := subMilliseconds
|
||||
|
||||
instance : HAdd Timestamp Nanosecond.Offset Timestamp where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub Timestamp Nanosecond.Offset Timestamp where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HSub Timestamp Timestamp Duration where
|
||||
hSub x y := x.val - y.val
|
||||
|
||||
end Timestamp
|
||||
361
src/Std/Time/Duration.lean
Normal file
361
src/Std/Time/Duration.lean
Normal file
@@ -0,0 +1,361 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date
|
||||
import Std.Time.Time
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a time interval with nanoseconds precision.
|
||||
-/
|
||||
structure Duration where
|
||||
|
||||
/--
|
||||
Second offset of the duration.
|
||||
-/
|
||||
second : Second.Offset
|
||||
|
||||
/--
|
||||
Nanosecond span that ranges from -999999999 and 999999999
|
||||
-/
|
||||
nano : Nanosecond.Span
|
||||
|
||||
/--
|
||||
Proof that the duration is valid, ensuring that the `second` and `nano` values are correctly related.
|
||||
-/
|
||||
proof : (second.val ≥ 0 ∧ nano.val ≥ 0) ∨ (second.val ≤ 0 ∧ nano.val ≤ 0)
|
||||
deriving Repr
|
||||
|
||||
instance : ToString Duration where
|
||||
toString s :=
|
||||
let (sign, secs, nanos) :=
|
||||
if s.second.val > 0 then ("" ,s.second, s.nano.val)
|
||||
else if s.second.val < 0 then ("-", -s.second, -s.nano.val)
|
||||
else if s.nano.val < 0 then ("-", -s.second, -s.nano.val) else ("", s.second, s.nano.val)
|
||||
sign ++ toString secs ++ (if s.nano.val == 0 then "" else "." ++ (leftPad 9 <| toString nanos)) ++ "s"
|
||||
where
|
||||
leftPad n s := "".pushn '0' (n - s.length) ++ s
|
||||
|
||||
instance : Repr Duration where
|
||||
reprPrec s := reprPrec (toString s)
|
||||
|
||||
instance : BEq Duration where
|
||||
beq x y := x.second == y.second && y.nano == x.nano
|
||||
|
||||
instance : Inhabited Duration where
|
||||
default := ⟨0, Bounded.LE.mk 0 (by decide), by decide⟩
|
||||
|
||||
instance : OfNat Duration n where
|
||||
ofNat := by
|
||||
refine ⟨.ofInt n, ⟨0, by decide⟩, ?_⟩
|
||||
simp <;> exact Int.le_total n 0 |>.symm
|
||||
|
||||
namespace Duration
|
||||
|
||||
/--
|
||||
Negates a `Duration`, flipping its second and nanosecond values.
|
||||
-/
|
||||
@[inline]
|
||||
protected def neg (duration : Duration) : Duration := by
|
||||
refine ⟨-duration.second, duration.nano.neg, ?_⟩
|
||||
cases duration.proof with
|
||||
| inl n => exact Or.inr (n.imp Int.neg_le_neg Int.neg_le_neg)
|
||||
| inr n => exact Or.inl (n.imp Int.neg_le_neg Int.neg_le_neg)
|
||||
|
||||
/--
|
||||
Creates a new `Duration` out of `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (s : Second.Offset) : Duration := by
|
||||
refine ⟨s, ⟨0, by decide⟩, ?_⟩
|
||||
simp <;> exact Int.le_total s.val 0 |>.symm
|
||||
|
||||
/--
|
||||
Creates a new `Duration` out of `Nanosecond.Offset`.
|
||||
-/
|
||||
def ofNanoseconds (s : Nanosecond.Offset) : Duration := by
|
||||
refine ⟨s.ediv 1000000000, Bounded.LE.byMod s.val 1000000000 (by decide), ?_⟩
|
||||
cases Int.le_total s.val 0
|
||||
next n => exact Or.inr (And.intro (Int.ediv_le_ediv (by decide) n) (mod_nonpos 1000000000 n (by decide)))
|
||||
next n => exact Or.inl (And.intro (Int.ediv_nonneg n (by decide)) (Int.tmod_nonneg 1000000000 n))
|
||||
where
|
||||
mod_nonpos : ∀ {a : Int} (b : Int), (a ≤ 0) → (b ≥ 0) → 0 ≥ a.tmod b
|
||||
| .negSucc m, .ofNat n, _, _ => Int.neg_le_neg (Int.tmod_nonneg (↑n) (Int.ofNat_le.mpr (Nat.zero_le (m + 1))))
|
||||
| 0, n, _, _ => Int.eq_iff_le_and_ge.mp (Int.zero_tmod n) |>.left
|
||||
|
||||
/--
|
||||
Creates a new `Duration` out of `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMillisecond (s : Millisecond.Offset) : Duration :=
|
||||
ofNanoseconds (s.mul 1000000)
|
||||
|
||||
/--
|
||||
Checks if the duration is zero seconds and zero nanoseconds.
|
||||
-/
|
||||
@[inline]
|
||||
def isZero (d : Duration) : Bool :=
|
||||
d.second.val = 0 ∧ d.nano.val = 0
|
||||
|
||||
/--
|
||||
Converts a `Duration` to a `Second.Offset`
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (duration : Duration) : Second.Offset :=
|
||||
duration.second
|
||||
|
||||
/--
|
||||
Converts a `Duration` to a `Millisecond.Offset`
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (duration : Duration) : Millisecond.Offset :=
|
||||
let secMillis := duration.second.mul 1000
|
||||
let nanosMillis := duration.nano.ediv 1000000 (by decide)
|
||||
let millis := secMillis + (.ofInt nanosMillis.val)
|
||||
millis
|
||||
|
||||
/--
|
||||
Converts a `Duration` to a `Nanosecond.Offset`
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (duration : Duration) : Nanosecond.Offset :=
|
||||
let nanos := duration.second.mul 1000000000
|
||||
let nanos := nanos + (.ofInt duration.nano.val)
|
||||
nanos
|
||||
|
||||
instance : LE Duration where
|
||||
le d1 d2 := d1.toNanoseconds ≤ d2.toNanoseconds
|
||||
|
||||
instance {x y : Duration} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.toNanoseconds ≤ y.toNanoseconds))
|
||||
|
||||
/--
|
||||
Converts a `Duration` to a `Minute.Offset`
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (tm : Duration) : Minute.Offset :=
|
||||
tm.second.ediv 60
|
||||
|
||||
/--
|
||||
Converts a `Duration` to a `Day.Offset`
|
||||
-/
|
||||
@[inline]
|
||||
def toDays (tm : Duration) : Day.Offset :=
|
||||
tm.second.ediv 86400
|
||||
|
||||
/--
|
||||
Normalizes `Second.Offset` and `NanoSecond.span` in order to build a new `Duration` out of it.
|
||||
-/
|
||||
@[inline]
|
||||
def fromComponents (secs : Second.Offset) (nanos : Nanosecond.Span) : Duration :=
|
||||
ofNanoseconds (secs.toNanoseconds + nanos.toOffset)
|
||||
|
||||
/--
|
||||
Adds two durations together, handling any carry-over in nanoseconds.
|
||||
-/
|
||||
@[inline]
|
||||
def add (t₁ t₂ : Duration) : Duration :=
|
||||
ofNanoseconds (toNanoseconds t₁ + toNanoseconds t₂)
|
||||
|
||||
/--
|
||||
Subtracts one `Duration` from another.
|
||||
-/
|
||||
@[inline]
|
||||
def sub (t₁ t₂ : Duration) : Duration :=
|
||||
t₁.add t₂.neg
|
||||
|
||||
/--
|
||||
Adds a `Nanosecond.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addNanoseconds (t : Duration) (s : Nanosecond.Offset) : Duration :=
|
||||
t.add (ofNanoseconds s)
|
||||
|
||||
/--
|
||||
Adds a `Millisecond.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addMilliseconds (t : Duration) (s : Millisecond.Offset) : Duration :=
|
||||
t.add (ofNanoseconds s.toNanoseconds)
|
||||
|
||||
/--
|
||||
Adds a `Millisecond.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subMilliseconds (t : Duration) (s : Millisecond.Offset) : Duration :=
|
||||
t.sub (ofNanoseconds s.toNanoseconds)
|
||||
|
||||
/--
|
||||
Adds a `Nanosecond.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subNanoseconds (t : Duration) (s : Nanosecond.Offset) : Duration :=
|
||||
t.sub (ofNanoseconds s)
|
||||
|
||||
/--
|
||||
Adds a `Second.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addSeconds (t : Duration) (s : Second.Offset) : Duration :=
|
||||
t.add (ofSeconds s)
|
||||
|
||||
/--
|
||||
Subtracts a `Second.Offset` from a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subSeconds (t : Duration) (s : Second.Offset) : Duration :=
|
||||
t.sub (ofSeconds s)
|
||||
|
||||
/--
|
||||
Adds a `Minute.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addMinutes (t : Duration) (m : Minute.Offset) : Duration :=
|
||||
let seconds := m.mul 60
|
||||
t.addSeconds seconds
|
||||
|
||||
/--
|
||||
Subtracts a `Minute.Offset` from a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subMinutes (t : Duration) (m : Minute.Offset) : Duration :=
|
||||
let seconds := m.mul 60
|
||||
t.subSeconds seconds
|
||||
|
||||
/--
|
||||
Adds an `Hour.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addHours (t : Duration) (h : Hour.Offset) : Duration :=
|
||||
let seconds := h.mul 3600
|
||||
t.addSeconds seconds
|
||||
|
||||
/--
|
||||
Subtracts an `Hour.Offset` from a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subHours (t : Duration) (h : Hour.Offset) : Duration :=
|
||||
let seconds := h.mul 3600
|
||||
t.subSeconds seconds
|
||||
|
||||
/--
|
||||
Adds a `Day.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addDays (t : Duration) (d : Day.Offset) : Duration :=
|
||||
let seconds := d.mul 86400
|
||||
t.addSeconds seconds
|
||||
|
||||
/--
|
||||
Subtracts a `Day.Offset` from a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subDays (t : Duration) (d : Day.Offset) : Duration :=
|
||||
let seconds := d.mul 86400
|
||||
t.subSeconds seconds
|
||||
|
||||
/--
|
||||
Adds a `Week.Offset` to a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def addWeeks (t : Duration) (w : Week.Offset) : Duration :=
|
||||
let seconds := w.mul 604800
|
||||
t.addSeconds seconds
|
||||
|
||||
/--
|
||||
Subtracts a `Week.Offset` from a `Duration`
|
||||
-/
|
||||
@[inline]
|
||||
def subWeeks (t : Duration) (w : Week.Offset) : Duration :=
|
||||
let seconds := w.mul 604800
|
||||
t.subSeconds seconds
|
||||
|
||||
instance : HAdd Duration Day.Offset Duration where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub Duration Day.Offset Duration where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd Duration Week.Offset Duration where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub Duration Week.Offset Duration where
|
||||
hSub := subWeeks
|
||||
|
||||
instance : HAdd Duration Hour.Offset Duration where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub Duration Hour.Offset Duration where
|
||||
hSub := subHours
|
||||
|
||||
instance : HAdd Duration Minute.Offset Duration where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub Duration Minute.Offset Duration where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd Duration Second.Offset Duration where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub Duration Second.Offset Duration where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd Duration Nanosecond.Offset Duration where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub Duration Nanosecond.Offset Duration where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HAdd Duration Millisecond.Offset Duration where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub Duration Millisecond.Offset Duration where
|
||||
hSub := subMilliseconds
|
||||
|
||||
instance : HSub Duration Duration Duration where
|
||||
hSub := sub
|
||||
|
||||
instance : HAdd Duration Duration Duration where
|
||||
hAdd := add
|
||||
|
||||
instance : Coe Nanosecond.Offset Duration where
|
||||
coe := ofNanoseconds
|
||||
|
||||
instance : Coe Second.Offset Duration where
|
||||
coe := ofSeconds
|
||||
|
||||
instance : Coe Minute.Offset Duration where
|
||||
coe := ofSeconds ∘ Minute.Offset.toSeconds
|
||||
|
||||
instance : Coe Hour.Offset Duration where
|
||||
coe := ofSeconds ∘ Hour.Offset.toSeconds
|
||||
|
||||
instance : Coe Week.Offset Duration where
|
||||
coe := ofSeconds ∘ Day.Offset.toSeconds ∘ Week.Offset.toDays
|
||||
|
||||
instance : Coe Day.Offset Duration where
|
||||
coe := ofSeconds ∘ Day.Offset.toSeconds
|
||||
|
||||
instance : HMul Int Duration Duration where
|
||||
hMul i d := Duration.ofNanoseconds <| Nanosecond.Offset.ofInt (d.toNanoseconds.val * i)
|
||||
|
||||
instance : HMul Duration Int Duration where
|
||||
hMul d i := Duration.ofNanoseconds <| Nanosecond.Offset.ofInt (d.toNanoseconds.val * i)
|
||||
|
||||
instance : HAdd PlainTime Duration PlainTime where
|
||||
hAdd pt d := PlainTime.ofNanoseconds (d.toNanoseconds + pt.toNanoseconds)
|
||||
|
||||
instance : HSub PlainTime Duration PlainTime where
|
||||
hSub pt d := PlainTime.ofNanoseconds (d.toNanoseconds - pt.toNanoseconds)
|
||||
|
||||
end Duration
|
||||
end Time
|
||||
end Std
|
||||
623
src/Std/Time/Format.lean
Normal file
623
src/Std/Time/Format.lean
Normal file
@@ -0,0 +1,623 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Notation.Spec
|
||||
import Std.Time.Format.Basic
|
||||
import Std.Time.Internal.Bounded
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Formats
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
The ISO8601 format, which is always 24 or 27 characters long, used for representing date and time in
|
||||
a standardized format. The format follows the pattern `uuuu-MM-dd'T'HH:mm:ssZ`.
|
||||
-/
|
||||
def iso8601 : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm.ssZ")
|
||||
|
||||
/--
|
||||
The americanDate format, which follows the pattern `MM-dd-uuuu`.
|
||||
-/
|
||||
def americanDate : GenericFormat .any := datespec("MM-dd-uuuu")
|
||||
|
||||
/--
|
||||
The europeanDate format, which follows the pattern `dd-MM-uuuu`.
|
||||
-/
|
||||
def europeanDate : GenericFormat .any := datespec("dd-MM-uuuu")
|
||||
|
||||
/--
|
||||
The time12Hour format, which follows the pattern `hh:mm:ss aa` for representing time
|
||||
in a 12-hour clock format with an upper case AM/PM marker.
|
||||
-/
|
||||
def time12Hour : GenericFormat .any := datespec("hh:mm:ss aa")
|
||||
|
||||
/--
|
||||
The Time24Hour format, which follows the pattern `HH:mm:ss` for representing time
|
||||
in a 24-hour clock format.
|
||||
-/
|
||||
def time24Hour : GenericFormat .any := datespec("HH:mm:ss")
|
||||
|
||||
/--
|
||||
The DateTimeZone24Hour format, which follows the pattern `uuuu-MM-dd:HH:mm:ss.SSSSSSSSS` for
|
||||
representing date, time, and time zone.
|
||||
-/
|
||||
def dateTime24Hour : GenericFormat (.only .GMT) := datespec("uuuu-MM-dd:HH:mm:ss.SSSSSSSSS")
|
||||
|
||||
/--
|
||||
The DateTimeWithZone format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZ`
|
||||
for representing date, time, and time zone.
|
||||
-/
|
||||
def dateTimeWithZone : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZ")
|
||||
|
||||
/--
|
||||
The leanTime24Hour format, which follows the pattern `HH:mm:ss.SSSSSSSSS` for representing time
|
||||
in a 24-hour clock format. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanTime24Hour : GenericFormat .any := datespec("HH:mm:ss.SSSSSSSSS")
|
||||
|
||||
/--
|
||||
The leanTime24HourNoNanos format, which follows the pattern `HH:mm:ss` for representing time
|
||||
in a 24-hour clock format. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanTime24HourNoNanos : GenericFormat .any := datespec("HH:mm:ss")
|
||||
|
||||
/--
|
||||
The leanDateTime24Hour format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS` for
|
||||
representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTime24Hour : GenericFormat (.only .GMT) := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS")
|
||||
|
||||
/--
|
||||
The leanDateTime24HourNoNanos format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss` for
|
||||
representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTime24HourNoNanos : GenericFormat (.only .GMT) := datespec("uuuu-MM-dd'T'HH:mm:ss")
|
||||
|
||||
/--
|
||||
The leanDateTimeWithZone format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZZZ`
|
||||
for representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTimeWithZone : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZZZZZ")
|
||||
|
||||
/--
|
||||
The leanDateTimeWithZoneNoNanos format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ssZZZZZ`
|
||||
for representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTimeWithZoneNoNanos : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ssZZZZZ")
|
||||
|
||||
/--
|
||||
The leanDateTimeWithIdentifier format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss[z]`
|
||||
for representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTimeWithIdentifier : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss'['zzzz']'")
|
||||
|
||||
/--
|
||||
The leanDateTimeWithIdentifierAndNanos format, which follows the pattern `uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS'[z]'`
|
||||
for representing date, time, and time zone. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDateTimeWithIdentifierAndNanos : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS'['zzzz']'")
|
||||
|
||||
/--
|
||||
The Lean Date format, which follows the pattern `uuuu-MM-dd`. It uses the default value that can be parsed with the
|
||||
notation of dates.
|
||||
-/
|
||||
def leanDate : GenericFormat .any := datespec("uuuu-MM-dd")
|
||||
|
||||
/--
|
||||
The SQLDate format, which follows the pattern `uuuu-MM-dd` and is commonly used
|
||||
in SQL databases to represent dates.
|
||||
-/
|
||||
def sqlDate : GenericFormat .any := datespec("uuuu-MM-dd")
|
||||
|
||||
/--
|
||||
The LongDateFormat, which follows the pattern `EEEE, MMMM D, uuuu HH:mm:ss` for
|
||||
representing a full date and time with the day of the week and month name.
|
||||
-/
|
||||
def longDateFormat : GenericFormat (.only .GMT) := datespec("EEEE, MMMM D, uuuu HH:mm:ss")
|
||||
|
||||
/--
|
||||
The AscTime format, which follows the pattern `EEE MMM d HH:mm:ss uuuu`. This format
|
||||
is often used in older systems for logging and time-stamping events.
|
||||
-/
|
||||
def ascTime : GenericFormat (.only .GMT) := datespec("EEE MMM d HH:mm:ss uuuu")
|
||||
|
||||
/--
|
||||
The RFC822 format, which follows the pattern `eee, dd MMM uuuu HH:mm:ss ZZZ`.
|
||||
This format is used in email headers and HTTP headers.
|
||||
-/
|
||||
def rfc822 : GenericFormat .any := datespec("eee, dd MMM uuuu HH:mm:ss ZZZ")
|
||||
|
||||
/--
|
||||
The RFC850 format, which follows the pattern `eee, dd-MMM-YY HH:mm:ss ZZZ`.
|
||||
This format is an older standard for representing date and time in headers.
|
||||
-/
|
||||
def rfc850 : GenericFormat .any := datespec("eee, dd-MM-uuuu HH:mm:ss ZZZ")
|
||||
|
||||
end Formats
|
||||
|
||||
namespace TimeZone
|
||||
|
||||
/--
|
||||
Parses a string into a `TimeZone` object. The input string must be in the format `"VV ZZZZZ"`.
|
||||
-/
|
||||
def fromTimeZone (input : String) : Except String TimeZone := do
|
||||
let spec : GenericFormat .any := datespec("VV ZZZZZ")
|
||||
spec.parseBuilder (fun id off => some (TimeZone.mk off id (off.toIsoString true) false)) input
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Parses a string representing an offset into an `Offset` object. The input string must follow the `"xxx"` format.
|
||||
-/
|
||||
def fromOffset (input : String) : Except String Offset := do
|
||||
let spec : GenericFormat .any := datespec("xxx")
|
||||
spec.parseBuilder some input
|
||||
|
||||
end Offset
|
||||
end TimeZone
|
||||
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Formats a `PlainDate` using a specific format.
|
||||
-/
|
||||
def format (date : PlainDate) (format : String) : String :=
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec format
|
||||
match format with
|
||||
| .error err => s!"error: {err}"
|
||||
| .ok res =>
|
||||
let res := res.formatGeneric fun
|
||||
| .G _ => some date.era
|
||||
| .y _ => some date.year
|
||||
| .u _ => some date.year
|
||||
| .D _ => some (Sigma.mk date.year.isLeap date.dayOfYear)
|
||||
| .Qorq _ => some date.quarter
|
||||
| .w _ => some date.weekOfYear
|
||||
| .W _ => some date.alignedWeekOfMonth
|
||||
| .MorL _ => some date.month
|
||||
| .d _ => some date.day
|
||||
| .E _ => some date.weekday
|
||||
| .eorc _ => some date.weekday
|
||||
| .F _ => some date.weekOfMonth
|
||||
| _ => none
|
||||
match res with
|
||||
| some res => res
|
||||
| none => "invalid time"
|
||||
|
||||
/--
|
||||
Parses a date string in the American format (`MM-dd-uuuu`) and returns a `PlainDate`.
|
||||
-/
|
||||
def fromAmericanDateString (input : String) : Except String PlainDate := do
|
||||
Formats.americanDate.parseBuilder (fun m d y => PlainDate.ofYearMonthDay? y m d) input
|
||||
|
||||
/--
|
||||
Converts a date in the American format (`MM-dd-uuuu`) into a `String`.
|
||||
-/
|
||||
def toAmericanDateString (input : PlainDate) : String :=
|
||||
Formats.americanDate.formatBuilder input.month input.day input.year
|
||||
|
||||
/--
|
||||
Parses a date string in the SQL format (`uuuu-MM-dd`) and returns a `PlainDate`.
|
||||
-/
|
||||
def fromSQLDateString (input : String) : Except String PlainDate := do
|
||||
Formats.sqlDate.parseBuilder PlainDate.ofYearMonthDay? input
|
||||
|
||||
/--
|
||||
Converts a date in the SQL format (`uuuu-MM-dd`) into a `String`.
|
||||
-/
|
||||
def toSQLDateString (input : PlainDate) : String :=
|
||||
Formats.sqlDate.formatBuilder input.year input.month input.day
|
||||
|
||||
/--
|
||||
Parses a date string in the Lean format (`uuuu-MM-dd`) and returns a `PlainDate`.
|
||||
-/
|
||||
def fromLeanDateString (input : String) : Except String PlainDate := do
|
||||
Formats.leanDate.parseBuilder PlainDate.ofYearMonthDay? input
|
||||
|
||||
/--
|
||||
Converts a date in the Lean format (`uuuu-MM-dd`) into a `String`.
|
||||
-/
|
||||
def toLeanDateString (input : PlainDate) : String :=
|
||||
Formats.leanDate.formatBuilder input.year input.month input.day
|
||||
|
||||
/--
|
||||
Parses a `String` in the `AmericanDate` or `SQLDate` format and returns a `PlainDate`.
|
||||
-/
|
||||
def parse (input : String) : Except String PlainDate :=
|
||||
fromAmericanDateString input
|
||||
<|> fromSQLDateString input
|
||||
|
||||
instance : ToString PlainDate where
|
||||
toString := toLeanDateString
|
||||
|
||||
instance : Repr PlainDate where
|
||||
reprPrec data := Repr.addAppParen ("date(\"" ++ toLeanDateString data ++ "\")")
|
||||
|
||||
end PlainDate
|
||||
|
||||
namespace PlainTime
|
||||
|
||||
/--
|
||||
Formats a `PlainTime` using a specific format.
|
||||
-/
|
||||
def format (time : PlainTime) (format : String) : String :=
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec format
|
||||
match format with
|
||||
| .error err => s!"error: {err}"
|
||||
| .ok res =>
|
||||
let res := res.formatGeneric fun
|
||||
| .H _ => some time.hour
|
||||
| .k _ => some (time.hour.shiftTo1BasedHour)
|
||||
| .m _ => some time.minute
|
||||
| .n _ => some time.nanosecond
|
||||
| .s _ => some time.second
|
||||
| .a _ => some (HourMarker.ofOrdinal time.hour)
|
||||
| .h _ => some time.hour.toRelative
|
||||
| .K _ => some (time.hour.emod 12 (by decide))
|
||||
| .S _ => some time.nanosecond
|
||||
| .A _ => some time.toMilliseconds
|
||||
| .N _ => some time.toNanoseconds
|
||||
| _ => none
|
||||
match res with
|
||||
| some res => res
|
||||
| none => "invalid time"
|
||||
|
||||
/--
|
||||
Parses a time string in the 24-hour format (`HH:mm:ss`) and returns a `PlainTime`.
|
||||
-/
|
||||
def fromTime24Hour (input : String) : Except String PlainTime :=
|
||||
Formats.time24Hour.parseBuilder (fun h m s => some (PlainTime.ofHourMinuteSeconds h m s.snd)) input
|
||||
|
||||
/--
|
||||
Formats a `PlainTime` value into a 24-hour format string (`HH:mm:ss`).
|
||||
-/
|
||||
def toTime24Hour (input : PlainTime) : String :=
|
||||
Formats.time24Hour.formatBuilder input.hour input.minute input.second
|
||||
|
||||
/--
|
||||
Parses a time string in the lean 24-hour format (`HH:mm:ss.SSSSSSSSS` or `HH:mm:ss`) and returns a `PlainTime`.
|
||||
-/
|
||||
def fromLeanTime24Hour (input : String) : Except String PlainTime :=
|
||||
Formats.leanTime24Hour.parseBuilder (fun h m s n => some (PlainTime.ofHourMinuteSecondsNano h m s.snd n)) input
|
||||
<|> Formats.leanTime24HourNoNanos.parseBuilder (fun h m s => some (PlainTime.ofHourMinuteSecondsNano h m s.snd 0)) input
|
||||
|
||||
/--
|
||||
Formats a `PlainTime` value into a 24-hour format string (`HH:mm:ss.SSSSSSSSS`).
|
||||
-/
|
||||
def toLeanTime24Hour (input : PlainTime) : String :=
|
||||
Formats.leanTime24Hour.formatBuilder input.hour input.minute input.second input.nanosecond
|
||||
|
||||
/--
|
||||
Parses a time string in the 12-hour format (`hh:mm:ss aa`) and returns a `PlainTime`.
|
||||
-/
|
||||
def fromTime12Hour (input : String) : Except String PlainTime := do
|
||||
let builder h m s a : Option PlainTime := do
|
||||
let value ← Internal.Bounded.ofInt? h.val
|
||||
some <| PlainTime.ofHourMinuteSeconds (HourMarker.toAbsolute a value) m s.snd
|
||||
|
||||
Formats.time12Hour.parseBuilder builder input
|
||||
|
||||
/--
|
||||
Formats a `PlainTime` value into a 12-hour format string (`hh:mm:ss aa`).
|
||||
-/
|
||||
def toTime12Hour (input : PlainTime) : String :=
|
||||
Formats.time12Hour.formatBuilder (input.hour.emod 12 (by decide) |>.add 1) input.minute input.second (if input.hour.val ≥ 12 then HourMarker.pm else HourMarker.am)
|
||||
|
||||
/--
|
||||
Parses a `String` in the `Time12Hour` or `Time24Hour` format and returns a `PlainTime`.
|
||||
-/
|
||||
def parse (input : String) : Except String PlainTime :=
|
||||
fromTime12Hour input
|
||||
<|> fromTime24Hour input
|
||||
|
||||
instance : ToString PlainTime where
|
||||
toString := toLeanTime24Hour
|
||||
|
||||
instance : Repr PlainTime where
|
||||
reprPrec data := Repr.addAppParen ("time(\"" ++ toLeanTime24Hour data ++ "\")")
|
||||
|
||||
end PlainTime
|
||||
|
||||
namespace ZonedDateTime
|
||||
|
||||
/--
|
||||
Formats a `ZonedDateTime` using a specific format.
|
||||
-/
|
||||
def format (data: ZonedDateTime) (format : String) : String :=
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec format
|
||||
match format with
|
||||
| .error err => s!"error: {err}"
|
||||
| .ok res => res.format data.toDateTime
|
||||
|
||||
/--
|
||||
Parses a `String` in the `ISO8601` format and returns a `ZonedDateTime`.
|
||||
-/
|
||||
def fromISO8601String (input : String) : Except String ZonedDateTime :=
|
||||
Formats.iso8601.parse input
|
||||
|
||||
/--
|
||||
Formats a `ZonedDateTime` value into an ISO8601 string.
|
||||
-/
|
||||
def toISO8601String (date : ZonedDateTime) : String :=
|
||||
Formats.iso8601.format date.toDateTime
|
||||
|
||||
/--
|
||||
Parses a `String` in the rfc822 format and returns a `ZonedDateTime`.
|
||||
-/
|
||||
def fromRFC822String (input : String) : Except String ZonedDateTime :=
|
||||
Formats.rfc822.parse input
|
||||
|
||||
/--
|
||||
Formats a `ZonedDateTime` value into an RFC822 format string.
|
||||
-/
|
||||
def toRFC822String (date : ZonedDateTime) : String :=
|
||||
Formats.rfc822.format date.toDateTime
|
||||
|
||||
/--
|
||||
Parses a `String` in the rfc850 format and returns a `ZonedDateTime`.
|
||||
-/
|
||||
def fromRFC850String (input : String) : Except String ZonedDateTime :=
|
||||
Formats.rfc850.parse input
|
||||
|
||||
/--
|
||||
Formats a `ZonedDateTime` value into an RFC850 format string.
|
||||
-/
|
||||
def toRFC850String (date : ZonedDateTime) : String :=
|
||||
Formats.rfc850.format date.toDateTime
|
||||
|
||||
/--
|
||||
Parses a `String` in the dateTimeWithZone format and returns a `ZonedDateTime` object in the GMT time zone.
|
||||
-/
|
||||
def fromDateTimeWithZoneString (input : String) : Except String ZonedDateTime :=
|
||||
Formats.dateTimeWithZone.parse input
|
||||
|
||||
/--
|
||||
Formats a `ZonedDateTime` value into a simple date time with timezone string.
|
||||
-/
|
||||
def toDateTimeWithZoneString (pdt : ZonedDateTime) : String :=
|
||||
Formats.dateTimeWithZone.format pdt.toDateTime
|
||||
|
||||
/--
|
||||
Parses a `String` in the lean date time format with timezone format and returns a `ZonedDateTime` object.
|
||||
-/
|
||||
def fromLeanDateTimeWithZoneString (input : String) : Except String ZonedDateTime :=
|
||||
Formats.leanDateTimeWithZone.parse input
|
||||
<|> Formats.leanDateTimeWithZoneNoNanos.parse input
|
||||
|
||||
/--
|
||||
Parses a `String` in the lean date time format with identifier and returns a `ZonedDateTime` object.
|
||||
-/
|
||||
def fromLeanDateTimeWithIdentifierString (input : String) : Except String ZonedDateTime :=
|
||||
Formats.leanDateTimeWithIdentifier.parse input
|
||||
<|> Formats.leanDateTimeWithIdentifierAndNanos.parse input
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into a simple date time with timezone string that can be parsed by the date% notation.
|
||||
-/
|
||||
def toLeanDateTimeWithZoneString (zdt : ZonedDateTime) : String :=
|
||||
Formats.leanDateTimeWithZone.formatBuilder zdt.year zdt.month zdt.day zdt.hour zdt.minute zdt.date.get.time.second zdt.nanosecond zdt.offset
|
||||
/--
|
||||
Formats a `DateTime` value into a simple date time with timezone string that can be parsed by the date% notation with the timezone identifier.
|
||||
-/
|
||||
def toLeanDateTimeWithIdentifierString (zdt : ZonedDateTime) : String :=
|
||||
Formats.leanDateTimeWithIdentifierAndNanos.formatBuilder zdt.year zdt.month zdt.day zdt.hour zdt.minute zdt.date.get.time.second zdt.nanosecond zdt.timezone.name
|
||||
|
||||
/--
|
||||
Parses a `String` in the `ISO8601`, `RFC822` or `RFC850` format and returns a `ZonedDateTime`.
|
||||
-/
|
||||
def parse (input : String) : Except String ZonedDateTime :=
|
||||
fromISO8601String input
|
||||
<|> fromRFC822String input
|
||||
<|> fromRFC850String input
|
||||
<|> fromDateTimeWithZoneString input
|
||||
<|> fromLeanDateTimeWithIdentifierString input
|
||||
|
||||
instance : ToString ZonedDateTime where
|
||||
toString := toLeanDateTimeWithIdentifierString
|
||||
|
||||
instance : Repr ZonedDateTime where
|
||||
reprPrec data := Repr.addAppParen ("zoned(\"" ++ toLeanDateTimeWithZoneString data ++ "\")")
|
||||
|
||||
end ZonedDateTime
|
||||
|
||||
namespace PlainDateTime
|
||||
|
||||
/--
|
||||
Formats a `PlainDateTime` using a specific format.
|
||||
-/
|
||||
def format (date : PlainDateTime) (format : String) : String :=
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec format
|
||||
match format with
|
||||
| .error err => s!"error: {err}"
|
||||
| .ok res =>
|
||||
let res := res.formatGeneric fun
|
||||
| .G _ => some date.era
|
||||
| .y _ => some date.year
|
||||
| .u _ => some date.year
|
||||
| .D _ => some (Sigma.mk date.year.isLeap date.dayOfYear)
|
||||
| .Qorq _ => some date.quarter
|
||||
| .w _ => some date.weekOfYear
|
||||
| .W _ => some date.alignedWeekOfMonth
|
||||
| .MorL _ => some date.month
|
||||
| .d _ => some date.day
|
||||
| .E _ => some date.weekday
|
||||
| .eorc _ => some date.weekday
|
||||
| .F _ => some date.weekOfMonth
|
||||
| .H _ => some date.hour
|
||||
| .k _ => some date.hour.shiftTo1BasedHour
|
||||
| .m _ => some date.minute
|
||||
| .n _ => some date.nanosecond
|
||||
| .s _ => some date.time.second
|
||||
| .a _ => some (HourMarker.ofOrdinal date.hour)
|
||||
| .h _ => some date.hour.toRelative
|
||||
| .K _ => some (date.hour.emod 12 (by decide))
|
||||
| .S _ => some date.nanosecond
|
||||
| .A _ => some date.time.toMilliseconds
|
||||
| .N _ => some date.time.toNanoseconds
|
||||
| _ => none
|
||||
match res with
|
||||
| some res => res
|
||||
| none => "invalid time"
|
||||
|
||||
/--
|
||||
Parses a `String` in the `AscTime` format and returns a `PlainDateTime` object in the GMT time zone.
|
||||
-/
|
||||
def fromAscTimeString (input : String) : Except String PlainDateTime :=
|
||||
Formats.ascTime.parse input
|
||||
|>.map DateTime.toPlainDateTime
|
||||
|
||||
/--
|
||||
Formats a `PlainDateTime` value into an AscTime format string.
|
||||
-/
|
||||
def toAscTimeString (pdt : PlainDateTime) : String :=
|
||||
Formats.ascTime.format (DateTime.ofPlainDateTimeAssumingUTC pdt .UTC)
|
||||
|
||||
/--
|
||||
Parses a `String` in the `LongDateFormat` and returns a `PlainDateTime` object in the GMT time zone.
|
||||
-/
|
||||
def fromLongDateFormatString (input : String) : Except String PlainDateTime :=
|
||||
Formats.longDateFormat.parse input
|
||||
|>.map DateTime.toPlainDateTime
|
||||
|
||||
/--
|
||||
Formats a `PlainDateTime` value into a LongDateFormat string.
|
||||
-/
|
||||
def toLongDateFormatString (pdt : PlainDateTime) : String :=
|
||||
Formats.longDateFormat.format (DateTime.ofPlainDateTimeAssumingUTC pdt .UTC)
|
||||
|
||||
/--
|
||||
Parses a `String` in the `DateTime` format and returns a `PlainDateTime`.
|
||||
-/
|
||||
def fromDateTimeString (input : String) : Except String PlainDateTime :=
|
||||
Formats.dateTime24Hour.parse input
|
||||
|>.map DateTime.toPlainDateTime
|
||||
|
||||
/--
|
||||
Formats a `PlainDateTime` value into a `DateTime` format string.
|
||||
-/
|
||||
def toDateTimeString (pdt : PlainDateTime) : String :=
|
||||
Formats.dateTime24Hour.formatBuilder pdt.year pdt.month pdt.day pdt.hour pdt.minute pdt.time.second pdt.nanosecond
|
||||
|
||||
/--
|
||||
Parses a `String` in the `DateTime` format and returns a `PlainDateTime`.
|
||||
-/
|
||||
def fromLeanDateTimeString (input : String) : Except String PlainDateTime :=
|
||||
(Formats.leanDateTime24Hour.parse input <|> Formats.leanDateTime24HourNoNanos.parse input)
|
||||
|>.map DateTime.toPlainDateTime
|
||||
|
||||
/--
|
||||
Formats a `PlainDateTime` value into a `DateTime` format string.
|
||||
-/
|
||||
def toLeanDateTimeString (pdt : PlainDateTime) : String :=
|
||||
Formats.leanDateTime24Hour.formatBuilder pdt.year pdt.month pdt.day pdt.hour pdt.minute pdt.time.second pdt.nanosecond
|
||||
|
||||
/--
|
||||
Parses a `String` in the `AscTime` or `LongDate` format and returns a `PlainDateTime`.
|
||||
-/
|
||||
def parse (date : String) : Except String PlainDateTime :=
|
||||
fromAscTimeString date
|
||||
<|> fromLongDateFormatString date
|
||||
<|> fromDateTimeString date
|
||||
<|> fromLeanDateTimeString date
|
||||
|
||||
instance : ToString PlainDateTime where
|
||||
toString := toLeanDateTimeString
|
||||
|
||||
instance : Repr PlainDateTime where
|
||||
reprPrec data := Repr.addAppParen ("datetime(\"" ++ toLeanDateTimeString data ++ "\")")
|
||||
|
||||
end PlainDateTime
|
||||
|
||||
namespace DateTime
|
||||
|
||||
/--
|
||||
Formats a `DateTime` using a specific format.
|
||||
-/
|
||||
def format (data: DateTime tz) (format : String) : String :=
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec format
|
||||
match format with
|
||||
| .error err => s!"error: {err}"
|
||||
| .ok res => res.format data
|
||||
|
||||
/--
|
||||
Parses a `String` in the `AscTime` format and returns a `DateTime` object in the GMT time zone.
|
||||
-/
|
||||
def fromAscTimeString (input : String) : Except String (DateTime .GMT) :=
|
||||
Formats.ascTime.parse input
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into an AscTime format string.
|
||||
-/
|
||||
def toAscTimeString (datetime : DateTime .GMT) : String :=
|
||||
Formats.ascTime.format datetime
|
||||
|
||||
/--
|
||||
Parses a `String` in the `LongDateFormat` and returns a `DateTime` object in the GMT time zone.
|
||||
-/
|
||||
def fromLongDateFormatString (input : String) : Except String (DateTime .GMT) :=
|
||||
Formats.longDateFormat.parse input
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into a LongDateFormat string.
|
||||
-/
|
||||
def toLongDateFormatString (datetime : DateTime .GMT) : String :=
|
||||
Formats.longDateFormat.format datetime
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into an ISO8601 string.
|
||||
-/
|
||||
def toISO8601String (date : DateTime tz) : String :=
|
||||
Formats.iso8601.format date
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into an RFC822 format string.
|
||||
-/
|
||||
def toRFC822String (date : DateTime tz) : String :=
|
||||
Formats.rfc822.format date
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into an RFC850 format string.
|
||||
-/
|
||||
def toRFC850String (date : DateTime tz) : String :=
|
||||
Formats.rfc850.format date
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into a `DateTimeWithZone` format string.
|
||||
-/
|
||||
def toDateTimeWithZoneString (pdt : DateTime tz) : String :=
|
||||
Formats.dateTimeWithZone.format pdt
|
||||
|
||||
/--
|
||||
Formats a `DateTime` value into a `DateTimeWithZone` format string that can be parsed by `date%`.
|
||||
-/
|
||||
def toLeanDateTimeWithZoneString (pdt : DateTime tz) : String :=
|
||||
Formats.leanDateTimeWithZone.format pdt
|
||||
|
||||
/--
|
||||
Parses a `String` in the `AscTime` or `LongDate` format and returns a `DateTime`.
|
||||
-/
|
||||
def parse (date : String) : Except String (DateTime .GMT) :=
|
||||
fromAscTimeString date
|
||||
<|> fromLongDateFormatString date
|
||||
|
||||
instance : Repr (DateTime tz) where
|
||||
reprPrec data := Repr.addAppParen (toLeanDateTimeWithZoneString data)
|
||||
|
||||
instance : ToString (DateTime tz) where
|
||||
toString := toLeanDateTimeWithZoneString
|
||||
|
||||
end DateTime
|
||||
1488
src/Std/Time/Format/Basic.lean
Normal file
1488
src/Std/Time/Format/Basic.lean
Normal file
File diff suppressed because it is too large
Load Diff
8
src/Std/Time/Internal.lean
Normal file
8
src/Std/Time/Internal.lean
Normal file
@@ -0,0 +1,8 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Internal.Bounded
|
||||
import Std.Time.Internal.UnitVal
|
||||
474
src/Std/Time/Internal/Bounded.lean
Normal file
474
src/Std/Time/Internal/Bounded.lean
Normal file
@@ -0,0 +1,474 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Int
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Internal
|
||||
|
||||
set_option linter.all true in
|
||||
|
||||
/--
|
||||
A `Bounded` is represented by an `Int` that is constrained by a lower and higher bounded using some
|
||||
relation `rel`. It includes all the integers that `rel lo val ∧ rel val hi`.
|
||||
-/
|
||||
def Bounded (rel : Int → Int → Prop) (lo : Int) (hi : Int) := { val : Int // rel lo val ∧ rel val hi }
|
||||
|
||||
namespace Bounded
|
||||
|
||||
@[always_inline]
|
||||
instance : LE (Bounded rel n m) where
|
||||
le l r := l.val ≤ r.val
|
||||
|
||||
@[always_inline]
|
||||
instance : LT (Bounded rel n m) where
|
||||
lt l r := l.val < r.val
|
||||
|
||||
@[always_inline]
|
||||
instance : Repr (Bounded rel m n) where
|
||||
reprPrec n := reprPrec n.val
|
||||
|
||||
@[always_inline]
|
||||
instance : BEq (Bounded rel n m) where
|
||||
beq x y := (x.val = y.val)
|
||||
|
||||
@[always_inline]
|
||||
instance {x y : Bounded rel a b} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
/--
|
||||
A `Bounded` integer that the relation used is the the less-equal relation so, it includes all
|
||||
integers that `lo ≤ val ≤ hi`.
|
||||
-/
|
||||
abbrev LE := @Bounded LE.le
|
||||
|
||||
/--
|
||||
Casts the boundaries of the `Bounded` using equivalences.
|
||||
-/
|
||||
@[inline]
|
||||
def cast {rel : Int → Int → Prop} {lo₁ lo₂ hi₁ hi₂ : Int} (h₁ : lo₁ = lo₂) (h₂ : hi₁ = hi₂) (b : Bounded rel lo₁ hi₁) : Bounded rel lo₂ hi₂ :=
|
||||
.mk b.val ⟨h₁ ▸ b.property.1, h₂ ▸ b.property.2⟩
|
||||
|
||||
/--
|
||||
A `Bounded` integer that the relation used is the the less-than relation so, it includes all
|
||||
integers that `lo < val < hi`.
|
||||
-/
|
||||
abbrev LT := @Bounded LT.lt
|
||||
|
||||
/--
|
||||
Creates a new `Bounded` Integer.
|
||||
-/
|
||||
@[inline]
|
||||
def mk {rel : Int → Int → Prop} (val : Int) (proof : rel lo val ∧ rel val hi) : @Bounded rel lo hi :=
|
||||
⟨val, proof⟩
|
||||
|
||||
/--
|
||||
Convert a `Int` to a `Bounded` if it checks.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt? [DecidableRel rel] (val : Int) : Option (Bounded rel lo hi) :=
|
||||
if h : rel lo ↑val ∧ rel ↑val hi then
|
||||
some ⟨val, h⟩
|
||||
else
|
||||
none
|
||||
|
||||
namespace LE
|
||||
|
||||
/--
|
||||
Convert a `Nat` to a `Bounded.LE` by wrapping it.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNatWrapping { lo hi : Int } (val : Int) (h : lo ≤ hi) : Bounded.LE lo hi := by
|
||||
let range := hi - lo + 1
|
||||
have range_pos := Int.add_pos_of_nonneg_of_pos (b := 1) (Int.sub_nonneg_of_le h) (by decide)
|
||||
have not_zero := Int.ne_iff_lt_or_gt.mpr (Or.inl range_pos)
|
||||
have mod_nonneg : 0 ≤ (val - lo) % range := Int.emod_nonneg (val - lo) not_zero.symm
|
||||
have add_nonneg : lo ≤ lo + (val - lo) % range := Int.le_add_of_nonneg_right mod_nonneg
|
||||
have mod_range : (val - lo) % (hi - lo + 1) < range := Int.emod_lt_of_pos (a := val - lo) range_pos
|
||||
refine ⟨((val - lo) % range + range) % range + lo, And.intro ?_ ?_⟩
|
||||
· simp_all [range]
|
||||
rw [Int.add_comm] at add_nonneg
|
||||
exact add_nonneg
|
||||
· apply Int.add_le_of_le_sub_right
|
||||
simp_all [range]
|
||||
exact Int.le_of_lt_add_one mod_range
|
||||
|
||||
instance {k : Nat} : OfNat (Bounded.LE lo (lo + k)) n where
|
||||
ofNat :=
|
||||
let h : lo ≤ lo + k := Int.le_add_of_nonneg_right (Int.ofNat_zero_le k)
|
||||
ofNatWrapping n h
|
||||
|
||||
instance {k : Nat} : Inhabited (Bounded.LE lo (lo + k)) where
|
||||
default :=
|
||||
let h : lo ≤ lo + k := Int.le_add_of_nonneg_right (Int.ofNat_zero_le k)
|
||||
ofNatWrapping lo h
|
||||
|
||||
/--
|
||||
Creates a new `Bounded` integer that the relation is less-equal.
|
||||
-/
|
||||
@[inline]
|
||||
def mk (val : Int) (proof : lo ≤ val ∧ val ≤ hi) : Bounded.LE lo hi :=
|
||||
⟨val, proof⟩
|
||||
|
||||
/--
|
||||
Creates a new `Bounded` integer that the relation is less-equal.
|
||||
-/
|
||||
@[inline]
|
||||
def exact (val : Nat) : Bounded.LE val val :=
|
||||
⟨val, by simp⟩
|
||||
|
||||
/--
|
||||
Creates a new `Bounded` integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt { lo hi : Int } (val : Int) : Option (Bounded.LE lo hi) :=
|
||||
if h : lo ≤ val ∧ val ≤ hi
|
||||
then some ⟨val, h⟩
|
||||
else none
|
||||
|
||||
/--
|
||||
Convert a `Nat` to a `Bounded.LE`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (val : Nat) (h : val ≤ hi) : Bounded.LE 0 hi :=
|
||||
Bounded.mk val (And.intro (Int.ofNat_zero_le val) (Int.ofNat_le.mpr h))
|
||||
|
||||
/--
|
||||
Convert a `Nat` to a `Bounded.LE` if it checks.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat? { hi : Nat } (val : Nat) : Option (Bounded.LE 0 hi) :=
|
||||
if h : val ≤ hi then
|
||||
ofNat val h
|
||||
else
|
||||
none
|
||||
|
||||
/--
|
||||
Convert a `Nat` to a `Bounded.LE` using the lower boundary too.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat' (val : Nat) (h : lo ≤ val ∧ val ≤ hi) : Bounded.LE lo hi :=
|
||||
Bounded.mk val (And.intro (Int.ofNat_le.mpr h.left) (Int.ofNat_le.mpr h.right))
|
||||
|
||||
/--
|
||||
Convert a `Nat` to a `Bounded.LE` using the lower boundary too.
|
||||
-/
|
||||
@[inline]
|
||||
def clip (val : Int) (h : lo ≤ hi) : Bounded.LE lo hi :=
|
||||
if h₀ : lo ≤ val then
|
||||
if h₁ : val ≤ hi
|
||||
then ⟨val, And.intro h₀ h₁⟩
|
||||
else ⟨hi, And.intro h (Int.le_refl hi)⟩
|
||||
else ⟨lo, And.intro (Int.le_refl lo) h⟩
|
||||
|
||||
/--
|
||||
Convert a `Bounded.LE` to a Nat.
|
||||
-/
|
||||
@[inline]
|
||||
def toNat (n : Bounded.LE lo hi) : Nat :=
|
||||
n.val.toNat
|
||||
|
||||
/--
|
||||
Convert a `Bounded.LE` to a Nat.
|
||||
-/
|
||||
@[inline]
|
||||
def toNat' (n : Bounded.LE lo hi) (h : lo ≥ 0) : Nat :=
|
||||
let h₁ := (Int.le_trans h n.property.left)
|
||||
match n.val, h₁ with
|
||||
| .ofNat n, _ => n
|
||||
| .negSucc _, h => by contradiction
|
||||
|
||||
/--
|
||||
Convert a `Bounded.LE` to an Int.
|
||||
-/
|
||||
@[inline]
|
||||
def toInt (n : Bounded.LE lo hi) : Int :=
|
||||
n.val
|
||||
|
||||
/--
|
||||
Convert a `Bounded.LE` to a `Fin`.
|
||||
-/
|
||||
@[inline, simp]
|
||||
def toFin (n : Bounded.LE lo hi) (h₀ : 0 ≤ lo) : Fin (hi + 1).toNat := by
|
||||
let h := n.property.right
|
||||
let h₁ := Int.le_trans h₀ n.property.left
|
||||
refine ⟨n.val.toNat, (Int.toNat_lt h₁).mpr ?_⟩
|
||||
rw [Int.toNat_of_nonneg (by omega)]
|
||||
exact Int.lt_add_one_of_le h
|
||||
|
||||
/--
|
||||
Convert a `Fin` to a `Bounded.LE`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (fin : Fin (Nat.succ hi)) : Bounded.LE 0 hi :=
|
||||
ofNat fin.val (Nat.le_of_lt_succ fin.isLt)
|
||||
|
||||
/--
|
||||
Convert a `Fin` to a `Bounded.LE`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin' {lo : Nat} (fin : Fin (Nat.succ hi)) (h : lo ≤ hi) : Bounded.LE lo hi :=
|
||||
if h₁ : fin.val ≥ lo
|
||||
then ofNat' fin.val (And.intro h₁ ((Nat.le_of_lt_succ fin.isLt)))
|
||||
else ofNat' lo (And.intro (Nat.le_refl lo) h)
|
||||
|
||||
/--
|
||||
Creates a new `Bounded.LE` using a the modulus of a number.
|
||||
-/
|
||||
@[inline]
|
||||
def byEmod (b : Int) (i : Int) (hi : i > 0) : Bounded.LE 0 (i - 1) := by
|
||||
refine ⟨b % i, And.intro ?_ ?_⟩
|
||||
· apply Int.emod_nonneg b
|
||||
intro a
|
||||
simp_all [Int.lt_irrefl]
|
||||
· apply Int.le_of_lt_add_one
|
||||
simp [Int.add_sub_assoc]
|
||||
exact Int.emod_lt_of_pos b hi
|
||||
|
||||
/--
|
||||
Creates a new `Bounded.LE` using a the Truncating modulus of a number.
|
||||
-/
|
||||
@[inline]
|
||||
def byMod (b : Int) (i : Int) (hi : 0 < i) : Bounded.LE (- (i - 1)) (i - 1) := by
|
||||
refine ⟨b.tmod i, And.intro ?_ ?_⟩
|
||||
· simp [Int.tmod]
|
||||
split <;> try contradiction
|
||||
next m n =>
|
||||
let h := Int.emod_nonneg (a := m) (b := n) (Int.ne_of_gt hi)
|
||||
apply (Int.le_trans · h)
|
||||
apply Int.le_of_neg_le_neg
|
||||
simp_all
|
||||
exact (Int.le_sub_one_of_lt hi)
|
||||
next m n =>
|
||||
apply Int.neg_le_neg
|
||||
have h := Int.tmod_lt_of_pos (m + 1) hi
|
||||
exact Int.le_sub_one_of_lt h
|
||||
· exact Int.le_sub_one_of_lt (Int.tmod_lt_of_pos b hi)
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by setting the lower bound to zero and the maximum value to (m - n).
|
||||
-/
|
||||
@[inline]
|
||||
def truncate (bounded : Bounded.LE n m) : Bounded.LE 0 (m - n) := by
|
||||
let ⟨left, right⟩ := bounded.property
|
||||
refine ⟨bounded.val - n, And.intro ?_ ?_⟩
|
||||
all_goals omega
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by changing the higher bound if another value `j` satisfies the same
|
||||
constraint.
|
||||
-/
|
||||
@[inline, simp]
|
||||
def truncateTop (bounded : Bounded.LE n m) (h : bounded.val ≤ j) : Bounded.LE n j := by
|
||||
refine ⟨bounded.val, And.intro ?_ ?_⟩
|
||||
· exact bounded.property.left
|
||||
· exact h
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by changing the lower bound if another value `j` satisfies the same
|
||||
constraint.
|
||||
-/
|
||||
@[inline]
|
||||
def truncateBottom (bounded : Bounded.LE n m) (h : bounded.val ≥ j) : Bounded.LE j m := by
|
||||
refine ⟨bounded.val, And.intro ?_ ?_⟩
|
||||
· exact h
|
||||
· exact bounded.property.right
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by adding a constant value to both the lower and upper bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def neg (bounded : Bounded.LE n m) : Bounded.LE (-m) (-n) := by
|
||||
refine ⟨-bounded.val, And.intro ?_ ?_⟩
|
||||
· exact Int.neg_le_neg bounded.property.right
|
||||
· exact Int.neg_le_neg bounded.property.left
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by adding a constant value to both the lower and upper bounds.
|
||||
-/
|
||||
@[inline, simp]
|
||||
def add (bounded : Bounded.LE n m) (num : Int) : Bounded.LE (n + num) (m + num) := by
|
||||
refine ⟨bounded.val + num, And.intro ?_ ?_⟩
|
||||
all_goals apply (Int.add_le_add · (Int.le_refl num))
|
||||
· exact bounded.property.left
|
||||
· exact bounded.property.right
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by adding a constant value to both the lower and upper bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def addProven (bounded : Bounded.LE n m) (h₀ : bounded.val + num ≤ m) (h₁ : num ≥ 0) : Bounded.LE n m := by
|
||||
refine ⟨bounded.val + num, And.intro ?_ ?_⟩
|
||||
· exact Int.le_trans bounded.property.left (Int.le_add_of_nonneg_right h₁)
|
||||
· exact h₀
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by adding a constant value to the upper bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def addTop (bounded : Bounded.LE n m) (num : Int) (h : num ≥ 0) : Bounded.LE n (m + num) := by
|
||||
refine ⟨bounded.val + num, And.intro ?_ ?_⟩
|
||||
· let h := Int.add_le_add bounded.property.left h
|
||||
simp at h
|
||||
exact h
|
||||
· exact Int.add_le_add bounded.property.right (Int.le_refl num)
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by adding a constant value to the lower bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def subBottom (bounded : Bounded.LE n m) (num : Int) (h : num ≥ 0) : Bounded.LE (n - num) m := by
|
||||
refine ⟨bounded.val - num, And.intro ?_ ?_⟩
|
||||
· exact Int.add_le_add bounded.property.left (Int.le_refl (-num))
|
||||
· let h := Int.sub_le_sub bounded.property.right h
|
||||
simp at h
|
||||
exact h
|
||||
|
||||
/--
|
||||
Adds two `Bounded` and adjust the boundaries.
|
||||
-/
|
||||
@[inline]
|
||||
def addBounds (bounded : Bounded.LE n m) (bounded₂ : Bounded.LE i j) : Bounded.LE (n + i) (m + j) := by
|
||||
refine ⟨bounded.val + bounded₂.val, And.intro ?_ ?_⟩
|
||||
· exact Int.add_le_add bounded.property.left bounded₂.property.left
|
||||
· exact Int.add_le_add bounded.property.right bounded₂.property.right
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by subtracting a constant value to both the lower and upper bounds.
|
||||
-/
|
||||
@[inline, simp]
|
||||
def sub (bounded : Bounded.LE n m) (num : Int) : Bounded.LE (n - num) (m - num) :=
|
||||
add bounded (-num)
|
||||
|
||||
/--
|
||||
Adds two `Bounded` and adjust the boundaries.
|
||||
-/
|
||||
@[inline]
|
||||
def subBounds (bounded : Bounded.LE n m) (bounded₂ : Bounded.LE i j) : Bounded.LE (n - j) (m - i) :=
|
||||
addBounds bounded bounded₂.neg
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by applying the emod operation constraining the lower bound to 0 and
|
||||
the upper bound to the value.
|
||||
-/
|
||||
@[inline]
|
||||
def emod (bounded : Bounded.LE n num) (num : Int) (hi : 0 < num) : Bounded.LE 0 (num - 1) :=
|
||||
byEmod bounded.val num hi
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by applying the mod operation.
|
||||
-/
|
||||
@[inline]
|
||||
def mod (bounded : Bounded.LE n num) (num : Int) (hi : 0 < num) : Bounded.LE (- (num - 1)) (num - 1) :=
|
||||
byMod bounded.val num hi
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by applying the multiplication operation with a positive number.
|
||||
-/
|
||||
@[inline]
|
||||
def mul_pos (bounded : Bounded.LE n m) (num : Int) (h : num ≥ 0) : Bounded.LE (n * num) (m * num) := by
|
||||
refine ⟨bounded.val * num, And.intro ?_ ?_⟩
|
||||
· exact Int.mul_le_mul_of_nonneg_right bounded.property.left h
|
||||
· exact Int.mul_le_mul_of_nonneg_right bounded.property.right h
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by applying the multiplication operation with a positive number.
|
||||
-/
|
||||
@[inline]
|
||||
def mul_neg (bounded : Bounded.LE n m) (num : Int) (h : num ≤ 0) : Bounded.LE (m * num) (n * num) := by
|
||||
refine ⟨bounded.val * num, And.intro ?_ ?_⟩
|
||||
· exact Int.mul_le_mul_of_nonpos_right bounded.property.right h
|
||||
· exact Int.mul_le_mul_of_nonpos_right bounded.property.left h
|
||||
|
||||
/--
|
||||
Adjust the bounds of a `Bounded` by applying the div operation.
|
||||
-/
|
||||
@[inline]
|
||||
def ediv (bounded : Bounded.LE n m) (num : Int) (h : num > 0) : Bounded.LE (n / num) (m / num) := by
|
||||
let ⟨left, right⟩ := bounded.property
|
||||
refine ⟨bounded.val.ediv num, And.intro ?_ ?_⟩
|
||||
apply Int.ediv_le_ediv
|
||||
· exact h
|
||||
· exact left
|
||||
· apply Int.ediv_le_ediv
|
||||
· exact h
|
||||
· exact right
|
||||
|
||||
@[inline]
|
||||
def eq {n : Int} : Bounded.LE n n :=
|
||||
⟨n, And.intro (Int.le_refl n) (Int.le_refl n)⟩
|
||||
|
||||
/--
|
||||
Expand the range of a bounded value.
|
||||
-/
|
||||
@[inline]
|
||||
def expand (bounded : Bounded.LE lo hi) (h : hi ≤ nhi) (h₁ : nlo ≤ lo) : Bounded.LE nlo nhi :=
|
||||
⟨bounded.val, And.intro (Int.le_trans h₁ bounded.property.left) (Int.le_trans bounded.property.right h)⟩
|
||||
|
||||
/--
|
||||
Expand the bottom of the bounded to a number `nhi` is `hi` is less or equal to the previous higher bound.
|
||||
-/
|
||||
@[inline]
|
||||
def expandTop (bounded : Bounded.LE lo hi) (h : hi ≤ nhi) : Bounded.LE lo nhi :=
|
||||
expand bounded h (Int.le_refl lo)
|
||||
|
||||
/--
|
||||
Expand the bottom of the bounded to a number `nlo` if `lo` is greater or equal to the previous lower bound.
|
||||
-/
|
||||
@[inline]
|
||||
def expandBottom (bounded : Bounded.LE lo hi) (h : nlo ≤ lo) : Bounded.LE nlo hi :=
|
||||
expand bounded (Int.le_refl hi) h
|
||||
|
||||
/--
|
||||
Adds one to the value of the bounded if the value is less than the higher bound of the bounded number.
|
||||
-/
|
||||
@[inline]
|
||||
def succ (bounded : Bounded.LE lo hi) (h : bounded.val < hi) : Bounded.LE lo hi :=
|
||||
let left := bounded.property.left
|
||||
⟨bounded.val + 1, And.intro (by omega) (by omega)⟩
|
||||
|
||||
/--
|
||||
Returns the absolute value of the bounded number `bo` with bounds `-(i - 1)` to `i - 1`. The result
|
||||
will be a new bounded number with bounds `0` to `i - 1`.
|
||||
-/
|
||||
@[inline]
|
||||
def abs (bo : Bounded.LE (-i) i) : Bounded.LE 0 i :=
|
||||
if h : bo.val ≥ 0 then
|
||||
bo.truncateBottom h
|
||||
else by
|
||||
let r := bo.truncateTop (Int.le_of_lt (Int.not_le.mp h)) |>.neg
|
||||
rw [Int.neg_neg] at r
|
||||
exact r
|
||||
|
||||
/--
|
||||
Returns the maximum between a number and the bounded.
|
||||
-/
|
||||
def max (bounded : Bounded.LE n m) (val : Int) : Bounded.LE (Max.max n val) (Max.max m val) := by
|
||||
let ⟨left, right⟩ := bounded.property
|
||||
refine ⟨Max.max bounded.val val, And.intro ?_ ?_⟩
|
||||
|
||||
all_goals
|
||||
simp [Int.max_def]
|
||||
split <;> split
|
||||
|
||||
next h => simp [h, Int.le_trans left h]
|
||||
next h h₁ => exact Int.le_of_lt <| Int.not_le.mp h₁
|
||||
next h => simp [h, Int.le_trans left h]
|
||||
next h h₁ => exact left
|
||||
next h h₁ => simp [h, Int.le_trans left h]
|
||||
next h h₁ => exact Int.le_of_lt <| Int.not_le.mp h₁
|
||||
next h h₁ =>
|
||||
let h₃ := Int.lt_of_lt_of_le (Int.not_le.mp h) right
|
||||
let h₄ := Int.not_le.mpr h₃ h₁
|
||||
contradiction
|
||||
next h h₁ => exact right
|
||||
|
||||
end LE
|
||||
end Bounded
|
||||
end Internal
|
||||
end Time
|
||||
end Std
|
||||
125
src/Std/Time/Internal/UnitVal.lean
Normal file
125
src/Std/Time/Internal/UnitVal.lean
Normal file
@@ -0,0 +1,125 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Init.Data
|
||||
import Std.Internal.Rat
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Internal
|
||||
open Std.Internal
|
||||
open Lean
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
A structure representing a unit of a given ratio type `α`.
|
||||
-/
|
||||
structure UnitVal (α : Rat) where
|
||||
/--
|
||||
Creates a `UnitVal` from an `Int`.
|
||||
-/
|
||||
ofInt ::
|
||||
|
||||
/--
|
||||
Value inside the UnitVal Value.
|
||||
-/
|
||||
val : Int
|
||||
deriving Inhabited, BEq
|
||||
|
||||
instance : LE (UnitVal x) where
|
||||
le x y := x.val ≤ y.val
|
||||
|
||||
instance { x y : UnitVal z }: Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
namespace UnitVal
|
||||
|
||||
/--
|
||||
Creates a `UnitVal` from a `Nat`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (value : Nat) : UnitVal α :=
|
||||
⟨value⟩
|
||||
|
||||
/--
|
||||
Converts a `UnitVal` to an `Int`.
|
||||
-/
|
||||
@[inline]
|
||||
def toInt (unit : UnitVal α) : Int :=
|
||||
unit.val
|
||||
|
||||
/--
|
||||
Multiplies the `UnitVal` by an `Int`, resulting in a new `UnitVal` with an adjusted ratio.
|
||||
-/
|
||||
@[inline]
|
||||
def mul (unit : UnitVal a) (factor : Int) : UnitVal (a / factor) :=
|
||||
⟨unit.val * factor⟩
|
||||
|
||||
/--
|
||||
Divides the `UnitVal` by an `Int`, resulting in a new `UnitVal` with an adjusted ratio.
|
||||
-/
|
||||
@[inline]
|
||||
def ediv (unit : UnitVal a) (divisor : Int) : UnitVal (a * divisor) :=
|
||||
⟨unit.val.ediv divisor⟩
|
||||
|
||||
/--
|
||||
Divides the `UnitVal` by an `Int`, resulting in a new `UnitVal` with an adjusted ratio.
|
||||
-/
|
||||
@[inline]
|
||||
def div (unit : UnitVal a) (divisor : Int) : UnitVal (a * divisor) :=
|
||||
⟨unit.val.tdiv divisor⟩
|
||||
|
||||
/--
|
||||
Adds two `UnitVal` values of the same ratio.
|
||||
-/
|
||||
@[inline]
|
||||
def add (u1 : UnitVal α) (u2 : UnitVal α) : UnitVal α :=
|
||||
⟨u1.val + u2.val⟩
|
||||
|
||||
/--
|
||||
Subtracts one `UnitVal` value from another of the same ratio.
|
||||
-/
|
||||
@[inline]
|
||||
def sub (u1 : UnitVal α) (u2 : UnitVal α) : UnitVal α :=
|
||||
⟨u1.val - u2.val⟩
|
||||
|
||||
/--
|
||||
Returns the absolute value of a `UnitVal`.
|
||||
-/
|
||||
@[inline]
|
||||
def abs (u : UnitVal α) : UnitVal α :=
|
||||
⟨u.val.natAbs⟩
|
||||
|
||||
/--
|
||||
Converts an `Offset` to another unit type.
|
||||
-/
|
||||
@[inline]
|
||||
def convert (val : UnitVal a) : UnitVal b :=
|
||||
let ratio := a.div b
|
||||
ofInt <| val.toInt * ratio.num / ratio.den
|
||||
|
||||
instance : OfNat (UnitVal α) n where ofNat := ⟨Int.ofNat n⟩
|
||||
|
||||
instance : Repr (UnitVal α) where reprPrec x p := reprPrec x.val p
|
||||
|
||||
instance : LE (UnitVal α) where le x y := x.val ≤ y.val
|
||||
|
||||
instance : LT (UnitVal α) where lt x y := x.val < y.val
|
||||
|
||||
instance : Add (UnitVal α) where add := UnitVal.add
|
||||
|
||||
instance : Sub (UnitVal α) where sub := UnitVal.sub
|
||||
|
||||
instance : Neg (UnitVal α) where neg x := ⟨-x.val⟩
|
||||
|
||||
instance : ToString (UnitVal n) where toString n := toString n.val
|
||||
|
||||
|
||||
end UnitVal
|
||||
end Internal
|
||||
end Time
|
||||
end Std
|
||||
246
src/Std/Time/Notation.lean
Normal file
246
src/Std/Time/Notation.lean
Normal file
@@ -0,0 +1,246 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date
|
||||
import Std.Time.Time
|
||||
import Std.Time.Zoned
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Format
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Lean Parser Command Std
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
private def convertText : Text → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.Text.short)
|
||||
| .full => `(Std.Time.Text.full)
|
||||
| .narrow => `(Std.Time.Text.narrow)
|
||||
|
||||
private def convertNumber : Number → MacroM (TSyntax `term)
|
||||
| ⟨padding⟩ => `(Std.Time.Number.mk $(quote padding))
|
||||
|
||||
private def convertFraction : Fraction → MacroM (TSyntax `term)
|
||||
| .nano => `(Std.Time.Fraction.nano)
|
||||
| .truncated digits => `(Std.Time.Fraction.truncated $(quote digits))
|
||||
|
||||
private def convertYear : Year → MacroM (TSyntax `term)
|
||||
| .twoDigit => `(Std.Time.Year.twoDigit)
|
||||
| .fourDigit => `(Std.Time.Year.fourDigit)
|
||||
| .extended n => `(Std.Time.Year.extended $(quote n))
|
||||
|
||||
private def convertZoneName : ZoneName → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.ZoneName.short)
|
||||
| .full => `(Std.Time.ZoneName.full)
|
||||
|
||||
private def convertOffsetX : OffsetX → MacroM (TSyntax `term)
|
||||
| .hour => `(Std.Time.OffsetX.hour)
|
||||
| .hourMinute => `(Std.Time.OffsetX.hourMinute)
|
||||
| .hourMinuteColon => `(Std.Time.OffsetX.hourMinuteColon)
|
||||
| .hourMinuteSecond => `(Std.Time.OffsetX.hourMinuteSecond)
|
||||
| .hourMinuteSecondColon => `(Std.Time.OffsetX.hourMinuteSecondColon)
|
||||
|
||||
private def convertOffsetO : OffsetO → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.OffsetO.short)
|
||||
| .full => `(Std.Time.OffsetO.full)
|
||||
|
||||
private def convertOffsetZ : OffsetZ → MacroM (TSyntax `term)
|
||||
| .hourMinute => `(Std.Time.OffsetZ.hourMinute)
|
||||
| .full => `(Std.Time.OffsetZ.full)
|
||||
| .hourMinuteSecondColon => `(Std.Time.OffsetZ.hourMinuteSecondColon)
|
||||
|
||||
private def convertModifier : Modifier → MacroM (TSyntax `term)
|
||||
| .G p => do `(Std.Time.Modifier.G $(← convertText p))
|
||||
| .y p => do `(Std.Time.Modifier.y $(← convertYear p))
|
||||
| .u p => do `(Std.Time.Modifier.u $(← convertYear p))
|
||||
| .D p => do `(Std.Time.Modifier.D $(← convertNumber p))
|
||||
| .MorL p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.MorL (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.MorL (.inr $(← convertText txt)))
|
||||
| .d p => do `(Std.Time.Modifier.d $(← convertNumber p))
|
||||
| .Qorq p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.Qorq (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.Qorq (.inr $(← convertText txt)))
|
||||
| .w p => do `(Std.Time.Modifier.w $(← convertNumber p))
|
||||
| .W p => do `(Std.Time.Modifier.W $(← convertNumber p))
|
||||
| .E p => do `(Std.Time.Modifier.E $(← convertText p))
|
||||
| .eorc p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.eorc (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.eorc (.inr $(← convertText txt)))
|
||||
| .F p => do `(Std.Time.Modifier.F $(← convertNumber p))
|
||||
| .a p => do `(Std.Time.Modifier.a $(← convertText p))
|
||||
| .h p => do `(Std.Time.Modifier.h $(← convertNumber p))
|
||||
| .K p => do `(Std.Time.Modifier.K $(← convertNumber p))
|
||||
| .k p => do `(Std.Time.Modifier.k $(← convertNumber p))
|
||||
| .H p => do `(Std.Time.Modifier.H $(← convertNumber p))
|
||||
| .m p => do `(Std.Time.Modifier.m $(← convertNumber p))
|
||||
| .s p => do `(Std.Time.Modifier.s $(← convertNumber p))
|
||||
| .S p => do `(Std.Time.Modifier.S $(← convertFraction p))
|
||||
| .A p => do `(Std.Time.Modifier.A $(← convertNumber p))
|
||||
| .n p => do `(Std.Time.Modifier.n $(← convertNumber p))
|
||||
| .N p => do `(Std.Time.Modifier.N $(← convertNumber p))
|
||||
| .V => `(Std.Time.Modifier.V)
|
||||
| .z p => do `(Std.Time.Modifier.z $(← convertZoneName p))
|
||||
| .O p => do `(Std.Time.Modifier.O $(← convertOffsetO p))
|
||||
| .X p => do `(Std.Time.Modifier.X $(← convertOffsetX p))
|
||||
| .x p => do `(Std.Time.Modifier.x $(← convertOffsetX p))
|
||||
| .Z p => do `(Std.Time.Modifier.Z $(← convertOffsetZ p))
|
||||
|
||||
private def convertFormatPart : FormatPart → MacroM (TSyntax `term)
|
||||
| .string s => `(.string $(Syntax.mkStrLit s))
|
||||
| .modifier mod => do `(.modifier $(← convertModifier mod))
|
||||
|
||||
private def syntaxNat (n : Nat) : MacroM (TSyntax `term) := do
|
||||
let info ← MonadRef.mkInfoFromRefPos
|
||||
pure { raw := Syntax.node1 info `num (Lean.Syntax.atom info (toString n)) }
|
||||
|
||||
private def syntaxString (n : String) : MacroM (TSyntax `term) := do
|
||||
let info ← MonadRef.mkInfoFromRefPos
|
||||
pure { raw := Syntax.node1 info `str (Lean.Syntax.atom info (toString n)) }
|
||||
|
||||
private def syntaxInt (n : Int) : MacroM (TSyntax `term) := do
|
||||
match n with
|
||||
| .ofNat n => `(Int.ofNat $(Syntax.mkNumLit <| toString n))
|
||||
| .negSucc n => `(Int.negSucc $(Syntax.mkNumLit <| toString n))
|
||||
|
||||
private def syntaxBounded (n : Int) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.Internal.Bounded.LE.ofNatWrapping $(← syntaxInt n) (by decide))
|
||||
|
||||
private def syntaxVal (n : Int) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.Internal.UnitVal.ofInt $(← syntaxInt n))
|
||||
|
||||
private def convertOffset (offset : Std.Time.TimeZone.Offset) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.TimeZone.Offset.ofSeconds $(← syntaxVal offset.second.val))
|
||||
|
||||
private def convertTimezone (tz : Std.Time.TimeZone) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.TimeZone.mk $(← convertOffset tz.offset) $(Syntax.mkStrLit tz.name) $(Syntax.mkStrLit tz.abbreviation) false)
|
||||
|
||||
private def convertPlainDate (d : Std.Time.PlainDate) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.PlainDate.ofYearMonthDayClip $(← syntaxInt d.year) $(← syntaxBounded d.month.val) $(← syntaxBounded d.day.val))
|
||||
|
||||
private def convertPlainTime (d : Std.Time.PlainTime) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.PlainTime.mk $(← syntaxBounded d.hour.val) $(← syntaxBounded d.minute.val) ⟨true, $(← syntaxBounded d.second.snd.val)⟩ $(← syntaxBounded d.nanosecond.val))
|
||||
|
||||
private def convertPlainDateTime (d : Std.Time.PlainDateTime) : MacroM (TSyntax `term) := do
|
||||
`(Std.Time.PlainDateTime.mk $(← convertPlainDate d.date) $(← convertPlainTime d.time))
|
||||
|
||||
private def convertZonedDateTime (d : Std.Time.ZonedDateTime) (identifier := false) : MacroM (TSyntax `term) := do
|
||||
let plain ← convertPlainDateTime d.toPlainDateTime
|
||||
|
||||
if identifier then
|
||||
`(Std.Time.ZonedDateTime.ofPlainDateTime $plain <$> Std.Time.Database.defaultGetZoneRules $(Syntax.mkStrLit d.timezone.name))
|
||||
else
|
||||
`(Std.Time.ZonedDateTime.ofPlainDateTime $plain (Std.Time.TimeZone.ZoneRules.ofTimeZone $(← convertTimezone d.timezone)))
|
||||
|
||||
/--
|
||||
Defines a syntax for zoned datetime values. It expects a string representing a datetime with
|
||||
timezone information.
|
||||
|
||||
Example:
|
||||
`zoned("2024-10-13T15:00:00-03:00")`
|
||||
-/
|
||||
syntax "zoned(" str ")" : term
|
||||
|
||||
/--
|
||||
Defines a syntax for zoned datetime values. It expects a string representing a datetime and a
|
||||
timezone information as a term.
|
||||
|
||||
Example:
|
||||
`zoned("2024-10-13T15:00:00", timezone)`
|
||||
-/
|
||||
syntax "zoned(" str "," term ")" : term
|
||||
|
||||
|
||||
/--
|
||||
Defines a syntax for datetime values without timezone. The input should be a string in an
|
||||
ISO8601-like format.
|
||||
|
||||
Example:
|
||||
`datetime("2024-10-13T15:00:00")`
|
||||
-/
|
||||
syntax "datetime(" str ")" : term
|
||||
|
||||
/--
|
||||
Defines a syntax for date-only values. The input string represents a date in formats like "YYYY-MM-DD".
|
||||
|
||||
Example:
|
||||
`date("2024-10-13")`
|
||||
-/
|
||||
syntax "date(" str ")" : term
|
||||
|
||||
/--
|
||||
Defines a syntax for time-only values. The string should represent a time, either in 24-hour or
|
||||
12-hour format.
|
||||
|
||||
Example:
|
||||
`time("15:00:00")` or `time("03:00:00 PM")`
|
||||
-/
|
||||
syntax "time(" str ")" : term
|
||||
|
||||
/--
|
||||
Defines a syntax for UTC offset values. The string should indicate the time difference from UTC
|
||||
(e.g., "-03:00").
|
||||
|
||||
Example:
|
||||
`offset("-03:00")`
|
||||
-/
|
||||
syntax "offset(" str ")" : term
|
||||
|
||||
/--
|
||||
Defines a syntax for timezone identifiers. The input string should be a valid timezone name or
|
||||
abbreviation.
|
||||
|
||||
Example:
|
||||
`timezone("America/Sao_Paulo")`
|
||||
-/
|
||||
syntax "timezone(" str ")" : term
|
||||
|
||||
|
||||
macro_rules
|
||||
| `(zoned( $date:str )) => do
|
||||
match ZonedDateTime.fromLeanDateTimeWithZoneString date.getString with
|
||||
| .ok res => do return ← convertZonedDateTime res
|
||||
| .error _ =>
|
||||
match ZonedDateTime.fromLeanDateTimeWithIdentifierString date.getString with
|
||||
| .ok res => do return ← convertZonedDateTime res (identifier := true)
|
||||
| .error res => Macro.throwErrorAt date s!"error: {res}"
|
||||
|
||||
| `(zoned( $date:str, $timezone )) => do
|
||||
match PlainDateTime.fromLeanDateTimeString date.getString with
|
||||
| .ok res => do
|
||||
let plain ← convertPlainDateTime res
|
||||
`(Std.Time.ZonedDateTime.ofPlainDateTime $plain $timezone)
|
||||
| .error res => Macro.throwErrorAt date s!"error: {res}"
|
||||
|
||||
| `(datetime( $date:str )) => do
|
||||
match PlainDateTime.fromLeanDateTimeString date.getString with
|
||||
| .ok res => do
|
||||
return ← convertPlainDateTime res
|
||||
| .error res => Macro.throwErrorAt date s!"error: {res}"
|
||||
|
||||
| `(date( $date:str )) => do
|
||||
match PlainDate.fromSQLDateString date.getString with
|
||||
| .ok res => return ← convertPlainDate res
|
||||
| .error res => Macro.throwErrorAt date s!"error: {res}"
|
||||
|
||||
| `(time( $time:str )) => do
|
||||
match PlainTime.fromLeanTime24Hour time.getString with
|
||||
| .ok res => return ← convertPlainTime res
|
||||
| .error res => Macro.throwErrorAt time s!"error: {res}"
|
||||
|
||||
| `(offset( $offset:str )) => do
|
||||
match TimeZone.Offset.fromOffset offset.getString with
|
||||
| .ok res => return ← convertOffset res
|
||||
| .error res => Macro.throwErrorAt offset s!"error: {res}"
|
||||
|
||||
| `(timezone( $tz:str )) => do
|
||||
match TimeZone.fromTimeZone tz.getString with
|
||||
| .ok res => return ← convertTimezone res
|
||||
| .error res => Macro.throwErrorAt tz s!"error: {res}"
|
||||
113
src/Std/Time/Notation/Spec.lean
Normal file
113
src/Std/Time/Notation/Spec.lean
Normal file
@@ -0,0 +1,113 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Date
|
||||
import Std.Time.Time
|
||||
import Std.Time.Zoned
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Format.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Lean Parser Command Std
|
||||
|
||||
private def convertText : Text → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.Text.short)
|
||||
| .full => `(Std.Time.Text.full)
|
||||
| .narrow => `(Std.Time.Text.narrow)
|
||||
|
||||
private def convertNumber : Number → MacroM (TSyntax `term)
|
||||
| ⟨padding⟩ => `(Std.Time.Number.mk $(quote padding))
|
||||
|
||||
private def convertFraction : Fraction → MacroM (TSyntax `term)
|
||||
| .nano => `(Std.Time.Fraction.nano)
|
||||
| .truncated digits => `(Std.Time.Fraction.truncated $(quote digits))
|
||||
|
||||
private def convertYear : Year → MacroM (TSyntax `term)
|
||||
| .twoDigit => `(Std.Time.Year.twoDigit)
|
||||
| .fourDigit => `(Std.Time.Year.fourDigit)
|
||||
| .extended n => `(Std.Time.Year.extended $(quote n))
|
||||
|
||||
private def convertZoneName : ZoneName → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.ZoneName.short)
|
||||
| .full => `(Std.Time.ZoneName.full)
|
||||
|
||||
private def convertOffsetX : OffsetX → MacroM (TSyntax `term)
|
||||
| .hour => `(Std.Time.OffsetX.hour)
|
||||
| .hourMinute => `(Std.Time.OffsetX.hourMinute)
|
||||
| .hourMinuteColon => `(Std.Time.OffsetX.hourMinuteColon)
|
||||
| .hourMinuteSecond => `(Std.Time.OffsetX.hourMinuteSecond)
|
||||
| .hourMinuteSecondColon => `(Std.Time.OffsetX.hourMinuteSecondColon)
|
||||
|
||||
private def convertOffsetO : OffsetO → MacroM (TSyntax `term)
|
||||
| .short => `(Std.Time.OffsetO.short)
|
||||
| .full => `(Std.Time.OffsetO.full)
|
||||
|
||||
private def convertOffsetZ : OffsetZ → MacroM (TSyntax `term)
|
||||
| .hourMinute => `(Std.Time.OffsetZ.hourMinute)
|
||||
| .full => `(Std.Time.OffsetZ.full)
|
||||
| .hourMinuteSecondColon => `(Std.Time.OffsetZ.hourMinuteSecondColon)
|
||||
|
||||
private def convertModifier : Modifier → MacroM (TSyntax `term)
|
||||
| .G p => do `(Std.Time.Modifier.G $(← convertText p))
|
||||
| .y p => do `(Std.Time.Modifier.y $(← convertYear p))
|
||||
| .u p => do `(Std.Time.Modifier.u $(← convertYear p))
|
||||
| .D p => do `(Std.Time.Modifier.D $(← convertNumber p))
|
||||
| .MorL p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.MorL (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.MorL (.inr $(← convertText txt)))
|
||||
| .d p => do `(Std.Time.Modifier.d $(← convertNumber p))
|
||||
| .Qorq p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.Qorq (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.Qorq (.inr $(← convertText txt)))
|
||||
| .w p => do `(Std.Time.Modifier.w $(← convertNumber p))
|
||||
| .W p => do `(Std.Time.Modifier.W $(← convertNumber p))
|
||||
| .E p => do `(Std.Time.Modifier.E $(← convertText p))
|
||||
| .eorc p =>
|
||||
match p with
|
||||
| .inl num => do `(Std.Time.Modifier.eorc (.inl $(← convertNumber num)))
|
||||
| .inr txt => do `(Std.Time.Modifier.eorc (.inr $(← convertText txt)))
|
||||
| .F p => do `(Std.Time.Modifier.F $(← convertNumber p))
|
||||
| .a p => do `(Std.Time.Modifier.a $(← convertText p))
|
||||
| .h p => do `(Std.Time.Modifier.h $(← convertNumber p))
|
||||
| .K p => do `(Std.Time.Modifier.K $(← convertNumber p))
|
||||
| .k p => do `(Std.Time.Modifier.k $(← convertNumber p))
|
||||
| .H p => do `(Std.Time.Modifier.H $(← convertNumber p))
|
||||
| .m p => do `(Std.Time.Modifier.m $(← convertNumber p))
|
||||
| .s p => do `(Std.Time.Modifier.s $(← convertNumber p))
|
||||
| .S p => do `(Std.Time.Modifier.S $(← convertFraction p))
|
||||
| .A p => do `(Std.Time.Modifier.A $(← convertNumber p))
|
||||
| .n p => do `(Std.Time.Modifier.n $(← convertNumber p))
|
||||
| .N p => do `(Std.Time.Modifier.N $(← convertNumber p))
|
||||
| .V => `(Std.Time.Modifier.V)
|
||||
| .z p => do `(Std.Time.Modifier.z $(← convertZoneName p))
|
||||
| .O p => do `(Std.Time.Modifier.O $(← convertOffsetO p))
|
||||
| .X p => do `(Std.Time.Modifier.X $(← convertOffsetX p))
|
||||
| .x p => do `(Std.Time.Modifier.x $(← convertOffsetX p))
|
||||
| .Z p => do `(Std.Time.Modifier.Z $(← convertOffsetZ p))
|
||||
|
||||
private def convertFormatPart : FormatPart → MacroM (TSyntax `term)
|
||||
| .string s => `(.string $(Syntax.mkStrLit s))
|
||||
| .modifier mod => do `(.modifier $(← convertModifier mod))
|
||||
|
||||
/--
|
||||
Syntax for defining a date spec at compile time.
|
||||
-/
|
||||
syntax "datespec(" str ")" : term
|
||||
|
||||
macro_rules
|
||||
| `(datespec( $format_string:str )) => do
|
||||
let input := format_string.getString
|
||||
let format : Except String (GenericFormat .any) := GenericFormat.spec input
|
||||
match format with
|
||||
| .ok res =>
|
||||
let alts ← res.string.mapM convertFormatPart
|
||||
let alts := alts.foldl Syntax.TSepArray.push (Syntax.TSepArray.mk #[] (sep := ","))
|
||||
`(⟨[$alts,*]⟩)
|
||||
| .error err =>
|
||||
Macro.throwErrorAt format_string s!"cannot compile spec: {err}"
|
||||
9
src/Std/Time/Time.lean
Normal file
9
src/Std/Time/Time.lean
Normal file
@@ -0,0 +1,9 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time.Basic
|
||||
import Std.Time.Time.HourMarker
|
||||
import Std.Time.Time.PlainTime
|
||||
7
src/Std/Time/Time/Basic.lean
Normal file
7
src/Std/Time/Time/Basic.lean
Normal file
@@ -0,0 +1,7 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time.Unit.Basic
|
||||
71
src/Std/Time/Time/HourMarker.lean
Normal file
71
src/Std/Time/Time/HourMarker.lean
Normal file
@@ -0,0 +1,71 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`HourMarker` represents the two 12-hour periods of the day: `am` for hours between 12:00 AM and
|
||||
11:59 AM, and `pm` for hours between 12:00 PM and 11:59 PM.
|
||||
-/
|
||||
inductive HourMarker
|
||||
|
||||
/--
|
||||
Ante meridiem.
|
||||
-/
|
||||
| am
|
||||
|
||||
/--
|
||||
Post meridiem.
|
||||
-/
|
||||
| pm
|
||||
deriving Repr, BEq
|
||||
|
||||
namespace HourMarker
|
||||
|
||||
/--
|
||||
`ofOrdinal` converts an `Hour.Ordinal` value to an `HourMarker`, indicating whether it is AM or PM.
|
||||
-/
|
||||
def ofOrdinal (time : Hour.Ordinal) : HourMarker :=
|
||||
if time.val ≥ 12 then
|
||||
.pm
|
||||
else
|
||||
.am
|
||||
|
||||
/--
|
||||
Converts a 12-hour clock time to a 24-hour clock time based on the `HourMarker`.
|
||||
-/
|
||||
def toAbsolute (marker : HourMarker) (time : Bounded.LE 1 12) : Hour.Ordinal :=
|
||||
match marker with
|
||||
| .am => if time.val = 12 then 0 else time.expand (by decide) (by decide)
|
||||
| .pm => if time.val = 12 then 12 else time.add 12 |>.emod 24 (by decide)
|
||||
|
||||
/--
|
||||
Converts a 24-hour clock time to a 12-hour clock time with a `HourMarker`.
|
||||
-/
|
||||
def toRelative (hour : Hour.Ordinal) : Bounded.LE 1 12 × HourMarker :=
|
||||
if h₀ : hour.val = 0 then
|
||||
(⟨12, by decide⟩, .am)
|
||||
else if h₁ : hour.val ≤ 12 then
|
||||
if hour.val = 12 then
|
||||
(⟨12, by decide⟩, .pm)
|
||||
else
|
||||
Int.ne_iff_lt_or_gt.mp h₀ |>.by_cases
|
||||
(nomatch Int.not_le.mpr · <| hour.property.left)
|
||||
(⟨hour.val, And.intro · h₁⟩, .am)
|
||||
else
|
||||
let h := Int.not_le.mp h₁
|
||||
let t := hour |>.truncateBottom h |>.sub 12
|
||||
(t.expandTop (by decide), .pm)
|
||||
|
||||
end HourMarker
|
||||
end Time
|
||||
end Std
|
||||
297
src/Std/Time/Time/PlainTime.lean
Normal file
297
src/Std/Time/Time/PlainTime.lean
Normal file
@@ -0,0 +1,297 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a specific point in a day, including hours, minutes, seconds, and nanoseconds.
|
||||
-/
|
||||
structure PlainTime where
|
||||
|
||||
/--
|
||||
`Hour` component of the `PlainTime`
|
||||
-/
|
||||
hour : Hour.Ordinal
|
||||
|
||||
/--
|
||||
`Minute` component of the `PlainTime`
|
||||
-/
|
||||
minute : Minute.Ordinal
|
||||
|
||||
/--
|
||||
`Second` component of the `PlainTime`
|
||||
-/
|
||||
second : Sigma Second.Ordinal
|
||||
|
||||
/--
|
||||
`Nanoseconds` component of the `PlainTime`
|
||||
-/
|
||||
nanosecond : Nanosecond.Ordinal
|
||||
deriving Repr
|
||||
|
||||
instance : Inhabited PlainTime where
|
||||
default := ⟨0, 0, Sigma.mk false 0, 0, by decide⟩
|
||||
|
||||
instance : BEq PlainTime where
|
||||
beq x y := x.hour.val == y.hour.val && x.minute == y.minute
|
||||
&& x.second.snd.val == y.second.snd.val && x.nanosecond == y.nanosecond
|
||||
|
||||
namespace PlainTime
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value representing midnight (00:00:00.000000000).
|
||||
-/
|
||||
def midnight : PlainTime :=
|
||||
⟨0, 0, ⟨true, 0⟩, 0⟩
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from the provided hours, minutes, seconds and nanoseconds components.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHourMinuteSecondsNano (hour : Hour.Ordinal) (minute : Minute.Ordinal) (second : Second.Ordinal leap) (nano : Nanosecond.Ordinal) : PlainTime :=
|
||||
⟨hour, minute, Sigma.mk leap second, nano⟩
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from the provided hours, minutes, and seconds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHourMinuteSeconds (hour : Hour.Ordinal) (minute : Minute.Ordinal) (second : Second.Ordinal leap) : PlainTime :=
|
||||
ofHourMinuteSecondsNano hour minute second 0
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` value to the total number of milliseconds.
|
||||
-/
|
||||
def toMilliseconds (time : PlainTime) : Millisecond.Offset :=
|
||||
time.hour.toOffset.toMilliseconds +
|
||||
time.minute.toOffset.toMilliseconds +
|
||||
time.second.snd.toOffset.toMilliseconds +
|
||||
time.nanosecond.toOffset.toMilliseconds
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` value to the total number of nanoseconds.
|
||||
-/
|
||||
def toNanoseconds (time : PlainTime) : Nanosecond.Offset :=
|
||||
time.hour.toOffset.toNanoseconds +
|
||||
time.minute.toOffset.toNanoseconds +
|
||||
time.second.snd.toOffset.toNanoseconds +
|
||||
time.nanosecond.toOffset
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` value to the total number of seconds.
|
||||
-/
|
||||
def toSeconds (time : PlainTime) : Second.Offset :=
|
||||
time.hour.toOffset.toSeconds +
|
||||
time.minute.toOffset.toSeconds +
|
||||
time.second.snd.toOffset
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` value to the total number of minutes.
|
||||
-/
|
||||
def toMinutes (time : PlainTime) : Minute.Offset :=
|
||||
time.hour.toOffset.toMinutes +
|
||||
time.minute.toOffset +
|
||||
time.second.snd.toOffset.toMinutes
|
||||
|
||||
/--
|
||||
Converts a `PlainTime` value to the total number of hours.
|
||||
-/
|
||||
def toHours (time : PlainTime) : Hour.Offset :=
|
||||
time.hour.toOffset
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from a total number of nanoseconds.
|
||||
-/
|
||||
def ofNanoseconds (nanos : Nanosecond.Offset) : PlainTime :=
|
||||
have totalSeconds := nanos.ediv 1000000000
|
||||
have remainingNanos := Bounded.LE.byEmod nanos.val 1000000000 (by decide)
|
||||
have hours := Bounded.LE.byEmod (totalSeconds.val / 3600) 24 (by decide)
|
||||
have minutes := (Bounded.LE.byEmod totalSeconds.val 3600 (by decide)).ediv 60 (by decide)
|
||||
have seconds := Bounded.LE.byEmod totalSeconds.val 60 (by decide)
|
||||
let nanos := Bounded.LE.byEmod nanos.val 1000000000 (by decide)
|
||||
PlainTime.mk hours minutes (Sigma.mk false seconds) nanos
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from a total number of millisecond.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (millis : Millisecond.Offset) : PlainTime :=
|
||||
ofNanoseconds millis.toNanoseconds
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from a total number of seconds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (secs : Second.Offset) : PlainTime :=
|
||||
ofNanoseconds secs.toNanoseconds
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from a total number of minutes.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (secs : Minute.Offset) : PlainTime :=
|
||||
ofNanoseconds secs.toNanoseconds
|
||||
|
||||
/--
|
||||
Creates a `PlainTime` value from a total number of hours.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (hour : Hour.Offset) : PlainTime :=
|
||||
ofNanoseconds hour.toNanoseconds
|
||||
|
||||
/--
|
||||
Adds seconds to a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addSeconds (time : PlainTime) (secondsToAdd : Second.Offset) : PlainTime :=
|
||||
let totalSeconds := time.toNanoseconds + secondsToAdd.toNanoseconds
|
||||
ofNanoseconds totalSeconds
|
||||
|
||||
/--
|
||||
Subtracts seconds from a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subSeconds (time : PlainTime) (secondsToSub : Second.Offset) : PlainTime :=
|
||||
addSeconds time (-secondsToSub)
|
||||
|
||||
/--
|
||||
Adds minutes to a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMinutes (time : PlainTime) (minutesToAdd : Minute.Offset) : PlainTime :=
|
||||
let total := time.toNanoseconds + minutesToAdd.toNanoseconds
|
||||
ofNanoseconds total
|
||||
|
||||
/--
|
||||
Subtracts minutes from a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMinutes (time : PlainTime) (minutesToSub : Minute.Offset) : PlainTime :=
|
||||
addMinutes time (-minutesToSub)
|
||||
|
||||
/--
|
||||
Adds hours to a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addHours (time : PlainTime) (hoursToAdd : Hour.Offset) : PlainTime :=
|
||||
let total := time.toNanoseconds + hoursToAdd.toNanoseconds
|
||||
ofNanoseconds total
|
||||
|
||||
/--
|
||||
Subtracts hours from a `PlainTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subHours (time : PlainTime) (hoursToSub : Hour.Offset) : PlainTime :=
|
||||
addHours time (-hoursToSub)
|
||||
|
||||
/--
|
||||
Adds nanoseconds to a `PlainTime`.
|
||||
-/
|
||||
def addNanoseconds (time : PlainTime) (nanosToAdd : Nanosecond.Offset) : PlainTime :=
|
||||
let total := time.toNanoseconds + nanosToAdd
|
||||
ofNanoseconds total
|
||||
|
||||
/--
|
||||
Subtracts nanoseconds from a `PlainTime`.
|
||||
-/
|
||||
def subNanoseconds (time : PlainTime) (nanosToSub : Nanosecond.Offset) : PlainTime :=
|
||||
addNanoseconds time (-nanosToSub)
|
||||
|
||||
/--
|
||||
Adds milliseconds to a `PlainTime`.
|
||||
-/
|
||||
def addMilliseconds (time : PlainTime) (millisToAdd : Millisecond.Offset) : PlainTime :=
|
||||
let total := time.toMilliseconds + millisToAdd
|
||||
ofMilliseconds total
|
||||
|
||||
/--
|
||||
Subtracts milliseconds from a `PlainTime`.
|
||||
-/
|
||||
def subMilliseconds (time : PlainTime) (millisToSub : Millisecond.Offset) : PlainTime :=
|
||||
addMilliseconds time (-millisToSub)
|
||||
|
||||
/--
|
||||
Creates a new `PlainTime` by adjusting the `second` component to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withSeconds (pt : PlainTime) (second : Sigma Second.Ordinal) : PlainTime :=
|
||||
{ pt with second := second }
|
||||
|
||||
/--
|
||||
Creates a new `PlainTime` by adjusting the `minute` component to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withMinutes (pt : PlainTime) (minute : Minute.Ordinal) : PlainTime :=
|
||||
{ pt with minute := minute }
|
||||
|
||||
/--
|
||||
Creates a new `PlainTime` by adjusting the milliseconds component inside the `nano` component of its `time` to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withMilliseconds (pt : PlainTime) (millis : Millisecond.Ordinal) : PlainTime :=
|
||||
let minorPart := pt.nanosecond.emod 1000 (by decide)
|
||||
let majorPart := millis.mul_pos 1000000 (by decide) |>.addBounds minorPart
|
||||
{ pt with nanosecond := majorPart |>.expandTop (by decide) }
|
||||
|
||||
/--
|
||||
Creates a new `PlainTime` by adjusting the `nano` component to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withNanoseconds (pt : PlainTime) (nano : Nanosecond.Ordinal) : PlainTime :=
|
||||
{ pt with nanosecond := nano }
|
||||
|
||||
/--
|
||||
Creates a new `PlainTime` by adjusting the `hour` component to the given value.
|
||||
-/
|
||||
@[inline]
|
||||
def withHours (pt : PlainTime) (hour : Hour.Ordinal) : PlainTime :=
|
||||
{ pt with hour := hour }
|
||||
|
||||
/--
|
||||
`Millisecond` component of the `PlainTime`
|
||||
-/
|
||||
@[inline]
|
||||
def millisecond (pt : PlainTime) : Millisecond.Ordinal :=
|
||||
pt.nanosecond.ediv 1000000 (by decide)
|
||||
|
||||
instance : HAdd PlainTime Nanosecond.Offset PlainTime where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub PlainTime Nanosecond.Offset PlainTime where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HAdd PlainTime Millisecond.Offset PlainTime where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub PlainTime Millisecond.Offset PlainTime where
|
||||
hSub := subMilliseconds
|
||||
|
||||
instance : HAdd PlainTime Second.Offset PlainTime where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub PlainTime Second.Offset PlainTime where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd PlainTime Minute.Offset PlainTime where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub PlainTime Minute.Offset PlainTime where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd PlainTime Hour.Offset PlainTime where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub PlainTime Hour.Offset PlainTime where
|
||||
hSub := subHours
|
||||
|
||||
end PlainTime
|
||||
end Time
|
||||
end Std
|
||||
329
src/Std/Time/Time/Unit/Basic.lean
Normal file
329
src/Std/Time/Time/Unit/Basic.lean
Normal file
@@ -0,0 +1,329 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Internal
|
||||
import Std.Time.Time.Unit.Hour
|
||||
import Std.Time.Time.Unit.Minute
|
||||
import Std.Time.Time.Unit.Second
|
||||
import Std.Time.Time.Unit.Nanosecond
|
||||
import Std.Time.Time.Unit.Millisecond
|
||||
|
||||
/-!
|
||||
This module defines various units used for measuring, counting, and converting between hours, minutes,
|
||||
second, nanosecond, millisecond and nanoseconds.
|
||||
|
||||
The units are organized into types representing these time-related concepts, with operations provided
|
||||
to facilitate conversions and manipulations between them.
|
||||
-/
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
namespace Nanosecond.Offset
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (offset : Nanosecond.Offset) : Millisecond.Offset :=
|
||||
offset.div 1000000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (offset : Millisecond.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 1000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (offset : Nanosecond.Offset) : Second.Offset :=
|
||||
offset.div 1000000000
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (offset : Second.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 1000000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (offset : Nanosecond.Offset) : Minute.Offset :=
|
||||
offset.div 60000000000
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (offset : Minute.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 60000000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (offset : Nanosecond.Offset) : Hour.Offset :=
|
||||
offset.div 3600000000000
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (offset : Hour.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 3600000000000
|
||||
|
||||
end Nanosecond.Offset
|
||||
|
||||
namespace Millisecond.Offset
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (offset : Millisecond.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 1000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (offset : Nanosecond.Offset) : Millisecond.Offset :=
|
||||
offset.div 1000000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (offset : Millisecond.Offset) : Second.Offset :=
|
||||
offset.div 1000
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (offset : Second.Offset) : Millisecond.Offset :=
|
||||
offset.mul 1000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (offset : Millisecond.Offset) : Minute.Offset :=
|
||||
offset.div 60000
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (offset : Minute.Offset) : Millisecond.Offset :=
|
||||
offset.mul 60000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (offset : Millisecond.Offset) : Hour.Offset :=
|
||||
offset.div 3600000
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (offset : Hour.Offset) : Millisecond.Offset :=
|
||||
offset.mul 3600000
|
||||
|
||||
end Millisecond.Offset
|
||||
|
||||
namespace Second.Offset
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (offset : Second.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 1000000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (offset : Nanosecond.Offset) : Second.Offset :=
|
||||
offset.div 1000000000
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (offset : Second.Offset) : Millisecond.Offset :=
|
||||
offset.mul 1000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (offset : Millisecond.Offset) : Second.Offset :=
|
||||
offset.div 1000
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (offset : Second.Offset) : Minute.Offset :=
|
||||
offset.div 60
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (offset : Minute.Offset) : Second.Offset :=
|
||||
offset.mul 60
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (offset : Second.Offset) : Hour.Offset :=
|
||||
offset.div 3600
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (offset : Hour.Offset) : Second.Offset :=
|
||||
offset.mul 3600
|
||||
|
||||
end Second.Offset
|
||||
|
||||
namespace Minute.Offset
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (offset : Minute.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 60000000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (offset : Nanosecond.Offset) : Minute.Offset :=
|
||||
offset.div 60000000000
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (offset : Minute.Offset) : Millisecond.Offset :=
|
||||
offset.mul 60000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (offset : Millisecond.Offset) : Minute.Offset :=
|
||||
offset.div 60000
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (offset : Minute.Offset) : Second.Offset :=
|
||||
offset.mul 60
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (offset : Second.Offset) : Minute.Offset :=
|
||||
offset.div 60
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toHours (offset : Minute.Offset) : Hour.Offset :=
|
||||
offset.div 60
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofHours (offset : Hour.Offset) : Minute.Offset :=
|
||||
offset.mul 60
|
||||
|
||||
end Minute.Offset
|
||||
|
||||
namespace Hour.Offset
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Nanosecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toNanoseconds (offset : Hour.Offset) : Nanosecond.Offset :=
|
||||
offset.mul 3600000000000
|
||||
|
||||
/--
|
||||
Converts a `Nanosecond.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNanoseconds (offset : Nanosecond.Offset) : Hour.Offset :=
|
||||
offset.div 3600000000000
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Millisecond.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMilliseconds (offset : Hour.Offset) : Millisecond.Offset :=
|
||||
offset.mul 3600000
|
||||
|
||||
/--
|
||||
Converts a `Millisecond.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMilliseconds (offset : Millisecond.Offset) : Hour.Offset :=
|
||||
offset.div 3600000
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toSeconds (offset : Hour.Offset) : Second.Offset :=
|
||||
offset.mul 3600
|
||||
|
||||
/--
|
||||
Converts a `Second.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofSeconds (offset : Second.Offset) : Hour.Offset :=
|
||||
offset.div 3600
|
||||
|
||||
/--
|
||||
Converts an `Hour.Offset` to a `Minute.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toMinutes (offset : Hour.Offset) : Minute.Offset :=
|
||||
offset.mul 60
|
||||
|
||||
/--
|
||||
Converts a `Minute.Offset` to an `Hour.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofMinutes (offset : Minute.Offset) : Hour.Offset :=
|
||||
offset.div 60
|
||||
|
||||
end Hour.Offset
|
||||
|
||||
end Time
|
||||
end Std
|
||||
111
src/Std/Time/Time/Unit/Hour.lean
Normal file
111
src/Std/Time/Time/Unit/Hour.lean
Normal file
@@ -0,0 +1,111 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Internal
|
||||
import Std.Time.Time.Unit.Minute
|
||||
import Std.Time.Time.Unit.Second
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Hour
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for hours, ranging from 0 to 23.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 0 23
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 0 (0 + (23 : Nat))) n)
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 0
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents an offset in hours, defined as an `Int`. This can be used to express durations
|
||||
or differences in hours.
|
||||
-/
|
||||
def Offset : Type := UnitVal 3600
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, ToString
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨UnitVal.ofNat n⟩
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 0 ≤ data ∧ data ≤ 23) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` into a relative hour in the range of 1 to 12.
|
||||
-/
|
||||
def toRelative (ordinal : Ordinal) : Bounded.LE 1 12 :=
|
||||
(ordinal.add 11).emod 12 (by decide) |>.add 1
|
||||
|
||||
/--
|
||||
Converts an Ordinal into a 1-based hour representation within the range of 1 to 24.
|
||||
-/
|
||||
def shiftTo1BasedHour (ordinal : Ordinal) : Bounded.LE 1 24 :=
|
||||
if h : ordinal.val < 1
|
||||
then Internal.Bounded.LE.ofNatWrapping 24 (by decide)
|
||||
else ordinal.truncateBottom (Int.not_lt.mp h) |>.expandTop (by decide)
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within the valid bounds for hours.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≤ 23) : Ordinal :=
|
||||
Bounded.LE.ofNat data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin` value.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 24) : Ordinal :=
|
||||
Bounded.LE.ofFin data
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`, which represents the duration in hours as an integer value.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
end Offset
|
||||
end Hour
|
||||
end Time
|
||||
end Std
|
||||
96
src/Std/Time/Time/Unit/Millisecond.lean
Normal file
96
src/Std/Time/Time/Unit/Millisecond.lean
Normal file
@@ -0,0 +1,96 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Internal
|
||||
import Std.Time.Time.Unit.Nanosecond
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Millisecond
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for milliseconds, ranging from 0 to 999 milliseconds.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 0 999
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 0 (0 + (999 : Nat))) n)
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 0
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents a duration offset in milliseconds.
|
||||
-/
|
||||
def Offset : Type := UnitVal (1 / 1000)
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨UnitVal.ofNat n⟩
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
end Offset
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 0 ≤ data ∧ data ≤ 999) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≤ 999) : Ordinal :=
|
||||
Bounded.LE.ofNat data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 1000) : Ordinal :=
|
||||
Bounded.LE.ofFin data
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
end Millisecond
|
||||
end Time
|
||||
end Std
|
||||
96
src/Std/Time/Time/Unit/Minute.lean
Normal file
96
src/Std/Time/Time/Unit/Minute.lean
Normal file
@@ -0,0 +1,96 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Internal
|
||||
import Std.Time.Time.Unit.Second
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Minute
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for minutes, ranging from 0 to 59. This is useful for representing the minute component of a time.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 0 59
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n :=
|
||||
inferInstanceAs (OfNat (Bounded.LE 0 (0 + (59 : Nat))) n)
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 0
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents a duration offset in minutes.
|
||||
-/
|
||||
def Offset : Type := UnitVal 60
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, ToString
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨UnitVal.ofInt <| Int.ofNat n⟩
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 0 ≤ data ∧ data ≤ 59) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≤ 59) : Ordinal :=
|
||||
Bounded.LE.ofNat data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 60) : Ordinal :=
|
||||
Bounded.LE.ofFin data
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
end Offset
|
||||
end Minute
|
||||
end Time
|
||||
end Std
|
||||
124
src/Std/Time/Time/Unit/Nanosecond.lean
Normal file
124
src/Std/Time/Time/Unit/Nanosecond.lean
Normal file
@@ -0,0 +1,124 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Internal
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Nanosecond
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a nanosecond value that is bounded between 0 and 999,999,999 nanoseconds.
|
||||
-/
|
||||
def Ordinal := Bounded.LE 0 999999999
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : OfNat Ordinal n where
|
||||
ofNat := Bounded.LE.ofFin (Fin.ofNat n)
|
||||
|
||||
instance : Inhabited Ordinal where
|
||||
default := 0
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents a time offset in nanoseconds.
|
||||
-/
|
||||
def Offset : Type := UnitVal (1 / 1000000000)
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance { x y : Offset } : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨UnitVal.ofNat n⟩
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
end Offset
|
||||
|
||||
/--
|
||||
`Span` represents a bounded value for nanoseconds, ranging between -999999999 and 999999999.
|
||||
This can be used for operations that involve differences or adjustments within this range.
|
||||
-/
|
||||
def Span := Bounded.LE (-999999999) 999999999
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
instance : Inhabited Span where default := Bounded.LE.mk 0 (by decide)
|
||||
|
||||
namespace Span
|
||||
|
||||
/--
|
||||
Creates a new `Offset` out of a `Span`.
|
||||
-/
|
||||
def toOffset (span : Span) : Offset :=
|
||||
UnitVal.ofInt span.val
|
||||
|
||||
end Span
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for nanoseconds in a day, which ranges between 0 and 86400000000000.
|
||||
-/
|
||||
def OfDay := Bounded.LE 0 86400000000000
|
||||
deriving Repr, BEq, LE, LT
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 0 ≤ data ∧ data ≤ 999999999) : Ordinal :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≤ 999999999) : Ordinal :=
|
||||
Bounded.LE.ofNat data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin 1000000000) : Ordinal :=
|
||||
Bounded.LE.ofFin data
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal) : Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
end Nanosecond
|
||||
end Time
|
||||
end Std
|
||||
110
src/Std/Time/Time/Unit/Second.lean
Normal file
110
src/Std/Time/Time/Unit/Second.lean
Normal file
@@ -0,0 +1,110 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Internal.Rat
|
||||
import Std.Time.Time.Unit.Nanosecond
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Second
|
||||
open Std.Internal
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
`Ordinal` represents a bounded value for second, which ranges between 0 and 59 or 60. This accounts
|
||||
for potential leap second.
|
||||
-/
|
||||
def Ordinal (leap : Bool) := Bounded.LE 0 (.ofNat (if leap then 60 else 59))
|
||||
|
||||
instance : BEq (Ordinal leap) where
|
||||
beq x y := BEq.beq x.val y.val
|
||||
|
||||
instance : LE (Ordinal leap) where
|
||||
le x y := LE.le x.val y.val
|
||||
|
||||
instance : LT (Ordinal leap) where
|
||||
lt x y := LT.lt x.val y.val
|
||||
|
||||
instance : Repr (Ordinal l) where
|
||||
reprPrec r := reprPrec r.val
|
||||
|
||||
instance : OfNat (Ordinal leap) n := by
|
||||
have inst := inferInstanceAs (OfNat (Bounded.LE 0 (0 + (59 : Nat))) n)
|
||||
cases leap
|
||||
· exact inst
|
||||
· exact ⟨inst.ofNat.expandTop (by decide)⟩
|
||||
|
||||
instance {x y : Ordinal l} : Decidable (x ≤ y) :=
|
||||
inferInstanceAs (Decidable (x.val ≤ y.val))
|
||||
|
||||
instance {x y : Ordinal l} : Decidable (x < y) :=
|
||||
inferInstanceAs (Decidable (x.val < y.val))
|
||||
|
||||
/--
|
||||
`Offset` represents an offset in seconds. It is defined as an `Int`.
|
||||
-/
|
||||
def Offset : Type := UnitVal 1
|
||||
deriving Repr, BEq, Inhabited, Add, Sub, Neg, LE, LT, ToString
|
||||
|
||||
instance : OfNat Offset n :=
|
||||
⟨UnitVal.ofNat n⟩
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Creates an `Second.Offset` from a natural number.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) : Second.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
/--
|
||||
Creates an `Second.Offset` from an integer.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) : Second.Offset :=
|
||||
UnitVal.ofInt data
|
||||
|
||||
end Offset
|
||||
|
||||
namespace Ordinal
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from an integer, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofInt (data : Int) (h : 0 ≤ data ∧ data ≤ Int.ofNat (if leap then 60 else 59)) : Ordinal leap :=
|
||||
Bounded.LE.mk data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a natural number, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofNat (data : Nat) (h : data ≤ (if leap then 60 else 59)) : Ordinal leap :=
|
||||
Bounded.LE.ofNat data h
|
||||
|
||||
/--
|
||||
Creates an `Ordinal` from a `Fin`, ensuring the value is within bounds.
|
||||
-/
|
||||
@[inline]
|
||||
def ofFin (data : Fin (if leap then 61 else 60)) : Ordinal leap :=
|
||||
match leap with
|
||||
| true => Bounded.LE.ofFin data
|
||||
| false => Bounded.LE.ofFin data
|
||||
|
||||
/--
|
||||
Converts an `Ordinal` to an `Second.Offset`.
|
||||
-/
|
||||
@[inline]
|
||||
def toOffset (ordinal : Ordinal leap) : Second.Offset :=
|
||||
UnitVal.ofInt ordinal.val
|
||||
|
||||
end Ordinal
|
||||
end Second
|
||||
end Time
|
||||
end Std
|
||||
180
src/Std/Time/Zoned.lean
Normal file
180
src/Std/Time/Zoned.lean
Normal file
@@ -0,0 +1,180 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Zoned.DateTime
|
||||
import Std.Time.Zoned.ZoneRules
|
||||
import Std.Time.Zoned.ZonedDateTime
|
||||
import Std.Time.Zoned.Database
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
namespace PlainDateTime
|
||||
|
||||
/--
|
||||
Get the current time.
|
||||
-/
|
||||
@[inline]
|
||||
def now : IO PlainDateTime := do
|
||||
let tm ← Timestamp.now
|
||||
let rules ← Database.defaultGetLocalZoneRules
|
||||
let ltt := rules.findLocalTimeTypeForTimestamp tm
|
||||
|
||||
return PlainDateTime.ofTimestampAssumingUTC tm |>.addSeconds ltt.getTimeZone.toSeconds
|
||||
|
||||
end PlainDateTime
|
||||
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Get the current date.
|
||||
-/
|
||||
@[inline]
|
||||
def now : IO PlainDate :=
|
||||
PlainDateTime.date <$> PlainDateTime.now
|
||||
|
||||
end PlainDate
|
||||
namespace PlainTime
|
||||
|
||||
/--
|
||||
Get the current time.
|
||||
-/
|
||||
@[inline]
|
||||
def now : IO PlainTime :=
|
||||
PlainDateTime.time <$> PlainDateTime.now
|
||||
|
||||
end PlainTime
|
||||
|
||||
namespace DateTime
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` with a `TimeZone` to a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDate (pd : PlainDate) (tz : TimeZone) : DateTime tz :=
|
||||
DateTime.ofTimestamp (Timestamp.ofPlainDateAssumingUTC pd) tz
|
||||
|
||||
/--
|
||||
Converts a `DateTime` to a `PlainDate`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDate (dt : DateTime tz) : PlainDate :=
|
||||
Timestamp.toPlainDateAssumingUTC dt.toTimestamp
|
||||
|
||||
/--
|
||||
Converts a `DateTime` to a `PlainTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainTime (dt : DateTime tz) : PlainTime :=
|
||||
dt.date.get.time
|
||||
|
||||
end DateTime
|
||||
namespace DateTime
|
||||
|
||||
/--
|
||||
Gets the current `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def now : IO (DateTime tz) := do
|
||||
let tm ← Timestamp.now
|
||||
return DateTime.ofTimestamp tm tz
|
||||
|
||||
end DateTime
|
||||
namespace ZonedDateTime
|
||||
|
||||
/--
|
||||
Gets the current `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def now : IO ZonedDateTime := do
|
||||
let tm ← Timestamp.now
|
||||
let rules ← Database.defaultGetLocalZoneRules
|
||||
return ZonedDateTime.ofTimestamp tm rules
|
||||
|
||||
/--
|
||||
Gets the current `ZonedDateTime` using the identifier of a time zone.
|
||||
-/
|
||||
@[inline]
|
||||
def nowAt (id : String) : IO ZonedDateTime := do
|
||||
let tm ← Timestamp.now
|
||||
let rules ← Database.defaultGetZoneRules id
|
||||
return ZonedDateTime.ofTimestamp tm rules
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDate (pd : PlainDate) (zr : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
ZonedDateTime.ofPlainDateTime (pd.atTime PlainTime.midnight) zr
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `ZonedDateTime` using `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateWithZone (pd : PlainDate) (zr : TimeZone) : ZonedDateTime :=
|
||||
ZonedDateTime.ofPlainDateTime (pd.atTime PlainTime.midnight) (TimeZone.ZoneRules.ofTimeZone zr)
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to a `PlainDate`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDate (dt : ZonedDateTime) : PlainDate :=
|
||||
dt.toPlainDateTime.date
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to a `PlainTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainTime (dt : ZonedDateTime) : PlainTime :=
|
||||
dt.toPlainDateTime.time
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `PlainDateTime` and a time zone identifier.
|
||||
-/
|
||||
@[inline]
|
||||
def of (pdt : PlainDateTime) (id : String) : IO ZonedDateTime := do
|
||||
let zr ← Database.defaultGetZoneRules id
|
||||
return ZonedDateTime.ofPlainDateTime pdt zr
|
||||
|
||||
end ZonedDateTime
|
||||
|
||||
namespace PlainDateTime
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `Timestamp` using the `ZoneRules`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestamp (pdt : PlainDateTime) (zr : TimeZone.ZoneRules) : Timestamp :=
|
||||
ZonedDateTime.ofPlainDateTime pdt zr |>.toTimestamp
|
||||
|
||||
/--
|
||||
Converts a `PlainDateTime` to a `Timestamp` using the `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestampWithZone (pdt : PlainDateTime) (tz : TimeZone) : Timestamp :=
|
||||
ZonedDateTime.ofPlainDateTimeWithZone pdt tz |>.toTimestamp
|
||||
|
||||
end PlainDateTime
|
||||
|
||||
namespace PlainDate
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `Timestamp` using the `ZoneRules`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestamp (dt : PlainDate) (zr : TimeZone.ZoneRules) : Timestamp :=
|
||||
ZonedDateTime.ofPlainDate dt zr |>.toTimestamp
|
||||
|
||||
/--
|
||||
Converts a `PlainDate` to a `Timestamp` using the `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestampWithZone (dt : PlainDate) (tz : TimeZone) : Timestamp :=
|
||||
ZonedDateTime.ofPlainDateWithZone dt tz |>.toTimestamp
|
||||
|
||||
end PlainDate
|
||||
38
src/Std/Time/Zoned/Database.lean
Normal file
38
src/Std/Time/Zoned/Database.lean
Normal file
@@ -0,0 +1,38 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Zoned.ZonedDateTime
|
||||
import Std.Time.Zoned.Database.Basic
|
||||
import Std.Time.Zoned.Database.TZdb
|
||||
import Std.Time.Zoned.Database.Windows
|
||||
import Init.System.Platform
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Database
|
||||
open TimeZone.ZoneRules
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Gets the zone rules for a specific time zone identifier, handling Windows and non-Windows platforms.
|
||||
In windows it uses the current `icu.h` in Windows SDK. If it's linux or macos then it will use the `tzdata`
|
||||
files.
|
||||
-/
|
||||
def defaultGetZoneRules : String → IO TimeZone.ZoneRules :=
|
||||
if System.Platform.isWindows
|
||||
then getZoneRules WindowsDb.default
|
||||
else getZoneRules TZdb.default
|
||||
|
||||
/--
|
||||
Gets the local zone rules, accounting for platform differences.
|
||||
In windows it uses the current `icu.h` in Windows SDK. If it's linux or macos then it will use the `tzdata`
|
||||
files.
|
||||
-/
|
||||
def defaultGetLocalZoneRules : IO TimeZone.ZoneRules :=
|
||||
if System.Platform.isWindows
|
||||
then getLocalZoneRules WindowsDb.default
|
||||
else getLocalZoneRules TZdb.default
|
||||
114
src/Std/Time/Zoned/Database/Basic.lean
Normal file
114
src/Std/Time/Zoned/Database/Basic.lean
Normal file
@@ -0,0 +1,114 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Zoned.ZoneRules
|
||||
import Std.Time.Zoned.Database.TzIf
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
A timezone database from which we can read the `ZoneRules` of some area by it's id.
|
||||
-/
|
||||
protected class Database (α : Type) where
|
||||
|
||||
/--
|
||||
Retrieves the zone rules information (`ZoneRules`) for a given area at a specific point in time.
|
||||
-/
|
||||
getZoneRules : α → String → IO TimeZone.ZoneRules
|
||||
|
||||
/--
|
||||
Retrieves the local zone rules information (`ZoneRules`) at a given timestamp.
|
||||
-/
|
||||
getLocalZoneRules : α → IO TimeZone.ZoneRules
|
||||
|
||||
namespace TimeZone
|
||||
|
||||
/--
|
||||
Converts a Boolean value to a corresponding `StdWall` type.
|
||||
-/
|
||||
def convertWall : Bool → StdWall
|
||||
| true => .standard
|
||||
| false => .wall
|
||||
|
||||
/--
|
||||
Converts a Boolean value to a corresponding `UTLocal` type.
|
||||
-/
|
||||
def convertUt : Bool → UTLocal
|
||||
| true => .ut
|
||||
| false => .local
|
||||
|
||||
/--
|
||||
Converts a given time index into a `LocalTimeType` by using a time zone (`tz`) and its identifier.
|
||||
-/
|
||||
def convertLocalTimeType (index : Nat) (tz : TZif.TZifV1) (identifier : String) : Option LocalTimeType := do
|
||||
let localType ← tz.localTimeTypes.get? index
|
||||
let offset := Offset.ofSeconds <| .ofInt localType.gmtOffset
|
||||
let abbreviation ← tz.abbreviations.getD index (offset.toIsoString true)
|
||||
let wallflag := convertWall (tz.stdWallIndicators.getD index true)
|
||||
let utLocal := convertUt (tz.utLocalIndicators.getD index true)
|
||||
|
||||
return {
|
||||
gmtOffset := offset
|
||||
isDst := localType.isDst
|
||||
abbreviation
|
||||
wall := wallflag
|
||||
utLocal
|
||||
identifier
|
||||
}
|
||||
|
||||
/--
|
||||
Converts a transition.
|
||||
-/
|
||||
def convertTransition (times: Array LocalTimeType) (index : Nat) (tz : TZif.TZifV1) : Option Transition := do
|
||||
let time := tz.transitionTimes.get! index
|
||||
let time := Second.Offset.ofInt time
|
||||
let indice := tz.transitionIndices.get! index
|
||||
return { time, localTimeType := times.get! indice.toNat }
|
||||
|
||||
/--
|
||||
Converts a `TZif.TZifV1` structure to a `ZoneRules` structure.
|
||||
-/
|
||||
def convertTZifV1 (tz : TZif.TZifV1) (id : String) : Except String ZoneRules := do
|
||||
let mut times : Array LocalTimeType := #[]
|
||||
|
||||
for i in [0:tz.header.typecnt.toNat] do
|
||||
if let some result := convertLocalTimeType i tz id
|
||||
then times := times.push result
|
||||
else .error s!"cannot convert local time {i} of the file"
|
||||
|
||||
let mut transitions := #[]
|
||||
|
||||
for i in [0:tz.transitionTimes.size] do
|
||||
if let some result := convertTransition times i tz
|
||||
then transitions := transitions.push result
|
||||
else .error s!"cannot convert transition {i} of the file"
|
||||
|
||||
-- Local time for timestamps before the first transition is specified by the first time
|
||||
-- type (time type 0).
|
||||
|
||||
let initialLocalTimeType ←
|
||||
if let some res := convertLocalTimeType 0 tz id
|
||||
then .ok res
|
||||
else .error s!"empty transitions for {id}"
|
||||
|
||||
.ok { transitions, initialLocalTimeType }
|
||||
|
||||
/--
|
||||
Converts a `TZif.TZifV2` structure to a `ZoneRules` structure.
|
||||
-/
|
||||
def convertTZifV2 (tz : TZif.TZifV2) (id : String) : Except String ZoneRules := do
|
||||
convertTZifV1 tz.toTZifV1 id
|
||||
|
||||
/--
|
||||
Converts a `TZif.TZif` structure to a `ZoneRules` structure.
|
||||
-/
|
||||
def convertTZif (tz : TZif.TZif) (id : String) : Except String ZoneRules := do
|
||||
if let some v2 := tz.v2
|
||||
then convertTZifV2 v2 id
|
||||
else convertTZifV1 tz.v1 id
|
||||
92
src/Std/Time/Zoned/Database/TZdb.lean
Normal file
92
src/Std/Time/Zoned/Database/TZdb.lean
Normal file
@@ -0,0 +1,92 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Zoned.TimeZone
|
||||
import Std.Time.Zoned.ZoneRules
|
||||
import Std.Time.Zoned.Database.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Database
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a Time Zone Database (TZdb) configuration with paths to local and general timezone data.
|
||||
-/
|
||||
structure TZdb where
|
||||
|
||||
/--
|
||||
The path to the local timezone file. This is typically a symlink to a file within the timezone
|
||||
database that corresponds to the current local time zone.
|
||||
-/
|
||||
localPath : System.FilePath := "/etc/localtime"
|
||||
|
||||
/--
|
||||
The path to the directory containing all available time zone files. These files define various
|
||||
time zones and their rules.
|
||||
-/
|
||||
zonesPath : System.FilePath := "/usr/share/zoneinfo/"
|
||||
|
||||
namespace TZdb
|
||||
open TimeZone
|
||||
|
||||
/--
|
||||
Returns a default `TZdb` instance with common timezone data paths for most Linux distributions and macOS.
|
||||
-/
|
||||
@[inline]
|
||||
def default : TZdb := {}
|
||||
|
||||
/--
|
||||
Parses binary timezone data into zone rules based on a given timezone ID.
|
||||
-/
|
||||
def parseTZif (bin : ByteArray) (id : String) : Except String ZoneRules := do
|
||||
let database ← TZif.parse.run bin
|
||||
convertTZif database id
|
||||
|
||||
/--
|
||||
Reads a TZif file from disk and retrieves the zone rules for the specified timezone ID.
|
||||
-/
|
||||
def parseTZIfFromDisk (path : System.FilePath) (id : String) : IO ZoneRules := do
|
||||
let binary ← try IO.FS.readBinFile path catch _ => throw <| IO.userError s!"cannot find {id} in the local timezone database"
|
||||
IO.ofExcept (parseTZif binary id)
|
||||
|
||||
/--
|
||||
Extracts a timezone ID from a file path.
|
||||
-/
|
||||
def idFromPath (path : System.FilePath) : Option String := do
|
||||
let res := path.components.toArray
|
||||
let last ← res.get? (res.size - 1)
|
||||
let last₁ ← res.get? (res.size - 2)
|
||||
|
||||
if last₁ = some "zoneinfo"
|
||||
then last
|
||||
else last₁ ++ "/" ++ last
|
||||
|
||||
/--
|
||||
Retrieves the timezone rules from the local timezone data file.
|
||||
-/
|
||||
def localRules (path : System.FilePath) : IO ZoneRules := do
|
||||
let localTimePath ←
|
||||
try
|
||||
IO.Process.run { cmd := "readlink", args := #["-f", path.toString] }
|
||||
catch _ =>
|
||||
throw <| IO.userError "cannot find the local timezone database"
|
||||
|
||||
if let some id := idFromPath localTimePath
|
||||
then parseTZIfFromDisk path id
|
||||
else throw (IO.userError "cannot read the id of the path.")
|
||||
|
||||
/--
|
||||
Reads timezone rules from disk based on the provided file path and timezone ID.
|
||||
-/
|
||||
def readRulesFromDisk (path : System.FilePath) (id : String) : IO ZoneRules := do
|
||||
parseTZIfFromDisk (System.FilePath.join path id) id
|
||||
|
||||
instance : Std.Time.Database TZdb where
|
||||
getLocalZoneRules db := localRules db.localPath
|
||||
getZoneRules db id := readRulesFromDisk db.zonesPath id
|
||||
328
src/Std/Time/Zoned/Database/TzIf.lean
Normal file
328
src/Std/Time/Zoned/Database/TzIf.lean
Normal file
@@ -0,0 +1,328 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Range
|
||||
import Std.Internal.Parsec
|
||||
import Std.Internal.Parsec.ByteArray
|
||||
|
||||
-- Based on: https://www.rfc-editor.org/rfc/rfc8536.html
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace TimeZone
|
||||
namespace TZif
|
||||
open Std.Internal.Parsec Std.Internal.Parsec.ByteArray
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
private abbrev Int32 := Int
|
||||
|
||||
private abbrev Int64 := Int
|
||||
|
||||
/--
|
||||
Represents the header of a TZif file, containing metadata about the file's structure.
|
||||
-/
|
||||
structure Header where
|
||||
|
||||
/--
|
||||
The version of the TZif file format.
|
||||
-/
|
||||
version : UInt8
|
||||
|
||||
/--
|
||||
The count of UT local indicators in the file.
|
||||
-/
|
||||
isutcnt : UInt32
|
||||
|
||||
/--
|
||||
The count of standard/wall indicators in the file.
|
||||
-/
|
||||
isstdcnt : UInt32
|
||||
|
||||
/--
|
||||
The number of leap second records.
|
||||
-/
|
||||
leapcnt : UInt32
|
||||
|
||||
/--
|
||||
The number of transition times in the file.
|
||||
-/
|
||||
timecnt : UInt32
|
||||
|
||||
/--
|
||||
The number of local time types in the file.
|
||||
-/
|
||||
typecnt : UInt32
|
||||
|
||||
/--
|
||||
The total number of characters used in abbreviations.
|
||||
-/
|
||||
charcnt : UInt32
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents the local time type information, including offset and daylight saving details.
|
||||
-/
|
||||
structure LocalTimeType where
|
||||
|
||||
/--
|
||||
The GMT offset in seconds for this local time type.
|
||||
-/
|
||||
gmtOffset : Int32
|
||||
|
||||
/--
|
||||
Indicates if this local time type observes daylight saving time.
|
||||
-/
|
||||
isDst : Bool
|
||||
|
||||
/--
|
||||
The index into the abbreviation string table for this time type.
|
||||
-/
|
||||
abbreviationIndex : UInt8
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents a leap second record, including the transition time and the correction applied.
|
||||
-/
|
||||
structure LeapSecond where
|
||||
|
||||
/--
|
||||
The transition time of the leap second event.
|
||||
-/
|
||||
transitionTime : Int64
|
||||
|
||||
/--
|
||||
The correction applied during the leap second event in seconds.
|
||||
-/
|
||||
correction : Int64
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents version 1 of the TZif format.
|
||||
-/
|
||||
structure TZifV1 where
|
||||
|
||||
/--
|
||||
The header information of the TZif file.
|
||||
-/
|
||||
header : Header
|
||||
|
||||
/--
|
||||
The array of transition times in seconds since the epoch.
|
||||
-/
|
||||
transitionTimes : Array Int32
|
||||
|
||||
/--
|
||||
The array of local time type indices corresponding to each transition time.
|
||||
-/
|
||||
transitionIndices : Array UInt8
|
||||
|
||||
/--
|
||||
The array of local time type structures.
|
||||
-/
|
||||
localTimeTypes : Array LocalTimeType
|
||||
|
||||
/--
|
||||
The array of abbreviation strings used by local time types.
|
||||
-/
|
||||
abbreviations : Array String
|
||||
|
||||
/--
|
||||
The array of leap second records.
|
||||
-/
|
||||
leapSeconds : Array LeapSecond
|
||||
|
||||
/--
|
||||
The array indicating whether each transition time uses wall clock time or standard time.
|
||||
-/
|
||||
stdWallIndicators : Array Bool
|
||||
|
||||
/--
|
||||
The array indicating whether each transition time uses universal time or local time.
|
||||
-/
|
||||
utLocalIndicators : Array Bool
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents version 2 of the TZif format, extending TZifV1 with an optional footer.
|
||||
-/
|
||||
structure TZifV2 extends TZifV1 where
|
||||
|
||||
/--
|
||||
An optional footer for additional metadata in version 2.
|
||||
-/
|
||||
footer : Option String
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents a TZif file, which can be either version 1 or version 2.
|
||||
-/
|
||||
structure TZif where
|
||||
|
||||
/--
|
||||
The data for version 1 of the TZif file.
|
||||
-/
|
||||
v1 : TZifV1
|
||||
|
||||
/--
|
||||
Optionally, the data for version 2 of the TZif file.
|
||||
-/
|
||||
v2 : Option TZifV2
|
||||
deriving Repr, Inhabited
|
||||
|
||||
private def toUInt32 (bs : ByteArray) : UInt32 :=
|
||||
assert! bs.size == 4
|
||||
(bs.get! 0).toUInt32 <<< 0x18 |||
|
||||
(bs.get! 1).toUInt32 <<< 0x10 |||
|
||||
(bs.get! 2).toUInt32 <<< 0x8 |||
|
||||
(bs.get! 3).toUInt32
|
||||
|
||||
private def toInt32 (bs : ByteArray) : Int32 :=
|
||||
let n := toUInt32 bs |>.toNat
|
||||
if n < (1 <<< 31)
|
||||
then Int.ofNat n
|
||||
else Int.negOfNat (UInt32.size - n)
|
||||
|
||||
private def toInt64 (bs : ByteArray) : Int64 :=
|
||||
let n := ByteArray.toUInt64BE! bs |>.toNat
|
||||
if n < (1 <<< 63)
|
||||
then Int.ofNat n
|
||||
else Int.negOfNat (UInt64.size - n)
|
||||
|
||||
private def manyN (n : Nat) (p : Parser α) : Parser (Array α) := do
|
||||
let mut result := #[]
|
||||
for _ in [0:n] do
|
||||
let x ← p
|
||||
result := result.push x
|
||||
return result
|
||||
|
||||
private def pu64 : Parser UInt64 := ByteArray.toUInt64LE! <$> take 8
|
||||
private def pi64 : Parser Int64 := toInt64 <$> take 8
|
||||
private def pu32 : Parser UInt32 := toUInt32 <$> take 4
|
||||
private def pi32 : Parser Int32 := toInt32 <$> take 4
|
||||
private def pu8 : Parser UInt8 := any
|
||||
private def pbool : Parser Bool := (· != 0) <$> pu8
|
||||
|
||||
private def parseHeader : Parser Header :=
|
||||
Header.mk
|
||||
<$> (pstring "TZif" *> pu8)
|
||||
<*> (take 15 *> pu32)
|
||||
<*> pu32
|
||||
<*> pu32
|
||||
<*> pu32
|
||||
<*> pu32
|
||||
<*> pu32
|
||||
|
||||
private def parseLocalTimeType : Parser LocalTimeType :=
|
||||
LocalTimeType.mk
|
||||
<$> pi32
|
||||
<*> pbool
|
||||
<*> pu8
|
||||
|
||||
private def parseLeapSecond (p : Parser Int) : Parser LeapSecond :=
|
||||
LeapSecond.mk
|
||||
<$> p
|
||||
<*> pi32
|
||||
|
||||
private def parseTransitionTimes (size : Parser Int32) (n : UInt32) : Parser (Array Int32) :=
|
||||
manyN (n.toNat) size
|
||||
|
||||
private def parseTransitionIndices (n : UInt32) : Parser (Array UInt8) :=
|
||||
manyN (n.toNat) pu8
|
||||
|
||||
private def parseLocalTimeTypes (n : UInt32) : Parser (Array LocalTimeType) :=
|
||||
manyN (n.toNat) parseLocalTimeType
|
||||
|
||||
private def parseAbbreviations (times : Array LocalTimeType) (n : UInt32) : Parser (Array String) := do
|
||||
let mut strings := #[]
|
||||
let mut current := ""
|
||||
let mut chars ← manyN n.toNat pu8
|
||||
|
||||
for time in times do
|
||||
for indx in [time.abbreviationIndex.toNat:n.toNat] do
|
||||
let char := chars.get! indx
|
||||
if char = 0 then
|
||||
strings := strings.push current
|
||||
current := ""
|
||||
break
|
||||
else
|
||||
current := current.push (Char.ofUInt8 char)
|
||||
|
||||
return strings
|
||||
|
||||
private def parseLeapSeconds (size : Parser Int) (n : UInt32) : Parser (Array LeapSecond) :=
|
||||
manyN (n.toNat) (parseLeapSecond size)
|
||||
|
||||
private def parseIndicators (n : UInt32) : Parser (Array Bool) :=
|
||||
manyN (n.toNat) pbool
|
||||
|
||||
private def parseTZifV1 : Parser TZifV1 := do
|
||||
let header ← parseHeader
|
||||
|
||||
let transitionTimes ← parseTransitionTimes pi32 header.timecnt
|
||||
let transitionIndices ← parseTransitionIndices header.timecnt
|
||||
let localTimeTypes ← parseLocalTimeTypes header.typecnt
|
||||
let abbreviations ← parseAbbreviations localTimeTypes header.charcnt
|
||||
let leapSeconds ← parseLeapSeconds pi32 header.leapcnt
|
||||
let stdWallIndicators ← parseIndicators header.isstdcnt
|
||||
let utLocalIndicators ← parseIndicators header.isutcnt
|
||||
|
||||
return {
|
||||
header
|
||||
transitionTimes
|
||||
transitionIndices
|
||||
localTimeTypes
|
||||
abbreviations
|
||||
leapSeconds
|
||||
stdWallIndicators
|
||||
utLocalIndicators
|
||||
}
|
||||
|
||||
private def parseFooter : Parser (Option String) := do
|
||||
let char ← pu8
|
||||
|
||||
if char = 0x0A then pure () else return none
|
||||
|
||||
let tzString ← many (satisfy (· ≠ 0x0A))
|
||||
let mut str := ""
|
||||
|
||||
for byte in tzString do
|
||||
str := str.push (Char.ofUInt8 byte)
|
||||
|
||||
return str
|
||||
|
||||
private def parseTZifV2 : Parser (Option TZifV2) := optional do
|
||||
let header ← parseHeader
|
||||
|
||||
let transitionTimes ← parseTransitionTimes pi64 header.timecnt
|
||||
let transitionIndices ← parseTransitionIndices header.timecnt
|
||||
let localTimeTypes ← parseLocalTimeTypes header.typecnt
|
||||
let abbreviations ← parseAbbreviations localTimeTypes header.charcnt
|
||||
let leapSeconds ← parseLeapSeconds pi64 header.leapcnt
|
||||
let stdWallIndicators ← parseIndicators header.isstdcnt
|
||||
let utLocalIndicators ← parseIndicators header.isutcnt
|
||||
|
||||
return {
|
||||
header
|
||||
transitionTimes
|
||||
transitionIndices
|
||||
localTimeTypes
|
||||
abbreviations
|
||||
leapSeconds
|
||||
stdWallIndicators
|
||||
utLocalIndicators
|
||||
footer := ← parseFooter
|
||||
}
|
||||
|
||||
/--
|
||||
Parses a TZif file, which may be in either version 1 or version 2 format.
|
||||
-/
|
||||
def parse : Parser TZif := do
|
||||
let v1 ← parseTZifV1
|
||||
let v2 ← parseTZifV2
|
||||
return { v1, v2 }
|
||||
|
||||
end TZif
|
||||
89
src/Std/Time/Zoned/Database/Windows.lean
Normal file
89
src/Std/Time/Zoned/Database/Windows.lean
Normal file
@@ -0,0 +1,89 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Zoned.TimeZone
|
||||
import Std.Time.Zoned.ZoneRules
|
||||
import Std.Time.Zoned.Database.Basic
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace Database
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
namespace Windows
|
||||
|
||||
/--
|
||||
Fetches the next timezone transition for a given timezone identifier and timestamp.
|
||||
-/
|
||||
@[extern "lean_windows_get_next_transition"]
|
||||
opaque getNextTransition : @&String → Int64 → Bool → IO (Option (Int64 × TimeZone))
|
||||
|
||||
/--
|
||||
Fetches the timezone at a timestamp.
|
||||
-/
|
||||
@[extern "lean_get_windows_local_timezone_id_at"]
|
||||
opaque getLocalTimeZoneIdentifierAt : Int64 → IO String
|
||||
|
||||
/--
|
||||
Retrieves the timezone rules, including all transitions, for a given timezone identifier.
|
||||
-/
|
||||
def getZoneRules (id : String) : IO TimeZone.ZoneRules := do
|
||||
let mut start := -2147483648
|
||||
let mut transitions : Array TimeZone.Transition := #[]
|
||||
|
||||
let mut initialLocalTimeType ←
|
||||
if let some res := ← Windows.getNextTransition id start true
|
||||
then pure (toLocalTime res.snd)
|
||||
else throw (IO.userError "cannot find first transition in zone rules")
|
||||
|
||||
while true do
|
||||
let result ← Windows.getNextTransition id start false
|
||||
|
||||
if let some res := result then
|
||||
transitions := transitions.push { time := Second.Offset.ofInt start.toInt, localTimeType := toLocalTime res.snd }
|
||||
|
||||
-- Avoid zone rules for more than year 3000
|
||||
if res.fst ≤ start ∨ res.fst >= 32503690800 then
|
||||
break
|
||||
|
||||
start := res.fst
|
||||
else
|
||||
break
|
||||
|
||||
return { transitions, initialLocalTimeType }
|
||||
|
||||
where
|
||||
toLocalTime (res : TimeZone) : TimeZone.LocalTimeType :=
|
||||
{
|
||||
gmtOffset := res.offset,
|
||||
abbreviation := res.abbreviation,
|
||||
identifier := res.name,
|
||||
isDst := res.isDST,
|
||||
wall := .wall,
|
||||
utLocal := .local
|
||||
}
|
||||
|
||||
end Windows
|
||||
|
||||
/--
|
||||
Represents a Time Zone Database that we get from ICU available on Windows SDK.
|
||||
-/
|
||||
structure WindowsDb where
|
||||
|
||||
namespace WindowsDb
|
||||
open TimeZone
|
||||
|
||||
/--
|
||||
Returns a default `WindowsDb` instance.
|
||||
-/
|
||||
@[inline]
|
||||
def default : WindowsDb := {}
|
||||
|
||||
instance : Std.Time.Database WindowsDb where
|
||||
getZoneRules _ id := Windows.getZoneRules id
|
||||
getLocalZoneRules _ := Windows.getZoneRules =<< Windows.getLocalTimeZoneIdentifierAt (-2147483648)
|
||||
516
src/Std/Time/Zoned/DateTime.lean
Normal file
516
src/Std/Time/Zoned/DateTime.lean
Normal file
@@ -0,0 +1,516 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Zoned.TimeZone
|
||||
import Std.Internal
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a specific point in time associated with a `TimeZone`.
|
||||
-/
|
||||
structure DateTime (tz : TimeZone) where
|
||||
private mk ::
|
||||
|
||||
/--
|
||||
`Timestamp` represents the exact moment in time. It's a UTC related `Timestamp`.
|
||||
-/
|
||||
timestamp : Timestamp
|
||||
|
||||
/--
|
||||
`Date` is a `Thunk` containing the `PlainDateTime` that represents the local date and time, it's
|
||||
used for accessing data like `day` and `month` without having to recompute the data everytime.
|
||||
-/
|
||||
date : Thunk PlainDateTime
|
||||
|
||||
instance : BEq (DateTime tz) where
|
||||
beq x y := x.timestamp == y.timestamp
|
||||
|
||||
instance : Inhabited (DateTime tz) where
|
||||
default := ⟨Inhabited.default, Thunk.mk fun _ => Inhabited.default⟩
|
||||
|
||||
namespace DateTime
|
||||
|
||||
/--
|
||||
Creates a new `DateTime` out of a `Timestamp` that is in a `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofTimestamp (tm : Timestamp) (tz : TimeZone) : DateTime tz :=
|
||||
DateTime.mk tm (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC |>.addSeconds tz.toSeconds)
|
||||
|
||||
/--
|
||||
Converts a `DateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
def toDaysSinceUNIXEpoch (date : DateTime tz) : Day.Offset :=
|
||||
date.date.get.toDaysSinceUNIXEpoch
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` out of a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestamp (date : DateTime tz) : Timestamp :=
|
||||
date.timestamp
|
||||
|
||||
/--
|
||||
Changes the `TimeZone` to a new one.
|
||||
-/
|
||||
@[inline]
|
||||
def convertTimeZone (date : DateTime tz) (tz₁ : TimeZone) : DateTime tz₁ :=
|
||||
ofTimestamp date.timestamp tz₁
|
||||
|
||||
/--
|
||||
Creates a new `DateTime` out of a `PlainDateTime`. It assumes that the `PlainDateTime` is relative
|
||||
to UTC.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTimeAssumingUTC (date : PlainDateTime) (tz : TimeZone) : DateTime tz :=
|
||||
let tm := Timestamp.ofPlainDateTimeAssumingUTC date
|
||||
DateTime.mk tm (Thunk.mk fun _ => date.addSeconds tz.toSeconds)
|
||||
|
||||
/--
|
||||
Creates a new `DateTime` from a `PlainDateTime`, assuming that the `PlainDateTime`
|
||||
is relative to the given `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTime (date : PlainDateTime) (tz : TimeZone) : DateTime tz :=
|
||||
let tm := date.subSeconds tz.toSeconds
|
||||
DateTime.mk (Timestamp.ofPlainDateTimeAssumingUTC tm) (Thunk.mk fun _ => date)
|
||||
|
||||
/--
|
||||
Add `Hour.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addHours (dt : DateTime tz) (hours : Hour.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addHours hours) tz
|
||||
|
||||
/--
|
||||
Subtract `Hour.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subHours (dt : DateTime tz) (hours : Hour.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subHours hours) tz
|
||||
|
||||
/--
|
||||
Add `Minute.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMinutes (dt : DateTime tz) (minutes : Minute.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addMinutes minutes) tz
|
||||
|
||||
/--
|
||||
Subtract `Minute.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMinutes (dt : DateTime tz) (minutes : Minute.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subMinutes minutes) tz
|
||||
|
||||
/--
|
||||
Add `Second.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addSeconds (dt : DateTime tz) (seconds : Second.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addSeconds seconds) tz
|
||||
|
||||
/--
|
||||
Subtract `Second.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subSeconds (dt : DateTime tz) (seconds : Second.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subSeconds seconds) tz
|
||||
|
||||
/--
|
||||
Add `Millisecond.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMilliseconds (dt : DateTime tz) (milliseconds : Millisecond.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addMilliseconds milliseconds) tz
|
||||
|
||||
/--
|
||||
Subtract `Millisecond.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMilliseconds (dt : DateTime tz) (milliseconds : Millisecond.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subMilliseconds milliseconds) tz
|
||||
|
||||
/--
|
||||
Add `Nanosecond.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addNanoseconds (dt : DateTime tz) (nanoseconds : Nanosecond.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addNanoseconds nanoseconds) tz
|
||||
|
||||
/--
|
||||
Subtract `Nanosecond.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subNanoseconds (dt : DateTime tz) (nanoseconds : Nanosecond.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subNanoseconds nanoseconds) tz
|
||||
|
||||
/--
|
||||
Add `Day.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addDays (dt : DateTime tz) (days : Day.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addDays days) tz
|
||||
|
||||
/--
|
||||
Subtracts `Day.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subDays (dt : DateTime tz) (days : Day.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subDays days) tz
|
||||
|
||||
/--
|
||||
Add `Week.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addWeeks (dt : DateTime tz) (weeks : Week.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addWeeks weeks) tz
|
||||
|
||||
/--
|
||||
Subtracts `Week.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subWeeks (dt : DateTime tz) (weeks : Week.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subWeeks weeks) tz
|
||||
|
||||
/--
|
||||
Add `Month.Offset` to a `DateTime`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
def addMonthsClip (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addMonthsClip months) tz
|
||||
|
||||
/--
|
||||
Subtracts `Month.Offset` from a `DateTime`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsClip (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subMonthsClip months) tz
|
||||
|
||||
/--
|
||||
Add `Month.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
||||
month.
|
||||
-/
|
||||
def addMonthsRollOver (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addMonthsRollOver months) tz
|
||||
|
||||
/--
|
||||
Subtract `Month.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
||||
month.
|
||||
-/
|
||||
@[inline]
|
||||
def subMonthsRollOver (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subMonthsRollOver months) tz
|
||||
|
||||
/--
|
||||
Add `Year.Offset` to a `DateTime`, this function rolls over any excess days into the following
|
||||
month.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsRollOver (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addYearsRollOver years) tz
|
||||
|
||||
/--
|
||||
Add `Year.Offset` to a `DateTime`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
@[inline]
|
||||
def addYearsClip (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.addYearsClip years) tz
|
||||
|
||||
/--
|
||||
Subtract `Year.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
||||
month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsRollOver (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subYearsRollOver years) tz
|
||||
|
||||
/--
|
||||
Subtract `Year.Offset` from to a `DateTime`, it clips the day to the last valid day of that month.
|
||||
-/
|
||||
@[inline]
|
||||
def subYearsClip (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.subYearsClip years) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysClip (dt : DateTime tz) (days : Day.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withDaysClip days) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days rolled over to the next month or year as needed.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysRollOver (dt : DateTime tz) (days : Day.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withDaysRollOver days) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the month to the given `month` value.
|
||||
The day remains unchanged, and any invalid days for the new month will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthClip (dt : DateTime tz) (month : Month.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withMonthClip month) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the month to the given `month` value.
|
||||
The day is rolled over to the next valid month if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthRollOver (dt : DateTime tz) (month : Month.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withMonthRollOver month) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the year to the given `year` value. The month and day remain unchanged,
|
||||
and any invalid days for the new year will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearClip (dt : DateTime tz) (year : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withYearClip year) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the year to the given `year` value. The month and day are rolled
|
||||
over to the next valid month and day if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearRollOver (dt : DateTime tz) (year : Year.Offset) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withYearRollOver year) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the `hour` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withHours (dt : DateTime tz) (hour : Hour.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withHours hour) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the `minute` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withMinutes (dt : DateTime tz) (minute : Minute.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withMinutes minute) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the `second` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withSeconds (dt : DateTime tz) (second : Sigma Second.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withSeconds second) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the `nano` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withNanoseconds (dt : DateTime tz) (nano : Nanosecond.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withNanoseconds nano) tz
|
||||
|
||||
/--
|
||||
Creates a new `DateTime tz` by adjusting the `millisecond` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withMilliseconds (dt : DateTime tz) (milli : Millisecond.Ordinal) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withMilliseconds milli) tz
|
||||
|
||||
/--
|
||||
Converts a `Timestamp` to a `PlainDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDateTime (dt : DateTime tz) : PlainDateTime :=
|
||||
dt.date.get
|
||||
|
||||
/--
|
||||
Getter for the `Year` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def year (dt : DateTime tz) : Year.Offset :=
|
||||
dt.date.get.year
|
||||
|
||||
/--
|
||||
Getter for the `Month` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def month (dt : DateTime tz) : Month.Ordinal :=
|
||||
dt.date.get.month
|
||||
|
||||
/--
|
||||
Getter for the `Day` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def day (dt : DateTime tz) : Day.Ordinal :=
|
||||
dt.date.get.day
|
||||
|
||||
/--
|
||||
Getter for the `Hour` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def hour (dt : DateTime tz) : Hour.Ordinal :=
|
||||
dt.date.get.hour
|
||||
|
||||
/--
|
||||
Getter for the `Minute` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def minute (dt : DateTime tz) : Minute.Ordinal :=
|
||||
dt.date.get.minute
|
||||
|
||||
/--
|
||||
Getter for the `Second` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def second (dt : DateTime tz) : Second.Ordinal dt.date.get.time.second.fst :=
|
||||
dt.date.get.second
|
||||
|
||||
/--
|
||||
Getter for the `Milliseconds` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def millisecond (dt : DateTime tz) : Millisecond.Ordinal :=
|
||||
dt.date.get.time.nanosecond.emod 1000 (by decide)
|
||||
|
||||
/--
|
||||
Getter for the `Nanosecond` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def nanosecond (dt : DateTime tz) : Nanosecond.Ordinal :=
|
||||
dt.date.get.time.nanosecond
|
||||
|
||||
/--
|
||||
Gets the `Weekday` of a DateTime.
|
||||
-/
|
||||
@[inline]
|
||||
def weekday (dt : DateTime tz) : Weekday :=
|
||||
dt.date.get.date.weekday
|
||||
|
||||
/--
|
||||
Determines the era of the given `DateTime` based on its year.
|
||||
-/
|
||||
def era (date : DateTime tz) : Year.Era :=
|
||||
date.year.era
|
||||
|
||||
/--
|
||||
Sets the `DateTime` to the specified `desiredWeekday`.
|
||||
-/
|
||||
def withWeekday (dt : DateTime tz) (desiredWeekday : Weekday) : DateTime tz :=
|
||||
ofPlainDateTime (dt.date.get.withWeekday desiredWeekday) tz
|
||||
|
||||
/--
|
||||
Checks if the `DateTime` is in a leap year.
|
||||
-/
|
||||
def inLeapYear (date : DateTime tz) : Bool :=
|
||||
date.year.isLeap
|
||||
|
||||
/--
|
||||
Determines the ordinal day of the year for the given `DateTime`.
|
||||
-/
|
||||
def dayOfYear (date : DateTime tz) : Day.Ordinal.OfYear date.year.isLeap :=
|
||||
ValidDate.dayOfYear ⟨⟨date.month, date.day⟩, date.date.get.date.valid⟩
|
||||
|
||||
/--
|
||||
Determines the week of the year for the given `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def weekOfYear (date : DateTime tz) : Week.Ordinal :=
|
||||
date.date.get.weekOfYear
|
||||
|
||||
/--
|
||||
Returns the unaligned week of the month for a `DateTime` (day divided by 7, plus 1).
|
||||
-/
|
||||
def weekOfMonth (date : DateTime tz) : Bounded.LE 1 5 :=
|
||||
date.date.get.weekOfMonth
|
||||
|
||||
/--
|
||||
Determines the week of the month for the given `DateTime`. The week of the month is calculated based
|
||||
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
||||
based on the Gregorian Calendar.
|
||||
-/
|
||||
@[inline]
|
||||
def alignedWeekOfMonth (date : DateTime tz) : Week.Ordinal.OfMonth :=
|
||||
date.date.get.alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
Determines the quarter of the year for the given `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def quarter (date : DateTime tz) : Bounded.LE 1 4 :=
|
||||
date.date.get.quarter
|
||||
|
||||
/--
|
||||
Getter for the `PlainTime` inside of a `DateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def time (zdt : DateTime tz) : PlainTime :=
|
||||
zdt.date.get.time
|
||||
|
||||
/--
|
||||
Converts a `DateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDaysSinceUNIXEpoch (days : Day.Offset) (time : PlainTime) (tz : TimeZone) : DateTime tz :=
|
||||
DateTime.ofPlainDateTime (PlainDateTime.ofDaysSinceUNIXEpoch days time) tz
|
||||
|
||||
instance : HAdd (DateTime tz) (Day.Offset) (DateTime tz) where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub (DateTime tz) (Day.Offset) (DateTime tz) where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd (DateTime tz) (Week.Offset) (DateTime tz) where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub (DateTime tz) (Week.Offset) (DateTime tz) where
|
||||
hSub := subWeeks
|
||||
|
||||
instance : HAdd (DateTime tz) (Hour.Offset) (DateTime tz) where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub (DateTime tz) (Hour.Offset) (DateTime tz) where
|
||||
hSub := subHours
|
||||
|
||||
instance : HAdd (DateTime tz) (Minute.Offset) (DateTime tz) where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub (DateTime tz) (Minute.Offset) (DateTime tz) where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd (DateTime tz) (Second.Offset) (DateTime tz) where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub (DateTime tz) (Second.Offset) (DateTime tz) where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd (DateTime tz) (Millisecond.Offset) (DateTime tz) where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub (DateTime tz) (Millisecond.Offset) (DateTime tz) where
|
||||
hSub := subMilliseconds
|
||||
|
||||
instance : HAdd (DateTime tz) (Nanosecond.Offset) (DateTime tz) where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub (DateTime tz) (Nanosecond.Offset) (DateTime tz) where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HSub (DateTime tz) (DateTime tz₁) Duration where
|
||||
hSub x y := x.toTimestamp - y.toTimestamp
|
||||
|
||||
instance : HAdd (DateTime tz) Duration (DateTime tz) where
|
||||
hAdd x y := x.addNanoseconds y.toNanoseconds
|
||||
|
||||
instance : HSub (DateTime tz) Duration (DateTime tz) where
|
||||
hSub x y := x.subNanoseconds y.toNanoseconds
|
||||
|
||||
end DateTime
|
||||
end Time
|
||||
end Std
|
||||
74
src/Std/Time/Zoned/Offset.lean
Normal file
74
src/Std/Time/Zoned/Offset.lean
Normal file
@@ -0,0 +1,74 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Time
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace TimeZone
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a timezone offset with an hour and second component.
|
||||
-/
|
||||
structure Offset where
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a given number of seconds.
|
||||
-/
|
||||
ofSeconds ::
|
||||
|
||||
/--
|
||||
The same timezone offset in seconds.
|
||||
-/
|
||||
second : Second.Offset
|
||||
deriving Repr
|
||||
|
||||
instance : Inhabited Offset where
|
||||
default := ⟨0⟩
|
||||
|
||||
instance : BEq Offset where
|
||||
beq x y := BEq.beq x.second y.second
|
||||
|
||||
namespace Offset
|
||||
|
||||
/--
|
||||
Converts an `Offset` to a string in ISO 8601 format. The `colon` parameter determines if the hour
|
||||
and minute components are separated by a colon (e.g., "+01:00" or "+0100").
|
||||
-/
|
||||
def toIsoString (offset : Offset) (colon : Bool) : String :=
|
||||
let (sign, time) := if offset.second.val ≥ 0 then ("+", offset.second) else ("-", -offset.second)
|
||||
let hour : Hour.Offset := time.ediv 3600
|
||||
let minute := Int.ediv (Int.tmod time.val 3600) 60
|
||||
let hourStr := if hour.val < 10 then s!"0{hour.val}" else toString hour.val
|
||||
let minuteStr := if minute < 10 then s!"0{minute}" else toString minute
|
||||
if colon then s!"{sign}{hourStr}:{minuteStr}"
|
||||
else s!"{sign}{hourStr}{minuteStr}"
|
||||
|
||||
/--
|
||||
A zero `Offset` representing UTC (no offset).
|
||||
-/
|
||||
def zero : Offset :=
|
||||
{ second := 0 }
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a given number of hour.
|
||||
-/
|
||||
def ofHours (n : Hour.Offset) : Offset :=
|
||||
ofSeconds n.toSeconds
|
||||
|
||||
/--
|
||||
Creates an `Offset` from a given number of hours and minutes.
|
||||
-/
|
||||
def ofHoursAndMinutes (n : Hour.Offset) (m : Minute.Offset) : Offset :=
|
||||
ofSeconds (n.toSeconds + m.toSeconds)
|
||||
|
||||
end Offset
|
||||
end TimeZone
|
||||
end Time
|
||||
end Std
|
||||
71
src/Std/Time/Zoned/TimeZone.lean
Normal file
71
src/Std/Time/Zoned/TimeZone.lean
Normal file
@@ -0,0 +1,71 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Zoned.Offset
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
A TimeZone structure that stores the timezone offset, the name, abbreviation and if it's in daylight
|
||||
saving time.
|
||||
-/
|
||||
structure TimeZone where
|
||||
|
||||
/--
|
||||
The `Offset` of the date time.
|
||||
-/
|
||||
offset : TimeZone.Offset
|
||||
|
||||
/--
|
||||
The name of the time zone.
|
||||
-/
|
||||
name : String
|
||||
|
||||
/--
|
||||
The abbreviation of the time zone.
|
||||
-/
|
||||
abbreviation : String
|
||||
|
||||
/--
|
||||
Day light saving flag.
|
||||
-/
|
||||
isDST : Bool
|
||||
deriving Inhabited, Repr, BEq
|
||||
|
||||
namespace TimeZone
|
||||
|
||||
/--
|
||||
A zeroed `Timezone` representing UTC (no offset).
|
||||
-/
|
||||
def UTC : TimeZone :=
|
||||
TimeZone.mk (Offset.zero) "UTC" "UTC" false
|
||||
|
||||
/--
|
||||
A zeroed `Timezone` representing GMT (no offset).
|
||||
-/
|
||||
def GMT : TimeZone :=
|
||||
TimeZone.mk (Offset.zero) "Greenwich Mean Time" "GMT" false
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` from a given number of hour.
|
||||
-/
|
||||
def ofHours (name : String) (abbreviation : String) (n : Hour.Offset) (isDST : Bool := false) : TimeZone :=
|
||||
TimeZone.mk (Offset.ofHours n) name abbreviation isDST
|
||||
|
||||
/--
|
||||
Creates a `Timestamp` from a given number of second.
|
||||
-/
|
||||
def ofSeconds (name : String) (abbreviation : String) (n : Second.Offset) (isDST : Bool := false) : TimeZone :=
|
||||
TimeZone.mk (Offset.ofSeconds n) name abbreviation isDST
|
||||
|
||||
/--
|
||||
Gets the number of seconds in a timezone offset.
|
||||
-/
|
||||
def toSeconds (tz : TimeZone) : Second.Offset :=
|
||||
tz.offset.second
|
||||
225
src/Std/Time/Zoned/ZoneRules.lean
Normal file
225
src/Std/Time/Zoned/ZoneRules.lean
Normal file
@@ -0,0 +1,225 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.DateTime
|
||||
import Std.Time.Zoned.TimeZone
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
namespace TimeZone
|
||||
open Internal
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents the type of local time in relation to UTC.
|
||||
-/
|
||||
inductive UTLocal
|
||||
/--
|
||||
Universal Time (UT), often referred to as UTC.
|
||||
-/
|
||||
| ut
|
||||
|
||||
/--
|
||||
Local time that is not necessarily UTC.
|
||||
-/
|
||||
| local
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents types of wall clocks or standard times.
|
||||
-/
|
||||
inductive StdWall
|
||||
/--
|
||||
Time based on a wall clock, which can include daylight saving adjustments.
|
||||
-/
|
||||
| wall
|
||||
|
||||
/--
|
||||
Standard time without adjustments for daylight saving.
|
||||
-/
|
||||
| standard
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents a type of local time, including offset and daylight saving information.
|
||||
-/
|
||||
structure LocalTimeType where
|
||||
|
||||
/--
|
||||
The offset from GMT for this local time.
|
||||
-/
|
||||
gmtOffset : TimeZone.Offset
|
||||
|
||||
/--
|
||||
Indicates if daylight saving time is observed.
|
||||
-/
|
||||
isDst : Bool
|
||||
|
||||
/--
|
||||
The abbreviation for this local time type (e.g., "EST", "PDT").
|
||||
-/
|
||||
abbreviation : String
|
||||
|
||||
/--
|
||||
Indicates if the time is wall clock or standard time.
|
||||
-/
|
||||
wall : StdWall
|
||||
|
||||
/--
|
||||
Distinguishes between universal time and local time.
|
||||
-/
|
||||
utLocal : UTLocal
|
||||
|
||||
/--
|
||||
ID of the timezone
|
||||
-/
|
||||
identifier : String
|
||||
deriving Repr, Inhabited
|
||||
|
||||
namespace LocalTimeType
|
||||
|
||||
/--
|
||||
Gets the `TimeZone` offset from a `LocalTimeType`.
|
||||
-/
|
||||
def getTimeZone (time : LocalTimeType) : TimeZone :=
|
||||
⟨time.gmtOffset, time.identifier, time.abbreviation, time.isDst⟩
|
||||
|
||||
end LocalTimeType
|
||||
|
||||
/--
|
||||
Represents a time zone transition, mapping a time to a local time type.
|
||||
-/
|
||||
structure Transition where
|
||||
|
||||
/--
|
||||
The specific time of the transition event.
|
||||
-/
|
||||
time : Second.Offset
|
||||
|
||||
/--
|
||||
The local time type associated with this transition.
|
||||
-/
|
||||
localTimeType : LocalTimeType
|
||||
deriving Repr, Inhabited
|
||||
|
||||
/--
|
||||
Represents the rules for a time zone.
|
||||
-/
|
||||
structure ZoneRules where
|
||||
|
||||
/--
|
||||
The first `LocalTimeType` used as a fallback when no matching transition is found.
|
||||
-/
|
||||
initialLocalTimeType : LocalTimeType
|
||||
|
||||
/--
|
||||
The array of transitions for the time zone.
|
||||
-/
|
||||
transitions : Array Transition
|
||||
|
||||
deriving Repr, Inhabited
|
||||
|
||||
namespace Transition
|
||||
|
||||
/--
|
||||
Create a TimeZone from a Transition.
|
||||
-/
|
||||
def createTimeZoneFromTransition (transition : Transition) : TimeZone :=
|
||||
let name := transition.localTimeType.identifier
|
||||
let offset := transition.localTimeType.gmtOffset
|
||||
let abbreviation := transition.localTimeType.abbreviation
|
||||
TimeZone.mk offset name abbreviation transition.localTimeType.isDst
|
||||
|
||||
/--
|
||||
Applies the transition to a Timestamp.
|
||||
-/
|
||||
def apply (timestamp : Timestamp) (transition : Transition) : Timestamp :=
|
||||
let offsetInSeconds := transition.localTimeType.gmtOffset.second |>.add transition.localTimeType.gmtOffset.second
|
||||
timestamp.addSeconds offsetInSeconds
|
||||
|
||||
/--
|
||||
Finds the transition corresponding to a given timestamp in `Array Transition`.
|
||||
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
||||
-/
|
||||
def findTransitionIndexForTimestamp (transitions : Array Transition) (timestamp : Timestamp) : Option Nat :=
|
||||
let value := timestamp.toSecondsSinceUnixEpoch
|
||||
if let some idx := transitions.findIdx? (fun t => t.time.val > value.val)
|
||||
then some idx
|
||||
else none
|
||||
|
||||
/--
|
||||
Finds the transition corresponding to a given timestamp in `Array Transition`.
|
||||
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
||||
-/
|
||||
def findTransitionForTimestamp (transitions : Array Transition) (timestamp : Timestamp) : Option Transition :=
|
||||
if let some idx := findTransitionIndexForTimestamp transitions timestamp
|
||||
then transitions.get? (idx - 1)
|
||||
else transitions.back?
|
||||
|
||||
/--
|
||||
Find the current `TimeZone` out of a `Transition` in a `Array Transition`
|
||||
-/
|
||||
def timezoneAt (transitions : Array Transition) (tm : Timestamp) : Except String TimeZone :=
|
||||
if let some transition := findTransitionForTimestamp transitions tm
|
||||
then .ok transition.createTimeZoneFromTransition
|
||||
else .error "cannot find local timezone."
|
||||
|
||||
end Transition
|
||||
namespace ZoneRules
|
||||
|
||||
/--
|
||||
Creates ZoneRules with a fixed GMT offset.
|
||||
-/
|
||||
def fixedOffsetZone (second : Second.Offset) (identifier : Option String := none) (abbreviation : Option String := none) : ZoneRules :=
|
||||
let offset : Offset := { second }
|
||||
{
|
||||
transitions := #[],
|
||||
initialLocalTimeType := {
|
||||
gmtOffset := offset,
|
||||
isDst := false, abbreviation := abbreviation.getD (offset.toIsoString true),
|
||||
wall := .standard,
|
||||
utLocal := .ut,
|
||||
identifier := identifier.getD (offset.toIsoString true)
|
||||
}
|
||||
}
|
||||
|
||||
/--
|
||||
Creates ZoneRules with a fixed offset of UTC (GMT+0).
|
||||
-/
|
||||
@[inline]
|
||||
def UTC : ZoneRules :=
|
||||
fixedOffsetZone 0 "UTC" "UTC"
|
||||
|
||||
/--
|
||||
Finds the `LocalTimeType` corresponding to a given `Timestamp` in `ZoneRules`.
|
||||
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
||||
If no transition is found, it falls back to `initialLocalTimeType`.
|
||||
-/
|
||||
@[inline]
|
||||
def findLocalTimeTypeForTimestamp (zr : ZoneRules) (timestamp : Timestamp) : LocalTimeType :=
|
||||
Transition.findTransitionForTimestamp zr.transitions timestamp
|
||||
|>.map (·.localTimeType)
|
||||
|>.getD zr.initialLocalTimeType
|
||||
|
||||
/--
|
||||
Find the current `TimeZone` out of a `Transition` in a `ZoneRules`
|
||||
-/
|
||||
@[inline]
|
||||
def timezoneAt (zr : ZoneRules) (tm : Timestamp) : TimeZone :=
|
||||
Transition.timezoneAt zr.transitions tm
|
||||
|>.toOption
|
||||
|>.getD (zr.initialLocalTimeType |>.getTimeZone)
|
||||
|
||||
/--
|
||||
Creates `ZoneRules` for the given `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofTimeZone (tz : TimeZone) : ZoneRules :=
|
||||
let ltt := LocalTimeType.mk tz.offset tz.isDST tz.abbreviation .wall .local tz.name
|
||||
ZoneRules.mk ltt #[]
|
||||
|
||||
end ZoneRules
|
||||
587
src/Std/Time/Zoned/ZonedDateTime.lean
Normal file
587
src/Std/Time/Zoned/ZonedDateTime.lean
Normal file
@@ -0,0 +1,587 @@
|
||||
/-
|
||||
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Sofia Rodrigues
|
||||
-/
|
||||
prelude
|
||||
import Std.Time.Zoned.DateTime
|
||||
import Std.Time.Zoned.ZoneRules
|
||||
|
||||
namespace Std
|
||||
namespace Time
|
||||
|
||||
set_option linter.all true
|
||||
|
||||
/--
|
||||
Represents a date and time with timezone information.
|
||||
-/
|
||||
structure ZonedDateTime where
|
||||
private mk::
|
||||
|
||||
/--
|
||||
The plain datetime component, evaluated lazily.
|
||||
-/
|
||||
date : Thunk PlainDateTime
|
||||
|
||||
/--
|
||||
The corresponding timestamp for the datetime.
|
||||
-/
|
||||
timestamp : Timestamp
|
||||
|
||||
/--
|
||||
The timezone rules applied to this datetime.
|
||||
-/
|
||||
rules : TimeZone.ZoneRules
|
||||
|
||||
/--
|
||||
The timezone associated with this datetime.
|
||||
-/
|
||||
timezone : TimeZone
|
||||
|
||||
instance : Inhabited ZonedDateTime where
|
||||
default := ⟨Thunk.mk Inhabited.default, Inhabited.default, Inhabited.default, Inhabited.default⟩
|
||||
|
||||
namespace ZonedDateTime
|
||||
open DateTime
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `Timestamp` and a `ZoneRules`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofTimestamp (tm : Timestamp) (rules : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
let tz := rules.timezoneAt tm
|
||||
ZonedDateTime.mk (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC.addSeconds tz.toSeconds) tm rules tz
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `PlainDateTime` and a `ZoneRules`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTime (pdt : PlainDateTime) (zr : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
let tm := pdt.toTimestampAssumingUTC
|
||||
|
||||
let transition :=
|
||||
let value := tm.toSecondsSinceUnixEpoch
|
||||
if let some idx := zr.transitions.findIdx? (fun t => t.time.val ≥ value.val)
|
||||
then do
|
||||
let last ← zr.transitions.get? (idx - 1)
|
||||
let next ← zr.transitions.get? idx <|> zr.transitions.back?
|
||||
|
||||
let utcNext := next.time.sub last.localTimeType.gmtOffset.second.abs
|
||||
|
||||
if utcNext.val > tm.toSecondsSinceUnixEpoch.val
|
||||
then pure last
|
||||
else pure next
|
||||
|
||||
else zr.transitions.back?
|
||||
|
||||
let tz :=
|
||||
transition
|
||||
|>.map (·.localTimeType)
|
||||
|>.getD zr.initialLocalTimeType
|
||||
|>.getTimeZone
|
||||
|
||||
let tm := tm.subSeconds tz.toSeconds
|
||||
ZonedDateTime.mk (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC.addSeconds tz.toSeconds) tm zr tz
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `Timestamp` and a `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofTimestampWithZone (tm : Timestamp) (tz : TimeZone) : ZonedDateTime :=
|
||||
ofTimestamp tm (TimeZone.ZoneRules.ofTimeZone tz)
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `PlainDateTime` and a `TimeZone`.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTimeWithZone (tm : PlainDateTime) (tz : TimeZone) : ZonedDateTime :=
|
||||
ofPlainDateTime tm (TimeZone.ZoneRules.ofTimeZone tz)
|
||||
|
||||
/--
|
||||
Creates a new `Timestamp` out of a `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def toTimestamp (date : ZonedDateTime) : Timestamp :=
|
||||
date.timestamp
|
||||
|
||||
/--
|
||||
Changes the `ZoleRules` to a new one.
|
||||
-/
|
||||
@[inline]
|
||||
def convertZoneRules (date : ZonedDateTime) (tz₁ : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
ofTimestamp date.toTimestamp tz₁
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` out of a `PlainDateTime`. It assumes that the `PlainDateTime` is relative
|
||||
to UTC.
|
||||
-/
|
||||
@[inline]
|
||||
def ofPlainDateTimeAssumingUTC (date : PlainDateTime) (tz : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
ofTimestamp date.toTimestampAssumingUTC tz
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to a `PlainDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toPlainDateTime (dt : ZonedDateTime) : PlainDateTime :=
|
||||
dt.date.get
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to a `PlainDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def toDateTime (dt : ZonedDateTime) : DateTime dt.timezone :=
|
||||
DateTime.ofTimestamp dt.timestamp dt.timezone
|
||||
|
||||
/--
|
||||
Getter for the `PlainTime` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def time (zdt : ZonedDateTime) : PlainTime :=
|
||||
zdt.date.get.time
|
||||
|
||||
/--
|
||||
Getter for the `Year` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def year (zdt : ZonedDateTime) : Year.Offset :=
|
||||
zdt.date.get.year
|
||||
|
||||
/--
|
||||
Getter for the `Month` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def month (zdt : ZonedDateTime) : Month.Ordinal :=
|
||||
zdt.date.get.month
|
||||
|
||||
/--
|
||||
Getter for the `Day` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def day (zdt : ZonedDateTime) : Day.Ordinal :=
|
||||
zdt.date.get.day
|
||||
|
||||
/--
|
||||
Getter for the `Hour` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def hour (zdt : ZonedDateTime) : Hour.Ordinal :=
|
||||
zdt.date.get.time.hour
|
||||
|
||||
/--
|
||||
Getter for the `Minute` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def minute (zdt : ZonedDateTime) : Minute.Ordinal :=
|
||||
zdt.date.get.minute
|
||||
|
||||
/--
|
||||
Getter for the `Second` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def second (zdt : ZonedDateTime) : Second.Ordinal zdt.date.get.time.second.fst :=
|
||||
zdt.date.get.time.second.snd
|
||||
|
||||
/--
|
||||
Getter for the `Millisecond` inside of a `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def millisecond (dt : ZonedDateTime) : Millisecond.Ordinal :=
|
||||
dt.date.get.time.millisecond
|
||||
|
||||
/--
|
||||
Getter for the `Nanosecond` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def nanosecond (zdt : ZonedDateTime) : Nanosecond.Ordinal :=
|
||||
zdt.date.get.time.nanosecond
|
||||
|
||||
/--
|
||||
Getter for the `TimeZone.Offset` inside of a `ZonedDateTime`
|
||||
-/
|
||||
@[inline]
|
||||
def offset (zdt : ZonedDateTime) : TimeZone.Offset :=
|
||||
zdt.timezone.offset
|
||||
|
||||
/--
|
||||
Returns the weekday.
|
||||
-/
|
||||
@[inline]
|
||||
def weekday (zdt : ZonedDateTime) : Weekday :=
|
||||
zdt.date.get.weekday
|
||||
|
||||
/--
|
||||
Transforms a tuple of a `ZonedDateTime` into a `Day.Ordinal.OfYear`.
|
||||
-/
|
||||
@[inline]
|
||||
def dayOfYear (date : ZonedDateTime) : Day.Ordinal.OfYear date.year.isLeap :=
|
||||
ValidDate.dayOfYear ⟨(date.month, date.day), date.date.get.date.valid⟩
|
||||
|
||||
/--
|
||||
Determines the week of the year for the given `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def weekOfYear (date : ZonedDateTime) : Week.Ordinal :=
|
||||
date.date.get.weekOfYear
|
||||
|
||||
/--
|
||||
Returns the unaligned week of the month for a `ZonedDateTime` (day divided by 7, plus 1).
|
||||
-/
|
||||
def weekOfMonth (date : ZonedDateTime) : Internal.Bounded.LE 1 5 :=
|
||||
date.date.get.weekOfMonth
|
||||
|
||||
/--
|
||||
Determines the week of the month for the given `ZonedDateTime`. The week of the month is calculated based
|
||||
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
||||
based on the Gregorian Calendar.
|
||||
-/
|
||||
@[inline]
|
||||
def alignedWeekOfMonth (date : ZonedDateTime) : Week.Ordinal.OfMonth :=
|
||||
date.date.get.alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
Determines the quarter of the year for the given `ZonedDateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def quarter (date : ZonedDateTime) : Internal.Bounded.LE 1 4 :=
|
||||
date.date.get.quarter
|
||||
|
||||
/--
|
||||
Add `Day.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addDays (dt : ZonedDateTime) (days : Day.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addDays days).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Day.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subDays (dt : ZonedDateTime) (days : Day.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subDays days).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Week.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addWeeks (dt : ZonedDateTime) (weeks : Week.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addWeeks weeks).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Week.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subWeeks (dt : ZonedDateTime) (weeks : Week.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subWeeks weeks).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Month.Offset` to a `ZonedDateTime`, clipping to the last valid day.
|
||||
-/
|
||||
def addMonthsClip (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addMonthsClip months).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Month.Offset` from a `ZonedDateTime`, clipping to the last valid day.
|
||||
-/
|
||||
def subMonthsClip (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subMonthsClip months).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Month.Offset` to a `ZonedDateTime`, rolling over excess days.
|
||||
-/
|
||||
def addMonthsRollOver (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addMonthsRollOver months).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Month.Offset` from a `ZonedDateTime`, rolling over excess days.
|
||||
-/
|
||||
def subMonthsRollOver (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subMonthsRollOver months).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Year.Offset` to a `ZonedDateTime`, rolling over excess days.
|
||||
-/
|
||||
def addYearsRollOver (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addYearsRollOver years).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Year.Offset` to a `ZonedDateTime`, clipping to the last valid day.
|
||||
-/
|
||||
def addYearsClip (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addYearsClip years).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Year.Offset` from a `ZonedDateTime`, clipping to the last valid day.
|
||||
-/
|
||||
def subYearsClip (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subYearsClip years).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Year.Offset` from a `ZonedDateTime`, rolling over excess days.
|
||||
-/
|
||||
def subYearsRollOver (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subYearsRollOver years).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Hour.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addHours (dt : ZonedDateTime) (hours : Hour.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addHours hours).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Hour.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subHours (dt : ZonedDateTime) (hours : Hour.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subHours hours).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Minute.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addMinutes (dt : ZonedDateTime) (minutes : Minute.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addMinutes minutes).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Minute.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subMinutes (dt : ZonedDateTime) (minutes : Minute.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subMinutes minutes).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Millisecond.Offset` to a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def addMilliseconds (dt : ZonedDateTime) (milliseconds : Millisecond.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addMilliseconds milliseconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Millisecond.Offset` from a `DateTime`.
|
||||
-/
|
||||
@[inline]
|
||||
def subMilliseconds (dt : ZonedDateTime) (milliseconds : Millisecond.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subMilliseconds milliseconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Second.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addSeconds (dt : ZonedDateTime) (seconds : Second.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addSeconds seconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Second.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subSeconds (dt : ZonedDateTime) (seconds : Second.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subSeconds seconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Add `Nanosecond.Offset` to a `ZonedDateTime`.
|
||||
-/
|
||||
def addNanoseconds (dt : ZonedDateTime) (nanoseconds : Nanosecond.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.addNanoseconds nanoseconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Subtract `Nanosecond.Offset` from a `ZonedDateTime`.
|
||||
-/
|
||||
def subNanoseconds (dt : ZonedDateTime) (nanoseconds : Nanosecond.Offset) : ZonedDateTime :=
|
||||
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
||||
ZonedDateTime.ofTimestamp (date.subNanoseconds nanoseconds).toTimestampAssumingUTC dt.rules
|
||||
|
||||
/--
|
||||
Determines the era of the given `ZonedDateTime` based on its year.
|
||||
-/
|
||||
@[inline]
|
||||
def era (date : ZonedDateTime) : Year.Era :=
|
||||
date.date.get.era
|
||||
|
||||
/--
|
||||
Sets the `ZonedDateTime` to the specified `desiredWeekday`.
|
||||
-/
|
||||
def withWeekday (dt : ZonedDateTime) (desiredWeekday : Weekday) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withWeekday desiredWeekday) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days clipped to the nearest valid date.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysClip (dt : ZonedDateTime) (days : Day.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withDaysClip days) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the day of the month to the given `days` value, with any
|
||||
out-of-range days rolled over to the next month or year as needed.
|
||||
-/
|
||||
@[inline]
|
||||
def withDaysRollOver (dt : ZonedDateTime) (days : Day.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withDaysRollOver days) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the month to the given `month` value.
|
||||
The day remains unchanged, and any invalid days for the new month will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthClip (dt : ZonedDateTime) (month : Month.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withMonthClip month) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the month to the given `month` value.
|
||||
The day is rolled over to the next valid month if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withMonthRollOver (dt : ZonedDateTime) (month : Month.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withMonthRollOver month) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the year to the given `year` value. The month and day remain unchanged,
|
||||
and any invalid days for the new year will be handled according to the `clip` behavior.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearClip (dt : ZonedDateTime) (year : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
|
||||
ZonedDateTime.ofPlainDateTime (date.withYearClip year) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the year to the given `year` value. The month and day are rolled
|
||||
over to the next valid month and day if necessary.
|
||||
-/
|
||||
@[inline]
|
||||
def withYearRollOver (dt : ZonedDateTime) (year : Year.Offset) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withYearRollOver year) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the `hour` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withHours (dt : ZonedDateTime) (hour : Hour.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withHours hour) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the `minute` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withMinutes (dt : ZonedDateTime) (minute : Minute.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withMinutes minute) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the `second` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withSeconds (dt : ZonedDateTime) (second : Sigma Second.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withSeconds second) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the `nano` component with a new `millis` that will set
|
||||
in the millisecond scale.
|
||||
-/
|
||||
@[inline]
|
||||
def withMilliseconds (dt : ZonedDateTime) (millis : Millisecond.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withMilliseconds millis) dt.rules
|
||||
|
||||
/--
|
||||
Creates a new `ZonedDateTime` by adjusting the `nano` component.
|
||||
-/
|
||||
@[inline]
|
||||
def withNanoseconds (dt : ZonedDateTime) (nano : Nanosecond.Ordinal) : ZonedDateTime :=
|
||||
let date := dt.date.get
|
||||
ZonedDateTime.ofPlainDateTime (date.withNanoseconds nano) dt.rules
|
||||
|
||||
/--
|
||||
Checks if the `ZonedDateTime` is in a leap year.
|
||||
-/
|
||||
def inLeapYear (date : ZonedDateTime) : Bool :=
|
||||
date.year.isLeap
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
def toDaysSinceUNIXEpoch (date : ZonedDateTime) : Day.Offset :=
|
||||
date.date.get.toDaysSinceUNIXEpoch
|
||||
|
||||
/--
|
||||
Converts a `ZonedDateTime` to the number of days since the UNIX epoch.
|
||||
-/
|
||||
@[inline]
|
||||
def ofDaysSinceUNIXEpoch (days : Day.Offset) (time : PlainTime) (zt : TimeZone.ZoneRules) : ZonedDateTime :=
|
||||
ZonedDateTime.ofPlainDateTime (PlainDateTime.ofDaysSinceUNIXEpoch days time) zt
|
||||
|
||||
instance : HAdd ZonedDateTime Day.Offset ZonedDateTime where
|
||||
hAdd := addDays
|
||||
|
||||
instance : HSub ZonedDateTime Day.Offset ZonedDateTime where
|
||||
hSub := subDays
|
||||
|
||||
instance : HAdd ZonedDateTime Week.Offset ZonedDateTime where
|
||||
hAdd := addWeeks
|
||||
|
||||
instance : HSub ZonedDateTime Week.Offset ZonedDateTime where
|
||||
hSub := subWeeks
|
||||
|
||||
instance : HAdd ZonedDateTime Hour.Offset ZonedDateTime where
|
||||
hAdd := addHours
|
||||
|
||||
instance : HSub ZonedDateTime Hour.Offset ZonedDateTime where
|
||||
hSub := subHours
|
||||
|
||||
instance : HAdd ZonedDateTime Minute.Offset ZonedDateTime where
|
||||
hAdd := addMinutes
|
||||
|
||||
instance : HSub ZonedDateTime Minute.Offset ZonedDateTime where
|
||||
hSub := subMinutes
|
||||
|
||||
instance : HAdd ZonedDateTime Second.Offset ZonedDateTime where
|
||||
hAdd := addSeconds
|
||||
|
||||
instance : HSub ZonedDateTime Second.Offset ZonedDateTime where
|
||||
hSub := subSeconds
|
||||
|
||||
instance : HAdd ZonedDateTime Millisecond.Offset ZonedDateTime where
|
||||
hAdd := addMilliseconds
|
||||
|
||||
instance : HSub ZonedDateTime Millisecond.Offset ZonedDateTime where
|
||||
hSub := subMilliseconds
|
||||
|
||||
instance : HAdd ZonedDateTime Nanosecond.Offset ZonedDateTime where
|
||||
hAdd := addNanoseconds
|
||||
|
||||
instance : HSub ZonedDateTime Nanosecond.Offset ZonedDateTime where
|
||||
hSub := subNanoseconds
|
||||
|
||||
instance : HSub ZonedDateTime ZonedDateTime Duration where
|
||||
hSub x y := x.toTimestamp - y.toTimestamp
|
||||
|
||||
instance : HAdd ZonedDateTime Duration ZonedDateTime where
|
||||
hAdd x y := x.addNanoseconds y.toNanoseconds
|
||||
|
||||
instance : HSub ZonedDateTime Duration ZonedDateTime where
|
||||
hSub x y := x.subNanoseconds y.toNanoseconds
|
||||
|
||||
end ZonedDateTime
|
||||
end Time
|
||||
end Std
|
||||
@@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Sebastian Ullrich
|
||||
*/
|
||||
#if defined(LEAN_WINDOWS)
|
||||
#include <icu.h>
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#define NOMINMAX // prevent ntdef.h from defining min/max macros
|
||||
@@ -630,6 +631,176 @@ extern "C" LEAN_EXPORT obj_res lean_io_prim_handle_put_str(b_obj_arg h, b_obj_ar
|
||||
}
|
||||
}
|
||||
|
||||
/* Std.Time.Timestamp.now : IO Timestamp */
|
||||
extern "C" LEAN_EXPORT obj_res lean_get_current_time(obj_arg /* w */) {
|
||||
using namespace std::chrono;
|
||||
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
long long timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
|
||||
|
||||
long long secs = timestamp / 1000000000;
|
||||
long long nano = timestamp % 1000000000;
|
||||
|
||||
lean_object *lean_ts = lean_alloc_ctor(0, 2, 0);
|
||||
lean_ctor_set(lean_ts, 0, lean_int64_to_int(secs));
|
||||
lean_ctor_set(lean_ts, 1, lean_int64_to_int(nano));
|
||||
|
||||
return lean_io_result_mk_ok(lean_ts);
|
||||
}
|
||||
|
||||
/* Std.Time.Database.Windows.getNextTransition : @&String -> Int64 -> Bool -> IO (Option (Int64 × TimeZone)) */
|
||||
extern "C" LEAN_EXPORT obj_res lean_windows_get_next_transition(b_obj_arg timezone_str, uint64_t tm_obj, uint8 default_time, obj_arg /* w */) {
|
||||
#if defined(LEAN_WINDOWS)
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const char* dst_name_id = lean_string_cstr(timezone_str);
|
||||
|
||||
UChar tzID[256];
|
||||
u_strFromUTF8(tzID, sizeof(tzID) / sizeof(tzID[0]), NULL, dst_name_id, strlen(dst_name_id), &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to read identifier")));
|
||||
}
|
||||
|
||||
UCalendar *cal = ucal_open(tzID, -1, NULL, UCAL_GREGORIAN, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to open calendar")));
|
||||
}
|
||||
|
||||
int64_t tm = 0;
|
||||
|
||||
if (!default_time) {
|
||||
int64_t timestamp_secs = (int64_t)tm_obj;
|
||||
|
||||
ucal_setMillis(cal, timestamp_secs * 1000, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to set calendar time")));
|
||||
}
|
||||
|
||||
UDate nextTransition;
|
||||
if (!ucal_getTimeZoneTransitionDate(cal, UCAL_TZ_TRANSITION_NEXT, &nextTransition, &status)) {
|
||||
ucal_close(cal);
|
||||
return io_result_mk_ok(mk_option_none());
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get next transation")));
|
||||
}
|
||||
|
||||
tm = (int64_t)(nextTransition / 1000.0);
|
||||
}
|
||||
|
||||
int32_t dst_offset = ucal_get(cal, UCAL_DST_OFFSET, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get dst_offset")));
|
||||
}
|
||||
|
||||
int is_dst = dst_offset != 0;
|
||||
|
||||
int32_t tzIDLength = ucal_getTimeZoneDisplayName(cal, is_dst ? UCAL_DST : UCAL_STANDARD, "en_US", tzID, 32, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to timezone identifier")));
|
||||
}
|
||||
|
||||
char dst_name[256];
|
||||
int32_t dst_name_len;
|
||||
u_strToUTF8(dst_name, sizeof(dst_name), &dst_name_len, tzID, tzIDLength, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to convert DST name to UTF-8")));
|
||||
}
|
||||
|
||||
UChar display_name[32];
|
||||
int32_t display_name_len = ucal_getTimeZoneDisplayName(cal, is_dst ? UCAL_SHORT_DST : UCAL_SHORT_STANDARD, "en_US", display_name, 32, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to read abbreaviation")));
|
||||
}
|
||||
|
||||
char display_name_str[256];
|
||||
int32_t display_name_str_len;
|
||||
u_strToUTF8(display_name_str, sizeof(display_name_str), &display_name_str_len, display_name, display_name_len, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get abbreviation to cstr")));
|
||||
}
|
||||
|
||||
int32_t zone_offset = ucal_get(cal, UCAL_ZONE_OFFSET, &status);
|
||||
zone_offset += dst_offset;
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get zone_offset")));
|
||||
}
|
||||
|
||||
ucal_close(cal);
|
||||
|
||||
int offset_seconds = zone_offset / 1000;
|
||||
|
||||
lean_object *lean_tz = lean_alloc_ctor(0, 3, 1);
|
||||
lean_ctor_set(lean_tz, 0, lean_int_to_int(offset_seconds));
|
||||
lean_ctor_set(lean_tz, 1, lean_mk_string_from_bytes_unchecked(dst_name, dst_name_len));
|
||||
lean_ctor_set(lean_tz, 2, lean_mk_string_from_bytes_unchecked(display_name_str, display_name_str_len));
|
||||
lean_ctor_set_uint8(lean_tz, sizeof(void*)*3, is_dst);
|
||||
|
||||
lean_object *lean_pair = lean_alloc_ctor(0, 2, 0);
|
||||
lean_ctor_set(lean_pair, 0, lean_box_uint64((uint64_t)tm));
|
||||
lean_ctor_set(lean_pair, 1, lean_tz);
|
||||
|
||||
return lean_io_result_mk_ok(mk_option_some(lean_pair));
|
||||
#else
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get timezone, its windows only.")));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Std.Time.Database.Windows.getLocalTimeZoneIdentifierAt : Int64 → IO String */
|
||||
extern "C" LEAN_EXPORT obj_res lean_get_windows_local_timezone_id_at(uint64_t tm_obj, obj_arg /* w */) {
|
||||
#if defined(LEAN_WINDOWS)
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCalendar* cal = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to open calendar")));
|
||||
}
|
||||
|
||||
int64_t timestamp_secs = (int64_t)tm_obj;
|
||||
ucal_setMillis(cal, timestamp_secs * 1000, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
ucal_close(cal);
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to set calendar time")));
|
||||
}
|
||||
|
||||
UChar tzId[256];
|
||||
int32_t tzIdLength = ucal_getTimeZoneID(cal, tzId, sizeof(tzId) / sizeof(tzId[0]), &status);
|
||||
ucal_close(cal);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to get timezone ID")));
|
||||
}
|
||||
|
||||
char tzIdStr[256];
|
||||
u_strToUTF8(tzIdStr, sizeof(tzIdStr), NULL, tzId, tzIdLength, &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("failed to convert timezone ID to UTF-8")));
|
||||
}
|
||||
|
||||
return lean_io_result_mk_ok(lean_mk_ascii_string_unchecked(tzIdStr));
|
||||
#else
|
||||
return lean_io_result_mk_error(lean_decode_io_error(EINVAL, mk_string("timezone retrieval is Windows-only")));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* monoMsNow : BaseIO Nat */
|
||||
extern "C" LEAN_EXPORT obj_res lean_io_mono_ms_now(obj_arg /* w */) {
|
||||
static_assert(sizeof(std::chrono::milliseconds::rep) <= sizeof(uint64), "size of std::chrono::nanoseconds::rep may not exceed 64");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Lean.Data.Rat
|
||||
import Std.Internal.Rat
|
||||
open Std.Internal
|
||||
|
||||
/-!
|
||||
Test the `rightact%` elaborator for `HPow.hPow`, added to address #2854
|
||||
@@ -14,7 +15,7 @@ variable (n : Nat) (m : Int) (q : Rat)
|
||||
#check (n ^ 2 + (1 : Nat) : Int)
|
||||
|
||||
instance instNatPowRat : NatPow Rat where
|
||||
pow q n := Lean.mkRat (q.num ^ n) (q.den ^ n)
|
||||
pow q n := Std.Internal.mkRat (q.num ^ n) (q.den ^ n)
|
||||
|
||||
instance instPowRatInt : Pow Rat Int where
|
||||
pow q m := if 0 ≤ m then q ^ (m.toNat : Nat) else (1/q) ^ ((-m).toNat)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Lean.Data.Rat
|
||||
import Std.Internal.Rat
|
||||
open Std.Internal
|
||||
|
||||
/-!
|
||||
Tests for `pp.numericTypes` and `pp.natLit`
|
||||
@@ -6,7 +7,7 @@ Tests for `pp.numericTypes` and `pp.natLit`
|
||||
RFC #3021
|
||||
-/
|
||||
|
||||
open Lean (Rat)
|
||||
open Lean
|
||||
|
||||
section
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Lean.Data.Rat
|
||||
import Std.Internal.Rat
|
||||
open Std.Internal
|
||||
open Lean
|
||||
|
||||
#eval (15 : Rat) / 10
|
||||
|
||||
@@ -6,6 +6,6 @@ open Meta -- ok
|
||||
end
|
||||
|
||||
section
|
||||
open Lean hiding Rat
|
||||
open Lean hiding AttributeImplCore
|
||||
open Meta -- ok
|
||||
end
|
||||
|
||||
908
tests/lean/run/timeAPI.lean
Normal file
908
tests/lean/run/timeAPI.lean
Normal file
@@ -0,0 +1,908 @@
|
||||
import Std.Time
|
||||
import Init
|
||||
open Std.Time
|
||||
|
||||
def sao_paulo : TimeZone.ZoneRules :=
|
||||
{
|
||||
initialLocalTimeType :=
|
||||
{
|
||||
gmtOffset := { second := -11188 },
|
||||
isDst := false,
|
||||
abbreviation := "LMT",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
},
|
||||
transitions := #[
|
||||
{
|
||||
time := 782276400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 793159200,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 813726000,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 824004000,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 844570800,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 856058400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 876106800,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 888717600,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 908074800,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 919562400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 938919600,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 951616800,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 970974000,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 982461600,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 1003028400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 1013911200,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 1036292400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 1045360800,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -10800 },
|
||||
isDst := false,
|
||||
abbreviation := "-03",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
},
|
||||
{ time := 1066532400,
|
||||
localTimeType := {
|
||||
gmtOffset := { second := -7200 },
|
||||
isDst := true,
|
||||
abbreviation := "-02",
|
||||
wall := Std.Time.TimeZone.StdWall.standard,
|
||||
utLocal := Std.Time.TimeZone.UTLocal.ut,
|
||||
identifier := "America/Sao_Paulo"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/-
|
||||
Unit conversion tests.
|
||||
-/
|
||||
|
||||
/--
|
||||
info: --- nanosecond
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- millisecond
|
||||
nanoseconds: 1209600000000000
|
||||
seconds: 1209600
|
||||
milliseconds: 1209600000
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- second
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- minute
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- hour
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- day
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
--- week
|
||||
nanoseconds: 1209600000000000
|
||||
milliseconds: 1209600000
|
||||
seconds: 1209600
|
||||
minutes: 20160
|
||||
hours: 336
|
||||
days: 14
|
||||
weeks: 2
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let nanosecond: Nanosecond.Offset := 1209600000000000
|
||||
println! s!"--- nanosecond"
|
||||
println! s!"nanoseconds: {nanosecond}"
|
||||
println! s!"milliseconds: {nanosecond.toMilliseconds}"
|
||||
println! s!"seconds: {nanosecond.toSeconds}"
|
||||
println! s!"minutes: {nanosecond.toMinutes}"
|
||||
println! s!"hours: {nanosecond.toHours}"
|
||||
println! s!"days: {nanosecond.toDays}"
|
||||
println! s!"weeks: {nanosecond.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let millisecond: Millisecond.Offset := 1209600000
|
||||
println! s!"--- millisecond"
|
||||
println! s!"nanoseconds: {millisecond.toNanoseconds}"
|
||||
println! s!"seconds: {millisecond.toSeconds}"
|
||||
println! s!"milliseconds: {millisecond}"
|
||||
println! s!"minutes: {millisecond.toMinutes}"
|
||||
println! s!"hours: {millisecond.toHours}"
|
||||
println! s!"days: {millisecond.toDays}"
|
||||
println! s!"weeks: {millisecond.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let second : Second.Offset := 1209600
|
||||
println! s!"--- second"
|
||||
println! s!"nanoseconds: {second.toNanoseconds}"
|
||||
println! s!"milliseconds: {second.toMilliseconds}"
|
||||
println! s!"seconds: {second}"
|
||||
println! s!"minutes: {second.toMinutes}"
|
||||
println! s!"hours: {second.toHours}"
|
||||
println! s!"days: {second.toDays}"
|
||||
println! s!"weeks: {second.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let minute: Minute.Offset := 20160
|
||||
println! s!"--- minute"
|
||||
println! s!"nanoseconds: {minute.toNanoseconds}"
|
||||
println! s!"milliseconds: {minute.toMilliseconds}"
|
||||
println! s!"seconds: {minute.toSeconds}"
|
||||
println! s!"minutes: {minute}"
|
||||
println! s!"hours: {minute.toHours}"
|
||||
println! s!"days: {minute.toDays}"
|
||||
println! s!"weeks: {minute.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let hour: Hour.Offset := 336
|
||||
println! s!"--- hour"
|
||||
println! s!"nanoseconds: {hour.toNanoseconds}"
|
||||
println! s!"milliseconds: {hour.toMilliseconds}"
|
||||
println! s!"seconds: {hour.toSeconds}"
|
||||
println! s!"minutes: {hour.toMinutes}"
|
||||
println! s!"hours: {hour}"
|
||||
println! s!"days: {hour.toDays}"
|
||||
println! s!"weeks: {hour.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let day: Day.Offset := 14
|
||||
println! s!"--- day"
|
||||
println! s!"nanoseconds: {day.toNanoseconds}"
|
||||
println! s!"milliseconds: {day.toMilliseconds}"
|
||||
println! s!"seconds: {day.toSeconds}"
|
||||
println! s!"minutes: {day.toMinutes}"
|
||||
println! s!"hours: {day.toHours}"
|
||||
println! s!"days: {day}"
|
||||
println! s!"weeks: {day.toWeeks}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
let week: Week.Offset := 2
|
||||
println! s!"--- week"
|
||||
println! s!"nanoseconds: {week.toNanoseconds}"
|
||||
println! s!"milliseconds: {week.toMilliseconds}"
|
||||
println! s!"seconds: {week.toSeconds}"
|
||||
println! s!"minutes: {week.toMinutes}"
|
||||
println! s!"hours: {week.toHours}"
|
||||
println! s!"days: {week.toDays}"
|
||||
println! s!"weeks: {week}"
|
||||
-- Cannot do this for months or years because sizes are variable.
|
||||
|
||||
theorem nano_nano_nano : (1 : Nanosecond.Offset) + (1 : Nanosecond.Offset) = (2 : Nanosecond.Offset) := rfl
|
||||
theorem nano_milli_nano : (1 : Nanosecond.Offset) + (1 : Millisecond.Offset) = (1000001 : Nanosecond.Offset) := rfl
|
||||
theorem nano_sec_nano : (1 : Nanosecond.Offset) + (1 : Second.Offset) = (1000000001 : Nanosecond.Offset) := rfl
|
||||
theorem nano_min_nano : (1 : Nanosecond.Offset) + (1 : Minute.Offset) = (60000000001 : Nanosecond.Offset) := rfl
|
||||
theorem nano_hour_nano : (1 : Nanosecond.Offset) + (1 : Hour.Offset) = (3600000000001 : Nanosecond.Offset) := rfl
|
||||
theorem nano_day_nano : (1 : Nanosecond.Offset) + (1 : Day.Offset) = (86400000000001 : Nanosecond.Offset) := rfl
|
||||
theorem nano_week_nano : (1 : Nanosecond.Offset) + (1 : Week.Offset) = (604800000000001 : Nanosecond.Offset) := rfl
|
||||
|
||||
theorem milli_nano_nano : (1 : Millisecond.Offset) + (1 : Nanosecond.Offset) = (1000001 : Nanosecond.Offset) := rfl
|
||||
theorem milli_milli_milli : (1 : Millisecond.Offset) + (1 : Millisecond.Offset) = (2 : Millisecond.Offset) := rfl
|
||||
theorem milli_sec_milli : (1 : Millisecond.Offset) + (1 : Second.Offset) = (1001 : Millisecond.Offset) := rfl
|
||||
theorem milli_min_milli : (1 : Millisecond.Offset) + (1 : Minute.Offset) = (60001 : Millisecond.Offset) := rfl
|
||||
theorem milli_hour_milli : (1 : Millisecond.Offset) + (1 : Hour.Offset) = (3600001 : Millisecond.Offset) := rfl
|
||||
theorem milli_day_milli : (1 : Millisecond.Offset) + (1 : Day.Offset) = (86400001 : Millisecond.Offset) := rfl
|
||||
theorem milli_week_milli : (1 : Millisecond.Offset) + (1 : Week.Offset) = (604800001 : Millisecond.Offset) := rfl
|
||||
|
||||
theorem sec_nano_nano : (1 : Second.Offset) + (1 : Nanosecond.Offset) = (1000000001 : Nanosecond.Offset) := rfl
|
||||
theorem sec_milli_milli : (1 : Second.Offset) + (1 : Millisecond.Offset) = (1001 : Millisecond.Offset) := rfl
|
||||
theorem sec_sec_sec : (1 : Second.Offset) + (1 : Second.Offset) = (2 : Second.Offset) := rfl
|
||||
theorem sec_min_sec : (1 : Second.Offset) + (1 : Minute.Offset) = (61 : Second.Offset) := rfl
|
||||
theorem sec_hour_sec : (1 : Second.Offset) + (1 : Hour.Offset) = (3601 : Second.Offset) := rfl
|
||||
theorem sec_day_sec : (1 : Second.Offset) + (1 : Day.Offset) = (86401 : Second.Offset) := rfl
|
||||
theorem sec_week_sec : (1 : Second.Offset) + (1 : Week.Offset) = (604801 : Second.Offset) := rfl
|
||||
|
||||
theorem min_nano_nano : (1 : Minute.Offset) + (1 : Nanosecond.Offset) = (60000000001 : Nanosecond.Offset) := rfl
|
||||
theorem min_milli_milli : (1 : Minute.Offset) + (1 : Millisecond.Offset) = (60001 : Millisecond.Offset) := rfl
|
||||
theorem min_sec_sec : (1 : Minute.Offset) + (1 : Second.Offset) = (61 : Second.Offset) := rfl
|
||||
theorem min_min_min : (1 : Minute.Offset) + (1 : Minute.Offset) = (2 : Minute.Offset) := rfl
|
||||
theorem min_hour_min : (1 : Minute.Offset) + (1 : Hour.Offset) = (61 : Minute.Offset) := rfl
|
||||
theorem min_day_min : (1 : Minute.Offset) + (1 : Day.Offset) = (1441 : Minute.Offset) := rfl
|
||||
theorem min_week_min : (1 : Minute.Offset) + (1 : Week.Offset) = (10081 : Minute.Offset) := rfl
|
||||
|
||||
theorem hour_nano_nano : (1 : Hour.Offset) + (1 : Nanosecond.Offset) = (3600000000001 : Nanosecond.Offset) := rfl
|
||||
theorem hour_milli_milli : (1 : Hour.Offset) + (1 : Millisecond.Offset) = (3600001 : Millisecond.Offset) := rfl
|
||||
theorem hour_sec_sec : (1 : Hour.Offset) + (1 : Second.Offset) = (3601 : Second.Offset) := rfl
|
||||
theorem hour_min_min : (1 : Hour.Offset) + (1 : Minute.Offset) = (61 : Minute.Offset) := rfl
|
||||
theorem hour_hour_hour : (1 : Hour.Offset) + (1 : Hour.Offset) = (2 : Hour.Offset) := rfl
|
||||
theorem hour_day_hour : (1 : Hour.Offset) + (1 : Day.Offset) = (25 : Hour.Offset) := rfl
|
||||
theorem hour_week_hour : (1 : Hour.Offset) + (1 : Week.Offset) = (169 : Hour.Offset) := rfl
|
||||
|
||||
theorem day_nano_nano : (1 : Day.Offset) + (1 : Nanosecond.Offset) = (86400000000001 : Nanosecond.Offset) := rfl
|
||||
theorem day_milli_milli : (1 : Day.Offset) + (1 : Millisecond.Offset) = (86400001 : Millisecond.Offset) := rfl
|
||||
theorem day_sec_sec : (1 : Day.Offset) + (1 : Second.Offset) = (86401 : Second.Offset) := rfl
|
||||
theorem day_min_min : (1 : Day.Offset) + (1 : Minute.Offset) = (1441 : Minute.Offset) := rfl
|
||||
theorem day_hour_hour : (1 : Day.Offset) + (1 : Hour.Offset) = (25 : Hour.Offset) := rfl
|
||||
theorem day_day_day : (1 : Day.Offset) + (1 : Day.Offset) = (2 : Day.Offset) := rfl
|
||||
theorem day_week_day : (1 : Day.Offset) + (1 : Week.Offset) = (8 : Day.Offset) := rfl
|
||||
|
||||
theorem week_nano_nano : (1 : Week.Offset) + (1 : Nanosecond.Offset) = (604800000000001 : Nanosecond.Offset) := rfl
|
||||
theorem week_milli_milli : (1 : Week.Offset) + (1 : Millisecond.Offset) = (604800001 : Millisecond.Offset) := rfl
|
||||
theorem week_sec_sec : (1 : Week.Offset) + (1 : Second.Offset) = (604801 : Second.Offset) := rfl
|
||||
theorem week_min_min : (1 : Week.Offset) + (1 : Minute.Offset) = (10081 : Minute.Offset) := rfl
|
||||
theorem week_hour_hour : (1 : Week.Offset) + (1 : Hour.Offset) = (169 : Hour.Offset) := rfl
|
||||
theorem week_day_day : (1 : Week.Offset) + (1 : Day.Offset) = (8 : Day.Offset) := rfl
|
||||
theorem week_week_week : (1 : Week.Offset) + (1 : Week.Offset) = (2 : Week.Offset) := rfl
|
||||
|
||||
theorem nano_nano_nano_sub : (1 : Nanosecond.Offset) - (1 : Nanosecond.Offset) = (0 : Nanosecond.Offset) := rfl
|
||||
theorem nano_milli_nano_sub : (1 : Nanosecond.Offset) - (1 : Millisecond.Offset) = (-999999 : Nanosecond.Offset) := rfl
|
||||
theorem nano_sec_nano_sub : (1 : Nanosecond.Offset) - (1 : Second.Offset) = (-999999999 : Nanosecond.Offset) := rfl
|
||||
theorem nano_min_nano_sub : (1 : Nanosecond.Offset) - (1 : Minute.Offset) = (-59999999999 : Nanosecond.Offset) := rfl
|
||||
theorem nano_hour_nano_sub : (1 : Nanosecond.Offset) - (1 : Hour.Offset) = (-3599999999999 : Nanosecond.Offset) := rfl
|
||||
theorem nano_day_nano_sub : (1 : Nanosecond.Offset) - (1 : Day.Offset) = (-86399999999999 : Nanosecond.Offset) := rfl
|
||||
theorem nano_week_nano_sub : (1 : Nanosecond.Offset) - (1 : Week.Offset) = (-604799999999999 : Nanosecond.Offset) := rfl
|
||||
|
||||
theorem milli_nano_nano_sub : (1 : Millisecond.Offset) - (1 : Nanosecond.Offset) = (999999 : Nanosecond.Offset) := rfl
|
||||
theorem milli_milli_milli_sub : (1 : Millisecond.Offset) - (1 : Millisecond.Offset) = (0 : Millisecond.Offset) := rfl
|
||||
theorem milli_sec_milli_sub : (1 : Millisecond.Offset) - (1 : Second.Offset) = (-999 : Millisecond.Offset) := rfl
|
||||
theorem milli_min_milli_sub : (1 : Millisecond.Offset) - (1 : Minute.Offset) = (-59999 : Millisecond.Offset) := rfl
|
||||
theorem milli_hour_milli_sub : (1 : Millisecond.Offset) - (1 : Hour.Offset) = (-3599999 : Millisecond.Offset) := rfl
|
||||
theorem milli_day_milli_sub : (1 : Millisecond.Offset) - (1 : Day.Offset) = (-86399999 : Millisecond.Offset) := rfl
|
||||
theorem milli_week_milli_sub : (1 : Millisecond.Offset) - (1 : Week.Offset) = (-604799999 : Millisecond.Offset) := rfl
|
||||
|
||||
theorem sec_nano_nano_sub : (1 : Second.Offset) - (1 : Nanosecond.Offset) = (999999999 : Nanosecond.Offset) := rfl
|
||||
theorem sec_milli_milli_sub : (1 : Second.Offset) - (1 : Millisecond.Offset) = (999 : Millisecond.Offset) := rfl
|
||||
theorem sec_sec_sec_sub : (1 : Second.Offset) - (1 : Second.Offset) = (0 : Second.Offset) := rfl
|
||||
theorem sec_min_sec_sub : (1 : Second.Offset) - (1 : Minute.Offset) = (-59 : Second.Offset) := rfl
|
||||
theorem sec_hour_sec_sub : (1 : Second.Offset) - (1 : Hour.Offset) = (-3599 : Second.Offset) := rfl
|
||||
theorem sec_day_sec_sub : (1 : Second.Offset) - (1 : Day.Offset) = (-86399 : Second.Offset) := rfl
|
||||
theorem sec_week_sec_sub : (1 : Second.Offset) - (1 : Week.Offset) = (-604799 : Second.Offset) := rfl
|
||||
|
||||
theorem min_nano_nano_sub : (1 : Minute.Offset) - (1 : Nanosecond.Offset) = (59999999999 : Nanosecond.Offset) := rfl
|
||||
theorem min_milli_milli_sub : (1 : Minute.Offset) - (1 : Millisecond.Offset) = (59999 : Millisecond.Offset) := rfl
|
||||
theorem min_sec_sec_sub : (1 : Minute.Offset) - (1 : Second.Offset) = (59 : Second.Offset) := rfl
|
||||
theorem min_min_min_sub : (1 : Minute.Offset) - (1 : Minute.Offset) = (0 : Minute.Offset) := rfl
|
||||
theorem min_hour_min_sub : (1 : Minute.Offset) - (1 : Hour.Offset) = (-59 : Minute.Offset) := rfl
|
||||
theorem min_day_min_sub : (1 : Minute.Offset) - (1 : Day.Offset) = (-1439 : Minute.Offset) := rfl
|
||||
theorem min_week_min_sub : (1 : Minute.Offset) - (1 : Week.Offset) = (-10079 : Minute.Offset) := rfl
|
||||
|
||||
theorem hour_nano_nano_sub : (1 : Hour.Offset) - (1 : Nanosecond.Offset) = (3599999999999 : Nanosecond.Offset) := rfl
|
||||
theorem hour_milli_milli_sub : (1 : Hour.Offset) - (1 : Millisecond.Offset) = (3599999 : Millisecond.Offset) := rfl
|
||||
theorem hour_sec_sec_sub : (1 : Hour.Offset) - (1 : Second.Offset) = (3599 : Second.Offset) := rfl
|
||||
theorem hour_min_min_sub : (1 : Hour.Offset) - (1 : Minute.Offset) = (59 : Minute.Offset) := rfl
|
||||
theorem hour_hour_hour_sub : (1 : Hour.Offset) - (1 : Hour.Offset) = (0 : Hour.Offset) := rfl
|
||||
theorem hour_day_hour_sub : (1 : Hour.Offset) - (1 : Day.Offset) = (-23 : Hour.Offset) := rfl
|
||||
theorem hour_week_hour_sub : (1 : Hour.Offset) - (1 : Week.Offset) = (-167 : Hour.Offset) := rfl
|
||||
|
||||
theorem day_nano_nano_sub : (1 : Day.Offset) - (1 : Nanosecond.Offset) = (86399999999999 : Nanosecond.Offset) := rfl
|
||||
theorem day_milli_milli_sub : (1 : Day.Offset) - (1 : Millisecond.Offset) = (86399999 : Millisecond.Offset) := rfl
|
||||
theorem day_sec_sec_sub : (1 : Day.Offset) - (1 : Second.Offset) = (86399 : Second.Offset) := rfl
|
||||
theorem day_min_min_sub : (1 : Day.Offset) - (1 : Minute.Offset) = (1439 : Minute.Offset) := rfl
|
||||
theorem day_hour_hour_sub : (1 : Day.Offset) - (1 : Hour.Offset) = (23 : Hour.Offset) := rfl
|
||||
theorem day_day_day_sub : (1 : Day.Offset) - (1 : Day.Offset) = (0 : Day.Offset) := rfl
|
||||
theorem day_week_day_sub : (1 : Day.Offset) - (1 : Week.Offset) = (-6 : Day.Offset) := rfl
|
||||
|
||||
theorem week_nano_nano_sub : (1 : Week.Offset) - (1 : Nanosecond.Offset) = (604799999999999 : Nanosecond.Offset) := rfl
|
||||
theorem week_milli_milli_sub : (1 : Week.Offset) - (1 : Millisecond.Offset) = (604799999 : Millisecond.Offset) := rfl
|
||||
theorem week_sec_sec_sub : (1 : Week.Offset) - (1 : Second.Offset) = (604799 : Second.Offset) := rfl
|
||||
theorem week_min_min_sub : (1 : Week.Offset) - (1 : Minute.Offset) = (10079 : Minute.Offset) := rfl
|
||||
theorem week_hour_hour_sub : (1 : Week.Offset) - (1 : Hour.Offset) = (167 : Hour.Offset) := rfl
|
||||
theorem week_day_day_sub : (1 : Week.Offset) - (1 : Day.Offset) = (6 : Day.Offset) := rfl
|
||||
theorem week_week_week_sub : (1 : Week.Offset) - (1 : Week.Offset) = (0 : Week.Offset) := rfl
|
||||
|
||||
/-
|
||||
Of and To basic units
|
||||
-/
|
||||
|
||||
example : (1 : Nanosecond.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Millisecond.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Second.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Minute.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Hour.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Day.Offset).toInt = (1 : Int) := rfl
|
||||
example : (1 : Week.Offset).toInt = (1 : Int) := rfl
|
||||
|
||||
example : Nanosecond.Offset.ofInt 1 = (1 : Nanosecond.Offset) := rfl
|
||||
example : Millisecond.Offset.ofInt 1 = (1 : Millisecond.Offset) := rfl
|
||||
example : Second.Offset.ofInt 1 = (1 : Second.Offset) := rfl
|
||||
example : Minute.Offset.ofInt 1 = (1 : Minute.Offset) := rfl
|
||||
example : Hour.Offset.ofInt 1 = (1 : Hour.Offset) := rfl
|
||||
example : Day.Offset.ofInt 1 = (1 : Day.Offset) := rfl
|
||||
example : Week.Offset.ofInt 1 = (1 : Week.Offset) := rfl
|
||||
|
||||
example : Nanosecond.Offset.ofNat 1 = (1 : Nanosecond.Offset) := rfl
|
||||
example : Millisecond.Offset.ofNat 1 = (1 : Millisecond.Offset) := rfl
|
||||
example : Second.Offset.ofNat 1 = (1 : Second.Offset) := rfl
|
||||
example : Minute.Offset.ofNat 1 = (1 : Minute.Offset) := rfl
|
||||
example : Hour.Offset.ofNat 1 = (1 : Hour.Offset) := rfl
|
||||
example : Day.Offset.ofNat 1 = (1 : Day.Offset) := rfl
|
||||
example : Week.Offset.ofNat 1 = (1 : Week.Offset) := rfl
|
||||
|
||||
example := Nanosecond.Ordinal.ofInt 1 (by decide)
|
||||
example := Millisecond.Ordinal.ofInt 1 (by decide)
|
||||
example := Second.Ordinal.ofInt (leap := false) 59 (by decide)
|
||||
example := Second.Ordinal.ofInt (leap := true) 60 (by decide)
|
||||
example := Minute.Ordinal.ofInt 1 (by decide)
|
||||
example := Hour.Ordinal.ofInt 1 (by decide)
|
||||
example := Day.Ordinal.ofInt 1 (by decide)
|
||||
example := Week.Ordinal.ofInt 1 (by decide)
|
||||
|
||||
example := Nanosecond.Ordinal.ofFin 1
|
||||
example := Millisecond.Ordinal.ofFin 1
|
||||
example := Second.Ordinal.ofFin (leap := false) (Fin.ofNat 1)
|
||||
example := Second.Ordinal.ofFin (leap := true) (Fin.ofNat 1)
|
||||
example := Minute.Ordinal.ofFin 1
|
||||
example := Hour.Ordinal.ofFin 1
|
||||
example := Day.Ordinal.ofFin 1
|
||||
example := Week.Ordinal.ofFin 1
|
||||
|
||||
example := Nanosecond.Ordinal.ofNat 1
|
||||
example := Millisecond.Ordinal.ofNat 1
|
||||
example := Second.Ordinal.ofNat (leap := false) 1
|
||||
example := Second.Ordinal.ofNat (leap := true) 1
|
||||
example := Minute.Ordinal.ofNat 1
|
||||
example := Hour.Ordinal.ofNat 1
|
||||
example := Day.Ordinal.ofNat 1
|
||||
example := Week.Ordinal.ofNat 1
|
||||
|
||||
example := Nanosecond.Ordinal.toOffset 1
|
||||
example := Millisecond.Ordinal.toOffset 1
|
||||
example := Second.Ordinal.toOffset (leap := false) 1
|
||||
example := Second.Ordinal.toOffset (leap := true) 1
|
||||
example := Minute.Ordinal.toOffset 1
|
||||
example := Hour.Ordinal.toOffset 1
|
||||
example := Day.Ordinal.toOffset 1
|
||||
example := Week.Ordinal.toOffset 1
|
||||
|
||||
example : (1 : Nanosecond.Ordinal).toInt = (1 : Int) := rfl
|
||||
example : (1 : Millisecond.Ordinal).toInt = (1 : Int) := rfl
|
||||
example : (1 : Second.Ordinal false).toInt = (1 : Int) := rfl
|
||||
example : (1 : Second.Ordinal true).toInt = (1 : Int) := rfl
|
||||
example : (1 : Minute.Ordinal).toInt = (1 : Int) := rfl
|
||||
example : (1 : Hour.Ordinal).toInt = (1 : Int) := rfl
|
||||
example : (1 : Day.Ordinal).toInt = (1 : Int) := rfl
|
||||
example : (1 : Week.Ordinal).toInt = (1 : Int) := rfl
|
||||
|
||||
example : ((1 : Nanosecond.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Millisecond.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Second.Ordinal false).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Second.Ordinal true).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Minute.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Hour.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Day.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
example : ((1 : Week.Ordinal).toFin (by decide) |>.val) = 1 := rfl
|
||||
|
||||
example : (1 : Nanosecond.Ordinal).toNat = 1 := rfl
|
||||
example : (1 : Millisecond.Ordinal).toNat = 1 := rfl
|
||||
example : (1 : Second.Ordinal false).toNat = 1 := rfl
|
||||
example : (1 : Second.Ordinal true).toNat = 1 := rfl
|
||||
example : (1 : Minute.Ordinal).toNat = 1 := rfl
|
||||
example : (1 : Hour.Ordinal).toNat = 1 := rfl
|
||||
example : (1 : Day.Ordinal).toNat = 1 := rfl
|
||||
example : (1 : Week.Ordinal).toNat = 1 := rfl
|
||||
|
||||
/--
|
||||
info: 9
|
||||
2023-06-10
|
||||
2023-06-16
|
||||
2023-07-09
|
||||
2023-07-09
|
||||
2024-06-09
|
||||
2024-06-09
|
||||
2023-06-08
|
||||
2023-06-02
|
||||
2023-05-09
|
||||
2023-05-09
|
||||
2022-06-09
|
||||
2022-06-09
|
||||
2023-06-01
|
||||
2023-06-01
|
||||
2023-06-11
|
||||
2023-01-09
|
||||
2023-01-09
|
||||
0001-06-09
|
||||
0001-06-09
|
||||
23 2023 2023 2023 23 2023 2023 23 J
|
||||
false
|
||||
160
|
||||
CE
|
||||
2
|
||||
2
|
||||
Std.Time.Weekday.friday
|
||||
23
|
||||
2
|
||||
06-09-2023
|
||||
2023-06-09
|
||||
2023-06-09
|
||||
19517
|
||||
1686268800000
|
||||
1970-01-02
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
-- :>
|
||||
let date := date("2023-06-09")
|
||||
|
||||
println! repr date.day
|
||||
println! date.addDays 1
|
||||
println! date.addWeeks 1
|
||||
println! date.addMonthsClip 1
|
||||
println! date.addMonthsRollOver 1
|
||||
println! date.addYearsClip 1
|
||||
println! date.addYearsRollOver 1
|
||||
|
||||
println! date.subDays 1
|
||||
println! date.subWeeks 1
|
||||
println! date.subMonthsClip 1
|
||||
println! date.subMonthsRollOver 1
|
||||
println! date.subYearsClip 1
|
||||
println! date.subYearsRollOver 1
|
||||
|
||||
println! date.withDaysClip 1
|
||||
println! date.withDaysRollOver 1
|
||||
println! date.withWeekday .sunday
|
||||
println! date.withMonthClip 1
|
||||
println! date.withMonthRollOver 1
|
||||
println! date.withYearClip 1
|
||||
println! date.withYearRollOver 1
|
||||
|
||||
println! date.format "yy yyyy yyy yyy yy uuuu uuu uu MMMMM"
|
||||
println! date.inLeapYear
|
||||
println! date.dayOfYear
|
||||
println! date.era
|
||||
println! repr date.quarter
|
||||
println! repr date.alignedWeekOfMonth
|
||||
println! repr date.weekday
|
||||
println! repr date.weekOfYear
|
||||
println! repr date.weekOfMonth
|
||||
|
||||
println! date.toAmericanDateString
|
||||
println! date.toLeanDateString
|
||||
println! date.toSQLDateString
|
||||
|
||||
println! date.toDaysSinceUNIXEpoch
|
||||
println! date.toTimestampAssumingUTC
|
||||
println! PlainDate.ofDaysSinceUNIXEpoch 1
|
||||
|
||||
/--
|
||||
info: 1997-03-19T02:03:04.000000000
|
||||
1997-03-25T02:03:04.000000000
|
||||
1997-04-18T02:03:04.000000000
|
||||
1997-04-18T02:03:04.000000000
|
||||
1998-03-18T02:03:04.000000000
|
||||
1998-03-18T02:03:04.000000000
|
||||
1997-03-17T02:03:04.000000000
|
||||
1997-03-11T02:03:04.000000000
|
||||
1997-02-18T02:03:04.000000000
|
||||
1997-02-18T02:03:04.000000000
|
||||
1996-03-18T02:03:04.000000000
|
||||
1996-03-18T02:03:04.000000000
|
||||
1997-03-01T02:03:04.000000000
|
||||
1997-03-01T02:03:04.000000000
|
||||
1997-03-23T02:03:04.000000000
|
||||
1997-01-18T02:03:04.000000000
|
||||
1997-01-18T02:03:04.000000000
|
||||
0001-03-18T02:03:04.000000000
|
||||
0001-03-18T02:03:04.000000000
|
||||
97 1997 1997 1997 97 1997 1997 97 M
|
||||
false
|
||||
77
|
||||
CE
|
||||
1
|
||||
4
|
||||
Std.Time.Weekday.tuesday
|
||||
12
|
||||
3
|
||||
9938
|
||||
858650584000
|
||||
1970-01-02T00:00:00.000000000
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let plaindatetime := datetime("1997-03-18T02:03:04")
|
||||
|
||||
println! plaindatetime.addDays 1
|
||||
println! plaindatetime.addWeeks 1
|
||||
println! plaindatetime.addMonthsClip 1
|
||||
println! plaindatetime.addMonthsRollOver 1
|
||||
println! plaindatetime.addYearsClip 1
|
||||
println! plaindatetime.addYearsRollOver 1
|
||||
|
||||
println! plaindatetime.subDays 1
|
||||
println! plaindatetime.subWeeks 1
|
||||
println! plaindatetime.subMonthsClip 1
|
||||
println! plaindatetime.subMonthsRollOver 1
|
||||
println! plaindatetime.subYearsClip 1
|
||||
println! plaindatetime.subYearsRollOver 1
|
||||
|
||||
println! plaindatetime.withDaysClip 1
|
||||
println! plaindatetime.withDaysRollOver 1
|
||||
println! plaindatetime.withWeekday .sunday
|
||||
println! plaindatetime.withMonthClip 1
|
||||
println! plaindatetime.withMonthRollOver 1
|
||||
println! plaindatetime.withYearClip 1
|
||||
println! plaindatetime.withYearRollOver 1
|
||||
|
||||
println! plaindatetime.format "yy yyyy yyy yyy yy uuuu uuu uu MMMMM"
|
||||
println! plaindatetime.inLeapYear
|
||||
println! plaindatetime.dayOfYear
|
||||
println! plaindatetime.era
|
||||
println! repr plaindatetime.quarter
|
||||
println! repr plaindatetime.alignedWeekOfMonth
|
||||
println! repr plaindatetime.weekday
|
||||
println! repr plaindatetime.weekOfYear
|
||||
println! repr plaindatetime.weekOfMonth
|
||||
|
||||
println! plaindatetime.toDaysSinceUNIXEpoch
|
||||
println! plaindatetime.toTimestampAssumingUTC
|
||||
println! PlainDateTime.ofDaysSinceUNIXEpoch 1 PlainTime.midnight
|
||||
|
||||
/--
|
||||
info: 2024-09-13T02:01:02.000000000-03:00
|
||||
2024-09-19T02:01:02.000000000-03:00
|
||||
2024-10-12T02:01:02.000000000-03:00
|
||||
2024-10-12T02:01:02.000000000-03:00
|
||||
2025-09-12T02:01:02.000000000-03:00
|
||||
2025-09-12T02:01:02.000000000-03:00
|
||||
2024-09-11T02:01:02.000000000-03:00
|
||||
2024-09-05T02:01:02.000000000-03:00
|
||||
2024-08-12T02:01:02.000000000-03:00
|
||||
2024-08-12T02:01:02.000000000-03:00
|
||||
2023-09-12T02:01:02.000000000-03:00
|
||||
2023-09-12T02:01:02.000000000-03:00
|
||||
2024-09-01T02:01:02.000000000-03:00
|
||||
2024-09-01T02:01:02.000000000-03:00
|
||||
2024-09-15T02:01:02.000000000-03:00
|
||||
2024-01-12T02:01:02.000000000-03:00
|
||||
2024-01-12T02:01:02.000000000-03:00
|
||||
0001-09-12T02:01:02.000000000-03:00
|
||||
0001-09-12T02:01:02.000000000-03:00
|
||||
24 2024 2024 2024 24 2024 2024 24 S
|
||||
true
|
||||
256
|
||||
CE
|
||||
3
|
||||
3
|
||||
Std.Time.Weekday.thursday
|
||||
37
|
||||
2
|
||||
19978
|
||||
1726117262000
|
||||
1970-01-02T00:00:00.000000000Z
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let zoned := DateTime.ofPlainDateTimeAssumingUTC datetime("2024-09-12T05:01:02") timezone("America/Sao_Paulo -03:00")
|
||||
|
||||
println! zoned.addDays 1
|
||||
println! zoned.addWeeks 1
|
||||
println! zoned.addMonthsClip 1
|
||||
println! zoned.addMonthsRollOver 1
|
||||
println! zoned.addYearsClip 1
|
||||
println! zoned.addYearsRollOver 1
|
||||
|
||||
println! zoned.subDays 1
|
||||
println! zoned.subWeeks 1
|
||||
println! zoned.subMonthsClip 1
|
||||
println! zoned.subMonthsRollOver 1
|
||||
println! zoned.subYearsClip 1
|
||||
println! zoned.subYearsRollOver 1
|
||||
|
||||
println! zoned.withDaysClip 1
|
||||
println! zoned.withDaysRollOver 1
|
||||
println! zoned.withWeekday .sunday
|
||||
println! zoned.withMonthClip 1
|
||||
println! zoned.withMonthRollOver 1
|
||||
println! zoned.withYearClip 1
|
||||
println! zoned.withYearRollOver 1
|
||||
|
||||
println! zoned.format "yy yyyy yyy yyy yy uuuu uuu uu MMMMM"
|
||||
println! zoned.inLeapYear
|
||||
println! zoned.dayOfYear
|
||||
println! zoned.era
|
||||
println! repr zoned.quarter
|
||||
println! repr zoned.alignedWeekOfMonth
|
||||
println! repr zoned.weekday
|
||||
println! repr zoned.weekOfYear
|
||||
println! repr zoned.weekOfMonth
|
||||
|
||||
println! zoned.toDaysSinceUNIXEpoch
|
||||
println! zoned.toTimestamp
|
||||
println! DateTime.ofDaysSinceUNIXEpoch 1 PlainTime.midnight .UTC
|
||||
|
||||
/--
|
||||
info: 1997-03-19T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-25T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-04-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-04-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1998-03-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1998-03-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-17T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-17T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-11T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-02-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-02-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1996-03-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1996-03-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-01T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-01T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-03-23T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-01-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
1997-01-18T02:03:04.000000000[America/Sao_Paulo]
|
||||
0001-03-17T02:03:04.000000000[America/Sao_Paulo]
|
||||
0001-03-17T02:03:04.000000000[America/Sao_Paulo]
|
||||
97 1997 1997 1997 97 1997 1997 97 M
|
||||
false
|
||||
77
|
||||
CE
|
||||
1
|
||||
4
|
||||
Std.Time.Weekday.tuesday
|
||||
12
|
||||
3
|
||||
9938
|
||||
858661384000
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let zoned := zoned("1997-03-18T02:03:04", sao_paulo)
|
||||
|
||||
println! zoned.addDays 1
|
||||
println! zoned.addWeeks 1
|
||||
println! zoned.addMonthsClip 1
|
||||
println! zoned.addMonthsRollOver 1
|
||||
println! zoned.addYearsClip 1
|
||||
println! zoned.addYearsRollOver 1
|
||||
|
||||
println! zoned.subDays 1
|
||||
println! zoned.subDays 1
|
||||
println! zoned.subWeeks 1
|
||||
println! zoned.subMonthsClip 1
|
||||
println! zoned.subMonthsRollOver 1
|
||||
println! zoned.subYearsClip 1
|
||||
println! zoned.subYearsRollOver 1
|
||||
|
||||
println! zoned.withDaysClip 1
|
||||
println! zoned.withDaysRollOver 1
|
||||
println! zoned.withWeekday .sunday
|
||||
println! zoned.withMonthClip 1
|
||||
println! zoned.withMonthRollOver 1
|
||||
println! zoned.withYearClip 1
|
||||
println! zoned.withYearRollOver 1
|
||||
|
||||
println! zoned.format "yy yyyy yyy yyy yy uuuu uuu uu MMMMM"
|
||||
println! zoned.inLeapYear
|
||||
println! zoned.dayOfYear
|
||||
println! zoned.era
|
||||
println! repr zoned.quarter
|
||||
println! repr zoned.alignedWeekOfMonth
|
||||
println! repr zoned.weekday
|
||||
println! repr zoned.weekOfYear
|
||||
println! repr zoned.weekOfMonth
|
||||
|
||||
println! zoned.toDaysSinceUNIXEpoch
|
||||
println! zoned.toTimestamp
|
||||
|
||||
/--
|
||||
info: 2023-06-09T00:00:00.000000000
|
||||
0001-01-01T12:32:43.000000000
|
||||
2033-11-18T12:32:43.000000000
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
println! PlainDateTime.ofPlainDate date("2023-06-09")
|
||||
println! PlainDateTime.ofPlainTime time("12:32:43")
|
||||
println! PlainDateTime.ofDaysSinceUNIXEpoch 23332 time("12:32:43")
|
||||
|
||||
/--
|
||||
info: 1970-01-02T00:00:00.000000000Z
|
||||
1997-03-18T00:00:00.000000000Z
|
||||
1997-03-18T00:01:02.000000000Z
|
||||
1997-03-18T00:01:02.000000000Z
|
||||
2024-02-16T22:07:14.000000000Z
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
println! DateTime.ofDaysSinceUNIXEpoch 1 PlainTime.midnight .UTC
|
||||
println! DateTime.ofPlainDate date("1997-03-18") .UTC
|
||||
println! DateTime.ofPlainDateTime datetime("1997-03-18T00:01:02") .UTC
|
||||
println! DateTime.ofPlainDateTimeAssumingUTC datetime("1997-03-18T00:01:02") .UTC
|
||||
println! DateTime.ofTimestamp 1708121234 .UTC
|
||||
|
||||
/--
|
||||
info: 1970-01-02T00:00:00.000000000[UTC]
|
||||
1997-03-18T00:00:00.000000000[UTC]
|
||||
1997-03-18T00:00:00.000000000[UTC]
|
||||
1997-03-18T00:01:02.000000000[UTC]
|
||||
1997-03-18T00:01:02.000000000[UTC]
|
||||
1997-03-18T00:01:02.000000000[UTC]
|
||||
2024-02-16T22:07:14.000000000[UTC]
|
||||
2024-02-16T22:07:14.000000000[UTC]
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
println! ZonedDateTime.ofDaysSinceUNIXEpoch 1 PlainTime.midnight .UTC
|
||||
println! ZonedDateTime.ofPlainDate date("1997-03-18") .UTC
|
||||
println! ZonedDateTime.ofPlainDateWithZone date("1997-03-18") .UTC
|
||||
println! ZonedDateTime.ofPlainDateTime datetime("1997-03-18T00:01:02") .UTC
|
||||
println! ZonedDateTime.ofPlainDateTimeAssumingUTC datetime("1997-03-18T00:01:02") .UTC
|
||||
println! ZonedDateTime.ofPlainDateTimeWithZone datetime("1997-03-18T00:01:02") .UTC
|
||||
println! ZonedDateTime.ofTimestamp 1708121234 .UTC
|
||||
println! ZonedDateTime.ofTimestampWithZone 1708121234 .UTC
|
||||
188
tests/lean/run/timeClassOperations.lean
Normal file
188
tests/lean/run/timeClassOperations.lean
Normal file
@@ -0,0 +1,188 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def date := date("1970-01-20")
|
||||
|
||||
/--
|
||||
info: date("1970-02-01")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date + (12 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: date("1970-01-08")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date - (12 : Day.Offset)
|
||||
|
||||
def datetime := datetime("2000-01-20T03:02:01")
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T04:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T02:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:12:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime + (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T02:52:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime - (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:03:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime + (60 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:01:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime - (60 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-21T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-19T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime - (1 : Day.Offset)
|
||||
|
||||
def time := time("13:02:01")
|
||||
|
||||
/--
|
||||
info: time("14:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: time("12:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: time("13:12:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time + (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: time("12:52:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time - (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: time("13:03:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time + (60 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: time("13:01:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time - (60 : Second.Offset)
|
||||
|
||||
def datetimetz := zoned("2000-01-20T06:02:01-03:00")
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-22T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz + (2 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-19T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T07:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T05:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:12:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz + (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T05:52:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz - (10 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:03:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz + (60 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:01:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz - (60 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: "3600s"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned("2000-12-20T00:00:00-03:00") - zoned("2000-12-20T00:00:00-02:00")
|
||||
|
||||
def now := PlainDateTime.ofTimestampAssumingUTC ⟨1724859638, ⟨907328169, by decide⟩, by decide⟩
|
||||
def after := PlainDateTime.ofTimestampAssumingUTC ⟨1724859639, ⟨907641224, by decide⟩, by decide⟩
|
||||
def duration := after - now
|
||||
|
||||
/--
|
||||
info: "15:40:38.907328169"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval now.format "HH:mm:ss.SSSSSSSSS"
|
||||
|
||||
/--
|
||||
info: "15.40:39.907641224"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval after.format "HH.mm:ss.SSSSSSSSS"
|
||||
|
||||
/--
|
||||
info: "1.000313055s"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval duration
|
||||
812
tests/lean/run/timeFormats.lean
Normal file
812
tests/lean/run/timeFormats.lean
Normal file
@@ -0,0 +1,812 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def ISO8601UTC : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSZ")
|
||||
def RFC1123 : GenericFormat .any := datespec("eee, dd MMM uuuu HH:mm:ss ZZZ")
|
||||
def ShortDate : GenericFormat .any := datespec("MM/dd/uuuu")
|
||||
def LongDate : GenericFormat .any := datespec("MMMM D, uuuu")
|
||||
def ShortDateTime : GenericFormat .any := datespec("MM/dd/uuuu HH:mm:ss")
|
||||
def LongDateTime : GenericFormat .any := datespec("MMMM D, uuuu h:mm aa")
|
||||
def Time24Hour : GenericFormat .any := datespec("HH:mm:ss")
|
||||
def Time12Hour : GenericFormat .any := datespec("hh:mm:ss aa")
|
||||
def FullDayTimeZone : GenericFormat .any := datespec("EEEE, MMMM dd, uuuu HH:mm:ss ZZZ")
|
||||
def CustomDayTime : GenericFormat .any := datespec("EEE dd MMM uuuu HH:mm")
|
||||
def EraDate : GenericFormat .any := datespec("MM D, uuuu G")
|
||||
|
||||
-- Dates
|
||||
|
||||
def brTZ : TimeZone := timezone("America/Sao_Paulo -03:00")
|
||||
def jpTZ : TimeZone := timezone("Asia/Tokyo +09:00")
|
||||
|
||||
def date₁ := zoned("2014-06-16T03:03:03-03:00")
|
||||
|
||||
def time₁ := time("14:11:01")
|
||||
def time₂ := time("03:11:01")
|
||||
|
||||
/--
|
||||
info: "Monday, June 16, 2014 03:03:03 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format date₁.toDateTime
|
||||
|
||||
def tm := date₁.toTimestamp
|
||||
def date₂ := DateTime.ofTimestamp tm brTZ
|
||||
|
||||
/--
|
||||
info: "Monday, June 16, 2014 03:03:03 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format date₂
|
||||
|
||||
def tm₃ := date₁.toTimestamp
|
||||
def date₃ := DateTime.ofTimestamp tm₃ brTZ
|
||||
|
||||
/--
|
||||
info: "Monday, June 16, 2014 03:03:03 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format date₃
|
||||
|
||||
-- Section for testing timezone conversion.
|
||||
|
||||
-- the timestamp is always related to UTC.
|
||||
|
||||
/--
|
||||
Timestamp: 1723739292
|
||||
GMT: Thursday, 15 August 2024 16:28:12
|
||||
BR: 15 August 2024 13:28:12 GMT-03:00
|
||||
-/
|
||||
def tm₄ : Second.Offset := 1723739292
|
||||
|
||||
def dateBR := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) brTZ
|
||||
def dateJP := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) jpTZ
|
||||
def dateUTC := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) .UTC
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 13:28:12 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateBR
|
||||
|
||||
/--
|
||||
info: "Friday, August 16, 2024 01:28:12 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateJP
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 13:28:12 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateUTC.convertTimeZone brTZ)
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 13:28:12 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateJP.convertTimeZone brTZ)
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 16:28:12 +0000"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateUTC
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 16:28:12 +0000"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateBR.convertTimeZone .UTC)
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 16:28:12 +0000"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateJP.convertTimeZone .UTC)
|
||||
|
||||
/--
|
||||
info: "Friday, August 16, 2024 01:28:12 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateJP
|
||||
|
||||
/--
|
||||
info: "Friday, August 16, 2024 01:28:12 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateBR.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "Friday, August 16, 2024 01:28:12 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateUTC.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
TM: 1723730627
|
||||
GMT: Thursday, 15 August 2024 14:03:47
|
||||
Your time zone: 15 August 2024 11:03:47 GMT-03:00
|
||||
-/
|
||||
def localTm : Second.Offset := 1723730627
|
||||
|
||||
/--
|
||||
This PlainDate is relative to the local time.
|
||||
-/
|
||||
def PlainDate : PlainDateTime := Timestamp.toPlainDateTimeAssumingUTC (Timestamp.ofSecondsSinceUnixEpoch localTm)
|
||||
|
||||
def dateBR₁ := DateTime.ofPlainDateTime PlainDate brTZ
|
||||
def dateJP₁ := DateTime.ofPlainDateTime PlainDate jpTZ
|
||||
def dateUTC₁ := DateTime.ofPlainDateTime PlainDate .UTC
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 14:03:47 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateBR₁
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 14:03:47 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format dateJP₁
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 23:03:47 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateUTC₁.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "Friday, August 16, 2024 02:03:47 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateBR₁.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "Thursday, August 15, 2024 14:03:47 +0900"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format (dateJP₁.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "Monday, June 16, 2014 03:03:03 -0300"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval FullDayTimeZone.format date₂
|
||||
|
||||
/--
|
||||
info: "14:11:01"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Time24Hour.formatBuilder time₁.hour time₁.minute time₁.second
|
||||
|
||||
def l := Time12Hour.formatBuilder time₁.hour.toRelative time₁.minute time₁.second (if time₁.hour.val > 12 then HourMarker.pm else HourMarker.am)
|
||||
|
||||
/--
|
||||
info: "02:11:01 PM"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval l
|
||||
/--
|
||||
info: "03:11:01 AM"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Time12Hour.formatBuilder time₂.hour.toRelative time₂.minute time₂.second (if time₂.hour.val > 12 then HourMarker.pm else HourMarker.am)
|
||||
|
||||
/--
|
||||
info: "06/16/2014"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval ShortDate.formatBuilder date₁.month date₁.day date₁.year
|
||||
|
||||
/--
|
||||
info: "0053-06-19"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Formats.sqlDate.format (DateTime.ofPlainDate (PlainDate.ofDaysSinceUNIXEpoch ⟨-700000⟩) .UTC)
|
||||
|
||||
/--
|
||||
info: "-0002-09-16"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Formats.sqlDate.format (DateTime.ofPlainDate (PlainDate.ofDaysSinceUNIXEpoch ⟨-720000⟩) .UTC)
|
||||
|
||||
/--
|
||||
info: "-0084-07-28"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Formats.sqlDate.format (DateTime.ofPlainDate (PlainDate.ofDaysSinceUNIXEpoch ⟨-750000⟩) .UTC)
|
||||
|
||||
/--
|
||||
info: "-0221-09-04"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Formats.sqlDate.format (DateTime.ofPlainDate (PlainDate.ofDaysSinceUNIXEpoch ⟨-800000⟩) .UTC)
|
||||
|
||||
/--
|
||||
info: date("-0221-09-04")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDate.ofDaysSinceUNIXEpoch ⟨-800000⟩
|
||||
|
||||
/--
|
||||
info: date("-0221-09-04")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDate.ofDaysSinceUNIXEpoch ⟨-800000⟩
|
||||
|
||||
/--
|
||||
info: date("2002-07-14")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date("2002-07-14")
|
||||
|
||||
/--
|
||||
info: time("14:13:12.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time("14:13:12")
|
||||
|
||||
/--
|
||||
info: zoned("2002-07-14T14:13:12.000000000Z")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned("2002-07-14T14:13:12Z")
|
||||
|
||||
/--
|
||||
info: zoned("2002-07-14T14:13:12.000000000+09:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned("2002-07-14T14:13:12+09:00")
|
||||
|
||||
/--
|
||||
info: "2002-07-14"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned("2002-07-14T14:13:12+09:00").format "uuuu-MM-dd"
|
||||
|
||||
/--
|
||||
info: "14-13-12"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned("2002-07-14T14:13:12+09:00").format "HH-mm-ss"
|
||||
|
||||
/-
|
||||
Format
|
||||
-/
|
||||
|
||||
def time₄ := time("23:13:12.324354679")
|
||||
def date₄ := date("2002-07-14")
|
||||
def datetime₅ := PlainDateTime.mk (PlainDate.ofYearMonthDayClip (-2000) 3 4) (PlainTime.mk 12 23 ⟨false, 12⟩ 0)
|
||||
def datetime₄ := datetime("2002-07-14T23:13:12.324354679")
|
||||
def zoned₄ := zoned("2002-07-14T23:13:12.324354679+09:00")
|
||||
def zoned₅ := zoned("2002-07-14T23:13:12.324354679+00:00")
|
||||
def tz : TimeZone := { offset := { second := -3600 }, name := "America/Sao_Paulo", abbreviation := "BRT", isDST := false}
|
||||
def zoned₆ := ZonedDateTime.ofPlainDateTime (zoned₄.toPlainDateTime) (TimeZone.ZoneRules.ofTimeZone tz)
|
||||
|
||||
/--
|
||||
info: "CE CE CE Common Era C"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "G GG GGG GGGG GGGGG"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "yy yyyy yyyyyyyyy"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "uu uuuu uuuuuuuuu"
|
||||
|
||||
/--
|
||||
info: "195 195 195"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "D DD DDD"
|
||||
|
||||
/--
|
||||
info: "14 14 014 0014 00014"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "d dd ddd dddd ddddd"
|
||||
|
||||
/--
|
||||
info: "7 07 Jul July J"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "M MM MMM MMMM MMMMM"
|
||||
|
||||
/--
|
||||
info: "3 03 3rd quarter 3"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "Q QQ QQQQ QQQQQ"
|
||||
|
||||
/--
|
||||
info: "28 28 028 0028"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "w ww www wwww"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "W WW WWW WWWW"
|
||||
|
||||
/--
|
||||
info: "Sun Sun Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "E EE EEE EEEE EEEEE"
|
||||
|
||||
/--
|
||||
info: "7 07 Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "e ee eee eeee eeeee"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "F FF FFF FFFF"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 0011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "h hh hhh hhhh hhhh"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 000011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "K KK KKK KKKK KKKKKK"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 000023"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "k kk kkk kkkk kkkkkk"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 00023"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "H HH HHH HHHH HHHHH"
|
||||
|
||||
/--
|
||||
info: "13 13 013 0013 00013"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "m mm mmm mmmm mmmmm"
|
||||
|
||||
/--
|
||||
info: "12 12 012 0012 00012"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "s ss sss ssss sssss"
|
||||
|
||||
|
||||
/--
|
||||
info: "3 32 324 3243 32435"
|
||||
-/#guard_msgs in
|
||||
#eval zoned₄.format "S SS SSS SSSS SSSSS"
|
||||
|
||||
/--
|
||||
info: "83592324 83592324 83592324 83592324 083592324"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "A AA AAA AAAA AAAAAAAAA"
|
||||
|
||||
/--
|
||||
info: "324354679 324354679 324354679 324354679 324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "n nn nnn nnnn nnnnnnnnn"
|
||||
|
||||
/--
|
||||
info: "83592324354679 83592324354679 83592324354679 83592324354679 83592324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "N NN NNN NNNN NNNNNNNNN"
|
||||
|
||||
/--
|
||||
info: "+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "VV"
|
||||
|
||||
/--
|
||||
info: "+09:00 +09:00 +09:00 +09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "z zz zzzz zzzz"
|
||||
|
||||
/--
|
||||
info: "+00:00 +00:00 +00:00 +00:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₅.format "z zz zzzz zzzz"
|
||||
|
||||
/--
|
||||
info: "GMT+9 GMT+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "O OOOO"
|
||||
|
||||
/--
|
||||
info: "+09 +0900 +09:00 +0900 +09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "X XX XXX XXXX XXXXX"
|
||||
|
||||
/--
|
||||
info: "+09 +0900 +09:00 +0900 +09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "x xx xxx xxxx xxxxx"
|
||||
|
||||
/--
|
||||
info: "Z Z Z Z Z"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₅.format "X XX XXX XXXX XXXXX"
|
||||
|
||||
/--
|
||||
info: "+00 +0000 +00:00 +0000 +00:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₅.format "x xx xxx xxxx xxxxx"
|
||||
|
||||
/--
|
||||
info: "+0900 +0900 +0900 GMT+09:00 +09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₄.format "Z ZZ ZZZ ZZZZ ZZZZZ"
|
||||
|
||||
/--
|
||||
info: "CE CE CE Common Era C"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "G GG GGG GGGG GGGGG"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "yy yyyy yyyyyyyyy"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "uu uuuu uuuuuuuuu"
|
||||
|
||||
/--
|
||||
info: "195 195 195"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "D DD DDD"
|
||||
|
||||
/--
|
||||
info: "7 07 Jul J"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "M MM MMM MMMMM"
|
||||
|
||||
/--
|
||||
info: "14 14 014 0014 00014"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "d dd ddd dddd ddddd"
|
||||
|
||||
/--
|
||||
info: "7 07 Jul July J"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "M MM MMM MMMM MMMMM"
|
||||
|
||||
/--
|
||||
info: "14 14 0014 0014"
|
||||
-/#guard_msgs in
|
||||
#eval datetime₄.format "d dd dddd dddd"
|
||||
|
||||
/--
|
||||
info: "3 03 3rd quarter 3"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "Q QQ QQQQ QQQQQ"
|
||||
|
||||
/--
|
||||
info: "28 28 028 0028"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "w ww www wwww"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "W WW WWW WWWW"
|
||||
|
||||
/--
|
||||
info: "Sun Sun Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "E EE EEE EEEE EEEEE"
|
||||
|
||||
/--
|
||||
info: "7 07 Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "e ee eee eeee eeeee"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "F FF FFF FFFF"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 0011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "h hh hhh hhhh hhhh"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 000011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "K KK KKK KKKK KKKKKK"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 000023"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "k kk kkk kkkk kkkkkk"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 00023"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "H HH HHH HHHH HHHHH"
|
||||
|
||||
/--
|
||||
info: "13 13 013 0013 00013"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "m mm mmm mmmm mmmmm"
|
||||
|
||||
/--
|
||||
info: "12 12 012 0012 00012"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "s ss sss ssss sssss"
|
||||
|
||||
|
||||
/--
|
||||
info: "3 32 324 3243 32435"
|
||||
-/#guard_msgs in
|
||||
#eval datetime₄.format "S SS SSS SSSS SSSSS"
|
||||
|
||||
/--
|
||||
info: "3 32 324 3243 324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "S SS SSS SSSS SSSSSSSSS"
|
||||
|
||||
/--
|
||||
info: "83592324 83592324 83592324 83592324 083592324"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "A AA AAA AAAA AAAAAAAAA"
|
||||
|
||||
/--
|
||||
info: "324354679 324354679 324354679 324354679 324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "n nn nnn nnnn nnnnnnnnn"
|
||||
|
||||
/--
|
||||
info: "83592324354679 83592324354679 83592324354679 83592324354679 83592324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "N NN NNN NNNN NNNNNNNNN"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 0011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "h hh hhh hhhh hhhh"
|
||||
|
||||
/--
|
||||
info: "11 11 011 0011 000011"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "K KK KKK KKKK KKKKKK"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 000023"
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "k kk kkk kkkk kkkkkk"
|
||||
|
||||
/--
|
||||
info: "23 23 023 0023 00023"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "H HH HHH HHHH HHHHH"
|
||||
|
||||
/--
|
||||
info: "13 13 013 0013 00013"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "m mm mmm mmmm mmmmm"
|
||||
|
||||
/--
|
||||
info: "12 12 012 0012 00012"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "s ss sss ssss sssss"
|
||||
|
||||
|
||||
/--
|
||||
info: "3 32 324 3243 32435"
|
||||
-/#guard_msgs in
|
||||
#eval time₄.format "S SS SSS SSSS SSSSS"
|
||||
|
||||
/--
|
||||
info: "3 32 324 3243 324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "S SS SSS SSSS SSSSSSSSS"
|
||||
|
||||
/--
|
||||
info: "83592324 83592324 83592324 83592324 083592324"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "A AA AAA AAAA AAAAAAAAA"
|
||||
|
||||
/--
|
||||
info: "324354679 324354679 324354679 324354679 324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "n nn nnn nnnn nnnnnnnnn"
|
||||
|
||||
/--
|
||||
info: "83592324354679 83592324354679 83592324354679 83592324354679 83592324354679"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time₄.format "N NN NNN NNNN NNNNNNNNN"
|
||||
|
||||
/--
|
||||
info: "CE CE CE Common Era C"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "G GG GGG GGGG GGGGG"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "yy yyyy yyyyyyyyy"
|
||||
|
||||
/--
|
||||
info: "02 2002 000002002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "uu uuuu uuuuuuuuu"
|
||||
|
||||
/--
|
||||
info: "195 195 195"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "D DD DDD"
|
||||
|
||||
/--
|
||||
info: "7 07 Jul J"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "M MM MMM MMMMM"
|
||||
|
||||
/--
|
||||
info: "14 14 014 0014 00014"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "d dd ddd dddd ddddd"
|
||||
|
||||
/--
|
||||
info: "7 07 Jul July J"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "M MM MMM MMMM MMMMM"
|
||||
|
||||
/--
|
||||
info: "14 14 0014 0014"
|
||||
-/#guard_msgs in
|
||||
#eval date₄.format "d dd dddd dddd"
|
||||
|
||||
/--
|
||||
info: "3 03 3rd quarter 3"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "Q QQ QQQQ QQQQQ"
|
||||
|
||||
/--
|
||||
info: "28 28 028 0028"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "w ww www wwww"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "W WW WWW WWWW"
|
||||
|
||||
/--
|
||||
info: "Sun Sun Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "E EE EEE EEEE EEEEE"
|
||||
|
||||
/--
|
||||
info: "7 07 Sun Sunday S"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "e ee eee eeee eeeee"
|
||||
|
||||
/--
|
||||
info: "2 02 002 0002"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₄.format "F FF FFF FFFF"
|
||||
|
||||
/--
|
||||
info: "-2000 2001 BCE"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₅.format "uuuu yyyy G"
|
||||
|
||||
/--
|
||||
info: "2002 2002 CE"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime₄.format "uuuu yyyy G"
|
||||
|
||||
/--
|
||||
info: "BRT BRT BRT America/Sao_Paulo America/Sao_Paulo"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval zoned₆.format "z zz zzz zzzz zzzz"
|
||||
|
||||
/--
|
||||
info: 1
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := .ofPlainDateTime datetime("2018-12-31T12:00:00") (TimeZone.ZoneRules.ofTimeZone TimeZone.UTC)
|
||||
IO.println s!"{t.format "w"}"
|
||||
|
||||
/-
|
||||
Truncation Test
|
||||
-/
|
||||
|
||||
/--
|
||||
info: ("19343232432-01-04T01:04:03.000000000",
|
||||
Except.ok (datetime("19343232432-01-04T01:04:03.000000000")),
|
||||
datetime("1932-01-02T05:04:03.000000000"))
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let r := (PlainDateTime.mk (PlainDate.ofYearMonthDayClip 19343232432 1 4) (PlainTime.mk 25 64 ⟨true, 3⟩ 0))
|
||||
let s := r.toLeanDateTimeString
|
||||
let r := PlainDateTime.parse s
|
||||
(s, r, datetime("1932-01-02T05:04:03.000000000"))
|
||||
83
tests/lean/run/timeIO.lean
Normal file
83
tests/lean/run/timeIO.lean
Normal file
@@ -0,0 +1,83 @@
|
||||
import Std.Time
|
||||
import Init
|
||||
open Std.Time
|
||||
|
||||
/-
|
||||
Test for quantity
|
||||
-/
|
||||
|
||||
#eval do
|
||||
let res ← Database.defaultGetZoneRules "America/Sao_Paulo"
|
||||
if res.transitions.size < 1 then
|
||||
throw <| IO.userError "invalid quantity for America/Sao_Paulo"
|
||||
|
||||
/--
|
||||
info: { second := 0 }
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let res ← Database.defaultGetZoneRules "UTC"
|
||||
println! repr res.initialLocalTimeType.gmtOffset
|
||||
|
||||
/-
|
||||
The idea is just to check if there's no errors while computing the local zone rules.
|
||||
-/
|
||||
#eval do
|
||||
discard <| Database.defaultGetLocalZoneRules
|
||||
|
||||
/-
|
||||
Java:
|
||||
2013-10-19T23:59:59-03:00[America/Sao_Paulo] 1382237999
|
||||
2013-10-20T01:00-02:00[America/Sao_Paulo] 1382238000
|
||||
2013-10-20T01:00:01-02:00[America/Sao_Paulo] 1382238001
|
||||
-/
|
||||
|
||||
/--
|
||||
info: 2013-10-19T23:59:59.000000000-03:00
|
||||
2013-10-20T00:00:00.000000000-02:00
|
||||
2013-10-20T00:00:01.000000000-02:00
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let zr ← Database.defaultGetZoneRules "America/Sao_Paulo"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2013-10-19T23:59:59") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2013-10-20T00:00:00") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2013-10-20T00:00:01") zr |>.toLeanDateTimeWithZoneString}"
|
||||
|
||||
/-
|
||||
Java:
|
||||
2019-11-03T01:59:59-05:00[America/Chicago] 1572764399
|
||||
2019-11-03T02:00-06:00[America/Chicago] 1572768000
|
||||
2019-11-03T02:59:59-06:00[America/Chicago] 1572771599
|
||||
-/
|
||||
|
||||
/--
|
||||
info: 2019-11-03T01:59:59.000000000-05:00
|
||||
2019-11-03T02:00:00.000000000-06:00
|
||||
2019-11-03T02:59:59.000000000-06:00
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let zr ← Database.defaultGetZoneRules "America/Chicago"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2019-11-03T01:59:59") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2019-11-03T02:00:00") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2019-11-03T02:59:59") zr |>.toLeanDateTimeWithZoneString}"
|
||||
|
||||
/-
|
||||
Java:
|
||||
2003-10-26T01:59:59-05:00[America/Monterrey] 1067151599
|
||||
2003-10-26T02:00-06:00[America/Monterrey] 1067155200
|
||||
2003-10-26T02:59:59-06:00[America/Monterrey] 1067158799
|
||||
-/
|
||||
|
||||
/--
|
||||
info: 2003-10-26T01:59:59.000000000-05:00
|
||||
2003-10-26T02:00:00.000000000-06:00
|
||||
2003-10-26T02:59:59.000000000-06:00
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval do
|
||||
let zr ← Database.defaultGetZoneRules "America/Monterrey"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2003-10-26T01:59:59") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2003-10-26T02:00:00") zr |>.toLeanDateTimeWithZoneString}"
|
||||
println! "{ZonedDateTime.ofPlainDateTime datetime("2003-10-26T02:59:59") zr |>.toLeanDateTimeWithZoneString}"
|
||||
87
tests/lean/run/timeLocalDateTime.lean
Normal file
87
tests/lean/run/timeLocalDateTime.lean
Normal file
@@ -0,0 +1,87 @@
|
||||
import Std.Time
|
||||
import Init
|
||||
open Std.Time
|
||||
|
||||
def ShortDateTime : GenericFormat .any := datespec("dd/MM/uuuu HH:mm:ss")
|
||||
def ShortDate : GenericFormat .any := datespec("dd/MM/uuuu")
|
||||
|
||||
def format (PlainDate : PlainDateTime) : String := ShortDateTime.formatBuilder PlainDate.day PlainDate.month PlainDate.year PlainDate.time.hour PlainDate.minute PlainDate.time.second
|
||||
def format₂ (PlainDate : PlainDate) : String := ShortDate.formatBuilder PlainDate.day PlainDate.month PlainDate.year
|
||||
|
||||
def date₁ := datetime("1993-11-19T09:08:07")
|
||||
def date₂ := datetime("1993-05-09T12:59:59")
|
||||
def date₃ := date("2024-08-16")
|
||||
def date₄ := date("1500-08-16")
|
||||
|
||||
def tm₁ := 753700087
|
||||
def tm₂ := 736952399
|
||||
|
||||
/--
|
||||
info: "19/11/1993 09:08:07"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval format date₁
|
||||
|
||||
/--
|
||||
info: "09/05/1993 12:59:59"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval format date₂
|
||||
|
||||
/--
|
||||
info: 753700087
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.toTimestampAssumingUTC.toSecondsSinceUnixEpoch
|
||||
|
||||
/--
|
||||
info: 736952399
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₂.toTimestampAssumingUTC.toSecondsSinceUnixEpoch
|
||||
|
||||
/--
|
||||
info: "09/05/1993 12:59:59"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDateTime.ofTimestampAssumingUTC 736952399 |> format
|
||||
|
||||
/--
|
||||
info: 736952399
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDateTime.toTimestampAssumingUTC date₂ |>.toSecondsSinceUnixEpoch
|
||||
|
||||
/--
|
||||
info: "16/08/2024"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDate.ofDaysSinceUNIXEpoch 19951 |> format₂
|
||||
|
||||
/--
|
||||
info: 19951
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDate.toDaysSinceUNIXEpoch date₃
|
||||
|
||||
/--
|
||||
info: Std.Time.Weekday.friday
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval PlainDate.weekday date₃
|
||||
|
||||
/--
|
||||
info: #[]
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval Id.run do
|
||||
let mut res := #[]
|
||||
|
||||
for i in [0:10000] do
|
||||
let i := Int.ofNat i - 999975
|
||||
let date := PlainDate.ofDaysSinceUNIXEpoch (Day.Offset.ofInt i)
|
||||
let num := date.toDaysSinceUNIXEpoch
|
||||
if i ≠ num.val then
|
||||
res := res.push i
|
||||
|
||||
return res
|
||||
319
tests/lean/run/timeOperations.lean
Normal file
319
tests/lean/run/timeOperations.lean
Normal file
@@ -0,0 +1,319 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def date := date("1970-01-20")
|
||||
|
||||
/--
|
||||
info: date("1970-02-01")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date.addDays 12
|
||||
|
||||
/--
|
||||
info: date("1970-02-20")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date.addMonthsClip 1
|
||||
|
||||
/--
|
||||
info: date("1971-01-20")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date.addYearsRollOver 1
|
||||
|
||||
/--
|
||||
info: date("1969-01-20")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date.subYearsClip 1
|
||||
|
||||
/--
|
||||
info: date("1969-12-20")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date.subMonthsClip 1
|
||||
|
||||
def datetime := datetime("2000-01-20T03:02:01")
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T04:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addHours 1
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T02:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subHours 1
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:12:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addMinutes 10
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T02:52:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subMinutes 10
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:03:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addSeconds 60
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-20T03:01:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subSeconds 60
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-21T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addDays 1
|
||||
|
||||
/--
|
||||
info: datetime("2000-01-19T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subDays 1
|
||||
|
||||
/--
|
||||
info: datetime("2000-02-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addMonthsClip 1
|
||||
|
||||
/--
|
||||
info: datetime("1999-12-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subMonthsClip 1
|
||||
|
||||
/--
|
||||
info: datetime("2000-02-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addMonthsRollOver 1
|
||||
|
||||
/--
|
||||
info: datetime("1999-12-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subMonthsRollOver 1
|
||||
|
||||
/--
|
||||
info: datetime("2001-01-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addYearsClip 1
|
||||
|
||||
/--
|
||||
info: datetime("1999-01-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subYearsClip 1
|
||||
|
||||
/--
|
||||
info: datetime("2001-01-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.addYearsRollOver 1
|
||||
|
||||
/--
|
||||
info: datetime("1999-01-20T03:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetime.subYearsRollOver 1
|
||||
|
||||
def time := time("13:02:01")
|
||||
|
||||
/--
|
||||
info: time("14:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.addHours 1
|
||||
|
||||
/--
|
||||
info: time("12:02:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.subHours 1
|
||||
|
||||
/--
|
||||
info: time("13:12:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.addMinutes 10
|
||||
|
||||
/--
|
||||
info: time("12:52:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.subMinutes 10
|
||||
|
||||
/--
|
||||
info: time("13:03:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.addSeconds 60
|
||||
|
||||
/--
|
||||
info: time("13:01:01.000000000")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval time.subSeconds 60
|
||||
|
||||
def datetimetz := zoned("2000-01-20T06:02:01-03:00")
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:02:01.000000000-03:00")
|
||||
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-22T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addDays 2
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-19T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subDays 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-02-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addMonthsClip 1
|
||||
|
||||
/--
|
||||
info: zoned("1999-12-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subMonthsClip 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-02-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addMonthsRollOver 1
|
||||
|
||||
/--
|
||||
info: zoned("1999-12-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subMonthsRollOver 1
|
||||
|
||||
/--
|
||||
info: zoned("2001-01-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addYearsClip 1
|
||||
|
||||
/--
|
||||
info: zoned("2001-01-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addYearsClip 1
|
||||
|
||||
/--
|
||||
info: zoned("2001-01-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addYearsRollOver 1
|
||||
|
||||
/--
|
||||
info: zoned("1999-01-20T06:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subYearsRollOver 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T07:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addHours 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T05:02:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subHours 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:12:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addMinutes 10
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T05:52:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subMinutes 10
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:03:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.addSeconds 60
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:01:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subSeconds 60
|
||||
|
||||
def now := zoned("2024-08-29T10:56:43.276801081+02:00")
|
||||
|
||||
/--
|
||||
info: zoned("2024-08-29T10:56:43.276801081+02:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval now
|
||||
|
||||
/--
|
||||
info: zoned("2024-08-30T10:56:43.276801081+02:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval now.addDays 1
|
||||
|
||||
/--
|
||||
info: zoned("2000-01-20T06:01:01.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval datetimetz.subSeconds 60
|
||||
|
||||
/--
|
||||
info: 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date("2024-11-17").alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
info: 4
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date("2024-11-18").alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
info: 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date("2024-01-21").alignedWeekOfMonth
|
||||
|
||||
/--
|
||||
info: 4
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date("2024-01-22").alignedWeekOfMonth
|
||||
596
tests/lean/run/timeOperationsOffset.lean
Normal file
596
tests/lean/run/timeOperationsOffset.lean
Normal file
@@ -0,0 +1,596 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 1000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 1000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 60000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 3600000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 86400000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 604800000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 1000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 1001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 60001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 3600001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 86400001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 604800001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 1000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 1001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 61
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 3601
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 86401
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 604801
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 60000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 60001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 61
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 61
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 1441
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 10081
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 3600000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 3600001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 3601
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 61
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 25
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 169
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 86400000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 86400001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 86401
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 1441
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 25
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 8
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 604800000000001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 604800001
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 604801
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 10081
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 169
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 8
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 2
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) + (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: -999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: -999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: -59999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: -3599999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: -86399999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -604799999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Nanosecond.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: -999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: -59999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: -3599999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: -86399999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -604799999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Millisecond.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: -59
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: -3599
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: -86399
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -604799
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Second.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 59999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 59999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 59
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: -59
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: -1439
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -10079
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Minute.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 3599999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 3599999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 3599
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 59
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: -23
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -167
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Hour.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 86399999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 86399999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 86399
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 1439
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 23
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: -6
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Day.Offset) - (1 : Week.Offset)
|
||||
|
||||
/--
|
||||
info: 604799999999999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Nanosecond.Offset)
|
||||
|
||||
/--
|
||||
info: 604799999
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Millisecond.Offset)
|
||||
|
||||
/--
|
||||
info: 604799
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Second.Offset)
|
||||
|
||||
/--
|
||||
info: 10079
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Minute.Offset)
|
||||
|
||||
/--
|
||||
info: 167
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Hour.Offset)
|
||||
|
||||
/--
|
||||
info: 6
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Day.Offset)
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval (1 : Week.Offset) - (1 : Week.Offset)
|
||||
204
tests/lean/run/timeParse.lean
Normal file
204
tests/lean/run/timeParse.lean
Normal file
@@ -0,0 +1,204 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def ISO8601UTC : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
|
||||
def RFC1123 : GenericFormat .any := datespec("eee, dd MMM uuuu HH:mm:ss ZZZ")
|
||||
def ShortDate : GenericFormat .any := datespec("MM/dd/uuuu")
|
||||
def LongDate : GenericFormat .any := datespec("MMMM D, uuuu")
|
||||
def ShortDateTime : GenericFormat .any := datespec("MM/dd/uuuu HH:mm:ss")
|
||||
def LongDateTime : GenericFormat .any := datespec("MMMM dd, uuuu hh:mm aa")
|
||||
def Time24Hour : GenericFormat .any := datespec("HH:mm:ss")
|
||||
def Time12Hour : GenericFormat .any := datespec("hh:mm:ss aa")
|
||||
def FullDayTimeZone : GenericFormat .any := datespec("EEEE, MMMM dd, uuuu HH:mm:ss ZZZ")
|
||||
def CustomDayTime : GenericFormat .any := datespec("EEE dd MMM uuuu HH:mm")
|
||||
|
||||
def Full12HourWrong : GenericFormat .any := datespec("MM/dd/uuuu hh:mm:ss aa XXX")
|
||||
|
||||
-- Dates
|
||||
|
||||
def brTZ : TimeZone := timezone("America/Sao_Paulo -03:00")
|
||||
def jpTZ : TimeZone := timezone("Asia/Tokyo +09:00")
|
||||
|
||||
def date₁ := zoned("2014-06-16T03:03:03-03:00")
|
||||
|
||||
def time₁ := time("14:11:01")
|
||||
def time₂ := time("03:11:01")
|
||||
|
||||
/--
|
||||
info: "2014-06-16T03:03:03.000000100-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := ISO8601UTC.parse! "2014-06-16T03:03:03.000000100-03:00"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
def tm := date₁.toTimestamp
|
||||
def date₂ := DateTime.ofTimestamp tm brTZ
|
||||
|
||||
/--
|
||||
info: "2014-06-16T03:03:03.000000000-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := RFC1123.parse! "Mon, 16 Jun 2014 03:03:03 -0300"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
def tm₃ := date₁.toTimestamp
|
||||
def date₃ := DateTime.ofTimestamp tm₃ brTZ
|
||||
|
||||
/--
|
||||
info: "2014-06-16T00:00:00.000000000Z"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := ShortDate.parse! "06/16/2014"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
-- the timestamp is always related to UTC.
|
||||
|
||||
/--
|
||||
Timestamp: 1723739292
|
||||
GMT: Thursday, 15 August 2024 16:28:12
|
||||
BR: 15 August 2024 13:28:12 GMT-03:00
|
||||
-/
|
||||
def tm₄ : Second.Offset := 1723739292
|
||||
|
||||
def dateBR := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) brTZ
|
||||
def dateJP := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) jpTZ
|
||||
def dateUTC := DateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch tm₄) .UTC
|
||||
|
||||
/--
|
||||
info: "2024-08-15T13:28:12.000000000-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t := FullDayTimeZone.parse! "Thursday, August 15, 2024 13:28:12 -0300"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
/--
|
||||
info: "2024-08-16T01:28:00.000000000Z"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := LongDateTime.parse! "August 16, 2024 01:28 AM"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
/--
|
||||
info: "0000-12-30T22:28:12.000000000+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := Time24Hour.parse! "13:28:12"
|
||||
ISO8601UTC.format (t.toDateTime.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "0000-12-29T21:28:12.000000000-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t1 : ZonedDateTime := Time12Hour.parse! "12:28:12 AM"
|
||||
ISO8601UTC.format (t1.toDateTime.convertTimeZone brTZ)
|
||||
|
||||
/--
|
||||
info: "Thu 15 Aug 2024 16:28"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 : ZonedDateTime := FullDayTimeZone.parse! "Thursday, August 15, 2024 16:28:12 -0000"
|
||||
CustomDayTime.format t2.toDateTime
|
||||
|
||||
/--
|
||||
info: "2024-08-16T13:28:00.000000000Z"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t5 : ZonedDateTime := CustomDayTime.parse! "Thu 16 Aug 2024 13:28"
|
||||
ISO8601UTC.format t5.toDateTime
|
||||
|
||||
/--
|
||||
info: "2024-08-16T01:28:12.000000000+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t6 : ZonedDateTime := FullDayTimeZone.parse! "Friday, August 16, 2024 01:28:12 +0900"
|
||||
ISO8601UTC.format (t6.toDateTime.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
info: "2024-08-16T01:28:12.000000000+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t7 : ZonedDateTime := FullDayTimeZone.parse! "Friday, August 16, 2024 01:28:12 +0900"
|
||||
ISO8601UTC.format (t7.toDateTime.convertTimeZone jpTZ)
|
||||
|
||||
/--
|
||||
TM: 1723730627
|
||||
GMT: Thursday, 15 August 2024 14:03:47
|
||||
Your time zone: 15 Aguust 2024 11:03:47 GMT-03:00
|
||||
-/
|
||||
def localTm : Second.Offset := 1723730627
|
||||
|
||||
/--
|
||||
This PlainDate is relative to the local time.
|
||||
-/
|
||||
def PlainDate : PlainDateTime := Timestamp.toPlainDateTimeAssumingUTC (Timestamp.ofSecondsSinceUnixEpoch localTm)
|
||||
|
||||
def dateBR₁ := DateTime.ofPlainDateTime PlainDate brTZ
|
||||
def dateJP₁ := DateTime.ofPlainDateTime PlainDate jpTZ
|
||||
def dateUTC₁ := DateTime.ofPlainDateTime PlainDate .UTC
|
||||
|
||||
/--
|
||||
info: "2024-08-15T14:03:47.000000000-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := FullDayTimeZone.parse! "Thursday, August 15, 2024 14:03:47 -0300"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
/--
|
||||
info: "2024-08-15T14:03:47.000000000+09:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t1 : ZonedDateTime := FullDayTimeZone.parse! "Thursday, August 15, 2024 14:03:47 +0900"
|
||||
ISO8601UTC.format t1.toDateTime
|
||||
|
||||
/--
|
||||
info: "2014-06-16T03:03:03.000000000-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 : ZonedDateTime := FullDayTimeZone.parse! "Monday, June 16, 2014 03:03:03 -0300"
|
||||
ISO8601UTC.format t2.toDateTime
|
||||
|
||||
/--
|
||||
info: Except.ok "1993-05-10T10:30:23.000000000+03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 := Full12HourWrong.parse "05/10/1993 10:30:23 AM +03:00"
|
||||
(ISO8601UTC.format ·.toDateTime) <$> t2
|
||||
|
||||
/--
|
||||
info: Except.ok "1993-05-10T22:30:23.000000000+03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 := Full12HourWrong.parse "05/10/1993 10:30:23 PM +03:00"
|
||||
(ISO8601UTC.format ·.toDateTime) <$> t2
|
||||
|
||||
/--
|
||||
info: Except.error "offset 13: need a natural number in the interval of 1 to 12"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 := Full12HourWrong.parse "05/10/1993 20:30:23 AM +03:00"
|
||||
(ISO8601UTC.format ·.toDateTime) <$> t2
|
||||
|
||||
/--
|
||||
info: Except.error "offset 13: need a natural number in the interval of 1 to 12"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t2 := Full12HourWrong.parse "05/10/1993 20:30:23 PM +03:00"
|
||||
(ISO8601UTC.format ·.toDateTime) <$> t2
|
||||
100
tests/lean/run/timeSet.lean
Normal file
100
tests/lean/run/timeSet.lean
Normal file
@@ -0,0 +1,100 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def ISO8601UTC : GenericFormat .any := datespec("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
|
||||
def RFC1123 : GenericFormat .any := datespec("eee, dd MMM uuuu HH:mm:ss ZZZ")
|
||||
def ShortDate : GenericFormat .any := datespec("MM/dd/uuuu")
|
||||
def LongDate : GenericFormat .any := datespec("MMMM D, uuuu")
|
||||
def ShortDateTime : GenericFormat .any := datespec("MM/dd/uuuu HH:mm:ss")
|
||||
def LongDateTime : GenericFormat .any := datespec("MMMM dd, uuuu hh:mm aa")
|
||||
def Time24Hour : GenericFormat .any := datespec("HH:mm:ss")
|
||||
def Time12Hour : GenericFormat .any := datespec("hh:mm:ss aa")
|
||||
def FullDayTimeZone : GenericFormat .any := datespec("EEEE, MMMM dd, uuuu HH:mm:ss ZZZ")
|
||||
def CustomDayTime : GenericFormat .any := datespec("EEE dd MMM uuuu HH:mm")
|
||||
|
||||
def Full12HourWrong : GenericFormat .any := datespec("MM/dd/uuuu hh:mm:ss aa XXX")
|
||||
|
||||
-- Dates
|
||||
|
||||
def brTZ : TimeZone := timezone("America/Sao_Paulo -03:00")
|
||||
def jpTZ : TimeZone := timezone("Asia/Tokyo +09:00")
|
||||
|
||||
def date₁ := zoned("2014-06-16T10:03:03-03:00")
|
||||
|
||||
def time₁ := time("14:11:01")
|
||||
def time₂ := time("03:11:01")
|
||||
|
||||
/--
|
||||
info: "2014-06-16T10:03:03.000000100-03:00"
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval
|
||||
let t : ZonedDateTime := ISO8601UTC.parse! "2014-06-16T10:03:03.000000100-03:00"
|
||||
ISO8601UTC.format t.toDateTime
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-30T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withDaysClip 31
|
||||
|
||||
/--
|
||||
info: zoned("2014-07-01T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withDaysRollOver 31
|
||||
|
||||
/--
|
||||
info: zoned("2014-05-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withMonthClip 5
|
||||
|
||||
/--
|
||||
info: zoned("2014-05-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withMonthRollOver 5
|
||||
|
||||
/--
|
||||
info: zoned("2016-06-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withYearClip 2016
|
||||
|
||||
/--
|
||||
info: zoned("2016-06-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withYearRollOver 2016
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withDaysClip 16
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-16T10:45:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withMinutes 45
|
||||
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-16T10:03:03.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withHours 10
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-16T10:03:59.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withSeconds ⟨true, 59⟩
|
||||
|
||||
/--
|
||||
info: zoned("2014-06-16T10:03:03.000000002-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval date₁.withNanoseconds 2
|
||||
110
tests/lean/run/timeTzifParse.lean
Normal file
110
tests/lean/run/timeTzifParse.lean
Normal file
@@ -0,0 +1,110 @@
|
||||
import Std.Time
|
||||
open Std.Time
|
||||
|
||||
def file := ByteArray.mk <|
|
||||
#[84, 90, 105, 102, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0]
|
||||
++ #[0, 0, 3, 0, 0, 0, 12, 150, 170, 114, 180, 184, 15, 73, 224, 184, 253, 64, 160, 185, 241, 52, 48, 186, 222, 116, 32, 218, 56, 174, 48, 218, 235, 250, 48, 220, 25]
|
||||
++ #[225, 176, 220, 185, 89, 32, 221, 251, 21, 48, 222, 155, 222, 32, 223, 221, 154, 48, 224, 84, 51, 32, 244, 90, 9, 48, 245, 5, 94, 32, 246, 192, 100, 48, 247, 14, 30]
|
||||
++ #[160, 248, 81, 44, 48, 248, 199, 197, 32, 250, 10, 210, 176, 250, 168, 248, 160, 251, 236, 6, 48, 252, 139, 125, 160, 29, 201, 142, 48, 30, 120, 215, 160, 31, 160, 53, 176]
|
||||
++ #[32, 51, 207, 160, 33, 129, 105, 48, 34, 11, 200, 160, 35, 88, 16, 176, 35, 226, 112, 32, 37, 55, 242, 176, 37, 212, 199, 32, 39, 33, 15, 48, 39, 189, 227, 160, 41]
|
||||
++ #[0, 241, 48, 41, 148, 139, 32, 42, 234, 13, 176, 43, 107, 50, 160, 44, 192, 181, 48, 45, 102, 196, 32, 46, 160, 151, 48, 47, 70, 166, 32, 48, 128, 121, 48, 49, 29]
|
||||
++ #[77, 160, 50, 87, 32, 176, 51, 6, 106, 32, 52, 56, 84, 48, 52, 248, 193, 32, 54, 32, 31, 48, 54, 207, 104, 160, 55, 246, 198, 176, 56, 184, 133, 32, 57, 223, 227]
|
||||
++ #[48, 58, 143, 44, 160, 59, 200, 255, 176, 60, 111, 14, 160, 61, 196, 145, 48, 62, 78, 240, 160, 63, 145, 254, 48, 64, 46, 210, 160, 65, 134, 248, 48, 66, 23, 239, 32]
|
||||
++ #[67, 81, 194, 48, 67, 247, 209, 32, 69, 77, 83, 176, 69, 224, 237, 160, 71, 17, 134, 48, 71, 183, 149, 32, 72, 250, 162, 176, 73, 151, 119, 32, 74, 218, 132, 176, 75]
|
||||
++ #[128, 147, 160, 76, 186, 102, 176, 77, 96, 117, 160, 78, 154, 72, 176, 79, 73, 146, 32, 80, 131, 101, 48, 81, 32, 57, 160, 82, 99, 71, 48, 83, 0, 27, 160, 84, 67]
|
||||
++ #[41, 48, 84, 233, 56, 32, 86, 35, 11, 48, 86, 201, 26, 32, 88, 2, 237, 48, 88, 168, 252, 32, 89, 226, 207, 48, 90, 136, 222, 32, 91, 222, 96, 176, 92, 104, 192]
|
||||
++ #[32, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
|
||||
++ #[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
|
||||
++ #[1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 255, 255, 212, 76, 0, 0, 255, 255, 227, 224, 1, 4, 255, 255, 213, 208, 0, 8, 76]
|
||||
++ #[77, 84, 0, 45, 48, 50, 0, 45, 48, 51, 0, 84, 90, 105, 102, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
++ #[0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 3, 0, 0, 0, 12, 255, 255, 255, 255, 150, 170, 114, 180, 255, 255, 255, 255, 184, 15, 73, 224, 255, 255, 255]
|
||||
++ #[255, 184, 253, 64, 160, 255, 255, 255, 255, 185, 241, 52, 48, 255, 255, 255, 255, 186, 222, 116, 32, 255, 255, 255, 255, 218, 56, 174, 48, 255, 255, 255, 255, 218, 235, 250, 48]
|
||||
++ #[255, 255, 255, 255, 220, 25, 225, 176, 255, 255, 255, 255, 220, 185, 89, 32, 255, 255, 255, 255, 221, 251, 21, 48, 255, 255, 255, 255, 222, 155, 222, 32, 255, 255, 255, 255, 223]
|
||||
++ #[221, 154, 48, 255, 255, 255, 255, 224, 84, 51, 32, 255, 255, 255, 255, 244, 90, 9, 48, 255, 255, 255, 255, 245, 5, 94, 32, 255, 255, 255, 255, 246, 192, 100, 48, 255, 255]
|
||||
++ #[255, 255, 247, 14, 30, 160, 255, 255, 255, 255, 248, 81, 44, 48, 255, 255, 255, 255, 248, 199, 197, 32, 255, 255, 255, 255, 250, 10, 210, 176, 255, 255, 255, 255, 250, 168, 248]
|
||||
++ #[160, 255, 255, 255, 255, 251, 236, 6, 48, 255, 255, 255, 255, 252, 139, 125, 160, 0, 0, 0, 0, 29, 201, 142, 48, 0, 0, 0, 0, 30, 120, 215, 160, 0, 0, 0, 0]
|
||||
++ #[31, 160, 53, 176, 0, 0, 0, 0, 32, 51, 207, 160, 0, 0, 0, 0, 33, 129, 105, 48, 0, 0, 0, 0, 34, 11, 200, 160, 0, 0, 0, 0, 35, 88, 16, 176, 0]
|
||||
++ #[0, 0, 0, 35, 226, 112, 32, 0, 0, 0, 0, 37, 55, 242, 176, 0, 0, 0, 0, 37, 212, 199, 32, 0, 0, 0, 0, 39, 33, 15, 48, 0, 0, 0, 0, 39, 189]
|
||||
++ #[227, 160, 0, 0, 0, 0, 41, 0, 241, 48, 0, 0, 0, 0, 41, 148, 139, 32, 0, 0, 0, 0, 42, 234, 13, 176, 0, 0, 0, 0, 43, 107, 50, 160, 0, 0, 0]
|
||||
++ #[0, 44, 192, 181, 48, 0, 0, 0, 0, 45, 102, 196, 32, 0, 0, 0, 0, 46, 160, 151, 48, 0, 0, 0, 0, 47, 70, 166, 32, 0, 0, 0, 0, 48, 128, 121, 48]
|
||||
++ #[0, 0, 0, 0, 49, 29, 77, 160, 0, 0, 0, 0, 50, 87, 32, 176, 0, 0, 0, 0, 51, 6, 106, 32, 0, 0, 0, 0, 52, 56, 84, 48, 0, 0, 0, 0, 52]
|
||||
++ #[248, 193, 32, 0, 0, 0, 0, 54, 32, 31, 48, 0, 0, 0, 0, 54, 207, 104, 160, 0, 0, 0, 0, 55, 246, 198, 176, 0, 0, 0, 0, 56, 184, 133, 32, 0, 0]
|
||||
++ #[0, 0, 57, 223, 227, 48, 0, 0, 0, 0, 58, 143, 44, 160, 0, 0, 0, 0, 59, 200, 255, 176, 0, 0, 0, 0, 60, 111, 14, 160, 0, 0, 0, 0, 61, 196, 145]
|
||||
++ #[48, 0, 0, 0, 0, 62, 78, 240, 160, 0, 0, 0, 0, 63, 145, 254, 48, 0, 0, 0, 0, 64, 46, 210, 160, 0, 0, 0, 0, 65, 134, 248, 48, 0, 0, 0, 0]
|
||||
++ #[66, 23, 239, 32, 0, 0, 0, 0, 67, 81, 194, 48, 0, 0, 0, 0, 67, 247, 209, 32, 0, 0, 0, 0, 69, 77, 83, 176, 0, 0, 0, 0, 69, 224, 237, 160, 0]
|
||||
++ #[0, 0, 0, 71, 17, 134, 48, 0, 0, 0, 0, 71, 183, 149, 32, 0, 0, 0, 0, 72, 250, 162, 176, 0, 0, 0, 0, 73, 151, 119, 32, 0, 0, 0, 0, 74, 218]
|
||||
++ #[132, 176, 0, 0, 0, 0, 75, 128, 147, 160, 0, 0, 0, 0, 76, 186, 102, 176, 0, 0, 0, 0, 77, 96, 117, 160, 0, 0, 0, 0, 78, 154, 72, 176, 0, 0, 0]
|
||||
++ #[0, 79, 73, 146, 32, 0, 0, 0, 0, 80, 131, 101, 48, 0, 0, 0, 0, 81, 32, 57, 160, 0, 0, 0, 0, 82, 99, 71, 48, 0, 0, 0, 0, 83, 0, 27, 160]
|
||||
++ #[0, 0, 0, 0, 84, 67, 41, 48, 0, 0, 0, 0, 84, 233, 56, 32, 0, 0, 0, 0, 86, 35, 11, 48, 0, 0, 0, 0, 86, 201, 26, 32, 0, 0, 0, 0, 88]
|
||||
++ #[2, 237, 48, 0, 0, 0, 0, 88, 168, 252, 32, 0, 0, 0, 0, 89, 226, 207, 48, 0, 0, 0, 0, 90, 136, 222, 32, 0, 0, 0, 0, 91, 222, 96, 176, 0, 0]
|
||||
++ #[0, 0, 92, 104, 192, 32, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
|
||||
++ #[1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
|
||||
++ #[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 255, 255, 212, 76, 0, 0, 255, 255, 227, 224, 1, 4, 255, 255]
|
||||
++ #[213, 208, 0, 8, 76, 77, 84, 0, 45, 48, 50, 0, 45, 48, 51, 0, 10, 60, 45, 48, 51, 62, 51, 10]
|
||||
|
||||
def code := Std.Time.TimeZone.TZif.parse.run file |>.toOption |>.get!
|
||||
|
||||
def rules :=
|
||||
match TimeZone.convertTZif code "America/Sao_Paulo" with
|
||||
| .ok res => res
|
||||
| .error err => panic! err
|
||||
|
||||
/--
|
||||
info: { version := 50, isutcnt := 0, isstdcnt := 0, leapcnt := 0, timecnt := 91, typecnt := 3, charcnt := 12 }
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.header
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.leapSeconds.size
|
||||
|
||||
/--
|
||||
info: 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.abbreviations.size
|
||||
|
||||
/--
|
||||
info: 91
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.transitionIndices.size
|
||||
|
||||
/--
|
||||
info: 91
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.transitionTimes.size
|
||||
|
||||
/--
|
||||
info: 3
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.localTimeTypes.size
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.stdWallIndicators.size
|
||||
|
||||
/--
|
||||
info: 0
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval code.v1.utLocalIndicators.size
|
||||
|
||||
/--
|
||||
info: zoned("1969-12-31T21:00:00.000000000-03:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval ZonedDateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch 0) rules
|
||||
|
||||
/--
|
||||
info: zoned("2012-12-10T00:35:47.000000000-02:00")
|
||||
-/
|
||||
#guard_msgs in
|
||||
#eval ZonedDateTime.ofTimestamp (Timestamp.ofSecondsSinceUnixEpoch 1355106947) rules
|
||||
Reference in New Issue
Block a user