Skip to content

Commit f5550db

Browse files
committed
some more documentation tweaks
1 parent 935428d commit f5550db

File tree

6 files changed

+82
-62
lines changed

6 files changed

+82
-62
lines changed

examples/simple/src/simple.gleam

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,30 @@ pub fn main() {
99
let now = timestamp.system_time()
1010

1111
// Load the database from the operating system
12-
let db = database.load_from_os()
13-
14-
case tzcalendar.get_time_and_zone(now, "America/New_York", db) {
15-
Ok(time_and_zone) -> {
16-
// Successfully converted time to the requested time zone
17-
io.println(
18-
int.to_string(time_and_zone.time_of_day.hours)
19-
|> string.pad_start(2, "0")
20-
<> ":"
21-
<> int.to_string(time_and_zone.time_of_day.minutes)
22-
|> string.pad_start(2, "0")
23-
<> ":"
24-
<> int.to_string(time_and_zone.time_of_day.seconds)
25-
|> string.pad_start(2, "0")
26-
<> " "
27-
<> time_and_zone.designation,
28-
)
12+
case database.load_from_os() {
13+
Error(Nil) ->
14+
io.println("No parsable TZif files found in default location.")
15+
Ok(db) -> {
16+
case tzcalendar.get_time_and_zone(now, "America/New_York", db) {
17+
Ok(time_and_zone) -> {
18+
// Successfully converted time to the requested time zone
19+
io.println(
20+
int.to_string(time_and_zone.time_of_day.hours)
21+
|> string.pad_start(2, "0")
22+
<> ":"
23+
<> int.to_string(time_and_zone.time_of_day.minutes)
24+
|> string.pad_start(2, "0")
25+
<> ":"
26+
<> int.to_string(time_and_zone.time_of_day.seconds)
27+
|> string.pad_start(2, "0")
28+
<> " "
29+
<> time_and_zone.designation,
30+
)
31+
}
32+
Error(database.ZoneNameNotFound) -> io.println("Time zone not found")
33+
Error(database.InfoNotFound) ->
34+
io.println("Error processing time zone conversion")
35+
}
2936
}
30-
Error(database.ZoneNotFound) -> io.println("Time zone not found")
31-
Error(database.ProcessingError) ->
32-
io.println("Error processing time zone conversion")
3337
}
3438
}

examples/time_now/src/time_now.gleam

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import gleam/int
22
import gleam/io
33
import gleam/list
4+
import gleam/result
45
import gleam/string
56
import gleam/time/calendar
67
import gleam/time/timestamp
@@ -15,10 +16,8 @@ pub fn main() {
1516
}
1617
}
1718

18-
fn print_all_times(
19-
now: timestamp.Timestamp,
20-
) -> Result(Nil, database.TzDatabaseError) {
21-
let db = database.load_from_os()
19+
fn print_all_times(now: timestamp.Timestamp) -> Result(Nil, Nil) {
20+
use db <- result.map(database.load_from_os())
2221

2322
database.get_available_timezones(db)
2423
|> list.map(fn(zone_name) {
@@ -31,7 +30,7 @@ fn print_all_times(
3130
io.println(string.pad_end(zone_name <> ":", 40, " ") <> "ERROR")
3231
}
3332
})
34-
Ok(Nil)
33+
Nil
3534
}
3635

3736
fn format_time(tiz: tzcalendar.TimeAndZone) -> String {

src/tzif/database.gleam

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
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+
15
import filepath
26
import gleam/dict
37
import gleam/list
@@ -22,24 +26,26 @@ pub opaque type TzDatabase {
2226

2327
/// TzDatabase error types
2428
pub 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.
183195
pub 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) {

src/tzif/tzcalendar.gleam

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub type TimeAndZone {
4848
/// import tzif/database
4949
///
5050
/// let ts = timestamp.from_unix_seconds(1_758_223_300)
51-
/// let db = database.load_from_os()
51+
/// let assert Ok(db) = database.load_from_os()
5252
///
5353
/// get_time_and_zone(ts, "America/New_York", db)
5454
/// // Ok(TimeInZone(
@@ -92,7 +92,7 @@ pub fn get_time_and_zone(
9292
/// import tzif/database
9393
///
9494
/// let ts = timestamp.from_unix_seconds(1_758_223_300)
95-
/// let db = database.load_from_os()
95+
/// let assert Ok(db) = database.load_from_os()
9696
///
9797
/// to_calendar(ts, "America/New_York", db)
9898
/// // Ok(#(

src/tzif/tzparser.gleam

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ pub type TzFileError {
2323
/// Unexpected file format version
2424
HeaderVersionError
2525

26-
/// Error parsing fields within body of file
26+
/// Error parsing fields not specified elsewhere from the body of a TZif file.
2727
BodyParseError
2828

29-
/// Error parsing an integer
30-
IntegerParseError
29+
/// Transition time parsing error
30+
TransitionTimeParseError
31+
32+
/// TtInfo parsing error
33+
TtInfoParseError
3134

3235
/// Error Parsing leap second section
3336
LeapSecondParseError
@@ -160,14 +163,14 @@ fn parse_section(
160163
header.timecnt,
161164
fields,
162165
[],
163-
integer_parser(integer_size),
166+
integer_parser(integer_size, TransitionTimeParseError),
164167
)
165168
// Get the ttinfo index associated with each transition time
166169
use ttinfo_indecies, remain <- parse_list(
167170
header.timecnt,
168171
remain,
169172
[],
170-
unsigned_integer_parser(8),
173+
unsigned_integer_parser(8, TransitionTimeParseError),
171174
)
172175

173176
// Get the ttinfo structures used in this time zone
@@ -212,15 +215,15 @@ fn parse_section(
212215
header.ttisstdcnt,
213216
remain,
214217
[],
215-
unsigned_integer_parser(8),
218+
unsigned_integer_parser(8, BodyParseError),
216219
)
217220

218221
// Booleans to indicate if these are UT or local indicators
219222
use ut_local_indicators, remain <- parse_list(
220223
header.ttisutcnt,
221224
remain,
222225
[],
223-
unsigned_integer_parser(8),
226+
unsigned_integer_parser(8, BodyParseError),
224227
)
225228

226229
next(
@@ -257,9 +260,9 @@ fn parse_ttinfo(
257260
bits: BitArray,
258261
next: fn(TtInfo, BitArray) -> Result(a, TzFileError),
259262
) -> Result(a, TzFileError) {
260-
use utoff, bits <- integer_parser(32)(bits)
261-
use isdst, bits <- unsigned_integer_parser(8)(bits)
262-
use desigidx, bits <- unsigned_integer_parser(8)(bits)
263+
use utoff, bits <- integer_parser(32, TtInfoParseError)(bits)
264+
use isdst, bits <- unsigned_integer_parser(8, TtInfoParseError)(bits)
265+
use desigidx, bits <- unsigned_integer_parser(8, TtInfoParseError)(bits)
263266

264267
let ttinfo = TtInfo(utoff, isdst, desigidx)
265268

@@ -268,24 +271,26 @@ fn parse_ttinfo(
268271

269272
fn integer_parser(
270273
bit_size: Int,
274+
error_to_pass: TzFileError,
271275
) -> fn(BitArray, fn(Int, BitArray) -> Result(a, TzFileError)) ->
272276
Result(a, TzFileError) {
273277
fn(bits: BitArray, next: fn(Int, BitArray) -> Result(a, TzFileError)) {
274278
case bits {
275279
<<i:signed-int-big-size(bit_size), rest:bits>> -> next(i, rest)
276-
_ -> Error(IntegerParseError)
280+
_ -> Error(error_to_pass)
277281
}
278282
}
279283
}
280284

281285
fn unsigned_integer_parser(
282286
bit_size: Int,
287+
error_to_pass: TzFileError,
283288
) -> fn(BitArray, fn(Int, BitArray) -> Result(a, TzFileError)) ->
284289
Result(a, TzFileError) {
285290
fn(bits: BitArray, next: fn(Int, BitArray) -> Result(a, TzFileError)) {
286291
case bits {
287292
<<i:unsigned-int-big-size(bit_size), rest:bits>> -> next(i, rest)
288-
_ -> Error(IntegerParseError)
293+
_ -> Error(error_to_pass)
289294
}
290295
}
291296
}

test/tzif/database_test.gleam

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,16 @@ pub fn has_leap_second_test() {
119119
assert database.has_leap_second_data("UTC", db) == Ok(False)
120120
assert database.has_leap_second_data("right/UTC", db) == Ok(True)
121121
assert database.has_leap_second_data("Invalid", db)
122-
== Error(database.ZoneNotFound)
122+
== Error(database.ZoneNameNotFound)
123123
}
124124

125125
pub fn leap_second_check() {
126126
let db = get_database()
127127

128128
assert database.leap_seconds(timestamp.from_unix_seconds(0), "UTC", db)
129-
== Error(database.ProcessingError)
129+
== Error(database.InfoNotFound)
130130
assert database.leap_seconds(timestamp.from_unix_seconds(0), "Invalid", db)
131-
== Error(database.ZoneNotFound)
131+
== Error(database.ZoneNameNotFound)
132132

133133
let historical_leap_seconds =
134134
[

0 commit comments

Comments
 (0)