1+ //// These types and functions are designed to let you retrieve information
2+ //// from TZif formatted data you provide, or timezone data loaded from the
3+ //// operating system.
4+
15import filepath
26import gleam/dict
37import gleam/list
@@ -22,24 +26,26 @@ pub opaque type TzDatabase {
2226
2327/// TzDatabase error types
2428pub type TzDatabaseError {
25- /// There is no information available for this zone.
26- ZoneNotFound
27- /// Unable to provide zone information due to missing or
28- /// incomplete data .
29- ProcessingError
29+ /// The zone was not found in the database
30+ ZoneNameNotFound
31+ /// Information, including UTC offset, zone designation, or leap
32+ /// second information was not found for this time zone .
33+ InfoNotFound
3034}
3135
3236/// Load time zone database from default operating system location
33- /// which is typically "/usr/share/zoneinfo".
34- pub fn load_from_os ( ) {
37+ /// which is typically "/usr/share/zoneinfo". If no parsable TZif
38+ /// files were found, returns `Error(Nil)`.
39+ pub fn load_from_os ( ) -> Result ( TzDatabase , Nil ) {
3540 load_from_path ( "/usr/share/zoneinfo" )
3641}
3742
3843/// Load time zone database from provided directory. This is
3944/// useful if you have compiled your own version of the [IANA
4045/// time zone database](https://data.iana.org/time-zones/tz-link.html)
41- /// or they are not stored in the standard location.
42- pub fn load_from_path ( path : String ) {
46+ /// or they are not stored in the standard location. If no
47+ /// parsable TZif files were found, returns `Error(Nil)`.
48+ pub fn load_from_path ( path : String ) -> Result ( TzDatabase , Nil ) {
4349 let parts = filepath . split ( path )
4450 let drop_number = list . length ( parts )
4551 let assert Ok ( filenames ) = simplifile . get_files ( path )
@@ -49,10 +55,16 @@ pub fn load_from_path(path: String) {
4955 |> list . map ( process_tzfile ( _, drop_number ) )
5056 |> result . values
5157
52- TzDatabase (
53- list . map ( data , fn ( v ) { v . 0 } ) |> list . sort ( string . compare ) ,
54- dict . from_list ( data ) ,
55- )
58+ // If no parsable zone files were found return an Error rather than
59+ // fail silently.
60+ case list . length ( data ) {
61+ 0 -> Error ( Nil )
62+ _ ->
63+ Ok ( TzDatabase (
64+ list . map ( data , fn ( v ) { v . 0 } ) |> list . sort ( string . compare ) ,
65+ dict . from_list ( data ) ,
66+ ) )
67+ }
5668}
5769
5870/// Create new empty TzDatabase. This can be useful if you
@@ -130,7 +142,7 @@ pub type ZoneParameters {
130142/// import gleam/time/timestamp
131143///
132144/// let ts = timestamp.from_unix_seconds(1_758_223_300)
133- /// let db = database.load_from_os()
145+ /// let assert Ok(db) = database.load_from_os()
134146///
135147/// get_zone_parameters(ts, "America/New_York", db)
136148/// // Ok(ZoneParameters(
@@ -145,14 +157,14 @@ pub fn get_zone_parameters(
145157 db : TzDatabase ,
146158) -> Result ( ZoneParameters , TzDatabaseError ) {
147159 use tzdata <- result . try (
148- dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNotFound ) ,
160+ dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNameNotFound ) ,
149161 )
150162 let default = default_slice ( tzdata . fields )
151163
152164 let slices = create_slices ( tzdata . fields )
153165
154166 use slice <- result . try (
155- get_slice ( ts , slices , default ) |> result . replace_error ( ProcessingError ) ,
167+ get_slice ( ts , slices , default ) |> result . replace_error ( InfoNotFound ) ,
156168 )
157169
158170 Ok ( ZoneParameters ( slice . utoff , slice . isdst , slice . designation ) )
@@ -167,7 +179,7 @@ pub fn has_leap_second_data(
167179 db : TzDatabase ,
168180) -> Result ( Bool , TzDatabaseError ) {
169181 use tzdata <- result . try (
170- dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNotFound ) ,
182+ dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNameNotFound ) ,
171183 )
172184
173185 case list . length ( tzdata . fields . leapsecond_values ) {
@@ -178,21 +190,21 @@ pub fn has_leap_second_data(
178190
179191/// Find the number of leap seconds at a given `Timestamp` ts using the
180192/// given time zone. Not all time zones have leap second data, and if there
181- /// is no data present, this function will return a `ProcessingError `.
193+ /// is no data present, this function will return `Error(InfoNotFound) `.
182194/// Typically the "right/UTC" time zone will have leap second data.
183195pub fn leap_seconds (
184196 ts : timestamp . Timestamp ,
185197 zone_name : String ,
186198 db : TzDatabase ,
187199) -> Result ( Int , TzDatabaseError ) {
188200 use tzdata <- result . try (
189- dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNotFound ) ,
201+ dict . get ( db . zone_data , zone_name ) |> result . replace_error ( ZoneNameNotFound ) ,
190202 )
191203
192204 let # ( ts_seconds , _ ) = timestamp . to_unix_seconds_and_nanoseconds ( ts )
193205
194206 case list . length ( tzdata . fields . leapsecond_values ) {
195- 0 -> Error ( ProcessingError )
207+ 0 -> Error ( InfoNotFound )
196208 _ -> {
197209 tzdata . fields . leapsecond_values
198210 |> list . fold_until ( Ok ( 0 ) , fn ( acc , leap_second_info ) {
0 commit comments