Skip to content

Commit 0983e8d

Browse files
Add Time::Location.load? (#16121)
1 parent 468d0aa commit 0983e8d

File tree

3 files changed

+95
-5
lines changed

3 files changed

+95
-5
lines changed

spec/std/time/location_spec.cr

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,67 @@ class Time::Location
198198
end
199199
end
200200

201+
describe ".load?" do
202+
it "loads Europe/Berlin" do
203+
with_zoneinfo do
204+
Time::Location.load?("Europe/Berlin").should eq Time::Location.load("Europe/Berlin")
205+
end
206+
end
207+
208+
it "returns nil if unavailable" do
209+
Location.load?("Foobar/Baz").should be_nil
210+
211+
with_zoneinfo(datapath("zoneinfo")) do
212+
Location.load?("Foobar/Baz").should be_nil
213+
end
214+
end
215+
216+
it "invalid zone file" do
217+
expect_raises(Time::Location::InvalidTZDataError) do
218+
Location.load?("Foo/invalid", [datapath("zoneinfo")])
219+
end
220+
end
221+
222+
it "treats UTC as special case" do
223+
with_zoneinfo do
224+
Location.load?("UTC").should eq Location::UTC
225+
Location.load?("").should eq Location::UTC
226+
Location.load?("Etc/UTC").should eq Location::UTC
227+
end
228+
end
229+
230+
describe "raises on invalid location name" do
231+
it "absolute path" do
232+
with_zoneinfo do
233+
expect_raises(InvalidLocationNameError) do
234+
Location.load?("/America/New_York")
235+
end
236+
expect_raises(InvalidLocationNameError) do
237+
Location.load?("\\Zulu")
238+
end
239+
end
240+
end
241+
242+
it "dot dot" do
243+
with_zoneinfo do
244+
expect_raises(InvalidLocationNameError) do
245+
Location.load?("../zoneinfo/America/New_York")
246+
end
247+
expect_raises(InvalidLocationNameError) do
248+
Location.load?("a..")
249+
end
250+
end
251+
end
252+
end
253+
254+
it "caches result" do
255+
with_zoneinfo do
256+
location = Location.load?("Europe/Berlin")
257+
Location.load?("Europe/Berlin").should be location
258+
end
259+
end
260+
end
261+
201262
describe ".load_android" do
202263
it "loads Europe/Berlin" do
203264
Location.__clear_location_cache

src/time/location.cr

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,26 @@ class Time::Location
299299
# Files are cached based on the modification time, so subsequent request for
300300
# the same location name will most likely return the same instance of
301301
# `Location`, unless the time zone database has been updated in between.
302+
#
303+
# - `.load?` returns `nil` if the location is unavailable.
302304
def self.load(name : String) : Location
305+
load?(name) { |source| raise InvalidLocationNameError.new(name, source) }
306+
end
307+
308+
# :ditto:
309+
#
310+
# Returns `nil` if the location is unavailable.
311+
# Raises `InvalidLocationNameError` if the name is invalid.
312+
# Raises `InvalidTZDataError` if the loader encounters a format error in the
313+
# time zone database.
314+
#
315+
# - `.load` raises if the location is unavailable.
316+
def self.load?(name : String) : Location?
317+
load?(name) { return }
318+
end
319+
320+
# :nodoc:
321+
def self.load?(name : String, & : String? ->) : Location?
303322
case name
304323
when "", "UTC", "Etc/UTC"
305324
# `UTC` is a special identifier, empty string represents a fallback mechanism.
@@ -318,11 +337,11 @@ class Time::Location
318337
if location = load_from_dir_or_zip(name, zoneinfo)
319338
return location
320339
else
321-
raise InvalidLocationNameError.new(name, zoneinfo)
340+
yield zoneinfo
322341
end
323342
end
324343

325-
if location = load(name, Crystal::System::Time.zone_sources)
344+
if location = load(name, Crystal::System::Time.zone_sources) { |source| yield source }
326345
return location
327346
end
328347

@@ -341,7 +360,7 @@ class Time::Location
341360
return location
342361
end
343362

344-
raise InvalidLocationNameError.new(name)
363+
yield nil
345364
end
346365
end
347366

src/time/location/loader.cr

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ class Time::Location
2020

2121
# :nodoc:
2222
def self.load(name : String, sources : Enumerable(String)) : Time::Location?
23+
load(name, sources) { |source| raise InvalidLocationNameError.new(name, source) }
24+
end
25+
26+
# :nodoc:
27+
def self.load(name : String, sources : Enumerable(String), &) : Time::Location?
2328
if source = find_zoneinfo_file(name, sources)
24-
load_from_dir_or_zip(name, source) || raise InvalidLocationNameError.new(name, source)
29+
load_from_dir_or_zip(name, source) || yield source
2530
end
2631
end
2732

2833
# :nodoc:
2934
def self.load_android(name : String, sources : Enumerable(String)) : Time::Location?
35+
load_android(name, sources) { |path| raise InvalidLocationNameError.new(name, path) }
36+
end
37+
38+
# :nodoc:
39+
def self.load_android(name : String, sources : Enumerable(String), &) : Time::Location?
3040
if path = find_android_tzdata_file(sources)
31-
load_from_android_tzdata(name, path) || raise InvalidLocationNameError.new(name, path)
41+
load_from_android_tzdata(name, path) || yield path
3242
end
3343
end
3444

0 commit comments

Comments
 (0)