Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add date and time functionality #4904

Open
wants to merge 76 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9c1be6c
feat: add datetime library
algebraic-dev Aug 2, 2024
856f569
chore: remove useless piece of code
algebraic-dev Aug 2, 2024
a9bf653
feat: format and moved the folder to the right place
algebraic-dev Aug 5, 2024
36108dd
Merge branch 'leanprover:master' into master
algebraic-dev Aug 7, 2024
a2e3a4e
feat: api docs, parser changes, notation, renames and improve documen…
algebraic-dev Aug 7, 2024
21563e8
refactor: rename some functions
algebraic-dev Aug 7, 2024
e0da8a6
feat: zone rules
algebraic-dev Aug 9, 2024
5e269e2
fix: wrong import
algebraic-dev Aug 9, 2024
8d0f196
feat: add io functions for timestamp and instant
algebraic-dev Aug 9, 2024
2c8ef66
feat: removes LE, adds a bunch of add and sub functions, move interva…
algebraic-dev Aug 14, 2024
858415c
chore: fix comment
algebraic-dev Aug 14, 2024
882fb0e
style: fix comment whitespaces
algebraic-dev Aug 14, 2024
5c01abe
style: fix whitespaces between declarations
algebraic-dev Aug 14, 2024
cf9b567
style: fix some comments and add prelude on the top
algebraic-dev Aug 14, 2024
5d058ad
feat: add parsing functions
algebraic-dev Aug 14, 2024
4e3b9b1
style: rename function and whitespace
algebraic-dev Aug 14, 2024
abfd3be
style: add prelude import
algebraic-dev Aug 14, 2024
bd236c1
style: reorder imports and comments
algebraic-dev Aug 15, 2024
4032ad2
feat: isDST flag for timezones
algebraic-dev Aug 15, 2024
2376e62
feat: a lot of conversion functions
algebraic-dev Aug 15, 2024
d53c100
style: fix some comments
algebraic-dev Aug 16, 2024
f724551
feat: add some functions and tests
algebraic-dev Aug 16, 2024
248ed5e
feat: add missing functions
algebraic-dev Aug 16, 2024
58a446b
fix: wrong comments
algebraic-dev Aug 16, 2024
c480249
fix: some small fixes and add things to duration
algebraic-dev Aug 19, 2024
9abb7b6
fix: small bugs and tests
algebraic-dev Aug 20, 2024
e9be735
fix: a bunch of problems and added tests
algebraic-dev Aug 20, 2024
89cfd2c
refactor: some structures and add test
algebraic-dev Aug 21, 2024
b193aac
feat: change some native functions
algebraic-dev Aug 23, 2024
a48f4ff
fix: bug with timezone change in localdatetime
algebraic-dev Aug 23, 2024
7b5754e
feat: add format functions
algebraic-dev Aug 23, 2024
842fb3b
feat: add a bunch of arithmetic functions
algebraic-dev Aug 23, 2024
3b83c6c
feat: add more sub and add functions
algebraic-dev Aug 23, 2024
d1bef18
test: add test for time operations
algebraic-dev Aug 23, 2024
ee0578f
chore: remove useless comments
algebraic-dev Aug 23, 2024
ab1d8cd
feat: changed function names and added ofSeconds and ofNanoseconds to…
algebraic-dev Aug 23, 2024
cafb98f
chore: reverting the branch :/
algebraic-dev Aug 26, 2024
c82e856
chore: reverting the branch :/
algebraic-dev Aug 26, 2024
073409d
Merge branch 'master' of github.com:leanprover/lean4
algebraic-dev Aug 26, 2024
091a969
refactor: changes Rat to Std.Internal and Parsec imports that was broken
algebraic-dev Aug 26, 2024
6c3cec4
refactor: changed names add some utilitary functions
algebraic-dev Aug 28, 2024
c30e4a1
feat: add conversion functions
algebraic-dev Aug 28, 2024
ada5670
fix: problems with since functions
algebraic-dev Aug 28, 2024
408fc65
tests: add more tests
algebraic-dev Aug 28, 2024
3f9899b
feat: add more conversion functions for timestamp
algebraic-dev Aug 28, 2024
ebadb4f
feat: remove notation import
algebraic-dev Aug 28, 2024
8e0794f
fix: problems with tests and function names
algebraic-dev Aug 28, 2024
0f755ec
fix: tests using Rat
algebraic-dev Aug 28, 2024
751b852
fix: update src/Std/Time/Duration.lean
algebraic-dev Aug 28, 2024
ec3968c
fix: update src/Std/Time.lean message
algebraic-dev Aug 28, 2024
86fec58
style: remove whitspace.
algebraic-dev Aug 28, 2024
3dabdcd
refactor: change Timestamp to depend on Duration
algebraic-dev Aug 28, 2024
1cccd65
refactor: fix some function names
algebraic-dev Aug 28, 2024
b1da2da
feat: add some class operations that result in durations
algebraic-dev Aug 28, 2024
7bc1a7c
feat: adjust pretty printing and notation for dates
algebraic-dev Aug 28, 2024
fe48007
refactor: modified each formatter to use the a default format that ca…
algebraic-dev Aug 28, 2024
f85c0cf
feat: add a bunch of conversion functions and fixes
algebraic-dev Aug 29, 2024
79c90c9
refactor: change notation syntax, add error handling for the get_time…
algebraic-dev Aug 30, 2024
b32c8a6
fix: avoid creating new tokens
algebraic-dev Aug 30, 2024
60f5b92
refactor: rename err to tm_ptr in io.cpp
algebraic-dev Sep 2, 2024
27e9893
refactor: fix names of things that were changed with search-and-repla…
algebraic-dev Sep 2, 2024
8d23eb0
fix: name of the localPath that is wrong
algebraic-dev Sep 2, 2024
b84a0e2
style: changes all the lambdas for fun
algebraic-dev Sep 2, 2024
3031f26
fix: wrong name of function
algebraic-dev Sep 2, 2024
16b4a72
fix: function names, ofOrdinal and others
algebraic-dev Sep 2, 2024
c772bd7
Merge branch 'master' of github.com:algebraic-dev/lean4
algebraic-dev Sep 2, 2024
cab1802
reafactor: notation and format
algebraic-dev Sep 20, 2024
d152573
fix: set functions and hour marker
algebraic-dev Sep 20, 2024
c6659e9
Merge branch 'master' of github.com:leanprover/lean4
algebraic-dev Sep 20, 2024
594aa87
fix: deprecations
algebraic-dev Sep 20, 2024
5367936
feat: remove lean parser from notation
algebraic-dev Sep 20, 2024
18feda1
fix: rename div to tdiv
algebraic-dev Sep 20, 2024
b213fc7
fix: tests
algebraic-dev Sep 20, 2024
9e74aac
fix: a bunch of comments and inlines
algebraic-dev Sep 26, 2024
fb8f037
fix: grammar correction
algebraic-dev Sep 30, 2024
d353fa6
feat: add leap second parser
algebraic-dev Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Std.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Authors: Sebastian Ullrich
prelude
import Std.Data
import Std.Sat
import Std.Time
174 changes: 174 additions & 0 deletions src/Std/Time.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/-
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sofia Rodrigues
-/
import Std.Time.Time
import Std.Time.Date
import Std.Time.Zoned
import Std.Time.Format
import Std.Time.DateTime
import Std.Time.Notation

