Skip to content

Commit

Permalink
Adapt parser and formatter to support Time::Location
Browse files Browse the repository at this point in the history
  • Loading branch information
straight-shoota committed Nov 24, 2017
1 parent 4a58a3e commit 5ec821b
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 72 deletions.
2 changes: 1 addition & 1 deletion spec/std/http/http_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe HTTP do
it "parses and is local (#2744)" do
date = "Mon, 09 Sep 2011 23:36:00 -0300"
parsed_time = HTTP.parse_time(date).not_nil!
parsed_time.local?.should be_true
parsed_time.offset.should eq -3 * 3600
parsed_time.to_utc.to_s.should eq("2011-09-10 02:36:00 UTC")
end

Expand Down
162 changes: 144 additions & 18 deletions spec/std/time/time_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -263,24 +263,50 @@ describe Time do
t1.epoch_f.should be_close(t1.to_utc.epoch_f, 1e-01)
end

it "to_s" do
t = Time.new 2014, 10, 30, 21, 18, 13
t.to_s.should eq("2014-10-30 21:18:13")
describe "to_s" do
it "prints floating time" do
t = Time.new 2014, 10, 30, 21, 18, 13
t.to_s.should eq("2014-10-30 21:18:13")

t = Time.new 2014, 1, 30, 21, 18, 13
t.to_s.should eq("2014-01-30 21:18:13")

t = Time.new 2014, 1, 30, 21, 18, 13
t.to_s.should eq("2014-01-30 21:18:13")
t = Time.new 2014, 10, 1, 21, 18, 13
t.to_s.should eq("2014-10-01 21:18:13")

t = Time.new 2014, 10, 1, 21, 18, 13
t.to_s.should eq("2014-10-01 21:18:13")
t = Time.new 2014, 10, 30, 1, 18, 13
t.to_s.should eq("2014-10-30 01:18:13")

t = Time.new 2014, 10, 30, 1, 18, 13
t.to_s.should eq("2014-10-30 01:18:13")
t = Time.new 2014, 10, 30, 21, 1, 13
t.to_s.should eq("2014-10-30 21:01:13")

t = Time.new 2014, 10, 30, 21, 1, 13
t.to_s.should eq("2014-10-30 21:01:13")
t = Time.new 2014, 10, 30, 21, 18, 1
t.to_s.should eq("2014-10-30 21:18:01")
end

it "prints without nanoseconds" do
t = Time.new 2014, 10, 30, 21, 18, 13, nanosecond: 12345
t.to_s.should eq("2014-10-30 21:18:13")
end

t = Time.new 2014, 10, 30, 21, 18, 1
t.to_s.should eq("2014-10-30 21:18:01")
it "prints UTC" do
t = Time.utc 2014, 10, 30, 21, 18, 13
t.to_s.should eq("2014-10-30 21:18:13 UTC")
end

it "prints zone" do
location = Time::Location.load("Europe/Berlin")
t = Time.new 2014, 10, 30, 21, 18, 13, location: location
t.to_s.should eq("2014-10-30 21:18:13 +01:00 Europe/Berlin")

t = Time.new 2014, 10, 10, 21, 18, 13, location: location
t.to_s.should eq("2014-10-10 21:18:13 +02:00 Europe/Berlin")
end

it "prints offset" do
t = Time.new 2014, 10, 30, 21, 18, 13, location: Time::Location.fixed(-9000)
t.to_s.should eq("2014-10-30 21:18:13 -02:30")
end
end

it "formats" do
Expand Down Expand Up @@ -334,10 +360,41 @@ describe Time do
t.to_s("%L").to_s.should eq("006")
t.to_s("%N").to_s.should eq("006000000")

floating = Time.new(2017, 11, 24, 13, 5, 6, location: Time::Location::UNSPECIFIED)
expect_raises(Time::FloatingTimeConversionError) do
floating.to_s("%z")
end
expect_raises(Time::FloatingTimeConversionError) do
floating.to_s("%:z")
end
expect_raises(Time::FloatingTimeConversionError) do
floating.to_s("%::z")
end

