Add files using upload-large-folder tool
Browse files- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database.lean +38 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/Basic.lean +114 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/TZdb.lean +100 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/TzIf.lean +328 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/Windows.lean +90 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/DateTime.lean +523 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Offset.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/TimeZone.lean +71 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/ZoneRules.lean +223 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/ZonedDateTime.lean +588 -0
- backend/db/models.py +80 -0
- backend/db/models.py.bak +67 -0
- backend/db/session.py +40 -0
- backend/tests/test_analysis.py +20 -0
- backend/tests/test_axiom.py +12 -0
- backend/tests/test_db.py +9 -0
- backend/tests/test_health.py +8 -0
- backend/tests/test_theorem.py +15 -0
- backend/tests/test_universe.py +12 -0
- backend/tools/requirements_audit.py +83 -0
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database.lean
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.Zoned.ZonedDateTime
|
| 8 |
+
import Std.Time.Zoned.Database.Basic
|
| 9 |
+
import Std.Time.Zoned.Database.TZdb
|
| 10 |
+
import Std.Time.Zoned.Database.Windows
|
| 11 |
+
import Init.System.Platform
|
| 12 |
+
|
| 13 |
+
namespace Std
|
| 14 |
+
namespace Time
|
| 15 |
+
namespace Database
|
| 16 |
+
open TimeZone.ZoneRules
|
| 17 |
+
|
| 18 |
+
set_option linter.all true
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Gets the zone rules for a specific time zone identifier, handling Windows and non-Windows platforms.
|
| 22 |
+
In windows it uses the current `icu.h` in Windows SDK. If it's linux or macos then it will use the `tzdata`
|
| 23 |
+
files.
|
| 24 |
+
-/
|
| 25 |
+
def defaultGetZoneRules : String → IO TimeZone.ZoneRules :=
|
| 26 |
+
if System.Platform.isWindows
|
| 27 |
+
then getZoneRules WindowsDb.default
|
| 28 |
+
else getZoneRules TZdb.default
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
Gets the local zone rules, accounting for platform differences.
|
| 32 |
+
In windows it uses the current `icu.h` in Windows SDK. If it's linux or macos then it will use the `tzdata`
|
| 33 |
+
files.
|
| 34 |
+
-/
|
| 35 |
+
def defaultGetLocalZoneRules : IO TimeZone.ZoneRules :=
|
| 36 |
+
if System.Platform.isWindows
|
| 37 |
+
then getLocalZoneRules WindowsDb.default
|
| 38 |
+
else getLocalZoneRules TZdb.default
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/Basic.lean
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.Zoned.ZoneRules
|
| 8 |
+
import Std.Time.Zoned.Database.TzIf
|
| 9 |
+
|
| 10 |
+
namespace Std
|
| 11 |
+
namespace Time
|
| 12 |
+
|
| 13 |
+
set_option linter.all true
|
| 14 |
+
|
| 15 |
+
/--
|
| 16 |
+
A timezone database from which we can read the `ZoneRules` of some area by it's id.
|
| 17 |
+
-/
|
| 18 |
+
protected class Database (α : Type) where
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Retrieves the zone rules information (`ZoneRules`) for a given area at a specific point in time.
|
| 22 |
+
-/
|
| 23 |
+
getZoneRules : α → String → IO TimeZone.ZoneRules
|
| 24 |
+
|
| 25 |
+
/--
|
| 26 |
+
Retrieves the local zone rules information (`ZoneRules`) at a given timestamp.
|
| 27 |
+
-/
|
| 28 |
+
getLocalZoneRules : α → IO TimeZone.ZoneRules
|
| 29 |
+
|
| 30 |
+
namespace TimeZone
|
| 31 |
+
|
| 32 |
+
/--
|
| 33 |
+
Converts a Boolean value to a corresponding `StdWall` type.
|
| 34 |
+
-/
|
| 35 |
+
def convertWall : Bool → StdWall
|
| 36 |
+
| true => .standard
|
| 37 |
+
| false => .wall
|
| 38 |
+
|
| 39 |
+
/--
|
| 40 |
+
Converts a Boolean value to a corresponding `UTLocal` type.
|
| 41 |
+
-/
|
| 42 |
+
def convertUt : Bool → UTLocal
|
| 43 |
+
| true => .ut
|
| 44 |
+
| false => .local
|
| 45 |
+
|
| 46 |
+
/--
|
| 47 |
+
Converts a given time index into a `LocalTimeType` by using a time zone (`tz`) and its identifier.
|
| 48 |
+
-/
|
| 49 |
+
def convertLocalTimeType (index : Nat) (tz : TZif.TZifV1) (identifier : String) : Option LocalTimeType := do
|
| 50 |
+
let localType ← tz.localTimeTypes[index]?
|
| 51 |
+
let offset := Offset.ofSeconds <| .ofInt localType.gmtOffset
|
| 52 |
+
let abbreviation := tz.abbreviations.getD index (offset.toIsoString true)
|
| 53 |
+
let wallflag := convertWall (tz.stdWallIndicators.getD index true)
|
| 54 |
+
let utLocal := convertUt (tz.utLocalIndicators.getD index true)
|
| 55 |
+
|
| 56 |
+
return {
|
| 57 |
+
gmtOffset := offset
|
| 58 |
+
isDst := localType.isDst
|
| 59 |
+
abbreviation
|
| 60 |
+
wall := wallflag
|
| 61 |
+
utLocal
|
| 62 |
+
identifier
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/--
|
| 66 |
+
Converts a transition.
|
| 67 |
+
-/
|
| 68 |
+
def convertTransition (times: Array LocalTimeType) (index : Nat) (tz : TZif.TZifV1) : Option Transition := do
|
| 69 |
+
let time := tz.transitionTimes[index]!
|
| 70 |
+
let time := Second.Offset.ofInt time
|
| 71 |
+
let indice := tz.transitionIndices[index]!
|
| 72 |
+
return { time, localTimeType := times[indice.toNat]! }
|
| 73 |
+
|
| 74 |
+
/--
|
| 75 |
+
Converts a `TZif.TZifV1` structure to a `ZoneRules` structure.
|
| 76 |
+
-/
|
| 77 |
+
def convertTZifV1 (tz : TZif.TZifV1) (id : String) : Except String ZoneRules := do
|
| 78 |
+
let mut times : Array LocalTimeType := #[]
|
| 79 |
+
|
| 80 |
+
for i in [0:tz.header.typecnt.toNat] do
|
| 81 |
+
if let some result := convertLocalTimeType i tz id
|
| 82 |
+
then times := times.push result
|
| 83 |
+
else .error s!"cannot convert local time {i} of the file"
|
| 84 |
+
|
| 85 |
+
let mut transitions := #[]
|
| 86 |
+
|
| 87 |
+
for i in [0:tz.transitionTimes.size] do
|
| 88 |
+
if let some result := convertTransition times i tz
|
| 89 |
+
then transitions := transitions.push result
|
| 90 |
+
else .error s!"cannot convert transition {i} of the file"
|
| 91 |
+
|
| 92 |
+
-- Local time for timestamps before the first transition is specified by the first time
|
| 93 |
+
-- type (time type 0).
|
| 94 |
+
|
| 95 |
+
let initialLocalTimeType ←
|
| 96 |
+
if let some res := convertLocalTimeType 0 tz id
|
| 97 |
+
then .ok res
|
| 98 |
+
else .error s!"empty transitions for {id}"
|
| 99 |
+
|
| 100 |
+
.ok { transitions, initialLocalTimeType }
|
| 101 |
+
|
| 102 |
+
/--
|
| 103 |
+
Converts a `TZif.TZifV2` structure to a `ZoneRules` structure.
|
| 104 |
+
-/
|
| 105 |
+
def convertTZifV2 (tz : TZif.TZifV2) (id : String) : Except String ZoneRules := do
|
| 106 |
+
convertTZifV1 tz.toTZifV1 id
|
| 107 |
+
|
| 108 |
+
/--
|
| 109 |
+
Converts a `TZif.TZif` structure to a `ZoneRules` structure.
|
| 110 |
+
-/
|
| 111 |
+
def convertTZif (tz : TZif.TZif) (id : String) : Except String ZoneRules := do
|
| 112 |
+
if let some v2 := tz.v2
|
| 113 |
+
then convertTZifV2 v2 id
|
| 114 |
+
else convertTZifV1 tz.v1 id
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/TZdb.lean
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.DateTime
|
| 8 |
+
import Std.Time.Zoned.TimeZone
|
| 9 |
+
import Std.Time.Zoned.ZoneRules
|
| 10 |
+
import Std.Time.Zoned.Database.Basic
|
| 11 |
+
|
| 12 |
+
namespace Std
|
| 13 |
+
namespace Time
|
| 14 |
+
namespace Database
|
| 15 |
+
|
| 16 |
+
set_option linter.all true
|
| 17 |
+
|
| 18 |
+
/--
|
| 19 |
+
Represents a Time Zone Database (TZdb) configuration with paths to local and general timezone data.
|
| 20 |
+
-/
|
| 21 |
+
structure TZdb where
|
| 22 |
+
|
| 23 |
+
/--
|
| 24 |
+
The path to the local timezone file. This is typically a symlink to a file within the timezone
|
| 25 |
+
database that corresponds to the current local time zone.
|
| 26 |
+
-/
|
| 27 |
+
localPath : System.FilePath := "/etc/localtime"
|
| 28 |
+
|
| 29 |
+
/--
|
| 30 |
+
All the possible paths to the directories containing all available time zone files. These files define various
|
| 31 |
+
time zones and their rules.
|
| 32 |
+
-/
|
| 33 |
+
zonesPaths : Array System.FilePath := #["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo", "/usr/share/lib/zoneinfo"]
|
| 34 |
+
|
| 35 |
+
namespace TZdb
|
| 36 |
+
open TimeZone
|
| 37 |
+
|
| 38 |
+
/--
|
| 39 |
+
Returns a default `TZdb` instance with common timezone data paths for most Linux distributions and macOS.
|
| 40 |
+
-/
|
| 41 |
+
@[inline]
|
| 42 |
+
def default : TZdb := {}
|
| 43 |
+
|
| 44 |
+
/--
|
| 45 |
+
Parses binary timezone data into zone rules based on a given timezone ID.
|
| 46 |
+
-/
|
| 47 |
+
def parseTZif (bin : ByteArray) (id : String) : Except String ZoneRules := do
|
| 48 |
+
let database ← TZif.parse.run bin
|
| 49 |
+
convertTZif database id
|
| 50 |
+
|
| 51 |
+
/--
|
| 52 |
+
Reads a TZif file from disk and retrieves the zone rules for the specified timezone ID.
|
| 53 |
+
-/
|
| 54 |
+
def parseTZIfFromDisk (path : System.FilePath) (id : String) : IO ZoneRules := do
|
| 55 |
+
let binary ← try IO.FS.readBinFile path catch _ => throw <| IO.userError s!"unable to locate {id} in the local timezone database at '{path}'"
|
| 56 |
+
IO.ofExcept (parseTZif binary id)
|
| 57 |
+
|
| 58 |
+
/--
|
| 59 |
+
Extracts a timezone ID from a file path.
|
| 60 |
+
-/
|
| 61 |
+
def idFromPath (path : System.FilePath) : Option String := do
|
| 62 |
+
let res := path.components.toArray
|
| 63 |
+
let last ← res[res.size - 1]?
|
| 64 |
+
let last₁ ← res[res.size - 2]?
|
| 65 |
+
|
| 66 |
+
if last₁ = "zoneinfo"
|
| 67 |
+
then some <| last.trim
|
| 68 |
+
else some <| last₁.trim ++ "/" ++ last.trim
|
| 69 |
+
|
| 70 |
+
/--
|
| 71 |
+
Retrieves the timezone rules from the local timezone data file.
|
| 72 |
+
-/
|
| 73 |
+
def localRules (path : System.FilePath) : IO ZoneRules := do
|
| 74 |
+
let localTimePath ← IO.FS.realPath path
|
| 75 |
+
if let some id := idFromPath localTimePath
|
| 76 |
+
then parseTZIfFromDisk path id
|
| 77 |
+
else throw (IO.userError "cannot read the id of the path.")
|
| 78 |
+
|
| 79 |
+
/--
|
| 80 |
+
Reads timezone rules from disk based on the provided file path and timezone ID.
|
| 81 |
+
-/
|
| 82 |
+
def readRulesFromDisk (path : System.FilePath) (id : String) : IO ZoneRules := do
|
| 83 |
+
parseTZIfFromDisk (System.FilePath.join path id) id
|
| 84 |
+
|
| 85 |
+
instance : Std.Time.Database TZdb where
|
| 86 |
+
getLocalZoneRules db := localRules db.localPath
|
| 87 |
+
|
| 88 |
+
getZoneRules db id := do
|
| 89 |
+
let env ← IO.getEnv "TZDIR"
|
| 90 |
+
|
| 91 |
+
if let some path := env then
|
| 92 |
+
let result ← readRulesFromDisk path id
|
| 93 |
+
return result
|
| 94 |
+
|
| 95 |
+
for path in db.zonesPaths do
|
| 96 |
+
if ← System.FilePath.pathExists path then
|
| 97 |
+
let result ← readRulesFromDisk path id
|
| 98 |
+
return result
|
| 99 |
+
|
| 100 |
+
throw <| IO.userError s!"cannot find {id} in the local timezone database"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/TzIf.lean
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.Range
|
| 8 |
+
import Std.Internal.Parsec
|
| 9 |
+
import Std.Internal.Parsec.ByteArray
|
| 10 |
+
|
| 11 |
+
-- Based on: https://www.rfc-editor.org/rfc/rfc8536.html
|
| 12 |
+
|
| 13 |
+
namespace Std
|
| 14 |
+
namespace Time
|
| 15 |
+
namespace TimeZone
|
| 16 |
+
namespace TZif
|
| 17 |
+
open Std.Internal.Parsec Std.Internal.Parsec.ByteArray
|
| 18 |
+
|
| 19 |
+
set_option linter.all true
|
| 20 |
+
|
| 21 |
+
private abbrev Int32 := Int
|
| 22 |
+
|
| 23 |
+
private abbrev Int64 := Int
|
| 24 |
+
|
| 25 |
+
/--
|
| 26 |
+
Represents the header of a TZif file, containing metadata about the file's structure.
|
| 27 |
+
-/
|
| 28 |
+
structure Header where
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
The version of the TZif file format.
|
| 32 |
+
-/
|
| 33 |
+
version : UInt8
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
The count of UT local indicators in the file.
|
| 37 |
+
-/
|
| 38 |
+
isutcnt : UInt32
|
| 39 |
+
|
| 40 |
+
/--
|
| 41 |
+
The count of standard/wall indicators in the file.
|
| 42 |
+
-/
|
| 43 |
+
isstdcnt : UInt32
|
| 44 |
+
|
| 45 |
+
/--
|
| 46 |
+
The number of leap second records.
|
| 47 |
+
-/
|
| 48 |
+
leapcnt : UInt32
|
| 49 |
+
|
| 50 |
+
/--
|
| 51 |
+
The number of transition times in the file.
|
| 52 |
+
-/
|
| 53 |
+
timecnt : UInt32
|
| 54 |
+
|
| 55 |
+
/--
|
| 56 |
+
The number of local time types in the file.
|
| 57 |
+
-/
|
| 58 |
+
typecnt : UInt32
|
| 59 |
+
|
| 60 |
+
/--
|
| 61 |
+
The total number of characters used in abbreviations.
|
| 62 |
+
-/
|
| 63 |
+
charcnt : UInt32
|
| 64 |
+
deriving Repr, Inhabited
|
| 65 |
+
|
| 66 |
+
/--
|
| 67 |
+
Represents the local time type information, including offset and daylight saving details.
|
| 68 |
+
-/
|
| 69 |
+
structure LocalTimeType where
|
| 70 |
+
|
| 71 |
+
/--
|
| 72 |
+
The GMT offset in seconds for this local time type.
|
| 73 |
+
-/
|
| 74 |
+
gmtOffset : Int32
|
| 75 |
+
|
| 76 |
+
/--
|
| 77 |
+
Indicates if this local time type observes daylight saving time.
|
| 78 |
+
-/
|
| 79 |
+
isDst : Bool
|
| 80 |
+
|
| 81 |
+
/--
|
| 82 |
+
The index into the abbreviation string table for this time type.
|
| 83 |
+
-/
|
| 84 |
+
abbreviationIndex : UInt8
|
| 85 |
+
deriving Repr, Inhabited
|
| 86 |
+
|
| 87 |
+
/--
|
| 88 |
+
Represents a leap second record, including the transition time and the correction applied.
|
| 89 |
+
-/
|
| 90 |
+
structure LeapSecond where
|
| 91 |
+
|
| 92 |
+
/--
|
| 93 |
+
The transition time of the leap second event.
|
| 94 |
+
-/
|
| 95 |
+
transitionTime : Int64
|
| 96 |
+
|
| 97 |
+
/--
|
| 98 |
+
The correction applied during the leap second event in seconds.
|
| 99 |
+
-/
|
| 100 |
+
correction : Int64
|
| 101 |
+
deriving Repr, Inhabited
|
| 102 |
+
|
| 103 |
+
/--
|
| 104 |
+
Represents version 1 of the TZif format.
|
| 105 |
+
-/
|
| 106 |
+
structure TZifV1 where
|
| 107 |
+
|
| 108 |
+
/--
|
| 109 |
+
The header information of the TZif file.
|
| 110 |
+
-/
|
| 111 |
+
header : Header
|
| 112 |
+
|
| 113 |
+
/--
|
| 114 |
+
The array of transition times in seconds since the epoch.
|
| 115 |
+
-/
|
| 116 |
+
transitionTimes : Array Int32
|
| 117 |
+
|
| 118 |
+
/--
|
| 119 |
+
The array of local time type indices corresponding to each transition time.
|
| 120 |
+
-/
|
| 121 |
+
transitionIndices : Array UInt8
|
| 122 |
+
|
| 123 |
+
/--
|
| 124 |
+
The array of local time type structures.
|
| 125 |
+
-/
|
| 126 |
+
localTimeTypes : Array LocalTimeType
|
| 127 |
+
|
| 128 |
+
/--
|
| 129 |
+
The array of abbreviation strings used by local time types.
|
| 130 |
+
-/
|
| 131 |
+
abbreviations : Array String
|
| 132 |
+
|
| 133 |
+
/--
|
| 134 |
+
The array of leap second records.
|
| 135 |
+
-/
|
| 136 |
+
leapSeconds : Array LeapSecond
|
| 137 |
+
|
| 138 |
+
/--
|
| 139 |
+
The array indicating whether each transition time uses wall clock time or standard time.
|
| 140 |
+
-/
|
| 141 |
+
stdWallIndicators : Array Bool
|
| 142 |
+
|
| 143 |
+
/--
|
| 144 |
+
The array indicating whether each transition time uses universal time or local time.
|
| 145 |
+
-/
|
| 146 |
+
utLocalIndicators : Array Bool
|
| 147 |
+
deriving Repr, Inhabited
|
| 148 |
+
|
| 149 |
+
/--
|
| 150 |
+
Represents version 2 of the TZif format, extending TZifV1 with an optional footer.
|
| 151 |
+
-/
|
| 152 |
+
structure TZifV2 extends TZifV1 where
|
| 153 |
+
|
| 154 |
+
/--
|
| 155 |
+
An optional footer for additional metadata in version 2.
|
| 156 |
+
-/
|
| 157 |
+
footer : Option String
|
| 158 |
+
deriving Repr, Inhabited
|
| 159 |
+
|
| 160 |
+
/--
|
| 161 |
+
Represents a TZif file, which can be either version 1 or version 2.
|
| 162 |
+
-/
|
| 163 |
+
structure TZif where
|
| 164 |
+
|
| 165 |
+
/--
|
| 166 |
+
The data for version 1 of the TZif file.
|
| 167 |
+
-/
|
| 168 |
+
v1 : TZifV1
|
| 169 |
+
|
| 170 |
+
/--
|
| 171 |
+
Optionally, the data for version 2 of the TZif file.
|
| 172 |
+
-/
|
| 173 |
+
v2 : Option TZifV2
|
| 174 |
+
deriving Repr, Inhabited
|
| 175 |
+
|
| 176 |
+
private def toUInt32 (bs : ByteArray) : UInt32 :=
|
| 177 |
+
assert! bs.size == 4
|
| 178 |
+
(bs.get! 0).toUInt32 <<< 0x18 |||
|
| 179 |
+
(bs.get! 1).toUInt32 <<< 0x10 |||
|
| 180 |
+
(bs.get! 2).toUInt32 <<< 0x8 |||
|
| 181 |
+
(bs.get! 3).toUInt32
|
| 182 |
+
|
| 183 |
+
private def toInt32 (bs : ByteArray) : Int32 :=
|
| 184 |
+
let n := toUInt32 bs |>.toNat
|
| 185 |
+
if n < (1 <<< 31)
|
| 186 |
+
then Int.ofNat n
|
| 187 |
+
else Int.negOfNat (UInt32.size - n)
|
| 188 |
+
|
| 189 |
+
private def toInt64 (bs : ByteArray) : Int64 :=
|
| 190 |
+
let n := ByteArray.toUInt64BE! bs |>.toNat
|
| 191 |
+
if n < (1 <<< 63)
|
| 192 |
+
then Int.ofNat n
|
| 193 |
+
else Int.negOfNat (UInt64.size - n)
|
| 194 |
+
|
| 195 |
+
private def manyN (n : Nat) (p : Parser α) : Parser (Array α) := do
|
| 196 |
+
let mut result := #[]
|
| 197 |
+
for _ in [0:n] do
|
| 198 |
+
let x ← p
|
| 199 |
+
result := result.push x
|
| 200 |
+
return result
|
| 201 |
+
|
| 202 |
+
private def pu64 : Parser UInt64 := ByteArray.toUInt64LE! <$> take 8
|
| 203 |
+
private def pi64 : Parser Int64 := toInt64 <$> take 8
|
| 204 |
+
private def pu32 : Parser UInt32 := toUInt32 <$> take 4
|
| 205 |
+
private def pi32 : Parser Int32 := toInt32 <$> take 4
|
| 206 |
+
private def pu8 : Parser UInt8 := any
|
| 207 |
+
private def pbool : Parser Bool := (· != 0) <$> pu8
|
| 208 |
+
|
| 209 |
+
private def parseHeader : Parser Header :=
|
| 210 |
+
Header.mk
|
| 211 |
+
<$> (pstring "TZif" *> pu8)
|
| 212 |
+
<*> (take 15 *> pu32)
|
| 213 |
+
<*> pu32
|
| 214 |
+
<*> pu32
|
| 215 |
+
<*> pu32
|
| 216 |
+
<*> pu32
|
| 217 |
+
<*> pu32
|
| 218 |
+
|
| 219 |
+
private def parseLocalTimeType : Parser LocalTimeType :=
|
| 220 |
+
LocalTimeType.mk
|
| 221 |
+
<$> pi32
|
| 222 |
+
<*> pbool
|
| 223 |
+
<*> pu8
|
| 224 |
+
|
| 225 |
+
private def parseLeapSecond (p : Parser Int) : Parser LeapSecond :=
|
| 226 |
+
LeapSecond.mk
|
| 227 |
+
<$> p
|
| 228 |
+
<*> pi32
|
| 229 |
+
|
| 230 |
+
private def parseTransitionTimes (size : Parser Int32) (n : UInt32) : Parser (Array Int32) :=
|
| 231 |
+
manyN (n.toNat) size
|
| 232 |
+
|
| 233 |
+
private def parseTransitionIndices (n : UInt32) : Parser (Array UInt8) :=
|
| 234 |
+
manyN (n.toNat) pu8
|
| 235 |
+
|
| 236 |
+
private def parseLocalTimeTypes (n : UInt32) : Parser (Array LocalTimeType) :=
|
| 237 |
+
manyN (n.toNat) parseLocalTimeType
|
| 238 |
+
|
| 239 |
+
private def parseAbbreviations (times : Array LocalTimeType) (n : UInt32) : Parser (Array String) := do
|
| 240 |
+
let mut strings := #[]
|
| 241 |
+
let mut current := ""
|
| 242 |
+
let mut chars ← manyN n.toNat pu8
|
| 243 |
+
|
| 244 |
+
for time in times do
|
| 245 |
+
for indx in [time.abbreviationIndex.toNat:n.toNat] do
|
| 246 |
+
let char := chars[indx]!
|
| 247 |
+
if char = 0 then
|
| 248 |
+
strings := strings.push current
|
| 249 |
+
current := ""
|
| 250 |
+
break
|
| 251 |
+
else
|
| 252 |
+
current := current.push (Char.ofUInt8 char)
|
| 253 |
+
|
| 254 |
+
return strings
|
| 255 |
+
|
| 256 |
+
private def parseLeapSeconds (size : Parser Int) (n : UInt32) : Parser (Array LeapSecond) :=
|
| 257 |
+
manyN (n.toNat) (parseLeapSecond size)
|
| 258 |
+
|
| 259 |
+
private def parseIndicators (n : UInt32) : Parser (Array Bool) :=
|
| 260 |
+
manyN (n.toNat) pbool
|
| 261 |
+
|
| 262 |
+
private def parseTZifV1 : Parser TZifV1 := do
|
| 263 |
+
let header ← parseHeader
|
| 264 |
+
|
| 265 |
+
let transitionTimes ← parseTransitionTimes pi32 header.timecnt
|
| 266 |
+
let transitionIndices ← parseTransitionIndices header.timecnt
|
| 267 |
+
let localTimeTypes ← parseLocalTimeTypes header.typecnt
|
| 268 |
+
let abbreviations ← parseAbbreviations localTimeTypes header.charcnt
|
| 269 |
+
let leapSeconds ← parseLeapSeconds pi32 header.leapcnt
|
| 270 |
+
let stdWallIndicators ← parseIndicators header.isstdcnt
|
| 271 |
+
let utLocalIndicators ← parseIndicators header.isutcnt
|
| 272 |
+
|
| 273 |
+
return {
|
| 274 |
+
header
|
| 275 |
+
transitionTimes
|
| 276 |
+
transitionIndices
|
| 277 |
+
localTimeTypes
|
| 278 |
+
abbreviations
|
| 279 |
+
leapSeconds
|
| 280 |
+
stdWallIndicators
|
| 281 |
+
utLocalIndicators
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
private def parseFooter : Parser (Option String) := do
|
| 285 |
+
let char ← pu8
|
| 286 |
+
|
| 287 |
+
if char = 0x0A then pure () else return none
|
| 288 |
+
|
| 289 |
+
let tzString ← many (satisfy (· ≠ 0x0A))
|
| 290 |
+
let mut str := ""
|
| 291 |
+
|
| 292 |
+
for byte in tzString do
|
| 293 |
+
str := str.push (Char.ofUInt8 byte)
|
| 294 |
+
|
| 295 |
+
return some str
|
| 296 |
+
|
| 297 |
+
private def parseTZifV2 : Parser (Option TZifV2) := optional do
|
| 298 |
+
let header ← parseHeader
|
| 299 |
+
|
| 300 |
+
let transitionTimes ← parseTransitionTimes pi64 header.timecnt
|
| 301 |
+
let transitionIndices ← parseTransitionIndices header.timecnt
|
| 302 |
+
let localTimeTypes ← parseLocalTimeTypes header.typecnt
|
| 303 |
+
let abbreviations ← parseAbbreviations localTimeTypes header.charcnt
|
| 304 |
+
let leapSeconds ← parseLeapSeconds pi64 header.leapcnt
|
| 305 |
+
let stdWallIndicators ← parseIndicators header.isstdcnt
|
| 306 |
+
let utLocalIndicators ← parseIndicators header.isutcnt
|
| 307 |
+
|
| 308 |
+
return {
|
| 309 |
+
header
|
| 310 |
+
transitionTimes
|
| 311 |
+
transitionIndices
|
| 312 |
+
localTimeTypes
|
| 313 |
+
abbreviations
|
| 314 |
+
leapSeconds
|
| 315 |
+
stdWallIndicators
|
| 316 |
+
utLocalIndicators
|
| 317 |
+
footer := ← parseFooter
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
/--
|
| 321 |
+
Parses a TZif file, which may be in either version 1 or version 2 format.
|
| 322 |
+
-/
|
| 323 |
+
def parse : Parser TZif := do
|
| 324 |
+
let v1 ← parseTZifV1
|
| 325 |
+
let v2 ← parseTZifV2
|
| 326 |
+
return { v1, v2 }
|
| 327 |
+
|
| 328 |
+
end TZif
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Database/Windows.lean
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.SInt.Basic
|
| 8 |
+
import Std.Time.DateTime
|
| 9 |
+
import Std.Time.Zoned.TimeZone
|
| 10 |
+
import Std.Time.Zoned.ZoneRules
|
| 11 |
+
import Std.Time.Zoned.Database.Basic
|
| 12 |
+
|
| 13 |
+
namespace Std
|
| 14 |
+
namespace Time
|
| 15 |
+
namespace Database
|
| 16 |
+
|
| 17 |
+
set_option linter.all true
|
| 18 |
+
|
| 19 |
+
namespace Windows
|
| 20 |
+
|
| 21 |
+
/--
|
| 22 |
+
Fetches the next timezone transition for a given timezone identifier and timestamp.
|
| 23 |
+
-/
|
| 24 |
+
@[extern "lean_windows_get_next_transition"]
|
| 25 |
+
opaque getNextTransition : @&String → Int64 → Bool → IO (Option (Int64 × TimeZone))
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
Fetches the timezone at a timestamp.
|
| 29 |
+
-/
|
| 30 |
+
@[extern "lean_get_windows_local_timezone_id_at"]
|
| 31 |
+
opaque getLocalTimeZoneIdentifierAt : Int64 → IO String
|
| 32 |
+
|
| 33 |
+
/--
|
| 34 |
+
Retrieves the timezone rules, including all transitions, for a given timezone identifier.
|
| 35 |
+
-/
|
| 36 |
+
def getZoneRules (id : String) : IO TimeZone.ZoneRules := do
|
| 37 |
+
let mut start := -2147483648
|
| 38 |
+
let mut transitions : Array TimeZone.Transition := #[]
|
| 39 |
+
|
| 40 |
+
let mut initialLocalTimeType ←
|
| 41 |
+
if let some res := ← Windows.getNextTransition id start true
|
| 42 |
+
then pure (toLocalTime res.snd)
|
| 43 |
+
else throw (IO.userError "cannot find first transition in zone rules")
|
| 44 |
+
|
| 45 |
+
while true do
|
| 46 |
+
let result ← Windows.getNextTransition id start false
|
| 47 |
+
|
| 48 |
+
if let some res := result then
|
| 49 |
+
transitions := transitions.push { time := Second.Offset.ofInt start.toInt, localTimeType := toLocalTime res.snd }
|
| 50 |
+
|
| 51 |
+
-- Avoid zone rules for more than year 3000
|
| 52 |
+
if res.fst ≤ start ∨ res.fst >= 32503690800 then
|
| 53 |
+
break
|
| 54 |
+
|
| 55 |
+
start := res.fst
|
| 56 |
+
else
|
| 57 |
+
break
|
| 58 |
+
|
| 59 |
+
return { transitions, initialLocalTimeType }
|
| 60 |
+
|
| 61 |
+
where
|
| 62 |
+
toLocalTime (res : TimeZone) : TimeZone.LocalTimeType :=
|
| 63 |
+
{
|
| 64 |
+
gmtOffset := res.offset,
|
| 65 |
+
abbreviation := res.abbreviation,
|
| 66 |
+
identifier := res.name,
|
| 67 |
+
isDst := res.isDST,
|
| 68 |
+
wall := .wall,
|
| 69 |
+
utLocal := .local
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
end Windows
|
| 73 |
+
|
| 74 |
+
/--
|
| 75 |
+
Represents a Time Zone Database that we get from ICU available on Windows SDK.
|
| 76 |
+
-/
|
| 77 |
+
structure WindowsDb where
|
| 78 |
+
|
| 79 |
+
namespace WindowsDb
|
| 80 |
+
open TimeZone
|
| 81 |
+
|
| 82 |
+
/--
|
| 83 |
+
Returns a default `WindowsDb` instance.
|
| 84 |
+
-/
|
| 85 |
+
@[inline]
|
| 86 |
+
def default : WindowsDb := {}
|
| 87 |
+
|
| 88 |
+
instance : Std.Time.Database WindowsDb where
|
| 89 |
+
getZoneRules _ id := Windows.getZoneRules id
|
| 90 |
+
getLocalZoneRules _ := Windows.getZoneRules =<< Windows.getLocalTimeZoneIdentifierAt (-2147483648)
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/DateTime.lean
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.DateTime
|
| 8 |
+
import Std.Time.Zoned.TimeZone
|
| 9 |
+
|
| 10 |
+
namespace Std
|
| 11 |
+
namespace Time
|
| 12 |
+
open Internal
|
| 13 |
+
|
| 14 |
+
set_option linter.all true
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
Represents a specific point in time associated with a `TimeZone`.
|
| 18 |
+
-/
|
| 19 |
+
structure DateTime (tz : TimeZone) where
|
| 20 |
+
private mk ::
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
`Timestamp` represents the exact moment in time. It's a UTC related `Timestamp`.
|
| 24 |
+
-/
|
| 25 |
+
timestamp : Timestamp
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
`Date` is a `Thunk` containing the `PlainDateTime` that represents the local date and time, it's
|
| 29 |
+
used for accessing data like `day` and `month` without having to recompute the data everytime.
|
| 30 |
+
-/
|
| 31 |
+
date : Thunk PlainDateTime
|
| 32 |
+
|
| 33 |
+
instance : BEq (DateTime tz) where
|
| 34 |
+
beq x y := x.timestamp == y.timestamp
|
| 35 |
+
|
| 36 |
+
instance : Ord (DateTime tz) where
|
| 37 |
+
compare := compareOn (·.timestamp)
|
| 38 |
+
|
| 39 |
+
instance : TransOrd (DateTime tz) := inferInstanceAs <| TransCmp (compareOn _)
|
| 40 |
+
|
| 41 |
+
instance : LawfulBEqOrd (DateTime tz) where
|
| 42 |
+
compare_eq_iff_beq := LawfulBEqOrd.compare_eq_iff_beq (α := Timestamp)
|
| 43 |
+
|
| 44 |
+
namespace DateTime
|
| 45 |
+
|
| 46 |
+
/--
|
| 47 |
+
Creates a new `DateTime` out of a `Timestamp` that is in a `TimeZone`.
|
| 48 |
+
-/
|
| 49 |
+
@[inline]
|
| 50 |
+
def ofTimestamp (tm : Timestamp) (tz : TimeZone) : DateTime tz :=
|
| 51 |
+
DateTime.mk tm (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC |>.addSeconds tz.toSeconds)
|
| 52 |
+
|
| 53 |
+
instance : Inhabited (DateTime tz) where
|
| 54 |
+
default := ofTimestamp Inhabited.default tz
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Converts a `DateTime` to the number of days since the UNIX epoch.
|
| 58 |
+
-/
|
| 59 |
+
def toDaysSinceUNIXEpoch (date : DateTime tz) : Day.Offset :=
|
| 60 |
+
date.date.get.toDaysSinceUNIXEpoch
|
| 61 |
+
|
| 62 |
+
/--
|
| 63 |
+
Creates a `Timestamp` out of a `DateTime`.
|
| 64 |
+
-/
|
| 65 |
+
@[inline]
|
| 66 |
+
def toTimestamp (date : DateTime tz) : Timestamp :=
|
| 67 |
+
date.timestamp
|
| 68 |
+
|
| 69 |
+
/--
|
| 70 |
+
Changes the `TimeZone` to a new one.
|
| 71 |
+
-/
|
| 72 |
+
@[inline]
|
| 73 |
+
def convertTimeZone (date : DateTime tz) (tz₁ : TimeZone) : DateTime tz₁ :=
|
| 74 |
+
ofTimestamp date.timestamp tz₁
|
| 75 |
+
|
| 76 |
+
/--
|
| 77 |
+
Creates a new `DateTime` out of a `PlainDateTime`. It assumes that the `PlainDateTime` is relative
|
| 78 |
+
to UTC.
|
| 79 |
+
-/
|
| 80 |
+
@[inline]
|
| 81 |
+
def ofPlainDateTimeAssumingUTC (date : PlainDateTime) (tz : TimeZone) : DateTime tz :=
|
| 82 |
+
let tm := Timestamp.ofPlainDateTimeAssumingUTC date
|
| 83 |
+
DateTime.mk tm (Thunk.mk fun _ => date.addSeconds tz.toSeconds)
|
| 84 |
+
|
| 85 |
+
/--
|
| 86 |
+
Creates a new `DateTime` from a `PlainDateTime`, assuming that the `PlainDateTime`
|
| 87 |
+
is relative to the given `TimeZone`.
|
| 88 |
+
-/
|
| 89 |
+
@[inline]
|
| 90 |
+
def ofPlainDateTime (date : PlainDateTime) (tz : TimeZone) : DateTime tz :=
|
| 91 |
+
let tm := date.subSeconds tz.toSeconds
|
| 92 |
+
DateTime.mk (Timestamp.ofPlainDateTimeAssumingUTC tm) (Thunk.mk fun _ => date)
|
| 93 |
+
|
| 94 |
+
/--
|
| 95 |
+
Add `Hour.Offset` to a `DateTime`.
|
| 96 |
+
-/
|
| 97 |
+
@[inline]
|
| 98 |
+
def addHours (dt : DateTime tz) (hours : Hour.Offset) : DateTime tz :=
|
| 99 |
+
ofPlainDateTime (dt.date.get.addHours hours) tz
|
| 100 |
+
|
| 101 |
+
/--
|
| 102 |
+
Subtract `Hour.Offset` from a `DateTime`.
|
| 103 |
+
-/
|
| 104 |
+
@[inline]
|
| 105 |
+
def subHours (dt : DateTime tz) (hours : Hour.Offset) : DateTime tz :=
|
| 106 |
+
ofPlainDateTime (dt.date.get.subHours hours) tz
|
| 107 |
+
|
| 108 |
+
/--
|
| 109 |
+
Add `Minute.Offset` to a `DateTime`.
|
| 110 |
+
-/
|
| 111 |
+
@[inline]
|
| 112 |
+
def addMinutes (dt : DateTime tz) (minutes : Minute.Offset) : DateTime tz :=
|
| 113 |
+
ofPlainDateTime (dt.date.get.addMinutes minutes) tz
|
| 114 |
+
|
| 115 |
+
/--
|
| 116 |
+
Subtract `Minute.Offset` from a `DateTime`.
|
| 117 |
+
-/
|
| 118 |
+
@[inline]
|
| 119 |
+
def subMinutes (dt : DateTime tz) (minutes : Minute.Offset) : DateTime tz :=
|
| 120 |
+
ofPlainDateTime (dt.date.get.subMinutes minutes) tz
|
| 121 |
+
|
| 122 |
+
/--
|
| 123 |
+
Add `Second.Offset` to a `DateTime`.
|
| 124 |
+
-/
|
| 125 |
+
@[inline]
|
| 126 |
+
def addSeconds (dt : DateTime tz) (seconds : Second.Offset) : DateTime tz :=
|
| 127 |
+
ofPlainDateTime (dt.date.get.addSeconds seconds) tz
|
| 128 |
+
|
| 129 |
+
/--
|
| 130 |
+
Subtract `Second.Offset` from a `DateTime`.
|
| 131 |
+
-/
|
| 132 |
+
@[inline]
|
| 133 |
+
def subSeconds (dt : DateTime tz) (seconds : Second.Offset) : DateTime tz :=
|
| 134 |
+
ofPlainDateTime (dt.date.get.subSeconds seconds) tz
|
| 135 |
+
|
| 136 |
+
/--
|
| 137 |
+
Add `Millisecond.Offset` to a `DateTime`.
|
| 138 |
+
-/
|
| 139 |
+
@[inline]
|
| 140 |
+
def addMilliseconds (dt : DateTime tz) (milliseconds : Millisecond.Offset) : DateTime tz :=
|
| 141 |
+
ofPlainDateTime (dt.date.get.addMilliseconds milliseconds) tz
|
| 142 |
+
|
| 143 |
+
/--
|
| 144 |
+
Subtract `Millisecond.Offset` from a `DateTime`.
|
| 145 |
+
-/
|
| 146 |
+
@[inline]
|
| 147 |
+
def subMilliseconds (dt : DateTime tz) (milliseconds : Millisecond.Offset) : DateTime tz :=
|
| 148 |
+
ofPlainDateTime (dt.date.get.subMilliseconds milliseconds) tz
|
| 149 |
+
|
| 150 |
+
/--
|
| 151 |
+
Add `Nanosecond.Offset` to a `DateTime`.
|
| 152 |
+
-/
|
| 153 |
+
@[inline]
|
| 154 |
+
def addNanoseconds (dt : DateTime tz) (nanoseconds : Nanosecond.Offset) : DateTime tz :=
|
| 155 |
+
ofPlainDateTime (dt.date.get.addNanoseconds nanoseconds) tz
|
| 156 |
+
|
| 157 |
+
/--
|
| 158 |
+
Subtract `Nanosecond.Offset` from a `DateTime`.
|
| 159 |
+
-/
|
| 160 |
+
@[inline]
|
| 161 |
+
def subNanoseconds (dt : DateTime tz) (nanoseconds : Nanosecond.Offset) : DateTime tz :=
|
| 162 |
+
ofPlainDateTime (dt.date.get.subNanoseconds nanoseconds) tz
|
| 163 |
+
|
| 164 |
+
/--
|
| 165 |
+
Add `Day.Offset` to a `DateTime`.
|
| 166 |
+
-/
|
| 167 |
+
@[inline]
|
| 168 |
+
def addDays (dt : DateTime tz) (days : Day.Offset) : DateTime tz :=
|
| 169 |
+
ofPlainDateTime (dt.date.get.addDays days) tz
|
| 170 |
+
|
| 171 |
+
/--
|
| 172 |
+
Subtracts `Day.Offset` to a `DateTime`.
|
| 173 |
+
-/
|
| 174 |
+
@[inline]
|
| 175 |
+
def subDays (dt : DateTime tz) (days : Day.Offset) : DateTime tz :=
|
| 176 |
+
ofPlainDateTime (dt.date.get.subDays days) tz
|
| 177 |
+
|
| 178 |
+
/--
|
| 179 |
+
Add `Week.Offset` to a `DateTime`.
|
| 180 |
+
-/
|
| 181 |
+
@[inline]
|
| 182 |
+
def addWeeks (dt : DateTime tz) (weeks : Week.Offset) : DateTime tz :=
|
| 183 |
+
ofPlainDateTime (dt.date.get.addWeeks weeks) tz
|
| 184 |
+
|
| 185 |
+
/--
|
| 186 |
+
Subtracts `Week.Offset` to a `DateTime`.
|
| 187 |
+
-/
|
| 188 |
+
@[inline]
|
| 189 |
+
def subWeeks (dt : DateTime tz) (weeks : Week.Offset) : DateTime tz :=
|
| 190 |
+
ofPlainDateTime (dt.date.get.subWeeks weeks) tz
|
| 191 |
+
|
| 192 |
+
/--
|
| 193 |
+
Add `Month.Offset` to a `DateTime`, it clips the day to the last valid day of that month.
|
| 194 |
+
-/
|
| 195 |
+
def addMonthsClip (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
| 196 |
+
ofPlainDateTime (dt.date.get.addMonthsClip months) tz
|
| 197 |
+
|
| 198 |
+
/--
|
| 199 |
+
Subtracts `Month.Offset` from a `DateTime`, it clips the day to the last valid day of that month.
|
| 200 |
+
-/
|
| 201 |
+
@[inline]
|
| 202 |
+
def subMonthsClip (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
| 203 |
+
ofPlainDateTime (dt.date.get.subMonthsClip months) tz
|
| 204 |
+
|
| 205 |
+
/--
|
| 206 |
+
Add `Month.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
| 207 |
+
month.
|
| 208 |
+
-/
|
| 209 |
+
def addMonthsRollOver (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
| 210 |
+
ofPlainDateTime (dt.date.get.addMonthsRollOver months) tz
|
| 211 |
+
|
| 212 |
+
/--
|
| 213 |
+
Subtract `Month.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
| 214 |
+
month.
|
| 215 |
+
-/
|
| 216 |
+
@[inline]
|
| 217 |
+
def subMonthsRollOver (dt : DateTime tz) (months : Month.Offset) : DateTime tz :=
|
| 218 |
+
ofPlainDateTime (dt.date.get.subMonthsRollOver months) tz
|
| 219 |
+
|
| 220 |
+
/--
|
| 221 |
+
Add `Year.Offset` to a `DateTime`, this function rolls over any excess days into the following
|
| 222 |
+
month.
|
| 223 |
+
-/
|
| 224 |
+
@[inline]
|
| 225 |
+
def addYearsRollOver (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
| 226 |
+
ofPlainDateTime (dt.date.get.addYearsRollOver years) tz
|
| 227 |
+
|
| 228 |
+
/--
|
| 229 |
+
Add `Year.Offset` to a `DateTime`, it clips the day to the last valid day of that month.
|
| 230 |
+
-/
|
| 231 |
+
@[inline]
|
| 232 |
+
def addYearsClip (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
| 233 |
+
ofPlainDateTime (dt.date.get.addYearsClip years) tz
|
| 234 |
+
|
| 235 |
+
/--
|
| 236 |
+
Subtract `Year.Offset` from a `DateTime`, this function rolls over any excess days into the following
|
| 237 |
+
month.
|
| 238 |
+
-/
|
| 239 |
+
@[inline]
|
| 240 |
+
def subYearsRollOver (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
| 241 |
+
ofPlainDateTime (dt.date.get.subYearsRollOver years) tz
|
| 242 |
+
|
| 243 |
+
/--
|
| 244 |
+
Subtract `Year.Offset` from to a `DateTime`, it clips the day to the last valid day of that month.
|
| 245 |
+
-/
|
| 246 |
+
@[inline]
|
| 247 |
+
def subYearsClip (dt : DateTime tz) (years : Year.Offset) : DateTime tz :=
|
| 248 |
+
ofPlainDateTime (dt.date.get.subYearsClip years) tz
|
| 249 |
+
|
| 250 |
+
/--
|
| 251 |
+
Creates a new `DateTime tz` by adjusting the day of the month to the given `days` value, with any
|
| 252 |
+
out-of-range days clipped to the nearest valid date.
|
| 253 |
+
-/
|
| 254 |
+
@[inline]
|
| 255 |
+
def withDaysClip (dt : DateTime tz) (days : Day.Ordinal) : DateTime tz :=
|
| 256 |
+
ofPlainDateTime (dt.date.get.withDaysClip days) tz
|
| 257 |
+
|
| 258 |
+
/--
|
| 259 |
+
Creates a new `DateTime tz` by adjusting the day of the month to the given `days` value, with any
|
| 260 |
+
out-of-range days rolled over to the next month or year as needed.
|
| 261 |
+
-/
|
| 262 |
+
@[inline]
|
| 263 |
+
def withDaysRollOver (dt : DateTime tz) (days : Day.Ordinal) : DateTime tz :=
|
| 264 |
+
ofPlainDateTime (dt.date.get.withDaysRollOver days) tz
|
| 265 |
+
|
| 266 |
+
/--
|
| 267 |
+
Creates a new `DateTime tz` by adjusting the month to the given `month` value.
|
| 268 |
+
The day remains unchanged, and any invalid days for the new month will be handled according to the `clip` behavior.
|
| 269 |
+
-/
|
| 270 |
+
@[inline]
|
| 271 |
+
def withMonthClip (dt : DateTime tz) (month : Month.Ordinal) : DateTime tz :=
|
| 272 |
+
ofPlainDateTime (dt.date.get.withMonthClip month) tz
|
| 273 |
+
|
| 274 |
+
/--
|
| 275 |
+
Creates a new `DateTime tz` by adjusting the month to the given `month` value.
|
| 276 |
+
The day is rolled over to the next valid month if necessary.
|
| 277 |
+
-/
|
| 278 |
+
@[inline]
|
| 279 |
+
def withMonthRollOver (dt : DateTime tz) (month : Month.Ordinal) : DateTime tz :=
|
| 280 |
+
ofPlainDateTime (dt.date.get.withMonthRollOver month) tz
|
| 281 |
+
|
| 282 |
+
/--
|
| 283 |
+
Creates a new `DateTime tz` by adjusting the year to the given `year` value. The month and day remain unchanged,
|
| 284 |
+
and any invalid days for the new year will be handled according to the `clip` behavior.
|
| 285 |
+
-/
|
| 286 |
+
@[inline]
|
| 287 |
+
def withYearClip (dt : DateTime tz) (year : Year.Offset) : DateTime tz :=
|
| 288 |
+
ofPlainDateTime (dt.date.get.withYearClip year) tz
|
| 289 |
+
|
| 290 |
+
/--
|
| 291 |
+
Creates a new `DateTime tz` by adjusting the year to the given `year` value. The month and day are rolled
|
| 292 |
+
over to the next valid month and day if necessary.
|
| 293 |
+
-/
|
| 294 |
+
@[inline]
|
| 295 |
+
def withYearRollOver (dt : DateTime tz) (year : Year.Offset) : DateTime tz :=
|
| 296 |
+
ofPlainDateTime (dt.date.get.withYearRollOver year) tz
|
| 297 |
+
|
| 298 |
+
/--
|
| 299 |
+
Creates a new `DateTime tz` by adjusting the `hour` component.
|
| 300 |
+
-/
|
| 301 |
+
@[inline]
|
| 302 |
+
def withHours (dt : DateTime tz) (hour : Hour.Ordinal) : DateTime tz :=
|
| 303 |
+
ofPlainDateTime (dt.date.get.withHours hour) tz
|
| 304 |
+
|
| 305 |
+
/--
|
| 306 |
+
Creates a new `DateTime tz` by adjusting the `minute` component.
|
| 307 |
+
-/
|
| 308 |
+
@[inline]
|
| 309 |
+
def withMinutes (dt : DateTime tz) (minute : Minute.Ordinal) : DateTime tz :=
|
| 310 |
+
ofPlainDateTime (dt.date.get.withMinutes minute) tz
|
| 311 |
+
|
| 312 |
+
/--
|
| 313 |
+
Creates a new `DateTime tz` by adjusting the `second` component.
|
| 314 |
+
-/
|
| 315 |
+
@[inline]
|
| 316 |
+
def withSeconds (dt : DateTime tz) (second : Second.Ordinal true) : DateTime tz :=
|
| 317 |
+
ofPlainDateTime (dt.date.get.withSeconds second) tz
|
| 318 |
+
|
| 319 |
+
/--
|
| 320 |
+
Creates a new `DateTime tz` by adjusting the `nano` component.
|
| 321 |
+
-/
|
| 322 |
+
@[inline]
|
| 323 |
+
def withNanoseconds (dt : DateTime tz) (nano : Nanosecond.Ordinal) : DateTime tz :=
|
| 324 |
+
ofPlainDateTime (dt.date.get.withNanoseconds nano) tz
|
| 325 |
+
|
| 326 |
+
/--
|
| 327 |
+
Creates a new `DateTime tz` by adjusting the `millisecond` component.
|
| 328 |
+
-/
|
| 329 |
+
@[inline]
|
| 330 |
+
def withMilliseconds (dt : DateTime tz) (milli : Millisecond.Ordinal) : DateTime tz :=
|
| 331 |
+
ofPlainDateTime (dt.date.get.withMilliseconds milli) tz
|
| 332 |
+
|
| 333 |
+
/--
|
| 334 |
+
Converts a `Timestamp` to a `PlainDateTime`
|
| 335 |
+
-/
|
| 336 |
+
@[inline]
|
| 337 |
+
def toPlainDateTime (dt : DateTime tz) : PlainDateTime :=
|
| 338 |
+
dt.date.get
|
| 339 |
+
|
| 340 |
+
/--
|
| 341 |
+
Getter for the `Year` inside of a `DateTime`
|
| 342 |
+
-/
|
| 343 |
+
@[inline]
|
| 344 |
+
def year (dt : DateTime tz) : Year.Offset :=
|
| 345 |
+
dt.date.get.year
|
| 346 |
+
|
| 347 |
+
/--
|
| 348 |
+
Getter for the `Month` inside of a `DateTime`
|
| 349 |
+
-/
|
| 350 |
+
@[inline]
|
| 351 |
+
def month (dt : DateTime tz) : Month.Ordinal :=
|
| 352 |
+
dt.date.get.month
|
| 353 |
+
|
| 354 |
+
/--
|
| 355 |
+
Getter for the `Day` inside of a `DateTime`
|
| 356 |
+
-/
|
| 357 |
+
@[inline]
|
| 358 |
+
def day (dt : DateTime tz) : Day.Ordinal :=
|
| 359 |
+
dt.date.get.day
|
| 360 |
+
|
| 361 |
+
/--
|
| 362 |
+
Getter for the `Hour` inside of a `DateTime`
|
| 363 |
+
-/
|
| 364 |
+
@[inline]
|
| 365 |
+
def hour (dt : DateTime tz) : Hour.Ordinal :=
|
| 366 |
+
dt.date.get.hour
|
| 367 |
+
|
| 368 |
+
/--
|
| 369 |
+
Getter for the `Minute` inside of a `DateTime`
|
| 370 |
+
-/
|
| 371 |
+
@[inline]
|
| 372 |
+
def minute (dt : DateTime tz) : Minute.Ordinal :=
|
| 373 |
+
dt.date.get.minute
|
| 374 |
+
|
| 375 |
+
/--
|
| 376 |
+
Getter for the `Second` inside of a `DateTime`
|
| 377 |
+
-/
|
| 378 |
+
@[inline]
|
| 379 |
+
def second (dt : DateTime tz) : Second.Ordinal true :=
|
| 380 |
+
dt.date.get.second
|
| 381 |
+
|
| 382 |
+
/--
|
| 383 |
+
Getter for the `Milliseconds` inside of a `DateTime`
|
| 384 |
+
-/
|
| 385 |
+
@[inline]
|
| 386 |
+
def millisecond (dt : DateTime tz) : Millisecond.Ordinal :=
|
| 387 |
+
dt.date.get.time.nanosecond.emod 1000 (by decide)
|
| 388 |
+
|
| 389 |
+
/--
|
| 390 |
+
Getter for the `Nanosecond` inside of a `DateTime`
|
| 391 |
+
-/
|
| 392 |
+
@[inline]
|
| 393 |
+
def nanosecond (dt : DateTime tz) : Nanosecond.Ordinal :=
|
| 394 |
+
dt.date.get.time.nanosecond
|
| 395 |
+
|
| 396 |
+
/--
|
| 397 |
+
Gets the `Weekday` of a DateTime.
|
| 398 |
+
-/
|
| 399 |
+
@[inline]
|
| 400 |
+
def weekday (dt : DateTime tz) : Weekday :=
|
| 401 |
+
dt.date.get.date.weekday
|
| 402 |
+
|
| 403 |
+
/--
|
| 404 |
+
Determines the era of the given `DateTime` based on its year.
|
| 405 |
+
-/
|
| 406 |
+
def era (date : DateTime tz) : Year.Era :=
|
| 407 |
+
date.year.era
|
| 408 |
+
|
| 409 |
+
/--
|
| 410 |
+
Sets the `DateTime` to the specified `desiredWeekday`.
|
| 411 |
+
-/
|
| 412 |
+
def withWeekday (dt : DateTime tz) (desiredWeekday : Weekday) : DateTime tz :=
|
| 413 |
+
ofPlainDateTime (dt.date.get.withWeekday desiredWeekday) tz
|
| 414 |
+
|
| 415 |
+
/--
|
| 416 |
+
Checks if the `DateTime` is in a leap year.
|
| 417 |
+
-/
|
| 418 |
+
def inLeapYear (date : DateTime tz) : Bool :=
|
| 419 |
+
date.year.isLeap
|
| 420 |
+
|
| 421 |
+
/--
|
| 422 |
+
Determines the ordinal day of the year for the given `DateTime`.
|
| 423 |
+
-/
|
| 424 |
+
def dayOfYear (date : DateTime tz) : Day.Ordinal.OfYear date.year.isLeap :=
|
| 425 |
+
ValidDate.dayOfYear ⟨⟨date.month, date.day⟩, date.date.get.date.valid⟩
|
| 426 |
+
|
| 427 |
+
/--
|
| 428 |
+
Determines the week of the year for the given `DateTime`.
|
| 429 |
+
-/
|
| 430 |
+
@[inline]
|
| 431 |
+
def weekOfYear (date : DateTime tz) : Week.Ordinal :=
|
| 432 |
+
date.date.get.weekOfYear
|
| 433 |
+
|
| 434 |
+
/--
|
| 435 |
+
Returns the unaligned week of the month for a `DateTime` (day divided by 7, plus 1).
|
| 436 |
+
-/
|
| 437 |
+
def weekOfMonth (date : DateTime tz) : Bounded.LE 1 5 :=
|
| 438 |
+
date.date.get.weekOfMonth
|
| 439 |
+
|
| 440 |
+
/--
|
| 441 |
+
Determines the week of the month for the given `DateTime`. The week of the month is calculated based
|
| 442 |
+
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
| 443 |
+
based on the Gregorian Calendar.
|
| 444 |
+
-/
|
| 445 |
+
@[inline]
|
| 446 |
+
def alignedWeekOfMonth (date : DateTime tz) : Week.Ordinal.OfMonth :=
|
| 447 |
+
date.date.get.alignedWeekOfMonth
|
| 448 |
+
|
| 449 |
+
/--
|
| 450 |
+
Determines the quarter of the year for the given `DateTime`.
|
| 451 |
+
-/
|
| 452 |
+
@[inline]
|
| 453 |
+
def quarter (date : DateTime tz) : Bounded.LE 1 4 :=
|
| 454 |
+
date.date.get.quarter
|
| 455 |
+
|
| 456 |
+
/--
|
| 457 |
+
Getter for the `PlainTime` inside of a `DateTime`
|
| 458 |
+
-/
|
| 459 |
+
@[inline]
|
| 460 |
+
def time (zdt : DateTime tz) : PlainTime :=
|
| 461 |
+
zdt.date.get.time
|
| 462 |
+
|
| 463 |
+
/--
|
| 464 |
+
Converts a `DateTime` to the number of days since the UNIX epoch.
|
| 465 |
+
-/
|
| 466 |
+
@[inline]
|
| 467 |
+
def ofDaysSinceUNIXEpoch (days : Day.Offset) (time : PlainTime) (tz : TimeZone) : DateTime tz :=
|
| 468 |
+
DateTime.ofPlainDateTime (PlainDateTime.ofDaysSinceUNIXEpoch days time) tz
|
| 469 |
+
|
| 470 |
+
instance : HAdd (DateTime tz) (Day.Offset) (DateTime tz) where
|
| 471 |
+
hAdd := addDays
|
| 472 |
+
|
| 473 |
+
instance : HSub (DateTime tz) (Day.Offset) (DateTime tz) where
|
| 474 |
+
hSub := subDays
|
| 475 |
+
|
| 476 |
+
instance : HAdd (DateTime tz) (Week.Offset) (DateTime tz) where
|
| 477 |
+
hAdd := addWeeks
|
| 478 |
+
|
| 479 |
+
instance : HSub (DateTime tz) (Week.Offset) (DateTime tz) where
|
| 480 |
+
hSub := subWeeks
|
| 481 |
+
|
| 482 |
+
instance : HAdd (DateTime tz) (Hour.Offset) (DateTime tz) where
|
| 483 |
+
hAdd := addHours
|
| 484 |
+
|
| 485 |
+
instance : HSub (DateTime tz) (Hour.Offset) (DateTime tz) where
|
| 486 |
+
hSub := subHours
|
| 487 |
+
|
| 488 |
+
instance : HAdd (DateTime tz) (Minute.Offset) (DateTime tz) where
|
| 489 |
+
hAdd := addMinutes
|
| 490 |
+
|
| 491 |
+
instance : HSub (DateTime tz) (Minute.Offset) (DateTime tz) where
|
| 492 |
+
hSub := subMinutes
|
| 493 |
+
|
| 494 |
+
instance : HAdd (DateTime tz) (Second.Offset) (DateTime tz) where
|
| 495 |
+
hAdd := addSeconds
|
| 496 |
+
|
| 497 |
+
instance : HSub (DateTime tz) (Second.Offset) (DateTime tz) where
|
| 498 |
+
hSub := subSeconds
|
| 499 |
+
|
| 500 |
+
instance : HAdd (DateTime tz) (Millisecond.Offset) (DateTime tz) where
|
| 501 |
+
hAdd := addMilliseconds
|
| 502 |
+
|
| 503 |
+
instance : HSub (DateTime tz) (Millisecond.Offset) (DateTime tz) where
|
| 504 |
+
hSub := subMilliseconds
|
| 505 |
+
|
| 506 |
+
instance : HAdd (DateTime tz) (Nanosecond.Offset) (DateTime tz) where
|
| 507 |
+
hAdd := addNanoseconds
|
| 508 |
+
|
| 509 |
+
instance : HSub (DateTime tz) (Nanosecond.Offset) (DateTime tz) where
|
| 510 |
+
hSub := subNanoseconds
|
| 511 |
+
|
| 512 |
+
instance : HSub (DateTime tz) (DateTime tz₁) Duration where
|
| 513 |
+
hSub x y := x.toTimestamp - y.toTimestamp
|
| 514 |
+
|
| 515 |
+
instance : HAdd (DateTime tz) Duration (DateTime tz) where
|
| 516 |
+
hAdd x y := x.addNanoseconds y.toNanoseconds
|
| 517 |
+
|
| 518 |
+
instance : HSub (DateTime tz) Duration (DateTime tz) where
|
| 519 |
+
hSub x y := x.subNanoseconds y.toNanoseconds
|
| 520 |
+
|
| 521 |
+
end DateTime
|
| 522 |
+
end Time
|
| 523 |
+
end Std
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/Offset.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.Time
|
| 8 |
+
|
| 9 |
+
namespace Std
|
| 10 |
+
namespace Time
|
| 11 |
+
namespace TimeZone
|
| 12 |
+
open Internal
|
| 13 |
+
|
| 14 |
+
set_option linter.all true
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
Represents a timezone offset with an hour and second component.
|
| 18 |
+
-/
|
| 19 |
+
@[ext]
|
| 20 |
+
structure Offset where
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
Creates an `Offset` from a given number of seconds.
|
| 24 |
+
-/
|
| 25 |
+
ofSeconds ::
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
The same timezone offset in seconds.
|
| 29 |
+
-/
|
| 30 |
+
second : Second.Offset
|
| 31 |
+
deriving Repr, DecidableEq
|
| 32 |
+
|
| 33 |
+
instance : Inhabited Offset where
|
| 34 |
+
default := ⟨0⟩
|
| 35 |
+
|
| 36 |
+
instance : Ord Offset where
|
| 37 |
+
compare := compareOn (·.second)
|
| 38 |
+
|
| 39 |
+
theorem Offset.compare_def :
|
| 40 |
+
compare (α := Offset) = compareOn (·.second) := rfl
|
| 41 |
+
|
| 42 |
+
instance : TransOrd Offset := inferInstanceAs <| TransCmp (compareOn _)
|
| 43 |
+
|
| 44 |
+
instance : LawfulEqOrd Offset where
|
| 45 |
+
eq_of_compare {a b} h := by
|
| 46 |
+
simp only [Offset.compare_def] at h
|
| 47 |
+
apply Offset.ext
|
| 48 |
+
exact LawfulEqOrd.eq_of_compare h
|
| 49 |
+
|
| 50 |
+
namespace Offset
|
| 51 |
+
|
| 52 |
+
/--
|
| 53 |
+
Converts an `Offset` to a string in ISO 8601 format. The `colon` parameter determines if the hour
|
| 54 |
+
and minute components are separated by a colon (e.g., "+01:00" or "+0100").
|
| 55 |
+
-/
|
| 56 |
+
def toIsoString (offset : Offset) (colon : Bool) : String :=
|
| 57 |
+
let (sign, time) := if offset.second.val ≥ 0 then ("+", offset.second) else ("-", -offset.second)
|
| 58 |
+
let hour : Hour.Offset := time.ediv 3600
|
| 59 |
+
let minute := Int.ediv (Int.tmod time.val 3600) 60
|
| 60 |
+
let hourStr := if hour.val < 10 then s!"0{hour.val}" else toString hour.val
|
| 61 |
+
let minuteStr := if minute < 10 then s!"0{minute}" else toString minute
|
| 62 |
+
if colon then s!"{sign}{hourStr}:{minuteStr}"
|
| 63 |
+
else s!"{sign}{hourStr}{minuteStr}"
|
| 64 |
+
|
| 65 |
+
/--
|
| 66 |
+
A zero `Offset` representing UTC (no offset).
|
| 67 |
+
-/
|
| 68 |
+
def zero : Offset :=
|
| 69 |
+
{ second := 0 }
|
| 70 |
+
|
| 71 |
+
/--
|
| 72 |
+
Creates an `Offset` from a given number of hour.
|
| 73 |
+
-/
|
| 74 |
+
def ofHours (n : Hour.Offset) : Offset :=
|
| 75 |
+
ofSeconds n.toSeconds
|
| 76 |
+
|
| 77 |
+
/--
|
| 78 |
+
Creates an `Offset` from a given number of hours and minutes.
|
| 79 |
+
-/
|
| 80 |
+
def ofHoursAndMinutes (n : Hour.Offset) (m : Minute.Offset) : Offset :=
|
| 81 |
+
ofSeconds (n.toSeconds + m.toSeconds)
|
| 82 |
+
|
| 83 |
+
end Offset
|
| 84 |
+
end TimeZone
|
| 85 |
+
end Time
|
| 86 |
+
end Std
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/TimeZone.lean
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.Zoned.Offset
|
| 8 |
+
|
| 9 |
+
namespace Std
|
| 10 |
+
namespace Time
|
| 11 |
+
|
| 12 |
+
set_option linter.all true
|
| 13 |
+
|
| 14 |
+
/--
|
| 15 |
+
A TimeZone structure that stores the timezone offset, the name, abbreviation and if it's in daylight
|
| 16 |
+
saving time.
|
| 17 |
+
-/
|
| 18 |
+
structure TimeZone where
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
The `Offset` of the date time.
|
| 22 |
+
-/
|
| 23 |
+
offset : TimeZone.Offset
|
| 24 |
+
|
| 25 |
+
/--
|
| 26 |
+
The name of the time zone.
|
| 27 |
+
-/
|
| 28 |
+
name : String
|
| 29 |
+
|
| 30 |
+
/--
|
| 31 |
+
The abbreviation of the time zone.
|
| 32 |
+
-/
|
| 33 |
+
abbreviation : String
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
Day light saving flag.
|
| 37 |
+
-/
|
| 38 |
+
isDST : Bool
|
| 39 |
+
deriving Inhabited, Repr, DecidableEq
|
| 40 |
+
|
| 41 |
+
namespace TimeZone
|
| 42 |
+
|
| 43 |
+
/--
|
| 44 |
+
A zeroed `Timezone` representing UTC (no offset).
|
| 45 |
+
-/
|
| 46 |
+
def UTC : TimeZone :=
|
| 47 |
+
TimeZone.mk (Offset.zero) "UTC" "UTC" false
|
| 48 |
+
|
| 49 |
+
/--
|
| 50 |
+
A zeroed `Timezone` representing GMT (no offset).
|
| 51 |
+
-/
|
| 52 |
+
def GMT : TimeZone :=
|
| 53 |
+
TimeZone.mk (Offset.zero) "Greenwich Mean Time" "GMT" false
|
| 54 |
+
|
| 55 |
+
/--
|
| 56 |
+
Creates a `Timestamp` from a given number of hour.
|
| 57 |
+
-/
|
| 58 |
+
def ofHours (name : String) (abbreviation : String) (n : Hour.Offset) (isDST : Bool := false) : TimeZone :=
|
| 59 |
+
TimeZone.mk (Offset.ofHours n) name abbreviation isDST
|
| 60 |
+
|
| 61 |
+
/--
|
| 62 |
+
Creates a `Timestamp` from a given number of second.
|
| 63 |
+
-/
|
| 64 |
+
def ofSeconds (name : String) (abbreviation : String) (n : Second.Offset) (isDST : Bool := false) : TimeZone :=
|
| 65 |
+
TimeZone.mk (Offset.ofSeconds n) name abbreviation isDST
|
| 66 |
+
|
| 67 |
+
/--
|
| 68 |
+
Gets the number of seconds in a timezone offset.
|
| 69 |
+
-/
|
| 70 |
+
def toSeconds (tz : TimeZone) : Second.Offset :=
|
| 71 |
+
tz.offset.second
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/ZoneRules.lean
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.DateTime
|
| 8 |
+
import Std.Time.Zoned.TimeZone
|
| 9 |
+
|
| 10 |
+
namespace Std
|
| 11 |
+
namespace Time
|
| 12 |
+
namespace TimeZone
|
| 13 |
+
open Internal
|
| 14 |
+
|
| 15 |
+
set_option linter.all true
|
| 16 |
+
|
| 17 |
+
/--
|
| 18 |
+
Represents the type of local time in relation to UTC.
|
| 19 |
+
-/
|
| 20 |
+
inductive UTLocal
|
| 21 |
+
/--
|
| 22 |
+
Universal Time (UT), often referred to as UTC.
|
| 23 |
+
-/
|
| 24 |
+
| ut
|
| 25 |
+
|
| 26 |
+
/--
|
| 27 |
+
Local time that is not necessarily UTC.
|
| 28 |
+
-/
|
| 29 |
+
| local
|
| 30 |
+
deriving Repr, Inhabited
|
| 31 |
+
|
| 32 |
+
/--
|
| 33 |
+
Represents types of wall clocks or standard times.
|
| 34 |
+
-/
|
| 35 |
+
inductive StdWall
|
| 36 |
+
/--
|
| 37 |
+
Time based on a wall clock, which can include daylight saving adjustments.
|
| 38 |
+
-/
|
| 39 |
+
| wall
|
| 40 |
+
|
| 41 |
+
/--
|
| 42 |
+
Standard time without adjustments for daylight saving.
|
| 43 |
+
-/
|
| 44 |
+
| standard
|
| 45 |
+
deriving Repr, Inhabited
|
| 46 |
+
|
| 47 |
+
/--
|
| 48 |
+
Represents a type of local time, including offset and daylight saving information.
|
| 49 |
+
-/
|
| 50 |
+
structure LocalTimeType where
|
| 51 |
+
|
| 52 |
+
/--
|
| 53 |
+
The offset from GMT for this local time.
|
| 54 |
+
-/
|
| 55 |
+
gmtOffset : TimeZone.Offset
|
| 56 |
+
|
| 57 |
+
/--
|
| 58 |
+
Indicates if daylight saving time is observed.
|
| 59 |
+
-/
|
| 60 |
+
isDst : Bool
|
| 61 |
+
|
| 62 |
+
/--
|
| 63 |
+
The abbreviation for this local time type (e.g., "EST", "PDT").
|
| 64 |
+
-/
|
| 65 |
+
abbreviation : String
|
| 66 |
+
|
| 67 |
+
/--
|
| 68 |
+
Indicates if the time is wall clock or standard time.
|
| 69 |
+
-/
|
| 70 |
+
wall : StdWall
|
| 71 |
+
|
| 72 |
+
/--
|
| 73 |
+
Distinguishes between universal time and local time.
|
| 74 |
+
-/
|
| 75 |
+
utLocal : UTLocal
|
| 76 |
+
|
| 77 |
+
/--
|
| 78 |
+
ID of the timezone
|
| 79 |
+
-/
|
| 80 |
+
identifier : String
|
| 81 |
+
deriving Repr, Inhabited
|
| 82 |
+
|
| 83 |
+
namespace LocalTimeType
|
| 84 |
+
|
| 85 |
+
/--
|
| 86 |
+
Gets the `TimeZone` offset from a `LocalTimeType`.
|
| 87 |
+
-/
|
| 88 |
+
def getTimeZone (time : LocalTimeType) : TimeZone :=
|
| 89 |
+
⟨time.gmtOffset, time.identifier, time.abbreviation, time.isDst⟩
|
| 90 |
+
|
| 91 |
+
end LocalTimeType
|
| 92 |
+
|
| 93 |
+
/--
|
| 94 |
+
Represents a time zone transition, mapping a time to a local time type.
|
| 95 |
+
-/
|
| 96 |
+
structure Transition where
|
| 97 |
+
|
| 98 |
+
/--
|
| 99 |
+
The specific time of the transition event.
|
| 100 |
+
-/
|
| 101 |
+
time : Second.Offset
|
| 102 |
+
|
| 103 |
+
/--
|
| 104 |
+
The local time type associated with this transition.
|
| 105 |
+
-/
|
| 106 |
+
localTimeType : LocalTimeType
|
| 107 |
+
deriving Repr, Inhabited
|
| 108 |
+
|
| 109 |
+
/--
|
| 110 |
+
Represents the rules for a time zone.
|
| 111 |
+
-/
|
| 112 |
+
structure ZoneRules where
|
| 113 |
+
|
| 114 |
+
/--
|
| 115 |
+
The first `LocalTimeType` used as a fallback when no matching transition is found.
|
| 116 |
+
-/
|
| 117 |
+
initialLocalTimeType : LocalTimeType
|
| 118 |
+
|
| 119 |
+
/--
|
| 120 |
+
The array of transitions for the time zone.
|
| 121 |
+
-/
|
| 122 |
+
transitions : Array Transition
|
| 123 |
+
|
| 124 |
+
deriving Repr, Inhabited
|
| 125 |
+
|
| 126 |
+
namespace Transition
|
| 127 |
+
|
| 128 |
+
/--
|
| 129 |
+
Create a TimeZone from a Transition.
|
| 130 |
+
-/
|
| 131 |
+
def createTimeZoneFromTransition (transition : Transition) : TimeZone :=
|
| 132 |
+
let name := transition.localTimeType.identifier
|
| 133 |
+
let offset := transition.localTimeType.gmtOffset
|
| 134 |
+
let abbreviation := transition.localTimeType.abbreviation
|
| 135 |
+
TimeZone.mk offset name abbreviation transition.localTimeType.isDst
|
| 136 |
+
|
| 137 |
+
/--
|
| 138 |
+
Applies the transition to a Timestamp.
|
| 139 |
+
-/
|
| 140 |
+
def apply (timestamp : Timestamp) (transition : Transition) : Timestamp :=
|
| 141 |
+
let offsetInSeconds := transition.localTimeType.gmtOffset.second |>.add transition.localTimeType.gmtOffset.second
|
| 142 |
+
timestamp.addSeconds offsetInSeconds
|
| 143 |
+
|
| 144 |
+
/--
|
| 145 |
+
Finds the transition corresponding to a given timestamp in `Array Transition`.
|
| 146 |
+
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
| 147 |
+
-/
|
| 148 |
+
def findTransitionIndexForTimestamp (transitions : Array Transition) (timestamp : Timestamp) : Option Nat :=
|
| 149 |
+
let value := timestamp.toSecondsSinceUnixEpoch
|
| 150 |
+
transitions.findIdx? (fun t => t.time.val > value.val)
|
| 151 |
+
|
| 152 |
+
/--
|
| 153 |
+
Finds the transition corresponding to a given timestamp in `Array Transition`.
|
| 154 |
+
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
| 155 |
+
-/
|
| 156 |
+
def findTransitionForTimestamp (transitions : Array Transition) (timestamp : Timestamp) : Option Transition :=
|
| 157 |
+
if let some idx := findTransitionIndexForTimestamp transitions timestamp
|
| 158 |
+
then transitions[idx - 1]?
|
| 159 |
+
else transitions.back?
|
| 160 |
+
|
| 161 |
+
/--
|
| 162 |
+
Find the current `TimeZone` out of a `Transition` in a `Array Transition`
|
| 163 |
+
-/
|
| 164 |
+
def timezoneAt (transitions : Array Transition) (tm : Timestamp) : Except String TimeZone :=
|
| 165 |
+
if let some transition := findTransitionForTimestamp transitions tm
|
| 166 |
+
then .ok transition.createTimeZoneFromTransition
|
| 167 |
+
else .error "cannot find local timezone."
|
| 168 |
+
|
| 169 |
+
end Transition
|
| 170 |
+
namespace ZoneRules
|
| 171 |
+
|
| 172 |
+
/--
|
| 173 |
+
Creates ZoneRules with a fixed GMT offset.
|
| 174 |
+
-/
|
| 175 |
+
def fixedOffsetZone (second : Second.Offset) (identifier : Option String := none) (abbreviation : Option String := none) : ZoneRules :=
|
| 176 |
+
let offset : Offset := { second }
|
| 177 |
+
{
|
| 178 |
+
transitions := #[],
|
| 179 |
+
initialLocalTimeType := {
|
| 180 |
+
gmtOffset := offset,
|
| 181 |
+
isDst := false, abbreviation := abbreviation.getD (offset.toIsoString true),
|
| 182 |
+
wall := .standard,
|
| 183 |
+
utLocal := .ut,
|
| 184 |
+
identifier := identifier.getD (offset.toIsoString true)
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/--
|
| 189 |
+
Creates ZoneRules with a fixed offset of UTC (GMT+0).
|
| 190 |
+
-/
|
| 191 |
+
@[inline]
|
| 192 |
+
def UTC : ZoneRules :=
|
| 193 |
+
fixedOffsetZone 0 (some "UTC") (some "UTC")
|
| 194 |
+
|
| 195 |
+
/--
|
| 196 |
+
Finds the `LocalTimeType` corresponding to a given `Timestamp` in `ZoneRules`.
|
| 197 |
+
If the timestamp falls between two transitions, it returns the most recent transition before the timestamp.
|
| 198 |
+
If no transition is found, it falls back to `initialLocalTimeType`.
|
| 199 |
+
-/
|
| 200 |
+
@[inline]
|
| 201 |
+
def findLocalTimeTypeForTimestamp (zr : ZoneRules) (timestamp : Timestamp) : LocalTimeType :=
|
| 202 |
+
Transition.findTransitionForTimestamp zr.transitions timestamp
|
| 203 |
+
|>.map (·.localTimeType)
|
| 204 |
+
|>.getD zr.initialLocalTimeType
|
| 205 |
+
|
| 206 |
+
/--
|
| 207 |
+
Find the current `TimeZone` out of a `Transition` in a `ZoneRules`
|
| 208 |
+
-/
|
| 209 |
+
@[inline]
|
| 210 |
+
def timezoneAt (zr : ZoneRules) (tm : Timestamp) : TimeZone :=
|
| 211 |
+
Transition.timezoneAt zr.transitions tm
|
| 212 |
+
|>.toOption
|
| 213 |
+
|>.getD (zr.initialLocalTimeType |>.getTimeZone)
|
| 214 |
+
|
| 215 |
+
/--
|
| 216 |
+
Creates `ZoneRules` for the given `TimeZone`.
|
| 217 |
+
-/
|
| 218 |
+
@[inline]
|
| 219 |
+
def ofTimeZone (tz : TimeZone) : ZoneRules :=
|
| 220 |
+
let ltt := LocalTimeType.mk tz.offset tz.isDST tz.abbreviation .wall .local tz.name
|
| 221 |
+
ZoneRules.mk ltt #[]
|
| 222 |
+
|
| 223 |
+
end ZoneRules
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Std/Time/Zoned/ZonedDateTime.lean
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sofia Rodrigues
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Time.Zoned.DateTime
|
| 8 |
+
import Std.Time.Zoned.ZoneRules
|
| 9 |
+
|
| 10 |
+
namespace Std
|
| 11 |
+
namespace Time
|
| 12 |
+
|
| 13 |
+
-- TODO (@kim-em): re-enable this once there is a mechanism to exclude `linter.indexVariables`.
|
| 14 |
+
-- set_option linter.all true
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
Represents a date and time with timezone information.
|
| 18 |
+
-/
|
| 19 |
+
structure ZonedDateTime where
|
| 20 |
+
private mk::
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
The plain datetime component, evaluated lazily.
|
| 24 |
+
-/
|
| 25 |
+
date : Thunk PlainDateTime
|
| 26 |
+
|
| 27 |
+
/--
|
| 28 |
+
The corresponding timestamp for the datetime.
|
| 29 |
+
-/
|
| 30 |
+
timestamp : Timestamp
|
| 31 |
+
|
| 32 |
+
/--
|
| 33 |
+
The timezone rules applied to this datetime.
|
| 34 |
+
-/
|
| 35 |
+
rules : TimeZone.ZoneRules
|
| 36 |
+
|
| 37 |
+
/--
|
| 38 |
+
The timezone associated with this datetime.
|
| 39 |
+
-/
|
| 40 |
+
timezone : TimeZone
|
| 41 |
+
|
| 42 |
+
instance : Inhabited ZonedDateTime where
|
| 43 |
+
default := ⟨Thunk.mk Inhabited.default, Inhabited.default, Inhabited.default, Inhabited.default⟩
|
| 44 |
+
|
| 45 |
+
namespace ZonedDateTime
|
| 46 |
+
open DateTime
|
| 47 |
+
|
| 48 |
+
/--
|
| 49 |
+
Creates a new `ZonedDateTime` out of a `Timestamp` and a `ZoneRules`.
|
| 50 |
+
-/
|
| 51 |
+
@[inline]
|
| 52 |
+
def ofTimestamp (tm : Timestamp) (rules : TimeZone.ZoneRules) : ZonedDateTime :=
|
| 53 |
+
let tz := rules.timezoneAt tm
|
| 54 |
+
ZonedDateTime.mk (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC.addSeconds tz.toSeconds) tm rules tz
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Creates a new `ZonedDateTime` out of a `PlainDateTime` and a `ZoneRules`.
|
| 58 |
+
-/
|
| 59 |
+
@[inline]
|
| 60 |
+
def ofPlainDateTime (pdt : PlainDateTime) (zr : TimeZone.ZoneRules) : ZonedDateTime :=
|
| 61 |
+
let tm := pdt.toTimestampAssumingUTC
|
| 62 |
+
|
| 63 |
+
let transition :=
|
| 64 |
+
let value := tm.toSecondsSinceUnixEpoch
|
| 65 |
+
if let some idx := zr.transitions.findFinIdx? (fun t => t.time.val ≥ value.val)
|
| 66 |
+
then
|
| 67 |
+
let last := zr.transitions[idx.1 - 1]
|
| 68 |
+
let next := zr.transitions[idx]
|
| 69 |
+
|
| 70 |
+
let utcNext := next.time.sub last.localTimeType.gmtOffset.second.abs
|
| 71 |
+
|
| 72 |
+
if utcNext.val > tm.toSecondsSinceUnixEpoch.val
|
| 73 |
+
then some last
|
| 74 |
+
else some next
|
| 75 |
+
|
| 76 |
+
else zr.transitions.back?
|
| 77 |
+
|
| 78 |
+
let tz :=
|
| 79 |
+
transition
|
| 80 |
+
|>.map (·.localTimeType)
|
| 81 |
+
|>.getD zr.initialLocalTimeType
|
| 82 |
+
|>.getTimeZone
|
| 83 |
+
|
| 84 |
+
let tm := tm.subSeconds tz.toSeconds
|
| 85 |
+
ZonedDateTime.mk (Thunk.mk fun _ => tm.toPlainDateTimeAssumingUTC.addSeconds tz.toSeconds) tm zr tz
|
| 86 |
+
|
| 87 |
+
/--
|
| 88 |
+
Creates a new `ZonedDateTime` out of a `Timestamp` and a `TimeZone`.
|
| 89 |
+
-/
|
| 90 |
+
@[inline]
|
| 91 |
+
def ofTimestampWithZone (tm : Timestamp) (tz : TimeZone) : ZonedDateTime :=
|
| 92 |
+
ofTimestamp tm (TimeZone.ZoneRules.ofTimeZone tz)
|
| 93 |
+
|
| 94 |
+
/--
|
| 95 |
+
Creates a new `ZonedDateTime` out of a `PlainDateTime` and a `TimeZone`.
|
| 96 |
+
-/
|
| 97 |
+
@[inline]
|
| 98 |
+
def ofPlainDateTimeWithZone (tm : PlainDateTime) (tz : TimeZone) : ZonedDateTime :=
|
| 99 |
+
ofPlainDateTime tm (TimeZone.ZoneRules.ofTimeZone tz)
|
| 100 |
+
|
| 101 |
+
/--
|
| 102 |
+
Creates a new `Timestamp` out of a `ZonedDateTime`.
|
| 103 |
+
-/
|
| 104 |
+
@[inline]
|
| 105 |
+
def toTimestamp (date : ZonedDateTime) : Timestamp :=
|
| 106 |
+
date.timestamp
|
| 107 |
+
|
| 108 |
+
/--
|
| 109 |
+
Changes the `ZoleRules` to a new one.
|
| 110 |
+
-/
|
| 111 |
+
@[inline]
|
| 112 |
+
def convertZoneRules (date : ZonedDateTime) (tz₁ : TimeZone.ZoneRules) : ZonedDateTime :=
|
| 113 |
+
ofTimestamp date.toTimestamp tz₁
|
| 114 |
+
|
| 115 |
+
/--
|
| 116 |
+
Creates a new `ZonedDateTime` out of a `PlainDateTime`. It assumes that the `PlainDateTime` is relative
|
| 117 |
+
to UTC.
|
| 118 |
+
-/
|
| 119 |
+
@[inline]
|
| 120 |
+
def ofPlainDateTimeAssumingUTC (date : PlainDateTime) (tz : TimeZone.ZoneRules) : ZonedDateTime :=
|
| 121 |
+
ofTimestamp date.toTimestampAssumingUTC tz
|
| 122 |
+
|
| 123 |
+
/--
|
| 124 |
+
Converts a `ZonedDateTime` to a `PlainDateTime`
|
| 125 |
+
-/
|
| 126 |
+
@[inline]
|
| 127 |
+
def toPlainDateTime (dt : ZonedDateTime) : PlainDateTime :=
|
| 128 |
+
dt.date.get
|
| 129 |
+
|
| 130 |
+
/--
|
| 131 |
+
Converts a `ZonedDateTime` to a `PlainDateTime`
|
| 132 |
+
-/
|
| 133 |
+
@[inline]
|
| 134 |
+
def toDateTime (dt : ZonedDateTime) : DateTime dt.timezone :=
|
| 135 |
+
DateTime.ofTimestamp dt.timestamp dt.timezone
|
| 136 |
+
|
| 137 |
+
/--
|
| 138 |
+
Getter for the `PlainTime` inside of a `ZonedDateTime`
|
| 139 |
+
-/
|
| 140 |
+
@[inline]
|
| 141 |
+
def time (zdt : ZonedDateTime) : PlainTime :=
|
| 142 |
+
zdt.date.get.time
|
| 143 |
+
|
| 144 |
+
/--
|
| 145 |
+
Getter for the `Year` inside of a `ZonedDateTime`
|
| 146 |
+
-/
|
| 147 |
+
@[inline]
|
| 148 |
+
def year (zdt : ZonedDateTime) : Year.Offset :=
|
| 149 |
+
zdt.date.get.year
|
| 150 |
+
|
| 151 |
+
/--
|
| 152 |
+
Getter for the `Month` inside of a `ZonedDateTime`
|
| 153 |
+
-/
|
| 154 |
+
@[inline]
|
| 155 |
+
def month (zdt : ZonedDateTime) : Month.Ordinal :=
|
| 156 |
+
zdt.date.get.month
|
| 157 |
+
|
| 158 |
+
/--
|
| 159 |
+
Getter for the `Day` inside of a `ZonedDateTime`
|
| 160 |
+
-/
|
| 161 |
+
@[inline]
|
| 162 |
+
def day (zdt : ZonedDateTime) : Day.Ordinal :=
|
| 163 |
+
zdt.date.get.day
|
| 164 |
+
|
| 165 |
+
/--
|
| 166 |
+
Getter for the `Hour` inside of a `ZonedDateTime`
|
| 167 |
+
-/
|
| 168 |
+
@[inline]
|
| 169 |
+
def hour (zdt : ZonedDateTime) : Hour.Ordinal :=
|
| 170 |
+
zdt.date.get.time.hour
|
| 171 |
+
|
| 172 |
+
/--
|
| 173 |
+
Getter for the `Minute` inside of a `ZonedDateTime`
|
| 174 |
+
-/
|
| 175 |
+
@[inline]
|
| 176 |
+
def minute (zdt : ZonedDateTime) : Minute.Ordinal :=
|
| 177 |
+
zdt.date.get.minute
|
| 178 |
+
|
| 179 |
+
/--
|
| 180 |
+
Getter for the `Second` inside of a `ZonedDateTime`
|
| 181 |
+
-/
|
| 182 |
+
@[inline]
|
| 183 |
+
def second (zdt : ZonedDateTime) : Second.Ordinal true :=
|
| 184 |
+
zdt.date.get.time.second
|
| 185 |
+
|
| 186 |
+
/--
|
| 187 |
+
Getter for the `Millisecond` inside of a `ZonedDateTime`.
|
| 188 |
+
-/
|
| 189 |
+
@[inline]
|
| 190 |
+
def millisecond (dt : ZonedDateTime) : Millisecond.Ordinal :=
|
| 191 |
+
dt.date.get.time.millisecond
|
| 192 |
+
|
| 193 |
+
/--
|
| 194 |
+
Getter for the `Nanosecond` inside of a `ZonedDateTime`
|
| 195 |
+
-/
|
| 196 |
+
@[inline]
|
| 197 |
+
def nanosecond (zdt : ZonedDateTime) : Nanosecond.Ordinal :=
|
| 198 |
+
zdt.date.get.time.nanosecond
|
| 199 |
+
|
| 200 |
+
/--
|
| 201 |
+
Getter for the `TimeZone.Offset` inside of a `ZonedDateTime`
|
| 202 |
+
-/
|
| 203 |
+
@[inline]
|
| 204 |
+
def offset (zdt : ZonedDateTime) : TimeZone.Offset :=
|
| 205 |
+
zdt.timezone.offset
|
| 206 |
+
|
| 207 |
+
/--
|
| 208 |
+
Returns the weekday.
|
| 209 |
+
-/
|
| 210 |
+
@[inline]
|
| 211 |
+
def weekday (zdt : ZonedDateTime) : Weekday :=
|
| 212 |
+
zdt.date.get.weekday
|
| 213 |
+
|
| 214 |
+
/--
|
| 215 |
+
Transforms a tuple of a `ZonedDateTime` into a `Day.Ordinal.OfYear`.
|
| 216 |
+
-/
|
| 217 |
+
@[inline]
|
| 218 |
+
def dayOfYear (date : ZonedDateTime) : Day.Ordinal.OfYear date.year.isLeap :=
|
| 219 |
+
ValidDate.dayOfYear ⟨(date.month, date.day), date.date.get.date.valid⟩
|
| 220 |
+
|
| 221 |
+
/--
|
| 222 |
+
Determines the week of the year for the given `ZonedDateTime`.
|
| 223 |
+
-/
|
| 224 |
+
@[inline]
|
| 225 |
+
def weekOfYear (date : ZonedDateTime) : Week.Ordinal :=
|
| 226 |
+
date.date.get.weekOfYear
|
| 227 |
+
|
| 228 |
+
/--
|
| 229 |
+
Returns the unaligned week of the month for a `ZonedDateTime` (day divided by 7, plus 1).
|
| 230 |
+
-/
|
| 231 |
+
def weekOfMonth (date : ZonedDateTime) : Internal.Bounded.LE 1 5 :=
|
| 232 |
+
date.date.get.weekOfMonth
|
| 233 |
+
|
| 234 |
+
/--
|
| 235 |
+
Determines the week of the month for the given `ZonedDateTime`. The week of the month is calculated based
|
| 236 |
+
on the day of the month and the weekday. Each week starts on Monday because the entire library is
|
| 237 |
+
based on the Gregorian Calendar.
|
| 238 |
+
-/
|
| 239 |
+
@[inline]
|
| 240 |
+
def alignedWeekOfMonth (date : ZonedDateTime) : Week.Ordinal.OfMonth :=
|
| 241 |
+
date.date.get.alignedWeekOfMonth
|
| 242 |
+
|
| 243 |
+
/--
|
| 244 |
+
Determines the quarter of the year for the given `ZonedDateTime`.
|
| 245 |
+
-/
|
| 246 |
+
@[inline]
|
| 247 |
+
def quarter (date : ZonedDateTime) : Internal.Bounded.LE 1 4 :=
|
| 248 |
+
date.date.get.quarter
|
| 249 |
+
|
| 250 |
+
/--
|
| 251 |
+
Add `Day.Offset` to a `ZonedDateTime`.
|
| 252 |
+
-/
|
| 253 |
+
def addDays (dt : ZonedDateTime) (days : Day.Offset) : ZonedDateTime :=
|
| 254 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 255 |
+
ZonedDateTime.ofTimestamp (date.addDays days).toTimestampAssumingUTC dt.rules
|
| 256 |
+
|
| 257 |
+
/--
|
| 258 |
+
Subtract `Day.Offset` from a `ZonedDateTime`.
|
| 259 |
+
-/
|
| 260 |
+
def subDays (dt : ZonedDateTime) (days : Day.Offset) : ZonedDateTime :=
|
| 261 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 262 |
+
ZonedDateTime.ofTimestamp (date.subDays days).toTimestampAssumingUTC dt.rules
|
| 263 |
+
|
| 264 |
+
/--
|
| 265 |
+
Add `Week.Offset` to a `ZonedDateTime`.
|
| 266 |
+
-/
|
| 267 |
+
def addWeeks (dt : ZonedDateTime) (weeks : Week.Offset) : ZonedDateTime :=
|
| 268 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 269 |
+
ZonedDateTime.ofTimestamp (date.addWeeks weeks).toTimestampAssumingUTC dt.rules
|
| 270 |
+
|
| 271 |
+
/--
|
| 272 |
+
Subtract `Week.Offset` from a `ZonedDateTime`.
|
| 273 |
+
-/
|
| 274 |
+
def subWeeks (dt : ZonedDateTime) (weeks : Week.Offset) : ZonedDateTime :=
|
| 275 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 276 |
+
ZonedDateTime.ofTimestamp (date.subWeeks weeks).toTimestampAssumingUTC dt.rules
|
| 277 |
+
|
| 278 |
+
/--
|
| 279 |
+
Add `Month.Offset` to a `ZonedDateTime`, clipping to the last valid day.
|
| 280 |
+
-/
|
| 281 |
+
def addMonthsClip (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
| 282 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 283 |
+
ZonedDateTime.ofTimestamp (date.addMonthsClip months).toTimestampAssumingUTC dt.rules
|
| 284 |
+
|
| 285 |
+
/--
|
| 286 |
+
Subtract `Month.Offset` from a `ZonedDateTime`, clipping to the last valid day.
|
| 287 |
+
-/
|
| 288 |
+
def subMonthsClip (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
| 289 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 290 |
+
ZonedDateTime.ofTimestamp (date.subMonthsClip months).toTimestampAssumingUTC dt.rules
|
| 291 |
+
|
| 292 |
+
/--
|
| 293 |
+
Add `Month.Offset` to a `ZonedDateTime`, rolling over excess days.
|
| 294 |
+
-/
|
| 295 |
+
def addMonthsRollOver (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
| 296 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 297 |
+
ZonedDateTime.ofTimestamp (date.addMonthsRollOver months).toTimestampAssumingUTC dt.rules
|
| 298 |
+
|
| 299 |
+
/--
|
| 300 |
+
Subtract `Month.Offset` from a `ZonedDateTime`, rolling over excess days.
|
| 301 |
+
-/
|
| 302 |
+
def subMonthsRollOver (dt : ZonedDateTime) (months : Month.Offset) : ZonedDateTime :=
|
| 303 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 304 |
+
ZonedDateTime.ofTimestamp (date.subMonthsRollOver months).toTimestampAssumingUTC dt.rules
|
| 305 |
+
|
| 306 |
+
/--
|
| 307 |
+
Add `Year.Offset` to a `ZonedDateTime`, rolling over excess days.
|
| 308 |
+
-/
|
| 309 |
+
def addYearsRollOver (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
| 310 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 311 |
+
ZonedDateTime.ofTimestamp (date.addYearsRollOver years).toTimestampAssumingUTC dt.rules
|
| 312 |
+
|
| 313 |
+
/--
|
| 314 |
+
Add `Year.Offset` to a `ZonedDateTime`, clipping to the last valid day.
|
| 315 |
+
-/
|
| 316 |
+
def addYearsClip (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
| 317 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 318 |
+
ZonedDateTime.ofTimestamp (date.addYearsClip years).toTimestampAssumingUTC dt.rules
|
| 319 |
+
|
| 320 |
+
/--
|
| 321 |
+
Subtract `Year.Offset` from a `ZonedDateTime`, clipping to the last valid day.
|
| 322 |
+
-/
|
| 323 |
+
def subYearsClip (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
| 324 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 325 |
+
ZonedDateTime.ofTimestamp (date.subYearsClip years).toTimestampAssumingUTC dt.rules
|
| 326 |
+
|
| 327 |
+
/--
|
| 328 |
+
Subtract `Year.Offset` from a `ZonedDateTime`, rolling over excess days.
|
| 329 |
+
-/
|
| 330 |
+
def subYearsRollOver (dt : ZonedDateTime) (years : Year.Offset) : ZonedDateTime :=
|
| 331 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 332 |
+
ZonedDateTime.ofTimestamp (date.subYearsRollOver years).toTimestampAssumingUTC dt.rules
|
| 333 |
+
|
| 334 |
+
/--
|
| 335 |
+
Add `Hour.Offset` to a `ZonedDateTime`.
|
| 336 |
+
-/
|
| 337 |
+
def addHours (dt : ZonedDateTime) (hours : Hour.Offset) : ZonedDateTime :=
|
| 338 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 339 |
+
ZonedDateTime.ofTimestamp (date.addHours hours).toTimestampAssumingUTC dt.rules
|
| 340 |
+
|
| 341 |
+
/--
|
| 342 |
+
Subtract `Hour.Offset` from a `ZonedDateTime`.
|
| 343 |
+
-/
|
| 344 |
+
def subHours (dt : ZonedDateTime) (hours : Hour.Offset) : ZonedDateTime :=
|
| 345 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 346 |
+
ZonedDateTime.ofTimestamp (date.subHours hours).toTimestampAssumingUTC dt.rules
|
| 347 |
+
|
| 348 |
+
/--
|
| 349 |
+
Add `Minute.Offset` to a `ZonedDateTime`.
|
| 350 |
+
-/
|
| 351 |
+
def addMinutes (dt : ZonedDateTime) (minutes : Minute.Offset) : ZonedDateTime :=
|
| 352 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 353 |
+
ZonedDateTime.ofTimestamp (date.addMinutes minutes).toTimestampAssumingUTC dt.rules
|
| 354 |
+
|
| 355 |
+
/--
|
| 356 |
+
Subtract `Minute.Offset` from a `ZonedDateTime`.
|
| 357 |
+
-/
|
| 358 |
+
def subMinutes (dt : ZonedDateTime) (minutes : Minute.Offset) : ZonedDateTime :=
|
| 359 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 360 |
+
ZonedDateTime.ofTimestamp (date.subMinutes minutes).toTimestampAssumingUTC dt.rules
|
| 361 |
+
|
| 362 |
+
/--
|
| 363 |
+
Add `Millisecond.Offset` to a `DateTime`.
|
| 364 |
+
-/
|
| 365 |
+
@[inline]
|
| 366 |
+
def addMilliseconds (dt : ZonedDateTime) (milliseconds : Millisecond.Offset) : ZonedDateTime :=
|
| 367 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 368 |
+
ZonedDateTime.ofTimestamp (date.addMilliseconds milliseconds).toTimestampAssumingUTC dt.rules
|
| 369 |
+
|
| 370 |
+
/--
|
| 371 |
+
Subtract `Millisecond.Offset` from a `DateTime`.
|
| 372 |
+
-/
|
| 373 |
+
@[inline]
|
| 374 |
+
def subMilliseconds (dt : ZonedDateTime) (milliseconds : Millisecond.Offset) : ZonedDateTime :=
|
| 375 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 376 |
+
ZonedDateTime.ofTimestamp (date.subMilliseconds milliseconds).toTimestampAssumingUTC dt.rules
|
| 377 |
+
|
| 378 |
+
/--
|
| 379 |
+
Add `Second.Offset` to a `ZonedDateTime`.
|
| 380 |
+
-/
|
| 381 |
+
def addSeconds (dt : ZonedDateTime) (seconds : Second.Offset) : ZonedDateTime :=
|
| 382 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 383 |
+
ZonedDateTime.ofTimestamp (date.addSeconds seconds).toTimestampAssumingUTC dt.rules
|
| 384 |
+
|
| 385 |
+
/--
|
| 386 |
+
Subtract `Second.Offset` from a `ZonedDateTime`.
|
| 387 |
+
-/
|
| 388 |
+
def subSeconds (dt : ZonedDateTime) (seconds : Second.Offset) : ZonedDateTime :=
|
| 389 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 390 |
+
ZonedDateTime.ofTimestamp (date.subSeconds seconds).toTimestampAssumingUTC dt.rules
|
| 391 |
+
|
| 392 |
+
/--
|
| 393 |
+
Add `Nanosecond.Offset` to a `ZonedDateTime`.
|
| 394 |
+
-/
|
| 395 |
+
def addNanoseconds (dt : ZonedDateTime) (nanoseconds : Nanosecond.Offset) : ZonedDateTime :=
|
| 396 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 397 |
+
ZonedDateTime.ofTimestamp (date.addNanoseconds nanoseconds).toTimestampAssumingUTC dt.rules
|
| 398 |
+
|
| 399 |
+
/--
|
| 400 |
+
Subtract `Nanosecond.Offset` from a `ZonedDateTime`.
|
| 401 |
+
-/
|
| 402 |
+
def subNanoseconds (dt : ZonedDateTime) (nanoseconds : Nanosecond.Offset) : ZonedDateTime :=
|
| 403 |
+
let date := dt.timestamp.toPlainDateTimeAssumingUTC
|
| 404 |
+
ZonedDateTime.ofTimestamp (date.subNanoseconds nanoseconds).toTimestampAssumingUTC dt.rules
|
| 405 |
+
|
| 406 |
+
/--
|
| 407 |
+
Determines the era of the given `ZonedDateTime` based on its year.
|
| 408 |
+
-/
|
| 409 |
+
@[inline]
|
| 410 |
+
def era (date : ZonedDateTime) : Year.Era :=
|
| 411 |
+
date.date.get.era
|
| 412 |
+
|
| 413 |
+
/--
|
| 414 |
+
Sets the `ZonedDateTime` to the specified `desiredWeekday`.
|
| 415 |
+
-/
|
| 416 |
+
def withWeekday (dt : ZonedDateTime) (desiredWeekday : Weekday) : ZonedDateTime :=
|
| 417 |
+
let date := dt.date.get
|
| 418 |
+
ZonedDateTime.ofPlainDateTime (date.withWeekday desiredWeekday) dt.rules
|
| 419 |
+
|
| 420 |
+
/--
|
| 421 |
+
Creates a new `ZonedDateTime` by adjusting the day of the month to the given `days` value, with any
|
| 422 |
+
out-of-range days clipped to the nearest valid date.
|
| 423 |
+
-/
|
| 424 |
+
@[inline]
|
| 425 |
+
def withDaysClip (dt : ZonedDateTime) (days : Day.Ordinal) : ZonedDateTime :=
|
| 426 |
+
let date := dt.date.get
|
| 427 |
+
ZonedDateTime.ofPlainDateTime (date.withDaysClip days) dt.rules
|
| 428 |
+
|
| 429 |
+
/--
|
| 430 |
+
Creates a new `ZonedDateTime` by adjusting the day of the month to the given `days` value, with any
|
| 431 |
+
out-of-range days rolled over to the next month or year as needed.
|
| 432 |
+
-/
|
| 433 |
+
@[inline]
|
| 434 |
+
def withDaysRollOver (dt : ZonedDateTime) (days : Day.Ordinal) : ZonedDateTime :=
|
| 435 |
+
let date := dt.date.get
|
| 436 |
+
ZonedDateTime.ofPlainDateTime (date.withDaysRollOver days) dt.rules
|
| 437 |
+
|
| 438 |
+
/--
|
| 439 |
+
Creates a new `ZonedDateTime` by adjusting the month to the given `month` value.
|
| 440 |
+
The day remains unchanged, and any invalid days for the new month will be handled according to the `clip` behavior.
|
| 441 |
+
-/
|
| 442 |
+
@[inline]
|
| 443 |
+
def withMonthClip (dt : ZonedDateTime) (month : Month.Ordinal) : ZonedDateTime :=
|
| 444 |
+
let date := dt.date.get
|
| 445 |
+
ZonedDateTime.ofPlainDateTime (date.withMonthClip month) dt.rules
|
| 446 |
+
|
| 447 |
+
/--
|
| 448 |
+
Creates a new `ZonedDateTime` by adjusting the month to the given `month` value.
|
| 449 |
+
The day is rolled over to the next valid month if necessary.
|
| 450 |
+
-/
|
| 451 |
+
@[inline]
|
| 452 |
+
def withMonthRollOver (dt : ZonedDateTime) (month : Month.Ordinal) : ZonedDateTime :=
|
| 453 |
+
let date := dt.date.get
|
| 454 |
+
ZonedDateTime.ofPlainDateTime (date.withMonthRollOver month) dt.rules
|
| 455 |
+
|
| 456 |
+
/--
|
| 457 |
+
Creates a new `ZonedDateTime` by adjusting the year to the given `year` value. The month and day remain unchanged,
|
| 458 |
+
and any invalid days for the new year will be handled according to the `clip` behavior.
|
| 459 |
+
-/
|
| 460 |
+
@[inline]
|
| 461 |
+
def withYearClip (dt : ZonedDateTime) (year : Year.Offset) : ZonedDateTime :=
|
| 462 |
+
let date := dt.date.get
|
| 463 |
+
|
| 464 |
+
ZonedDateTime.ofPlainDateTime (date.withYearClip year) dt.rules
|
| 465 |
+
|
| 466 |
+
/--
|
| 467 |
+
Creates a new `ZonedDateTime` by adjusting the year to the given `year` value. The month and day are rolled
|
| 468 |
+
over to the next valid month and day if necessary.
|
| 469 |
+
-/
|
| 470 |
+
@[inline]
|
| 471 |
+
def withYearRollOver (dt : ZonedDateTime) (year : Year.Offset) : ZonedDateTime :=
|
| 472 |
+
let date := dt.date.get
|
| 473 |
+
ZonedDateTime.ofPlainDateTime (date.withYearRollOver year) dt.rules
|
| 474 |
+
|
| 475 |
+
/--
|
| 476 |
+
Creates a new `ZonedDateTime` by adjusting the `hour` component.
|
| 477 |
+
-/
|
| 478 |
+
@[inline]
|
| 479 |
+
def withHours (dt : ZonedDateTime) (hour : Hour.Ordinal) : ZonedDateTime :=
|
| 480 |
+
let date := dt.date.get
|
| 481 |
+
ZonedDateTime.ofPlainDateTime (date.withHours hour) dt.rules
|
| 482 |
+
|
| 483 |
+
/--
|
| 484 |
+
Creates a new `ZonedDateTime` by adjusting the `minute` component.
|
| 485 |
+
-/
|
| 486 |
+
@[inline]
|
| 487 |
+
def withMinutes (dt : ZonedDateTime) (minute : Minute.Ordinal) : ZonedDateTime :=
|
| 488 |
+
let date := dt.date.get
|
| 489 |
+
ZonedDateTime.ofPlainDateTime (date.withMinutes minute) dt.rules
|
| 490 |
+
|
| 491 |
+
/--
|
| 492 |
+
Creates a new `ZonedDateTime` by adjusting the `second` component.
|
| 493 |
+
-/
|
| 494 |
+
@[inline]
|
| 495 |
+
def withSeconds (dt : ZonedDateTime) (second : Second.Ordinal true) : ZonedDateTime :=
|
| 496 |
+
let date := dt.date.get
|
| 497 |
+
ZonedDateTime.ofPlainDateTime (date.withSeconds second) dt.rules
|
| 498 |
+
|
| 499 |
+
/--
|
| 500 |
+
Creates a new `ZonedDateTime` by adjusting the `nano` component with a new `millis` that will set
|
| 501 |
+
in the millisecond scale.
|
| 502 |
+
-/
|
| 503 |
+
@[inline]
|
| 504 |
+
def withMilliseconds (dt : ZonedDateTime) (millis : Millisecond.Ordinal) : ZonedDateTime :=
|
| 505 |
+
let date := dt.date.get
|
| 506 |
+
ZonedDateTime.ofPlainDateTime (date.withMilliseconds millis) dt.rules
|
| 507 |
+
|
| 508 |
+
/--
|
| 509 |
+
Creates a new `ZonedDateTime` by adjusting the `nano` component.
|
| 510 |
+
-/
|
| 511 |
+
@[inline]
|
| 512 |
+
def withNanoseconds (dt : ZonedDateTime) (nano : Nanosecond.Ordinal) : ZonedDateTime :=
|
| 513 |
+
let date := dt.date.get
|
| 514 |
+
ZonedDateTime.ofPlainDateTime (date.withNanoseconds nano) dt.rules
|
| 515 |
+
|
| 516 |
+
/--
|
| 517 |
+
Checks if the `ZonedDateTime` is in a leap year.
|
| 518 |
+
-/
|
| 519 |
+
def inLeapYear (date : ZonedDateTime) : Bool :=
|
| 520 |
+
date.year.isLeap
|
| 521 |
+
|
| 522 |
+
/--
|
| 523 |
+
Converts a `ZonedDateTime` to the number of days since the UNIX epoch.
|
| 524 |
+
-/
|
| 525 |
+
def toDaysSinceUNIXEpoch (date : ZonedDateTime) : Day.Offset :=
|
| 526 |
+
date.date.get.toDaysSinceUNIXEpoch
|
| 527 |
+
|
| 528 |
+
/--
|
| 529 |
+
Converts a `ZonedDateTime` to the number of days since the UNIX epoch.
|
| 530 |
+
-/
|
| 531 |
+
@[inline]
|
| 532 |
+
def ofDaysSinceUNIXEpoch (days : Day.Offset) (time : PlainTime) (zt : TimeZone.ZoneRules) : ZonedDateTime :=
|
| 533 |
+
ZonedDateTime.ofPlainDateTime (PlainDateTime.ofDaysSinceUNIXEpoch days time) zt
|
| 534 |
+
|
| 535 |
+
instance : HAdd ZonedDateTime Day.Offset ZonedDateTime where
|
| 536 |
+
hAdd := addDays
|
| 537 |
+
|
| 538 |
+
instance : HSub ZonedDateTime Day.Offset ZonedDateTime where
|
| 539 |
+
hSub := subDays
|
| 540 |
+
|
| 541 |
+
instance : HAdd ZonedDateTime Week.Offset ZonedDateTime where
|
| 542 |
+
hAdd := addWeeks
|
| 543 |
+
|
| 544 |
+
instance : HSub ZonedDateTime Week.Offset ZonedDateTime where
|
| 545 |
+
hSub := subWeeks
|
| 546 |
+
|
| 547 |
+
instance : HAdd ZonedDateTime Hour.Offset ZonedDateTime where
|
| 548 |
+
hAdd := addHours
|
| 549 |
+
|
| 550 |
+
instance : HSub ZonedDateTime Hour.Offset ZonedDateTime where
|
| 551 |
+
hSub := subHours
|
| 552 |
+
|
| 553 |
+
instance : HAdd ZonedDateTime Minute.Offset ZonedDateTime where
|
| 554 |
+
hAdd := addMinutes
|
| 555 |
+
|
| 556 |
+
instance : HSub ZonedDateTime Minute.Offset ZonedDateTime where
|
| 557 |
+
hSub := subMinutes
|
| 558 |
+
|
| 559 |
+
instance : HAdd ZonedDateTime Second.Offset ZonedDateTime where
|
| 560 |
+
hAdd := addSeconds
|
| 561 |
+
|
| 562 |
+
instance : HSub ZonedDateTime Second.Offset ZonedDateTime where
|
| 563 |
+
hSub := subSeconds
|
| 564 |
+
|
| 565 |
+
instance : HAdd ZonedDateTime Millisecond.Offset ZonedDateTime where
|
| 566 |
+
hAdd := addMilliseconds
|
| 567 |
+
|
| 568 |
+
instance : HSub ZonedDateTime Millisecond.Offset ZonedDateTime where
|
| 569 |
+
hSub := subMilliseconds
|
| 570 |
+
|
| 571 |
+
instance : HAdd ZonedDateTime Nanosecond.Offset ZonedDateTime where
|
| 572 |
+
hAdd := addNanoseconds
|
| 573 |
+
|
| 574 |
+
instance : HSub ZonedDateTime Nanosecond.Offset ZonedDateTime where
|
| 575 |
+
hSub := subNanoseconds
|
| 576 |
+
|
| 577 |
+
instance : HSub ZonedDateTime ZonedDateTime Duration where
|
| 578 |
+
hSub x y := x.toTimestamp - y.toTimestamp
|
| 579 |
+
|
| 580 |
+
instance : HAdd ZonedDateTime Duration ZonedDateTime where
|
| 581 |
+
hAdd x y := x.addNanoseconds y.toNanoseconds
|
| 582 |
+
|
| 583 |
+
instance : HSub ZonedDateTime Duration ZonedDateTime where
|
| 584 |
+
hSub x y := x.subNanoseconds y.toNanoseconds
|
| 585 |
+
|
| 586 |
+
end ZonedDateTime
|
| 587 |
+
end Time
|
| 588 |
+
end Std
|
backend/db/models.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
from sqlalchemy import Column, Integer, String, Text, ForeignKey, Index, UniqueConstraint
|
| 4 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 5 |
+
from sqlalchemy.sql import func
|
| 6 |
+
from sqlalchemy import DateTime
|
| 7 |
+
|
| 8 |
+
Base = declarative_base()
|
| 9 |
+
|
| 10 |
+
class Universe(Base): # type: ignore[valid-type]
|
| 11 |
+
"""Universe model: represents a mathematical universe."""
|
| 12 |
+
__tablename__ = 'universes'
|
| 13 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 14 |
+
name = Column(String, unique=True, index=True)
|
| 15 |
+
description = Column(Text)
|
| 16 |
+
universe_type = Column(String, default="generic", index=True) # e.g., group theory, geometry
|
| 17 |
+
version = Column(Integer, default=1)
|
| 18 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 19 |
+
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
| 20 |
+
|
| 21 |
+
__table_args__ = (
|
| 22 |
+
UniqueConstraint('name', 'version', name='uix_universe_name_version'),
|
| 23 |
+
Index('ix_universe_type_version', 'universe_type', 'version'),
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
class Axiom(Base): # type: ignore[valid-type]
|
| 27 |
+
"""Axiom model: represents an axiom in a universe."""
|
| 28 |
+
__tablename__ = 'axioms'
|
| 29 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 30 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 31 |
+
statement = Column(Text, index=True)
|
| 32 |
+
is_active = Column(Integer, default=1, index=True) # for axiom evolution
|
| 33 |
+
parent_axiom_id = Column(Integer, ForeignKey('axioms.id'), nullable=True, index=True) # lineage tracking
|
| 34 |
+
version = Column(Integer, default=1)
|
| 35 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 36 |
+
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
| 37 |
+
|
| 38 |
+
__table_args__ = (
|
| 39 |
+
Index('ix_axiom_universe_id_is_active', 'universe_id', 'is_active'),
|
| 40 |
+
Index('ix_axiom_parent_axiom_id', 'parent_axiom_id'),
|
| 41 |
+
UniqueConstraint('universe_id', 'statement', 'version', name='uix_axiom_universe_statement_version'),
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
class Theorem(Base): # type: ignore[valid-type]
|
| 45 |
+
"""Theorem model: represents a derived theorem."""
|
| 46 |
+
__tablename__ = 'theorems'
|
| 47 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 48 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 49 |
+
statement = Column(Text, index=True)
|
| 50 |
+
proof = Column(Text)
|
| 51 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 52 |
+
|
| 53 |
+
__table_args__ = (
|
| 54 |
+
Index('ix_theorem_universe_id_statement', 'universe_id', 'statement'),
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
class Proof(Base): # type: ignore[valid-type]
|
| 58 |
+
"""Proof model: represents a proof for an axiom."""
|
| 59 |
+
__tablename__ = 'proofs'
|
| 60 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 61 |
+
axiom_id = Column(Integer, ForeignKey('axioms.id'), index=True)
|
| 62 |
+
content = Column(Text)
|
| 63 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 64 |
+
|
| 65 |
+
__table_args__ = (
|
| 66 |
+
Index('ix_proof_axiom_id', 'axiom_id'),
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
class AnalysisResult(Base): # type: ignore[valid-type]
|
| 70 |
+
"""AnalysisResult model: stores analysis results for universes."""
|
| 71 |
+
__tablename__ = 'analysis_results'
|
| 72 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 73 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 74 |
+
result = Column(Text)
|
| 75 |
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
| 76 |
+
|
| 77 |
+
__table_args__ = (
|
| 78 |
+
Index('ix_analysisresult_universe_id', 'universe_id'),
|
| 79 |
+
)
|
| 80 |
+
|
backend/db/models.py.bak
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
from sqlalchemy import Column, Integer, String, Text, ForeignKey, Index, UniqueConstraint
|
| 4 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 5 |
+
|
| 6 |
+
Base = declarative_base()
|
| 7 |
+
|
| 8 |
+
class Universe(Base):
|
| 9 |
+
__tablename__ = 'universes'
|
| 10 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 11 |
+
name = Column(String, unique=True, index=True)
|
| 12 |
+
description = Column(Text)
|
| 13 |
+
universe_type = Column(String, default="generic", index=True) # e.g., group theory, geometry
|
| 14 |
+
version = Column(Integer, default=1)
|
| 15 |
+
created_at = Column(String)
|
| 16 |
+
|
| 17 |
+
__table_args__ = (
|
| 18 |
+
UniqueConstraint('name', 'version', name='uix_universe_name_version'),
|
| 19 |
+
Index('ix_universe_type_version', 'universe_type', 'version'),
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
class Axiom(Base):
|
| 23 |
+
__tablename__ = 'axioms'
|
| 24 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 25 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 26 |
+
statement = Column(Text, index=True)
|
| 27 |
+
is_active = Column(Integer, default=1, index=True) # for axiom evolution
|
| 28 |
+
parent_axiom_id = Column(Integer, ForeignKey('axioms.id'), nullable=True, index=True) # lineage tracking
|
| 29 |
+
version = Column(Integer, default=1)
|
| 30 |
+
created_at = Column(String)
|
| 31 |
+
|
| 32 |
+
__table_args__ = (
|
| 33 |
+
Index('ix_axiom_universe_id_is_active', 'universe_id', 'is_active'),
|
| 34 |
+
Index('ix_axiom_parent_axiom_id', 'parent_axiom_id'),
|
| 35 |
+
UniqueConstraint('universe_id', 'statement', 'version', name='uix_axiom_universe_statement_version'),
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
class Theorem(Base):
|
| 39 |
+
__tablename__ = 'theorems'
|
| 40 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 41 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 42 |
+
statement = Column(Text, index=True)
|
| 43 |
+
proof = Column(Text)
|
| 44 |
+
|
| 45 |
+
__table_args__ = (
|
| 46 |
+
Index('ix_theorem_universe_id_statement', 'universe_id', 'statement'),
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
class Proof(Base):
|
| 50 |
+
__tablename__ = 'proofs'
|
| 51 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 52 |
+
axiom_id = Column(Integer, ForeignKey('axioms.id'), index=True)
|
| 53 |
+
content = Column(Text)
|
| 54 |
+
|
| 55 |
+
__table_args__ = (
|
| 56 |
+
Index('ix_proof_axiom_id', 'axiom_id'),
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
class AnalysisResult(Base):
|
| 60 |
+
__tablename__ = 'analysis_results'
|
| 61 |
+
id = Column(Integer, primary_key=True, index=True)
|
| 62 |
+
universe_id = Column(Integer, ForeignKey('universes.id'), index=True)
|
| 63 |
+
result = Column(Text)
|
| 64 |
+
|
| 65 |
+
__table_args__ = (
|
| 66 |
+
Index('ix_analysisresult_universe_id', 'universe_id'),
|
| 67 |
+
)
|
backend/db/session.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sqlalchemy import create_engine
|
| 2 |
+
from sqlalchemy.orm import sessionmaker
|
| 3 |
+
from backend.config import DB_URL
|
| 4 |
+
|
| 5 |
+
engine = create_engine(DB_URL, connect_args={"check_same_thread": False})
|
| 6 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def get_db():
|
| 10 |
+
"""
|
| 11 |
+
Dependency for FastAPI routes: yields a SQLAlchemy session.
|
| 12 |
+
Usage: add as a dependency in route functions.
|
| 13 |
+
"""
|
| 14 |
+
db = SessionLocal()
|
| 15 |
+
try:
|
| 16 |
+
yield db
|
| 17 |
+
finally:
|
| 18 |
+
db.close()
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
from contextlib import contextmanager
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@contextmanager
|
| 25 |
+
def session_scope():
|
| 26 |
+
"""
|
| 27 |
+
Context manager for non-FastAPI scripts:
|
| 28 |
+
with session_scope() as db:
|
| 29 |
+
...
|
| 30 |
+
Commits on success, rolls back on exception.
|
| 31 |
+
"""
|
| 32 |
+
db = SessionLocal()
|
| 33 |
+
try:
|
| 34 |
+
yield db
|
| 35 |
+
db.commit()
|
| 36 |
+
except Exception:
|
| 37 |
+
db.rollback()
|
| 38 |
+
raise
|
| 39 |
+
finally:
|
| 40 |
+
db.close()
|
backend/tests/test_analysis.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.core.cross_universe_analysis import CrossUniverseAnalyzer
|
| 2 |
+
from backend.core.universe_generator import UniverseGenerator
|
| 3 |
+
from backend.db.session import SessionLocal
|
| 4 |
+
|
| 5 |
+
def test_shared_axioms_and_theorems():
|
| 6 |
+
db = SessionLocal()
|
| 7 |
+
generator = UniverseGenerator(db)
|
| 8 |
+
# Create universes with shared and unique axioms/theorems
|
| 9 |
+
u1 = generator.create_universe("U1", "Universe 1", "generic", ["A1", "A2", "A3"])
|
| 10 |
+
u2 = generator.create_universe("U2", "Universe 2", "generic", ["A2", "A3", "A4"])
|
| 11 |
+
u3 = generator.create_universe("U3", "Universe 3", "generic", ["A3", "A4", "A5"])
|
| 12 |
+
# Add theorems
|
| 13 |
+
generator.create_theorem(u1.id, "T1", "Proof1")
|
| 14 |
+
generator.create_theorem(u2.id, "T1", "Proof2")
|
| 15 |
+
generator.create_theorem(u3.id, "T2", "Proof3")
|
| 16 |
+
analyzer = CrossUniverseAnalyzer(db)
|
| 17 |
+
result = analyzer.analyze([u1.id, u2.id, u3.id])
|
| 18 |
+
assert "A3" in result["shared_axioms"]
|
| 19 |
+
assert "T1" in result["shared_theorems"]
|
| 20 |
+
db.close()
|
backend/tests/test_axiom.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.core.universe_generator import UniverseGenerator
|
| 2 |
+
from backend.db.session import SessionLocal
|
| 3 |
+
|
| 4 |
+
def test_axiom_evolution():
|
| 5 |
+
db = SessionLocal()
|
| 6 |
+
generator = UniverseGenerator(db)
|
| 7 |
+
universe = generator.create_universe("Evo Universe", "For axiom evolution", "generic", ["Axiom X"])
|
| 8 |
+
axiom = generator.add_axiom(universe.id, "Axiom Y")
|
| 9 |
+
evolved_axiom = generator.evolve_axiom(axiom.id, "Axiom Y evolved")
|
| 10 |
+
assert evolved_axiom.statement == "Axiom Y evolved"
|
| 11 |
+
assert evolved_axiom.parent_axiom_id == axiom.id
|
| 12 |
+
db.close()
|
backend/tests/test_db.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.db.session import engine
|
| 2 |
+
from backend.db.models import Base
|
| 3 |
+
|
| 4 |
+
def test_db_connection():
|
| 5 |
+
try:
|
| 6 |
+
Base.metadata.create_all(bind=engine)
|
| 7 |
+
assert True
|
| 8 |
+
except Exception as e:
|
| 9 |
+
assert False, f"DB connection failed: {e}"
|
backend/tests/test_health.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi.testclient import TestClient
|
| 2 |
+
from backend.app import app
|
| 3 |
+
|
| 4 |
+
def test_health():
|
| 5 |
+
client = TestClient(app)
|
| 6 |
+
response = client.get("/health")
|
| 7 |
+
assert response.status_code == 200
|
| 8 |
+
assert response.json() == {"status": "ok"}
|
backend/tests/test_theorem.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.core.universe_generator import UniverseGenerator
|
| 2 |
+
from backend.core.theorem_engine import TheoremEngine
|
| 3 |
+
from backend.db.session import SessionLocal
|
| 4 |
+
|
| 5 |
+
def test_theorem_derivation():
|
| 6 |
+
db = SessionLocal()
|
| 7 |
+
generator = UniverseGenerator(db)
|
| 8 |
+
universe = generator.create_universe("Thm Universe", "For theorem derivation", "generic", ["Closure", "Associativity"])
|
| 9 |
+
axiom1 = generator.add_axiom(universe.id, "Closure")
|
| 10 |
+
axiom2 = generator.add_axiom(universe.id, "Associativity")
|
| 11 |
+
engine = TheoremEngine(db)
|
| 12 |
+
theorem = engine.derive_theorem(universe.id, [axiom1.id, axiom2.id], "Closure Associativity")
|
| 13 |
+
assert theorem.statement == "Closure Associativity"
|
| 14 |
+
assert "Derived from axioms" in theorem.proof
|
| 15 |
+
db.close()
|
backend/tests/test_universe.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from backend.core.universe_generator import UniverseGenerator
|
| 2 |
+
from backend.db.session import SessionLocal
|
| 3 |
+
|
| 4 |
+
def test_create_universe():
|
| 5 |
+
db = SessionLocal()
|
| 6 |
+
generator = UniverseGenerator(db)
|
| 7 |
+
universe = generator.create_universe("Test Universe", "A test universe", "generic", ["Axiom 1", "Axiom 2"])
|
| 8 |
+
assert universe.name == "Test Universe"
|
| 9 |
+
assert universe.universe_type == "generic"
|
| 10 |
+
axioms = db.query(generator.db.models.Axiom).filter_by(universe_id=universe.id).all()
|
| 11 |
+
assert len(axioms) == 2
|
| 12 |
+
db.close()
|
backend/tools/requirements_audit.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Lightweight requirements auditor for the alphageometry repo.
|
| 2 |
+
|
| 3 |
+
This script looks for file patterns, imports and keywords to estimate which of
|
| 4 |
+
the requested components are present, partially implemented, or missing.
|
| 5 |
+
|
| 6 |
+
Usage: python -m backend.tools.requirements_audit
|
| 7 |
+
"""
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
import re
|
| 10 |
+
import json
|
| 11 |
+
import sys
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
ROOT = Path(__file__).resolve().parents[2]
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
COMPONENTS = {
|
| 18 |
+
"vector_db": [r"\bfaiss\b", r"vector_store", r"milvus", r"pinecone"],
|
| 19 |
+
"pytorch_models": [r"\bimport torch\b", r"torch\.nn", r"torch_geometric"],
|
| 20 |
+
"quantum": [r"\bqiskit\b", r"\bcirq\b", r"TensorNetwork", r"cuQuantum"],
|
| 21 |
+
"formal_prover": [r"\blean\b", r"\bcoq\b", r"lean-toolchain", r"theorem prover"],
|
| 22 |
+
"neo4j": [r"\bneo4j\b", r"py2neo", r"GraphDatabase"],
|
| 23 |
+
"orchestration": [r"\bray\b", r"\bdask\b", r"kubernetes", r"k8s", r"Dockerfile"],
|
| 24 |
+
"apis": [r"fastapi", r"graphql", r"gql", r"grpc"],
|
| 25 |
+
"security": [r"jwt", r"jose", r"oauth", r"rbac", r"encryption"],
|
| 26 |
+
"object_store": [r"s3", r"boto3", r"object store", r"minio"],
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def scan_files(root: Path, patterns):
|
| 31 |
+
hits = {}
|
| 32 |
+
for comp, pats in patterns.items():
|
| 33 |
+
hits[comp] = {"matches": [], "files": []}
|
| 34 |
+
for p in root.rglob("**/*.py"):
|
| 35 |
+
try:
|
| 36 |
+
text = p.read_text(encoding="utf-8")
|
| 37 |
+
except Exception:
|
| 38 |
+
continue
|
| 39 |
+
for comp, pats in patterns.items():
|
| 40 |
+
for pat in pats:
|
| 41 |
+
if re.search(pat, text, re.IGNORECASE):
|
| 42 |
+
hits[comp]["matches"].append(pat)
|
| 43 |
+
hits[comp]["files"].append(str(p.relative_to(root)))
|
| 44 |
+
# also check for manifest files
|
| 45 |
+
for fname in ["Dockerfile", "docker-compose.yml", "lean-toolchain", "k8s", "helm"]:
|
| 46 |
+
f = root / fname
|
| 47 |
+
if f.exists():
|
| 48 |
+
# attribute to orchestration
|
| 49 |
+
hits.setdefault("orchestration", {"matches": [], "files": []})
|
| 50 |
+
hits["orchestration"]["matches"].append(fname)
|
| 51 |
+
hits["orchestration"]["files"].append(str(f.relative_to(root)))
|
| 52 |
+
return hits
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def summarize(hits):
|
| 56 |
+
summary = {}
|
| 57 |
+
for comp, info in hits.items():
|
| 58 |
+
present = len(info.get("files", [])) > 0
|
| 59 |
+
summary[comp] = {
|
| 60 |
+
"present": present,
|
| 61 |
+
"files": sorted(set(info.get("files", [])))[:10],
|
| 62 |
+
"evidence": sorted(set(info.get("matches", [])))[:10],
|
| 63 |
+
}
|
| 64 |
+
return summary
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def run():
|
| 68 |
+
print(f"Scanning repository at: {ROOT}")
|
| 69 |
+
hits = scan_files(ROOT, COMPONENTS)
|
| 70 |
+
report = summarize(hits)
|
| 71 |
+
print(json.dumps(report, indent=2))
|
| 72 |
+
# human-friendly summary
|
| 73 |
+
print("\nHuman summary:")
|
| 74 |
+
for comp, info in report.items():
|
| 75 |
+
status = "FOUND" if info["present"] else "MISSING"
|
| 76 |
+
print(f" - {comp}: {status}")
|
| 77 |
+
if info["present"]:
|
| 78 |
+
print(f" files: {info['files']}")
|
| 79 |
+
print(f" evidence: {info['evidence']}")
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
if __name__ == "__main__":
|
| 83 |
+
run()
|