namespace Time

/-!
# Time
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved

The Lean4 API for date, time, and duration functionalities, following the ISO standards.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which ISO standards? From googling I assume 8601? Is there more involved?

Copy link
Author

@algebraic-dev algebraic-dev Aug 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was supposed to be entirely based on ISO 8601, when I was writing the library I used most of the naming conventions used in the ISO 8601 but then I had to change a bunch of stuff.

The Modifier type for formatting a date, right now does not support the creation of a 100% compatible ISO 8601 (When I tried it in Java it had some divergences with negative years too), I think that in the end, I'll need to make a parser and a formatter for this format by hand.

The Notation module does not describe accurately an ISO 8601 because I'm not sure how can I use the syntax declaration to do some stuff like using the T with noWs (that leads to the quotation being confused) or how to count the number of zeroes in the fractional part of the time.


# 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
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved

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 a internal type called `UnitVal`.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved

- Types with a correspondence to seconds:
- `Day.Offset`
- `Hour.Offset`
- `WeekOfYear.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`
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
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, 24 hours and 61 seconds) to accomodate special cases
with leap seconds like `24:00:00` 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 24.
- `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 composed of these components. Dates are "absolute" value in contrast with Offsets
that are just shifts in dates and times. Types like `Date` are made using of components such as `Year.Offset`,
`Month.Ordinal`, and `Day.Ordinal`, with a proof of the date's validity. Some types, like `Date.Scalar`,
are wrappers around offsets (e.g., `Day.Offset`) and represent absolute dates relative to the UNIX Epoch.

## Date
These types provide precision down to the day level, useful for representing and manipulating dates.

- **`LocalDate`:** Represents a calendar date in the format `YYYY-MM-DD`.
- **`LocalDate.Scalar`:** : Represents an absolute date with day-level precision in a compact format `DD`.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
It encapsulates days since the UNIX Epoch.
- **`WeekDate`:** Uses the `YYYY-Www` format with week level precision.

## Time
These types offer precision down to the nanosecond level, useful for representing and manipulating time of day.

- **`LocalTime`**: 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.

- **`LocalDateTime`**: Represents both date and time in the format `YYYY-MM-DDTHH:mm:ss.SSSSSSSSS`.
- **`Timestamp`**: Represents a point in time with second-level precision.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved

## 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`**: An existencial version of the `DateTime`.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved

