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:
Sofia Rodrigues
2024-11-14 06:04:19 -08:00
committed by GitHub
parent 6a5b122b40
commit e0d7c3ac79
63 changed files with 13765 additions and 11 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -6,5 +6,6 @@ Authors: Sebastian Ullrich
prelude
import Std.Data
import Std.Sat
import Std.Time
import Std.Tactic
import Std.Internal

View File

@@ -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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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
View 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}"

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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

View 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

View 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)

View 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

View 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

View 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

View 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

View 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

View File

@@ -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");

View File

@@ -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)

View File

@@ -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

View File

@@ -1,4 +1,5 @@
import Lean.Data.Rat
import Std.Internal.Rat
open Std.Internal
open Lean
#eval (15 : Rat) / 10

View File

@@ -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
View 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

View 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

View 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"))

View 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}"

View 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

View 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

View 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)

View 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
View 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

View 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