Time.utc_now.to_s("%z").should eq("+0000")
Time.utc_now.to_s("%:z").should eq("+00:00")
Time.utc_now.to_s("%::z").should eq("+00:00:00")

zoned = Time.new(2017, 11, 24, 13, 5, 6, location: Time::Location.load("Europe/Berlin"))
zoned.to_s("%z").should eq("+0100")
zoned.to_s("%:z").should eq("+01:00")
zoned.to_s("%::z").should eq("+01:00:00")

zoned = Time.new(2017, 11, 24, 13, 5, 6, location: Time::Location.load("America/Buenos_Aires"))
zoned.to_s("%z").should eq("-0300")
zoned.to_s("%:z").should eq("-03:00")
zoned.to_s("%::z").should eq("-03:00:00")

offset = Time.new(2017, 11, 24, 13, 5, 6, location: Time::Location.fixed(9000))
offset.to_s("%z").should eq("+0230")
offset.to_s("%:z").should eq("+02:30")
offset.to_s("%::z").should eq("+02:30:00")

offset = Time.new(2017, 11, 24, 13, 5, 6, location: Time::Location.fixed(9001))
offset.to_s("%z").should eq("+0230")
offset.to_s("%:z").should eq("+02:30")
offset.to_s("%::z").should eq("+02:30:01")

# TODO %N
# TODO %Z

Expand Down Expand Up @@ -436,6 +493,56 @@ describe Time do
it { Time.parse("epoch: 1459864667", "epoch: %s").epoch.should eq(1459864667) }
it { Time.parse("epoch: -1459864667", "epoch: %s").epoch.should eq(-1459864667) }

it "parses timezone" do
patterns = {"%z", "%:z", "%::z"}

{"+0000", "+00:00", "+00:00:00"}.zip(patterns) do |string, pattern|
time = Time.parse(string, pattern)
time.offset.should eq 0
time.utc?.should be_false
time.location.fixed?.should be_true
time.incremental?.should be_true
end

{"-0000", "-00:00", "-00:00:00"}.zip(patterns) do |string, pattern|
time = Time.parse(string, pattern)
time.offset.should eq 0
time.utc?.should be_false
time.location.fixed?.should be_true
time.incremental?.should be_true
end

{"-0200", "-02:00", "-02:00:00"}.zip(patterns) do |string, pattern|
time = Time.parse(string, pattern)
time.offset.should eq -2 * 3600
time.utc?.should be_false
time.location.fixed?.should be_true
time.incremental?.should be_true
end

{"Z", "Z", "Z"}.zip(patterns) do |string, pattern|
time = Time.parse(string, pattern)
time.offset.should eq 0
time.utc?.should be_true
time.location.fixed?.should be_true
time.incremental?.should be_true
end

{"UTC", "UTC", "UTC"}.zip(patterns) do |string, pattern|
time = Time.parse(string, pattern)
time.offset.should eq 0
time.utc?.should be_true
time.location.fixed?.should be_true
time.incremental?.should be_true
end

time = Time.parse("+04:12:39", "%::z")
time.offset.should eq 4 * 3600 + 12 * 60 + 39
time.utc?.should be_false
time.location.fixed?.should be_true
time.incremental?.should be_true
end

# TODO %N
# TODO %Z
# TODO %G
Expand Down Expand Up @@ -463,25 +570,37 @@ describe Time do

it do
time = Time.parse("2014-10-31 10:11:12 -06:00 hi", "%F %T %z hi")
time.local?.should be_true
time.incremental?.should be_true
time.utc?.should be_false
time.location.fixed?.should be_true
time.offset.should eq -6 * 3600
time.to_utc.to_s.should eq("2014-10-31 16:11:12 UTC")
end