## Duration
Represents spans of time and the difference between two points in time.

- **`Instant`**: Represents a non-negative instant in time with nanosecond precision, represents a
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
specific point on the time axis.
- **`Duration`**: Represents the time span or difference between two `Instant` values with nanosecond precision.

# Formats

Format strings are used to convert between `String` representations and date/time types, e.g, `YYYY-MM-DD'T'hh:mm:ss.sssZ`.
The supported formats include:

- **Year:**
- `YYYY`: Four-digit year (e.g., 2024)
- `YY`: Two-digit year (e.g., 24 for 2024)
- **Month:**
- `MMMM`: Full month name (e.g., January, February)
- `MMM`: Abbreviated month name (e.g., Jan, Feb)
- `MM`: Two-digit month (e.g., 01 for January)
- `M`: One or two-digit month (e.g., 1 for January, 10 for October)
- **Day:**
- `DD`: Two-digit day of the month (e.g., 01, 02)
- `D`: One or two-digit day of the month (e.g., 1, 2)
- `d`: One or two-digit day of the month with space padding (e.g., " 1", "12")
- **Day of Week:**
- `EEEE`: Full name of the day of the week (e.g., Monday, Tuesday)
- `EEE`: Abbreviated day of the week (e.g., Mon, Tue)
- **Hour:**
- `hh`: Two-digit hour in 24-hour format (e.g., 13, 14)
- `h`: One or two-digit hour in 24-hour format (e.g., 1, 2)
- `HH`: Two-digit hour in 12-hour format (e.g., 01, 02)
- `H`: One or two-digit hour in 12-hour format (e.g., 1, 2)
- **AM/PM Indicator:**
- `AA`: Uppercase AM/PM (e.g., AM, PM)
- `aa`: Lowercase am/pm (e.g., am, pm)
- **Minute:**
- `mm`: Two-digit minute (e.g., 01, 02)
- `m`: One or two-digit minute (e.g., 1, 2)
- **Second:**
- `sss`: Three-digit milliseconds (e.g., 001, 202)
- `ss`: Two-digit second (e.g., 01, 02)
- `s`: One or two-digit second (e.g., 1, 2)
- **Timezone:**
- `ZZZZZ`: Full timezone offset with hours and minutes (e.g., +03:00)
- `ZZZZ`: Timezone offset without the colon (e.g., +0300)
- `ZZZ`: Like `ZZZZ`, but with "UTC" for UTC
- `Z`: Like `ZZZZZ`, but with "Z" for UTC
- `z`: Timezone name (e.g., Brasilia Standard Time)

# Macros

In order to help the user build dates easily, there are a lot of macros available for creating dates.

- **`date% DD-MM-YYYY`**: Defines a date in the `DD-MM-YYYY` format.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
- **`date% HH:mm:ss`**: Defines a time in the `HH:mm:ss` format.
- **`date% HH:mm:ss.sssssss`**: Defines a time in the `HH:mm:ss.sssssss` format, including fractional seconds.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
- **`date% YYYY-MM-DD:HH:mm:ss`**: Defines a datetime in the `YYYY-MM-DD:HH:mm:ss` format.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
- **`date% YYYY-MM-DDTHH:mm:ssZ`**: Defines a datetime with a timezone in the `YYYY-MM-DDTHH:mm:ssZ` format.
- **`offset% +HH:mm`**: Defines a timezone offset in the format `+HH:mm`.
- **`timezone% NAME/ID offset% +HH:mm`**: Defines a timezone with a name and an offset.
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
- **`date-spec% "format"`**: Defines a date specification format at compile time using the provided format string.

-/
9 changes: 9 additions & 0 deletions src/Std/Time/Date.lean
Original file line number Diff line number Diff line change
@@ -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.Date.Basic
import Std.Time.Date.WeekDate
import Std.Time.Date.LocalDate
7 changes: 7 additions & 0 deletions src/Std/Time/Date/Basic.lean
Original file line number Diff line number Diff line change
@@ -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.Date.Unit.Basic
143 changes: 143 additions & 0 deletions src/Std/Time/Date/LocalDate.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/-
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.Time.Date.Scalar

