ZAIDX11 commited on
Commit
63d3857
·
verified ·
1 Parent(s): 6519678

Add files using upload-large-folder tool

Browse files
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()