it do
time = Time.parse("2014-10-31 10:11:12 +05:00 hi", "%F %T %z hi")
time.local?.should be_true
time.incremental?.should be_true
time.utc?.should be_false
time.location.fixed?.should be_true
time.offset.should eq 5 * 3600
time.to_utc.to_s.should eq("2014-10-31 05:11:12 UTC")
end

it do
time = Time.parse("2014-10-31 10:11:12 -06:00:00 hi", "%F %T %z hi")
time.local?.should be_true
time.incremental?.should be_true
time.utc?.should be_false
time.location.fixed?.should be_true
time.offset.should eq -6 * 3600
time.to_utc.to_s.should eq("2014-10-31 16:11:12 UTC")
end

it do
time = Time.parse("2014-10-31 10:11:12 -060000 hi", "%F %T %z hi")
time.local?.should be_true
time.incremental?.should be_true
time.utc?.should be_false
time.location.fixed?.should be_true
time.offset.should eq -6 * 3600
time.to_utc.to_s.should eq("2014-10-31 16:11:12 UTC")
end

Expand Down Expand Up @@ -526,9 +645,16 @@ describe Time do
time.second.should eq(12)
end

it "can parse in UTC" do
it "can parse in location" do
time = Time.parse("2014-10-31 11:12:13", "%F %T", Time::Location::UTC)
time.utc?.should be_true

location = Time::Location.load("Europe/Berlin")
time = Time.parse("2016-11-24 14:32:02", "%F %T", location)
time.location.should eq location

time = Time.parse("2016-11-24 14:32:02 +01:00", "%F %T %:z", location)
time.location.should eq Time::Location.fixed(3600)
end

it "at" do
Expand Down
3 changes: 2 additions & 1 deletion src/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ struct Time
self
else
Time.utc(
seconds: total_seconds,
# FIXME: Use proper zone lookup distance
seconds: total_seconds - location.lookup(total_seconds - UNIX_SECONDS).offset,
nanoseconds: nanosecond
)
end
Expand Down
66 changes: 33 additions & 33 deletions src/time/format/formatter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -139,55 +139,55 @@ struct Time::Format
io << time.epoch
end

def time_zone
case time
when .floating?
raise FloatingTimeConversionError.new
when .utc?
io << "+0000"
else
negative, hours, minutes = local_time_zone_info
io << (negative ? "-" : "+")
io << "0" if hours < 10
io << hours
io << "0" if minutes < 10
io << minutes
def time_zone(with_seconds = false)
raise FloatingTimeConversionError.new if time.floating?

negative, hours, minutes, seconds = local_time_zone_info
io << (negative ? "-" : "+")
io << "0" if hours < 10
io << hours
io << "0" if minutes < 10
io << minutes
if with_seconds
io << '0' if seconds < 10
io << seconds
end
end

def time_zone_colon
case time
when .floating?
raise FloatingTimeConversionError.new
when .utc?
io << "+00:00"
else
negative, hours, minutes = local_time_zone_info
io << (negative ? "-" : "+")
io << "0" if hours < 10
io << hours
io << ":"
io << "0" if minutes < 10
io << minutes
def time_zone_colon(with_seconds = false)
raise FloatingTimeConversionError.new if time.floating?

negative, hours, minutes, seconds = local_time_zone_info
io << (negative ? "-" : "+")
io << "0" if hours < 10
io << hours
io << ":"
io << "0" if minutes < 10
io << minutes
if with_seconds
io << ':'
io << '0' if seconds < 10
io << seconds
end
end

def time_zone_colon_with_seconds
time_zone_colon
io << ":00"
time_zone_colon(with_seconds: true)
end

def local_time_zone_info
minutes = time.offset / 60
if minutes < 0
minutes = -minutes
offset = time.offset
if offset < 0
offset = -offset
negative = true
else
negative = false
end
seconds = offset % 60
minutes = offset / 60
hours = minutes / 60
minutes = minutes % 60
{negative, hours, minutes}
{negative, hours, minutes, seconds}
end

def char(char)
Expand Down
Loading

0 comments on commit 5ec821b

Please sign in to comment.