namespace Std
namespace Time

/--
Date in YMD format.
-/
structure LocalDate where
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
year : Year.Offset
month : Month.Ordinal
day : Day.Ordinal
valid : year.Valid month day
deriving Repr

namespace LocalDate

/--
Force the date to be valid.
-/
def clip (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : LocalDate :=
let ⟨day, valid⟩ := month.clipDay year.isLeap day
LocalDate.mk year month day valid

instance : Inhabited LocalDate where
default := clip 0 1 1

/--
Creates a new `LocalDate` using YMD.
-/
def ofYearMonthDay (year : Year.Offset) (month : Month.Ordinal) (day : Day.Ordinal) : Option LocalDate :=
if valid : year.Valid month day
then some (LocalDate.mk year month day valid)
else none

/--
Creates a new `LocalDate` using YO.
-/
def ofYearOrdinal (year : Year.Offset) (ordinal : Day.Ordinal.OfYear year.isLeap) : LocalDate :=
let ⟨⟨month, day⟩, valid⟩ := ordinal.toMonthAndDay
LocalDate.mk year month day valid

/--
Creates a new `LocalDate` using the `Day.Offset` which `0` corresponds the UNIX Epoch.
-/
def ofDaysSinceUNIXEpoch (day : Day.Offset) : LocalDate :=
let z := day.toInt + 719468
let era := (if z ≥ 0 then z else z - 146096) / 146097
let doe := z - era * 146097
let yoe := (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365
let y := yoe + era * 400
let doy := doe - (365 * yoe + yoe / 4 - yoe / 100)
let mp : Int := (5 * doy + 2) / 153
let d := doy - (153 * mp + 2) / 5
let m := mp + (if mp < 10 then 3 else -9)
let y := y + (if m <= 2 then 1 else 0)

.clip y (.clip m (by decide)) (.clip (d + 1) (by decide))

/--
Returns the `Weekday` of a `LocalDate`.
-/
def weekday (date : LocalDate) : Weekday :=
let q := date.day.toInt
let m := date.month.toInt
let y := date.year.toInt

let y := if m < 2 then y - 1 else y
let m := if m < 2 then m + 12 else m

let k := y % 100
let j := y / 100
let part := q + (13 * (m + 1)) / 5 + k + (k / 4)
let h := if y ≥ 1582 then part + (j/4) - 2*j else part + 5 - j
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
let d := (h + 5) % 7

.ofFin ⟨d.toNat % 7, Nat.mod_lt d.toNat (by decide)⟩

/--
Returns the `Weekday` of a `LocalDate` using Zeller's Congruence for the Julian calendar.
-/
def weekdayJulian (date : LocalDate) : Weekday :=
let month := date.month.toInt
let year := date.year.toInt

let q := date.day.toInt
let m := if month < 3 then month + 12 else month
let y := if month < 3 then year - 1 else year

let k := y % 100
let j := y / 100

let h := (q + (13 * (m + 1)) / 5 + k + (k / 4) + 5 - j) % 7
let d := (h + 5 - 1) % 7

.ofFin ⟨d.toNat % 7, Nat.mod_lt d.toNat (by decide)⟩

/--
Convert `LocalDate` to `Day.Offset` since the UNIX Epoch.
-/
def toDaysSinceUNIXEpoch (date : LocalDate) : Day.Offset :=
algebraic-dev marked this conversation as resolved.
Show resolved Hide resolved
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) / 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) / 5 + d - 1
let doe := yoe * 365 + yoe / 4 - yoe / 100 + doy

.ofInt (era * 146097 + doe - 719468)

/--
Convert a `Scalar` to a `LocalDate` since the UNIX Epoch.
-/
def ofScalar (day : Scalar) : LocalDate :=
ofDaysSinceUNIXEpoch day.day

/--
Convert a `LocalDate` to a `Scalar` since the UNIX Epoch.
-/
def toScalar (date : LocalDate) : Scalar :=
⟨toDaysSinceUNIXEpoch date⟩

/--
Calculate the Year.Offset from a LocalDate
-/
def yearsSince (date : LocalDate) (year : Year.Offset) : Year.Offset :=
date.year - year

instance : HAdd LocalDate Day.Offset LocalDate where
hAdd date day := ofDaysSinceUNIXEpoch (toDaysSinceUNIXEpoch date + day)

end LocalDate
end Time
end Std
Loading
Loading