summaryrefslogtreecommitdiff
path: root/spec/ruby/core/time
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/time')
-rw-r--r--spec/ruby/core/time/_load_spec.rb3
-rw-r--r--spec/ruby/core/time/at_spec.rb102
-rw-r--r--spec/ruby/core/time/ceil_spec.rb64
-rw-r--r--spec/ruby/core/time/deconstruct_keys_spec.rb45
-rw-r--r--spec/ruby/core/time/fixtures/classes.rb1
-rw-r--r--spec/ruby/core/time/floor_spec.rb52
-rw-r--r--spec/ruby/core/time/getlocal_spec.rb102
-rw-r--r--spec/ruby/core/time/inspect_spec.rb44
-rw-r--r--spec/ruby/core/time/localtime_spec.rb16
-rw-r--r--spec/ruby/core/time/minus_spec.rb14
-rw-r--r--spec/ruby/core/time/new_spec.rb617
-rw-r--r--spec/ruby/core/time/now_spec.rb51
-rw-r--r--spec/ruby/core/time/plus_spec.rb14
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb4
-rw-r--r--spec/ruby/core/time/shared/local.rb11
-rw-r--r--spec/ruby/core/time/shared/time_params.rb11
-rw-r--r--spec/ruby/core/time/strftime_spec.rb41
-rw-r--r--spec/ruby/core/time/succ_spec.rb41
-rw-r--r--spec/ruby/core/time/utc_spec.rb51
-rw-r--r--spec/ruby/core/time/zone_spec.rb31
20 files changed, 902 insertions, 413 deletions
diff --git a/spec/ruby/core/time/_load_spec.rb b/spec/ruby/core/time/_load_spec.rb
index 152934370f..bb0d705bbc 100644
--- a/spec/ruby/core/time/_load_spec.rb
+++ b/spec/ruby/core/time/_load_spec.rb
@@ -44,8 +44,7 @@ describe "Time._load" do
end
it "treats the data as binary data" do
- data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE"
- data.force_encoding Encoding::UTF_8
+ data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE".dup.force_encoding Encoding::UTF_8
t = Marshal.load(data)
t.to_s.should == "2013-04-08 12:47:45 UTC"
end
diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb
index ff43537dcc..48fb3c6f52 100644
--- a/spec/ruby/core/time/at_spec.rb
+++ b/spec/ruby/core/time/at_spec.rb
@@ -32,13 +32,6 @@ describe "Time.at" do
t2.nsec.should == t.nsec
end
- describe "passed BigDecimal" do
- it "doesn't round input value" do
- require 'bigdecimal'
- Time.at(BigDecimal('1.1')).to_f.should == 1.1
- end
- end
-
describe "passed Rational" do
it "returns Time with correct microseconds" do
t = Time.at(Rational(1_486_570_508_539_759, 1_000_000))
@@ -203,7 +196,7 @@ describe "Time.at" do
end
it "does not try to convert format to Symbol with #to_sym" do
- format = "usec"
+ format = +"usec"
format.should_not_receive(:to_sym)
-> { Time.at(0, 123456, format) }.should raise_error(ArgumentError)
end
@@ -218,55 +211,74 @@ describe "Time.at" do
end
end
- ruby_version_is "2.6" do
- describe ":in keyword argument" do
- before do
- @epoch_time = Time.now.to_i
- end
+ describe ":in keyword argument" do
+ before do
+ @epoch_time = Time.now.to_i
+ end
- it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
- time = Time.at(@epoch_time, in: "+05:00")
+ it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
+ time = Time.at(@epoch_time, in: "+05:00")
- time.utc_offset.should == 5*60*60
- time.zone.should == nil
- time.to_i.should == @epoch_time
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
- time = Time.at(@epoch_time, in: "-09:00")
+ time = Time.at(@epoch_time, in: "-09:00")
- time.utc_offset.should == -9*60*60
- time.zone.should == nil
- time.to_i.should == @epoch_time
- end
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+ end
- it "could be UTC offset as a number of seconds" do
- time = Time.at(@epoch_time, in: 5*60*60)
+ it "could be UTC offset as a number of seconds" do
+ time = Time.at(@epoch_time, in: 5*60*60)
- time.utc_offset.should == 5*60*60
- time.zone.should == nil
- time.to_i.should == @epoch_time
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
- time = Time.at(@epoch_time, in: -9*60*60)
+ time = Time.at(@epoch_time, in: -9*60*60)
- time.utc_offset.should == -9*60*60
- time.zone.should == nil
- time.to_i.should == @epoch_time
- end
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+ end
- it "could be a timezone object" do
- zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
- time = Time.at(@epoch_time, in: zone)
+ it "could be UTC offset as a 'UTC' String" do
+ time = Time.at(@epoch_time, in: "UTC")
- time.utc_offset.should == 5*3600+30*60
- time.zone.should == zone
- time.to_i.should == @epoch_time
+ time.utc_offset.should == 0
+ time.zone.should == "UTC"
+ time.to_i.should == @epoch_time
+ end
- zone = TimeSpecs::TimezoneWithName.new(name: "PST")
- time = Time.at(@epoch_time, in: zone)
+ it "could be UTC offset as a military zone A-Z" do
+ time = Time.at(@epoch_time, in: "B")
- time.utc_offset.should == -9*60*60
- time.zone.should == zone
- time.to_i.should == @epoch_time
- end
+ time.utc_offset.should == 3600 * 2
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+ end
+
+ it "could be a timezone object" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.at(@epoch_time, in: zone)
+
+ time.utc_offset.should == 5*3600+30*60
+ time.zone.should == zone
+ time.to_i.should == @epoch_time
+
+ zone = TimeSpecs::TimezoneWithName.new(name: "PST")
+ time = Time.at(@epoch_time, in: zone)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == zone
+ time.to_i.should == @epoch_time
+ end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.at(@epoch_time, in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.at(@epoch_time, in: "ABC") }.should raise_error(ArgumentError)
end
end
end
diff --git a/spec/ruby/core/time/ceil_spec.rb b/spec/ruby/core/time/ceil_spec.rb
index 86029554db..9d624a1ed0 100644
--- a/spec/ruby/core/time/ceil_spec.rb
+++ b/spec/ruby/core/time/ceil_spec.rb
@@ -1,46 +1,44 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Time#ceil" do
- before do
- @time = Time.utc(2010, 3, 30, 5, 43, "25.0123456789".to_r)
- end
-
- it "defaults to ceiling to 0 places" do
- @time.ceil.should == Time.utc(2010, 3, 30, 5, 43, 26.to_r)
- end
+describe "Time#ceil" do
+ before do
+ @time = Time.utc(2010, 3, 30, 5, 43, "25.0123456789".to_r)
+ end
- it "ceils to 0 decimal places with an explicit argument" do
- @time.ceil(0).should == Time.utc(2010, 3, 30, 5, 43, 26.to_r)
- end
+ it "defaults to ceiling to 0 places" do
+ @time.ceil.should == Time.utc(2010, 3, 30, 5, 43, 26.to_r)
+ end
- it "ceils to 2 decimal places with an explicit argument" do
- @time.ceil(2).should == Time.utc(2010, 3, 30, 5, 43, "25.02".to_r)
- end
+ it "ceils to 0 decimal places with an explicit argument" do
+ @time.ceil(0).should == Time.utc(2010, 3, 30, 5, 43, 26.to_r)
+ end
- it "ceils to 4 decimal places with an explicit argument" do
- @time.ceil(4).should == Time.utc(2010, 3, 30, 5, 43, "25.0124".to_r)
- end
+ it "ceils to 2 decimal places with an explicit argument" do
+ @time.ceil(2).should == Time.utc(2010, 3, 30, 5, 43, "25.02".to_r)
+ end
- it "ceils to 7 decimal places with an explicit argument" do
- @time.ceil(7).should == Time.utc(2010, 3, 30, 5, 43, "25.0123457".to_r)
- end
+ it "ceils to 4 decimal places with an explicit argument" do
+ @time.ceil(4).should == Time.utc(2010, 3, 30, 5, 43, "25.0124".to_r)
+ end
- it "returns an instance of Time, even if #ceil is called on a subclass" do
- subclass = Class.new(Time)
- instance = subclass.at(0)
- instance.class.should equal subclass
- instance.ceil.should be_an_instance_of(Time)
- end
+ it "ceils to 7 decimal places with an explicit argument" do
+ @time.ceil(7).should == Time.utc(2010, 3, 30, 5, 43, "25.0123457".to_r)
+ end
- it "copies own timezone to the returning value" do
- @time.zone.should == @time.ceil.zone
+ it "returns an instance of Time, even if #ceil is called on a subclass" do
+ subclass = Class.new(Time)
+ instance = subclass.at(0)
+ instance.class.should equal subclass
+ instance.ceil.should be_an_instance_of(Time)
+ end
- time = with_timezone "JST-9" do
- Time.at 0, 1
- end
+ it "copies own timezone to the returning value" do
+ @time.zone.should == @time.ceil.zone
- time.zone.should == time.ceil.zone
+ time = with_timezone "JST-9" do
+ Time.at 0, 1
end
+
+ time.zone.should == time.ceil.zone
end
end
diff --git a/spec/ruby/core/time/deconstruct_keys_spec.rb b/spec/ruby/core/time/deconstruct_keys_spec.rb
new file mode 100644
index 0000000000..ee17e7dbd4
--- /dev/null
+++ b/spec/ruby/core/time/deconstruct_keys_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.2" do
+ describe "Time#deconstruct_keys" do
+ it "returns whole hash for nil as an argument" do
+ d = Time.utc(2022, 10, 5, 13, 30)
+ res = { year: 2022, month: 10, day: 5, yday: 278, wday: 3, hour: 13,
+ min: 30, sec: 0, subsec: 0, dst: false, zone: "UTC" }
+ d.deconstruct_keys(nil).should == res
+ end
+
+ it "returns only specified keys" do
+ d = Time.utc(2022, 10, 5, 13, 39)
+ d.deconstruct_keys([:zone, :subsec]).should == { zone: "UTC", subsec: 0 }
+ end
+
+ it "requires one argument" do
+ -> {
+ Time.new(2022, 10, 5, 13, 30).deconstruct_keys
+ }.should raise_error(ArgumentError)
+ end
+
+ it "it raises error when argument is neither nil nor array" do
+ d = Time.new(2022, 10, 5, 13, 30)
+
+ -> { d.deconstruct_keys(1) }.should raise_error(TypeError, "wrong argument type Integer (expected Array or nil)")
+ -> { d.deconstruct_keys("asd") }.should raise_error(TypeError, "wrong argument type String (expected Array or nil)")
+ -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, "wrong argument type Symbol (expected Array or nil)")
+ -> { d.deconstruct_keys({}) }.should raise_error(TypeError, "wrong argument type Hash (expected Array or nil)")
+ end
+
+ it "returns {} when passed []" do
+ Time.new(2022, 10, 5, 13, 30).deconstruct_keys([]).should == {}
+ end
+
+ it "ignores non-Symbol keys" do
+ Time.new(2022, 10, 5, 13, 30).deconstruct_keys(['year', []]).should == {}
+ end
+
+ it "ignores not existing Symbol keys and processes keys after the first non-existing one" do
+ d = Time.utc(2022, 10, 5, 13, 30)
+ d.deconstruct_keys([:year, :a, :month, :b, :day]).should == { year: 2022, month: 10, day: 5 }
+ end
+ end
+end
diff --git a/spec/ruby/core/time/fixtures/classes.rb b/spec/ruby/core/time/fixtures/classes.rb
index 1a9511b261..21c4e1effb 100644
--- a/spec/ruby/core/time/fixtures/classes.rb
+++ b/spec/ruby/core/time/fixtures/classes.rb
@@ -59,7 +59,6 @@ module TimeSpecs
Zone = Struct.new(:std, :dst, :dst_range)
Zones = {
"Asia/Colombo" => Zone[Z[5*3600+30*60, "MMT"], nil, nil],
- "Europe/Kiev" => Zone[Z[2*3600, "EET"], Z[3*3600, "EEST"], 4..10],
"PST" => Zone[Z[(-9*60*60), "PST"], nil, nil],
}
diff --git a/spec/ruby/core/time/floor_spec.rb b/spec/ruby/core/time/floor_spec.rb
index a19585b787..b0003469c9 100644
--- a/spec/ruby/core/time/floor_spec.rb
+++ b/spec/ruby/core/time/floor_spec.rb
@@ -1,38 +1,36 @@
require_relative '../../spec_helper'
-ruby_version_is "2.7" do
- describe "Time#floor" do
- before do
- @time = Time.utc(2010, 3, 30, 5, 43, "25.123456789".to_r)
- end
-
- it "defaults to flooring to 0 places" do
- @time.floor.should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
- end
+describe "Time#floor" do
+ before do
+ @time = Time.utc(2010, 3, 30, 5, 43, "25.123456789".to_r)
+ end
- it "floors to 0 decimal places with an explicit argument" do
- @time.floor(0).should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
- end
+ it "defaults to flooring to 0 places" do
+ @time.floor.should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
- it "floors to 7 decimal places with an explicit argument" do
- @time.floor(7).should == Time.utc(2010, 3, 30, 5, 43, "25.1234567".to_r)
- end
+ it "floors to 0 decimal places with an explicit argument" do
+ @time.floor(0).should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
- it "returns an instance of Time, even if #floor is called on a subclass" do
- subclass = Class.new(Time)
- instance = subclass.at(0)
- instance.class.should equal subclass
- instance.floor.should be_an_instance_of(Time)
- end
+ it "floors to 7 decimal places with an explicit argument" do
+ @time.floor(7).should == Time.utc(2010, 3, 30, 5, 43, "25.1234567".to_r)
+ end
- it "copies own timezone to the returning value" do
- @time.zone.should == @time.floor.zone
+ it "returns an instance of Time, even if #floor is called on a subclass" do
+ subclass = Class.new(Time)
+ instance = subclass.at(0)
+ instance.class.should equal subclass
+ instance.floor.should be_an_instance_of(Time)
+ end
- time = with_timezone "JST-9" do
- Time.at 0, 1
- end
+ it "copies own timezone to the returning value" do
+ @time.zone.should == @time.floor.zone
- time.zone.should == time.floor.zone
+ time = with_timezone "JST-9" do
+ Time.at 0, 1
end
+
+ time.zone.should == time.floor.zone
end
end
diff --git a/spec/ruby/core/time/getlocal_spec.rb b/spec/ruby/core/time/getlocal_spec.rb
index 7196577dab..926a6dbf45 100644
--- a/spec/ruby/core/time/getlocal_spec.rb
+++ b/spec/ruby/core/time/getlocal_spec.rb
@@ -97,71 +97,69 @@ describe "Time#getlocal" do
-> { t.getlocal(86400) }.should raise_error(ArgumentError)
end
- ruby_version_is "2.6" do
- describe "with a timezone argument" do
- it "returns a Time in the timezone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone)
-
- time.zone.should == zone
- time.utc_offset.should == 5*3600+30*60
- end
+ describe "with a timezone argument" do
+ it "returns a Time in the timezone" do
+ zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
+ time = Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone)
- it "accepts timezone argument that must have #local_to_utc and #utc_to_local methods" do
- zone = Object.new
- def zone.utc_to_local(time)
- time
- end
- def zone.local_to_utc(time)
- time
- end
+ time.zone.should == zone
+ time.utc_offset.should == 5*3600+30*60
+ end
- -> {
- Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone).should be_kind_of(Time)
- }.should_not raise_error
+ it "accepts timezone argument that must have #local_to_utc and #utc_to_local methods" do
+ zone = Object.new
+ def zone.utc_to_local(time)
+ time
+ end
+ def zone.local_to_utc(time)
+ time
end
- it "raises TypeError if timezone does not implement #utc_to_local method" do
- zone = Object.new
- def zone.local_to_utc(time)
- time
- end
+ -> {
+ Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone).should be_kind_of(Time)
+ }.should_not raise_error
+ end
- -> {
- Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone)
- }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
+ it "raises TypeError if timezone does not implement #utc_to_local method" do
+ zone = Object.new
+ def zone.local_to_utc(time)
+ time
end
- it "does not raise exception if timezone does not implement #local_to_utc method" do
- zone = Object.new
- def zone.utc_to_local(time)
- time
- end
+ -> {
+ Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone)
+ }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
+ end
- -> {
- Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone).should be_kind_of(Time)
- }.should_not raise_error
+ it "does not raise exception if timezone does not implement #local_to_utc method" do
+ zone = Object.new
+ def zone.utc_to_local(time)
+ time
end
- context "subject's class implements .find_timezone method" do
- it "calls .find_timezone to build a time object if passed zone name as a timezone argument" do
- time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0).getlocal("Asia/Colombo")
- time.zone.should be_kind_of TimeSpecs::TimezoneWithName
- time.zone.name.should == "Asia/Colombo"
+ -> {
+ Time.utc(2000, 1, 1, 12, 0, 0).getlocal(zone).should be_kind_of(Time)
+ }.should_not raise_error
+ end
+
+ context "subject's class implements .find_timezone method" do
+ it "calls .find_timezone to build a time object if passed zone name as a timezone argument" do
+ time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0).getlocal("Asia/Colombo")
+ time.zone.should be_kind_of TimeSpecs::TimezoneWithName
+ time.zone.name.should == "Asia/Colombo"
- time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0).getlocal("some invalid zone name")
- time.zone.should be_kind_of TimeSpecs::TimezoneWithName
- time.zone.name.should == "some invalid zone name"
- end
+ time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0).getlocal("some invalid zone name")
+ time.zone.should be_kind_of TimeSpecs::TimezoneWithName
+ time.zone.name.should == "some invalid zone name"
+ end
- it "does not call .find_timezone if passed any not string/numeric/timezone timezone argument" do
- [Object.new, [], {}, :"some zone"].each do |zone|
- time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0)
+ it "does not call .find_timezone if passed any not string/numeric/timezone timezone argument" do
+ [Object.new, [], {}, :"some zone"].each do |zone|
+ time = TimeSpecs::TimeWithFindTimezone.utc(2000, 1, 1, 12, 0, 0)
- -> {
- time.getlocal(zone)
- }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
- end
+ -> {
+ time.getlocal(zone)
+ }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
end
end
end
diff --git a/spec/ruby/core/time/inspect_spec.rb b/spec/ruby/core/time/inspect_spec.rb
index 6f1b2e3ef1..c3a4519a24 100644
--- a/spec/ruby/core/time/inspect_spec.rb
+++ b/spec/ruby/core/time/inspect_spec.rb
@@ -4,32 +4,30 @@ require_relative 'shared/inspect'
describe "Time#inspect" do
it_behaves_like :inspect, :inspect
- ruby_version_is "2.7" do
- it "preserves microseconds" do
- t = Time.utc(2007, 11, 1, 15, 25, 0, 123456)
- t.inspect.should == "2007-11-01 15:25:00.123456 UTC"
- end
+ it "preserves microseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456)
+ t.inspect.should == "2007-11-01 15:25:00.123456 UTC"
+ end
- it "omits trailing zeros from microseconds" do
- t = Time.utc(2007, 11, 1, 15, 25, 0, 100000)
- t.inspect.should == "2007-11-01 15:25:00.1 UTC"
- end
+ it "omits trailing zeros from microseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 100000)
+ t.inspect.should == "2007-11-01 15:25:00.1 UTC"
+ end
- it "uses the correct time zone without microseconds" do
- t = Time.utc(2000, 1, 1)
- t = t.localtime(9*3600)
- t.inspect.should == "2000-01-01 09:00:00 +0900"
- end
+ it "uses the correct time zone without microseconds" do
+ t = Time.utc(2000, 1, 1)
+ t = t.localtime(9*3600)
+ t.inspect.should == "2000-01-01 09:00:00 +0900"
+ end
- it "uses the correct time zone with microseconds" do
- t = Time.utc(2000, 1, 1, 0, 0, 0, 123456)
- t = t.localtime(9*3600)
- t.inspect.should == "2000-01-01 09:00:00.123456 +0900"
- end
+ it "uses the correct time zone with microseconds" do
+ t = Time.utc(2000, 1, 1, 0, 0, 0, 123456)
+ t = t.localtime(9*3600)
+ t.inspect.should == "2000-01-01 09:00:00.123456 +0900"
+ end
- it "preserves nanoseconds" do
- t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
- t.inspect.should == "2007-11-01 15:25:00.123456789 UTC"
- end
+ it "preserves nanoseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
+ t.inspect.should == "2007-11-01 15:25:00.123456789 UTC"
end
end
diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb
index 2975e112d0..609b6532a1 100644
--- a/spec/ruby/core/time/localtime_spec.rb
+++ b/spec/ruby/core/time/localtime_spec.rb
@@ -29,10 +29,10 @@ describe "Time#localtime" do
time.localtime.should equal(time)
end
- it "raises a RuntimeError if the time has a different time zone" do
+ it "raises a FrozenError if the time has a different time zone" do
time = Time.gm(2007, 1, 9, 12, 0, 0)
time.freeze
- -> { time.localtime }.should raise_error(RuntimeError)
+ -> { time.localtime }.should raise_error(FrozenError)
end
end
@@ -79,6 +79,18 @@ describe "Time#localtime" do
t.utc_offset.should == -3600
end
+ it "returns a Time with a UTC offset specified as UTC" do
+ t = Time.new(2007, 1, 9, 12, 0, 0, 3600)
+ t.localtime("UTC")
+ t.utc_offset.should == 0
+ end
+
+ it "returns a Time with a UTC offset specified as A-Z military zone" do
+ t = Time.new(2007, 1, 9, 12, 0, 0, 3600)
+ t.localtime("B")
+ t.utc_offset.should == 3600 * 2
+ end
+
platform_is_not :windows do
it "changes the timezone according to the set one" do
t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
diff --git a/spec/ruby/core/time/minus_spec.rb b/spec/ruby/core/time/minus_spec.rb
index 995dac8462..8449778465 100644
--- a/spec/ruby/core/time/minus_spec.rb
+++ b/spec/ruby/core/time/minus_spec.rb
@@ -98,14 +98,12 @@ describe "Time#-" do
time_with_zone.zone.should == (time_with_zone - 1).zone
end
- ruby_version_is "2.6" do
- context "zone is a timezone object" do
- it "preserves time zone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2012, 1, 1, 12, 0, 0, zone) - 1
-
- time.zone.should == zone
- end
+ context "zone is a timezone object" do
+ it "preserves time zone" do
+ zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
+ time = Time.new(2012, 1, 1, 12, 0, 0, zone) - 1
+
+ time.zone.should == zone
end
end
diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb
index a4bb5b362c..d686355270 100644
--- a/spec/ruby/core/time/new_spec.rb
+++ b/spec/ruby/core/time/new_spec.rb
@@ -58,6 +58,32 @@ describe "Time.new with a utc_offset argument" do
Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043
end
+ ruby_bug '#13669', ''...'3.1' do
+ it "returns a Time with a UTC offset specified as +HH" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05").utc_offset.should == 3600 * 5
+ end
+
+ it "returns a Time with a UTC offset specified as -HH" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-05").utc_offset.should == -3600 * 5
+ end
+
+ it "returns a Time with a UTC offset specified as +HHMM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+0530").utc_offset.should == 19800
+ end
+
+ it "returns a Time with a UTC offset specified as -HHMM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-0530").utc_offset.should == -19800
+ end
+
+ it "returns a Time with a UTC offset specified as +HHMMSS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+053037").utc_offset.should == 19837
+ end
+
+ it "returns a Time with a UTC offset specified as -HHMMSS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-053037").utc_offset.should == -19837
+ end
+ end
+
describe "with an argument that responds to #to_str" do
it "coerces using #to_str" do
o = mock('string')
@@ -66,6 +92,57 @@ describe "Time.new with a utc_offset argument" do
end
end
+ it "returns a Time with UTC offset specified as UTC" do
+ Time.new(2000, 1, 1, 0, 0, 0, "UTC").utc_offset.should == 0
+ end
+
+ it "returns a Time with UTC offset specified as a single letter military timezone" do
+ [
+ ["A", 3600],
+ ["B", 3600 * 2],
+ ["C", 3600 * 3],
+ ["D", 3600 * 4],
+ ["E", 3600 * 5],
+ ["F", 3600 * 6],
+ ["G", 3600 * 7],
+ ["H", 3600 * 8],
+ ["I", 3600 * 9],
+ # J is not supported
+ ["K", 3600 * 10],
+ ["L", 3600 * 11],
+ ["M", 3600 * 12],
+ ["N", 3600 * -1],
+ ["O", 3600 * -2],
+ ["P", 3600 * -3],
+ ["Q", 3600 * -4],
+ ["R", 3600 * -5],
+ ["S", 3600 * -6],
+ ["T", 3600 * -7],
+ ["U", 3600 * -8],
+ ["V", 3600 * -9],
+ ["W", 3600 * -10],
+ ["X", 3600 * -11],
+ ["Y", 3600 * -12],
+ ["Z", 0]
+ ].each do |letter, offset|
+ Time.new(2000, 1, 1, 0, 0, 0, letter).utc_offset.should == offset
+ end
+ end
+
+ ruby_version_is ""..."3.1" do
+ it "raises ArgumentError if the string argument is J" do
+ message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset'
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message)
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "raises ArgumentError if the string argument is J" do
+ message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: J'
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message)
+ end
+ end
+
it "returns a local Time if the argument is nil" do
with_timezone("PST", -8) do
t = Time.new(2000, 1, 1, 0, 0, 0, nil)
@@ -93,7 +170,12 @@ describe "Time.new with a utc_offset argument" do
end
it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
- -> { Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE")) }.should raise_error(ArgumentError)
+ # Don't check exception message - it was changed in previous CRuby versions:
+ # - "string contains null byte"
+ # - '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset'
+ -> {
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE"))
+ }.should raise_error(ArgumentError)
end
it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do
@@ -106,232 +188,459 @@ describe "Time.new with a utc_offset argument" do
-> { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.should raise_error(ArgumentError)
end
- it "raises ArgumentError if the seconds argument is negative" do
- -> { Time.new(2000, 1, 1, 0, 0, -1) }.should raise_error(ArgumentError)
- end
-
it "raises ArgumentError if the utc_offset argument is greater than or equal to 10e9" do
-> { Time.new(2000, 1, 1, 0, 0, 0, 1000000000) }.should raise_error(ArgumentError)
end
+end
+
+describe "Time.new with a timezone argument" do
+ it "returns a Time in the timezone" do
+ zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
+ time = Time.new(2000, 1, 1, 12, 0, 0, zone)
- it "raises ArgumentError if the month is greater than 12" do
- # For some reason MRI uses a different message for month in 13-15 and month>=16
- -> { Time.new(2000, 13, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, /(mon|argument) out of range/)
- -> { Time.new(2000, 16, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, "argument out of range")
+ time.zone.should == zone
+ time.utc_offset.should == 5*3600+30*60
+ time.wday.should == 6
+ time.yday.should == 1
end
-end
-ruby_version_is "2.6" do
- describe "Time.new with a timezone argument" do
- it "returns a Time in the timezone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+ it "accepts timezone argument that must have #local_to_utc and #utc_to_local methods" do
+ zone = Object.new
+ def zone.utc_to_local(time)
+ time
+ end
+ def zone.local_to_utc(time)
+ time
+ end
- time.zone.should == zone
- time.utc_offset.should == 5*3600+30*60
- ruby_version_is "3.0" do
- time.wday.should == 6
- time.yday.should == 1
- end
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ }.should_not raise_error
+ end
+
+ it "raises TypeError if timezone does not implement #local_to_utc method" do
+ zone = Object.new
+ def zone.utc_to_local(time)
+ time
end
- it "accepts timezone argument that must have #local_to_utc and #utc_to_local methods" do
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone)
+ }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
+ end
+
+ it "does not raise exception if timezone does not implement #utc_to_local method" do
+ zone = Object.new
+ def zone.local_to_utc(time)
+ time
+ end
+
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ }.should_not raise_error
+ end
+
+ # The result also should be a Time or Time-like object (not necessary to be the same class)
+ # The zone of the result is just ignored
+ describe "returned value by #utc_to_local and #local_to_utc methods" do
+ it "could be Time instance" do
zone = Object.new
- def zone.utc_to_local(time)
- time
- end
- def zone.local_to_utc(time)
- time
+ def zone.local_to_utc(t)
+ Time.utc(t.year, t.mon, t.day, t.hour - 1, t.min, t.sec)
end
-> {
Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
}.should_not raise_error
end
- it "raises TypeError if timezone does not implement #local_to_utc method" do
+ it "could be Time subclass instance" do
zone = Object.new
- def zone.utc_to_local(time)
- time
+ def zone.local_to_utc(t)
+ Class.new(Time).utc(t.year, t.mon, t.day, t.hour - 1, t.min, t.sec)
end
-> {
- Time.new(2000, 1, 1, 12, 0, 0, zone)
- }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
+ Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
+ }.should_not raise_error
end
- it "does not raise exception if timezone does not implement #utc_to_local method" do
+ it "could be any object with #to_i method" do
zone = Object.new
def zone.local_to_utc(time)
- time
+ Struct.new(:to_i).new(time.to_i - 60*60)
end
-> {
Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
}.should_not raise_error
end
- # The result also should be a Time or Time-like object (not necessary to be the same class)
- # The zone of the result is just ignored
- describe "returned value by #utc_to_local and #local_to_utc methods" do
- it "could be Time instance" do
- zone = Object.new
- def zone.local_to_utc(t)
- Time.utc(t.year, t.mon, t.day, t.hour - 1, t.min, t.sec)
- end
-
- -> {
- Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
- Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
- }.should_not raise_error
+ it "could have any #zone and #utc_offset because they are ignored" do
+ zone = Object.new
+ def zone.local_to_utc(time)
+ Struct.new(:to_i, :zone, :utc_offset).new(time.to_i, 'America/New_York', -5*60*60)
end
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 0
- it "could be Time subclass instance" do
- zone = Object.new
- def zone.local_to_utc(t)
- Class.new(Time).utc(t.year, t.mon, t.day, t.hour - 1, t.min, t.sec)
- end
+ zone = Object.new
+ def zone.local_to_utc(time)
+ Struct.new(:to_i, :zone, :utc_offset).new(time.to_i, 'Asia/Tokyo', 9*60*60)
+ end
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 0
+ end
- -> {
- Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
- Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
- }.should_not raise_error
+ it "leads to raising Argument error if difference between argument and result is too large" do
+ zone = Object.new
+ def zone.local_to_utc(t)
+ Time.utc(t.year, t.mon, t.day + 1, t.hour, t.min, t.sec)
end
- it "could be any object with #to_i method" do
- zone = Object.new
- def zone.local_to_utc(time)
- Struct.new(:to_i).new(time.to_i - 60*60)
- end
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone)
+ }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+ end
- -> {
- Time.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
- Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 60*60
- }.should_not raise_error
+ # https://github.com/ruby/ruby/blob/v2_6_0/time.c#L5330
+ #
+ # Time-like argument to these methods is similar to a Time object in UTC without sub-second;
+ # it has attribute readers for the parts, e.g. year, month, and so on, and epoch time readers, to_i
+ #
+ # The sub-second attributes are fixed as 0, and utc_offset, zone, isdst, and their aliases are same as a Time object in UTC
+ describe "Time-like argument of #utc_to_local and #local_to_utc methods" do
+ before do
+ @obj = TimeSpecs::TimeLikeArgumentRecorder.result
+ @obj.should_not == nil
+ end
+
+ it "implements subset of Time methods" do
+ [
+ :year, :mon, :month, :mday, :hour, :min, :sec,
+ :tv_sec, :tv_usec, :usec, :tv_nsec, :nsec, :subsec,
+ :to_i, :to_f, :to_r, :+, :-,
+ :isdst, :dst?, :zone, :gmtoff, :gmt_offset, :utc_offset, :utc?, :gmt?,
+ :to_s, :inspect, :to_a, :to_time,
+ ].each do |name|
+ @obj.respond_to?(name).should == true
end
+ end
- it "could have any #zone and #utc_offset because they are ignored" do
- zone = Object.new
- def zone.local_to_utc(time)
- Struct.new(:to_i, :zone, :utc_offset).new(time.to_i, 'America/New_York', -5*60*60)
- end
- Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 0
+ it "has attribute values the same as a Time object in UTC" do
+ @obj.usec.should == 0
+ @obj.nsec.should == 0
+ @obj.subsec.should == 0
+ @obj.tv_usec.should == 0
+ @obj.tv_nsec.should == 0
- zone = Object.new
- def zone.local_to_utc(time)
- Struct.new(:to_i, :zone, :utc_offset).new(time.to_i, 'Asia/Tokyo', 9*60*60)
- end
- Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 0
- end
+ @obj.utc_offset.should == 0
+ @obj.zone.should == "UTC"
+ @obj.isdst.should == Time.new.utc.isdst
+ end
+ end
- it "leads to raising Argument error if difference between argument and result is too large" do
- zone = Object.new
- def zone.local_to_utc(t)
- Time.utc(t.year, t.mon, t.day + 1, t.hour, t.min, t.sec)
- end
+ context "#name method" do
+ it "uses the optional #name method for marshaling" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+ time_loaded = Marshal.load(Marshal.dump(time))
+
+ time_loaded.zone.should == "Asia/Colombo"
+ time_loaded.utc_offset.should == 5*3600+30*60
+ end
+ it "cannot marshal Time if #name method isn't implemented" do
+ zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
+ time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+
+ -> {
+ Marshal.dump(time)
+ }.should raise_error(NoMethodError, /undefined method [`']name' for/)
+ end
+ end
+
+ it "the #abbr method is used by '%Z' in #strftime" do
+ zone = TimeSpecs::TimezoneWithAbbr.new(name: "Asia/Colombo")
+ time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+
+ time.strftime("%Z").should == "MMT"
+ end
+
+ # At loading marshaled data, a timezone name will be converted to a timezone object
+ # by find_timezone class method, if the method is defined.
+ # Similarly, that class method will be called when a timezone argument does not have
+ # the necessary methods mentioned above.
+ context "subject's class implements .find_timezone method" do
+ it "calls .find_timezone to build a time object at loading marshaled data" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, zone)
+ time_loaded = Marshal.load(Marshal.dump(time))
+
+ time_loaded.zone.should be_kind_of TimeSpecs::TimezoneWithName
+ time_loaded.zone.name.should == "Asia/Colombo"
+ time_loaded.utc_offset.should == 5*3600+30*60
+ end
+
+ it "calls .find_timezone to build a time object if passed zone name as a timezone argument" do
+ time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, "Asia/Colombo")
+ time.zone.should be_kind_of TimeSpecs::TimezoneWithName
+ time.zone.name.should == "Asia/Colombo"
+
+ time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, "some invalid zone name")
+ time.zone.should be_kind_of TimeSpecs::TimezoneWithName
+ time.zone.name.should == "some invalid zone name"
+ end
+
+ it "does not call .find_timezone if passed any not string/numeric/timezone timezone argument" do
+ [Object.new, [], {}, :"some zone"].each do |zone|
-> {
- Time.new(2000, 1, 1, 12, 0, 0, zone)
- }.should raise_error(ArgumentError, "utc_offset out of range")
+ TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, zone)
+ }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
end
end
+ end
- # https://github.com/ruby/ruby/blob/v2_6_0/time.c#L5330
- #
- # Time-like argument to these methods is similar to a Time object in UTC without sub-second;
- # it has attribute readers for the parts, e.g. year, month, and so on, and epoch time readers, to_i
- #
- # The sub-second attributes are fixed as 0, and utc_offset, zone, isdst, and their aliases are same as a Time object in UTC
- describe "Time-like argument of #utc_to_local and #local_to_utc methods" do
- before do
- @obj = TimeSpecs::TimeLikeArgumentRecorder.result
- @obj.should_not == nil
- end
+ ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/17485
+ describe ":in keyword argument" do
+ it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: "+05:00")
- it "implements subset of Time methods" do
- [
- :year, :mon, :month, :mday, :hour, :min, :sec,
- :tv_sec, :tv_usec, :usec, :tv_nsec, :nsec, :subsec,
- :to_i, :to_f, :to_r, :+, :-,
- :isdst, :dst?, :zone, :gmtoff, :gmt_offset, :utc_offset, :utc?, :gmt?,
- :to_s, :inspect, :to_a, :to_time,
- ].each do |name|
- @obj.respond_to?(name).should == true
- end
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: "-09:00")
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
end
- it "has attribute values the same as a Time object in UTC" do
- @obj.usec.should == 0
- @obj.nsec.should == 0
- @obj.subsec.should == 0
- @obj.tv_usec.should == 0
- @obj.tv_nsec.should == 0
+ it "could be UTC offset as a number of seconds" do
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: 5*60*60)
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: -9*60*60)
- @obj.utc_offset.should == 0
- @obj.zone.should == "UTC"
- @obj.isdst.should == Time.new.utc.isdst
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "returns a Time with UTC offset specified as a single letter military timezone" do
+ Time.new(2000, 1, 1, 0, 0, 0, in: "W").utc_offset.should == 3600 * -10
end
- end
- context "#name method" do
- it "uses the optional #name method for marshaling" do
+ it "could be a timezone object" do
zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
- time = Time.new(2000, 1, 1, 12, 0, 0, zone)
- time_loaded = Marshal.load(Marshal.dump(time))
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: zone)
- time_loaded.zone.should == "Asia/Colombo"
- time_loaded.utc_offset.should == 5*3600+30*60
+ time.utc_offset.should == 5*3600+30*60
+ time.zone.should == zone
+
+ zone = TimeSpecs::TimezoneWithName.new(name: "PST")
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: zone)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == zone
end
- it "cannot marshal Time if #name method isn't implemented" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+ it "allows omitting minor arguments" do
+ Time.new(2000, 1, 1, 12, 1, 1, in: "+05:00").should == Time.new(2000, 1, 1, 12, 1, 1, "+05:00")
+ Time.new(2000, 1, 1, 12, 1, in: "+05:00").should == Time.new(2000, 1, 1, 12, 1, 0, "+05:00")
+ Time.new(2000, 1, 1, 12, in: "+05:00").should == Time.new(2000, 1, 1, 12, 0, 0, "+05:00")
+ Time.new(2000, 1, 1, in: "+05:00").should == Time.new(2000, 1, 1, 0, 0, 0, "+05:00")
+ Time.new(2000, 1, in: "+05:00").should == Time.new(2000, 1, 1, 0, 0, 0, "+05:00")
+ Time.new(2000, in: "+05:00").should == Time.new(2000, 1, 1, 0, 0, 0, "+05:00")
+ Time.new(in: "+05:00").should be_close(Time.now.getlocal("+05:00"), TIME_TOLERANCE)
+ end
+ it "converts to a provided timezone if all the positional arguments are omitted" do
+ Time.new(in: "+05:00").utc_offset.should == 5*3600
+ end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.new(2000, 1, 1, 12, 0, 0, in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.new(2000, 1, 1, 12, 0, 0, in: "ABC") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if two offset arguments are given" do
-> {
- Marshal.dump(time)
- }.should raise_error(NoMethodError, /undefined method `name' for/)
+ Time.new(2000, 1, 1, 12, 0, 0, "+05:00", in: "+05:00")
+ }.should raise_error(ArgumentError, "timezone argument given as positional and keyword arguments")
end
end
+ end
- it "the #abbr method is used by '%Z' in #strftime" do
- zone = TimeSpecs::TimezoneWithAbbr.new(name: "Asia/Colombo")
- time = Time.new(2000, 1, 1, 12, 0, 0, zone)
+ ruby_version_is "3.2" do
+ describe "Time.new with a String argument" do
+ it "parses an ISO-8601 like format" do
+ t = Time.utc(2020, 12, 24, 15, 56, 17)
+
+ Time.new("2020-12-24T15:56:17Z").should == t
+ Time.new("2020-12-25 00:56:17 +09:00").should == t
+ Time.new("2020-12-25 00:57:47 +09:01:30").should == t
+ Time.new("2020-12-25 00:56:17 +0900").should == t
+ Time.new("2020-12-25 00:57:47 +090130").should == t
+ Time.new("2020-12-25T00:56:17+09:00").should == t
+ end
- time.strftime("%Z").should == "MMT"
- end
+ it "accepts precision keyword argument and truncates specified digits of sub-second part" do
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00").subsec.should == 0.123456789r
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: nil).subsec.should == 0.123456789876r
+ Time.new("2021-12-25 00:00:00 +09:00", precision: 0).subsec.should == 0
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: -1).subsec.should == 0.123456789876r
+ end
- # At loading marshaled data, a timezone name will be converted to a timezone object
- # by find_timezone class method, if the method is defined.
- # Similarly, that class method will be called when a timezone argument does not have
- # the necessary methods mentioned above.
- context "subject's class implements .find_timezone method" do
- it "calls .find_timezone to build a time object at loading marshaled data" do
- zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
- time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, zone)
- time_loaded = Marshal.load(Marshal.dump(time))
+ it "returns Time in local timezone if not provided in the String argument" do
+ Time.new("2021-12-25 00:00:00").zone.should == Time.new(2021, 12, 25).zone
+ Time.new("2021-12-25 00:00:00").utc_offset.should == Time.new(2021, 12, 25).utc_offset
+ end
- time_loaded.zone.should be_kind_of TimeSpecs::TimezoneWithName
- time_loaded.zone.name.should == "Asia/Colombo"
- time_loaded.utc_offset.should == 5*3600+30*60
+ it "returns Time in timezone specified in the String argument" do
+ Time.new("2021-12-25 00:00:00 +05:00").to_s.should == "2021-12-25 00:00:00 +0500"
end
- it "calls .find_timezone to build a time object if passed zone name as a timezone argument" do
- time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, "Asia/Colombo")
- time.zone.should be_kind_of TimeSpecs::TimezoneWithName
- time.zone.name.should == "Asia/Colombo"
+ it "returns Time in timezone specified in the String argument even if the in keyword argument provided" do
+ Time.new("2021-12-25 00:00:00 +09:00", in: "-01:00").to_s.should == "2021-12-25 00:00:00 +0900"
+ end
+
+ it "returns Time in timezone specified with in keyword argument if timezone isn't provided in the String argument" do
+ Time.new("2021-12-25 00:00:00", in: "-01:00").to_s.should == "2021-12-25 00:00:00 -0100"
+ end
- time = TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, "some invalid zone name")
- time.zone.should be_kind_of TimeSpecs::TimezoneWithName
- time.zone.name.should == "some invalid zone name"
+ it "converts precision keyword argument into Integer if is not nil" do
+ obj = Object.new
+ def obj.to_int; 3; end
+
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 1.2).subsec.should == 0.1r
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: obj).subsec.should == 0.123r
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 3r).subsec.should == 0.123r
end
- it "does not call .find_timezone if passed any not string/numeric/timezone timezone argument" do
- [Object.new, [], {}, :"some zone"].each do |zone|
+ ruby_version_is ""..."3.3" do
+ it "raise TypeError is can't convert precision keyword argument into Integer" do
-> {
- TimeSpecs::TimeWithFindTimezone.new(2000, 1, 1, 12, 0, 0, zone)
- }.should raise_error(TypeError, /can't convert \w+ into an exact number/)
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: "")
+ }.should raise_error(TypeError, "no implicit conversion from string")
end
end
+
+ ruby_version_is "3.3" do
+ it "raise TypeError is can't convert precision keyword argument into Integer" do
+ -> {
+ Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: "")
+ }.should raise_error(TypeError, "no implicit conversion of String into Integer")
+ end
+ end
+
+ it "raises ArgumentError if part of time string is missing" do
+ -> {
+ Time.new("2020-12-25 00:56 +09:00")
+ }.should raise_error(ArgumentError, "missing sec part: 00:56 ")
+
+ -> {
+ Time.new("2020-12-25 00 +09:00")
+ }.should raise_error(ArgumentError, "missing min part: 00 ")
+ end
+
+ it "raises ArgumentError if subsecond is missing after dot" do
+ -> {
+ Time.new("2020-12-25 00:56:17. +0900")
+ }.should raise_error(ArgumentError, "subsecond expected after dot: 00:56:17. ")
+ end
+
+ it "raises ArgumentError if String argument is not in the supported format" do
+ -> {
+ Time.new("021-12-25 00:00:00.123456 +09:00")
+ }.should raise_error(ArgumentError, "year must be 4 or more digits: 021")
+
+ -> {
+ Time.new("2020-012-25 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -012-25 00:\z/)
+
+ -> {
+ Time.new("2020-2-25 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -2-25 00:56\z/)
+
+ -> {
+ Time.new("2020-12-215 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mday is expected after [`']-': -215 00:56:\z/)
+
+ -> {
+ Time.new("2020-12-25 000:56:17 +0900")
+ }.should raise_error(ArgumentError, "two digits hour is expected: 000:56:17 ")
+
+ -> {
+ Time.new("2020-12-25 0:56:17 +0900")
+ }.should raise_error(ArgumentError, "two digits hour is expected: 0:56:17 +0")
+
+ -> {
+ Time.new("2020-12-25 00:516:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :516:17 \+09\z/)
+
+ -> {
+ Time.new("2020-12-25 00:6:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :6:17 \+0900\z/)
+
+ -> {
+ Time.new("2020-12-25 00:56:137 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :137 \+0900\z/)
+
+ -> {
+ Time.new("2020-12-25 00:56:7 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :7 \+0900\z/)
+
+ -> {
+ Time.new("2020-12-25 00:56. +0900")
+ }.should raise_error(ArgumentError, "fraction min is not supported: 00:56.")
+
+ -> {
+ Time.new("2020-12-25 00. +0900")
+ }.should raise_error(ArgumentError, "fraction hour is not supported: 00.")
+ end
+
+ it "raises ArgumentError if date/time parts values are not valid" do
+ -> {
+ Time.new("2020-13-25 00:56:17 +09:00")
+ }.should raise_error(ArgumentError, "mon out of range")
+
+ -> {
+ Time.new("2020-12-32 00:56:17 +09:00")
+ }.should raise_error(ArgumentError, "mday out of range")
+
+ -> {
+ Time.new("2020-12-25 25:56:17 +09:00")
+ }.should raise_error(ArgumentError, "hour out of range")
+
+ -> {
+ Time.new("2020-12-25 00:61:17 +09:00")
+ }.should raise_error(ArgumentError, "min out of range")
+
+ -> {
+ Time.new("2020-12-25 00:56:61 +09:00")
+ }.should raise_error(ArgumentError, "sec out of range")
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +23:59:60")
+ }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +24:00")
+ }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +23:61")
+ }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +23:61')
+ end
+
+ it "raises ArgumentError if string has not ascii-compatible encoding" do
+ -> {
+ Time.new("2021-11-31 00:00:60 +09:00".encode("utf-32le"))
+ }.should raise_error(ArgumentError, "time string should have ASCII compatible encoding")
+ end
end
end
end
diff --git a/spec/ruby/core/time/now_spec.rb b/spec/ruby/core/time/now_spec.rb
index 7dc7951996..d47f00723e 100644
--- a/spec/ruby/core/time/now_spec.rb
+++ b/spec/ruby/core/time/now_spec.rb
@@ -3,4 +3,55 @@ require_relative 'shared/now'
describe "Time.now" do
it_behaves_like :time_now, :now
+
+ ruby_version_is '3.1' do # https://bugs.ruby-lang.org/issues/17485
+ describe ":in keyword argument" do
+ it "could be UTC offset as a String in '+HH:MM or '-HH:MM' format" do
+ time = Time.now(in: "+05:00")
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.now(in: "-09:00")
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "could be UTC offset as a number of seconds" do
+ time = Time.now(in: 5*60*60)
+
+ time.utc_offset.should == 5*60*60
+ time.zone.should == nil
+
+ time = Time.now(in: -9*60*60)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ end
+
+ it "returns a Time with UTC offset specified as a single letter military timezone" do
+ Time.now(in: "W").utc_offset.should == 3600 * -10
+ end
+
+ it "could be a timezone object" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.now(in: zone)
+
+ time.utc_offset.should == 5*3600+30*60
+ time.zone.should == zone
+
+ zone = TimeSpecs::TimezoneWithName.new(name: "PST")
+ time = Time.now(in: zone)
+
+ time.utc_offset.should == -9*60*60
+ time.zone.should == zone
+ end
+
+ it "raises ArgumentError if format is invalid" do
+ -> { Time.now(in: "+09:99") }.should raise_error(ArgumentError)
+ -> { Time.now(in: "ABC") }.should raise_error(ArgumentError)
+ end
+ end
+ end
end
diff --git a/spec/ruby/core/time/plus_spec.rb b/spec/ruby/core/time/plus_spec.rb
index c59d9a32bb..642393b615 100644
--- a/spec/ruby/core/time/plus_spec.rb
+++ b/spec/ruby/core/time/plus_spec.rb
@@ -56,14 +56,12 @@ describe "Time#+" do
time_with_zone.zone.should == (time_with_zone + 1).zone
end
- ruby_version_is "2.6" do
- context "zone is a timezone object" do
- it "preserves time zone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2012, 1, 1, 12, 0, 0, zone) + 1
-
- time.zone.should == zone
- end
+ context "zone is a timezone object" do
+ it "preserves time zone" do
+ zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
+ time = Time.new(2012, 1, 1, 12, 0, 0, zone) + 1
+
+ time.zone.should == zone
end
end
diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb
index 5ed64c2ab6..bae19da462 100644
--- a/spec/ruby/core/time/shared/gmtime.rb
+++ b/spec/ruby/core/time/shared/gmtime.rb
@@ -22,11 +22,11 @@ describe :time_gmtime, shared: true do
time.send(@method).should equal(time)
end
- it "raises a RuntimeError if the time is not UTC" do
+ it "raises a FrozenError if the time is not UTC" do
with_timezone("CST", -6) do
time = Time.now
time.freeze
- -> { time.send(@method) }.should raise_error(RuntimeError)
+ -> { time.send(@method) }.should raise_error(FrozenError)
end
end
end
diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb
index 43f331c4c1..068e314999 100644
--- a/spec/ruby/core/time/shared/local.rb
+++ b/spec/ruby/core/time/shared/local.rb
@@ -7,12 +7,10 @@ describe :time_local, shared: true do
end
platform_is_not :windows do
- describe "timezone changes" do
- it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do
- with_timezone("Europe/Amsterdam") do
- Time.send(@method, 1940, 5, 16).to_a.should ==
- [0, 40, 1, 16, 5, 1940, 4, 137, true, "CEST"]
- end
+ it "uses the 'CET' timezone with TZ=Europe/Amsterdam in 1970" do
+ with_timezone("Europe/Amsterdam") do
+ Time.send(@method, 1970, 5, 16).to_a.should ==
+ [0, 0, 0, 16, 5, 1970, 6, 136, false, "CET"]
end
end
end
@@ -41,5 +39,4 @@ describe :time_local_10_arg, shared: true do
end
end
end
-
end
diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb
index 63d0dbc120..b6a6c88c8e 100644
--- a/spec/ruby/core/time/shared/time_params.rb
+++ b/spec/ruby/core/time/shared/time_params.rb
@@ -145,9 +145,10 @@ describe :time_params, shared: true do
end
it "raises an ArgumentError for out of range month" do
+ # For some reason MRI uses a different message for month in 13-15 and month>=16
-> {
- Time.send(@method, 2008, 13, 31, 23, 59, 59)
- }.should raise_error(ArgumentError)
+ Time.send(@method, 2008, 16, 31, 23, 59, 59)
+ }.should raise_error(ArgumentError, /(mon|argument) out of range/)
end
it "raises an ArgumentError for out of range day" do
@@ -169,9 +170,13 @@ describe :time_params, shared: true do
end
it "raises an ArgumentError for out of range second" do
+ # For some reason MRI uses different messages for seconds 61-63 and seconds >= 64
-> {
Time.send(@method, 2008, 12, 31, 23, 59, 61)
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, /(sec|argument) out of range/)
+ -> {
+ Time.send(@method, 2008, 12, 31, 23, 59, -1)
+ }.should raise_error(ArgumentError, "argument out of range")
end
it "raises ArgumentError when given 9 arguments" do
diff --git a/spec/ruby/core/time/strftime_spec.rb b/spec/ruby/core/time/strftime_spec.rb
index 1bd24b0538..4cb300c916 100644
--- a/spec/ruby/core/time/strftime_spec.rb
+++ b/spec/ruby/core/time/strftime_spec.rb
@@ -49,4 +49,45 @@ describe "Time#strftime" do
time = @new_time_with_offset[2012, 1, 1, 0, 0, 0, Rational(36645, 10)]
time.strftime("%::z").should == "+01:01:05"
end
+
+ ruby_version_is "3.1" do
+ it "supports RFC 3339 UTC for unknown offset local time, -0000, as %-z" do
+ time = Time.gm(2022)
+
+ time.strftime("%z").should == "+0000"
+ time.strftime("%-z").should == "-0000"
+ time.strftime("%-:z").should == "-00:00"
+ time.strftime("%-::z").should == "-00:00:00"
+ end
+
+ it "applies '-' flag to UTC time" do
+ time = Time.utc(2022)
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.gm(2022)
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "Z")
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "-00:00")
+ time.strftime("%-z").should == "-0000"
+
+ time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00").utc
+ time.strftime("%-z").should == "-0000"
+ end
+
+ it "ignores '-' flag for non-UTC time" do
+ time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00")
+ time.strftime("%-z").should == "+0300"
+ end
+
+ it "works correctly with width, _ and 0 flags, and :" do
+ Time.now.utc.strftime("%-_10z").should == " -000"
+ Time.now.utc.strftime("%-10z").should == "-000000000"
+ Time.now.utc.strftime("%-010:z").should == "-000000:00"
+ Time.now.utc.strftime("%-_10:z").should == " -0:00"
+ Time.now.utc.strftime("%-_10::z").should == " -0:00:00"
+ end
+ end
end
diff --git a/spec/ruby/core/time/succ_spec.rb b/spec/ruby/core/time/succ_spec.rb
deleted file mode 100644
index fa6343f8e2..0000000000
--- a/spec/ruby/core/time/succ_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-ruby_version_is ""..."3.0" do
- require_relative '../../spec_helper'
- require_relative 'fixtures/classes'
-
- describe "Time#succ" do
- it "returns a new time one second later than time" do
- suppress_warning {
- @result = Time.at(100).succ
- }
-
- @result.should == Time.at(101)
- end
-
- it "returns a new instance" do
- time = Time.at(100)
-
- suppress_warning {
- @result = time.succ
- }
-
- @result.should_not equal time
- end
-
- it "is obsolete" do
- -> {
- Time.at(100).succ
- }.should complain(/Time#succ is obsolete/)
- end
-
- ruby_version_is "2.6" do
- context "zone is a timezone object" do
- it "preserves time zone" do
- zone = TimeSpecs::Timezone.new(offset: (5*3600+30*60))
- time = Time.new(2012, 1, 1, 12, 0, 0, zone) - 1
-
- time.zone.should == zone
- end
- end
- end
- end
-end
diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb
index 74c17a93d1..566509fd33 100644
--- a/spec/ruby/core/time/utc_spec.rb
+++ b/spec/ruby/core/time/utc_spec.rb
@@ -4,8 +4,55 @@ require_relative 'shared/gmtime'
require_relative 'shared/time_params'
describe "Time#utc?" do
- it "returns true if time represents a time in UTC (GMT)" do
- Time.now.should_not.utc?
+ it "returns true only if time represents a time in UTC (GMT)" do
+ Time.now.utc?.should == false
+ Time.now.utc.utc?.should == true
+ end
+
+ it "treats time as UTC what was created in different ways" do
+ Time.now.utc.utc?.should == true
+ Time.now.gmtime.utc?.should == true
+ Time.now.getgm.utc?.should == true
+ Time.now.getutc.utc?.should == true
+ Time.utc(2022).utc?.should == true
+ end
+
+ it "does treat time with 'UTC' offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "UTC").utc?.should == true
+ Time.now.localtime("UTC").utc?.should == true
+ Time.at(Time.now, in: 'UTC').utc?.should == true
+
+ ruby_version_is "3.1" do
+ Time.new(2022, 1, 1, 0, 0, 0, in: "UTC").utc?.should == true
+ Time.now(in: "UTC").utc?.should == true
+ end
+ end
+
+ it "does treat time with Z offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "Z").utc?.should == true
+ Time.now.localtime("Z").utc?.should == true
+ Time.at(Time.now, in: 'Z').utc?.should == true
+
+ ruby_version_is "3.1" do
+ Time.new(2022, 1, 1, 0, 0, 0, in: "Z").utc?.should == true
+ Time.now(in: "Z").utc?.should == true
+ end
+ end
+
+ ruby_version_is "3.1" do
+ it "does treat time with -00:00 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "-00:00").utc?.should == true
+ Time.now.localtime("-00:00").utc?.should == true
+ Time.at(Time.now, in: '-00:00').utc?.should == true
+ end
+ end
+
+ it "does not treat time with +00:00 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, "+00:00").utc?.should == false
+ end
+
+ it "does not treat time with 0 offset as UTC" do
+ Time.new(2022, 1, 1, 0, 0, 0, 0).utc?.should == false
end
end
diff --git a/spec/ruby/core/time/zone_spec.rb b/spec/ruby/core/time/zone_spec.rb
index 907ccf9f4b..63c92602d1 100644
--- a/spec/ruby/core/time/zone_spec.rb
+++ b/spec/ruby/core/time/zone_spec.rb
@@ -52,14 +52,39 @@ describe "Time#zone" do
end
it "doesn't raise errors for a Time with a fixed offset" do
- -> {
- Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone
- }.should_not raise_error
+ Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil
end
end
it "returns UTC when called on a UTC time" do
Time.now.utc.zone.should == "UTC"
+ Time.now.gmtime.zone.should == "UTC"
+ Time.now.getgm.zone.should == "UTC"
+ Time.now.getutc.zone.should == "UTC"
+ Time.utc(2022).zone.should == "UTC"
+ Time.new(2022, 1, 1, 0, 0, 0, "UTC").zone.should == "UTC"
+ Time.new(2022, 1, 1, 0, 0, 0, "Z").zone.should == "UTC"
+ Time.now.localtime("UTC").zone.should == "UTC"
+ Time.now.localtime("Z").zone.should == "UTC"
+ Time.at(Time.now, in: 'UTC').zone.should == "UTC"
+ Time.at(Time.now, in: 'Z').zone.should == "UTC"
+
+ ruby_version_is "3.1" do
+ Time.new(2022, 1, 1, 0, 0, 0, "-00:00").zone.should == "UTC"
+ Time.now.localtime("-00:00").zone.should == "UTC"
+ Time.at(Time.now, in: '-00:00').zone.should == "UTC"
+ end
+
+ ruby_version_is "3.1" do
+ Time.new(2022, 1, 1, 0, 0, 0, in: "UTC").zone.should == "UTC"
+ Time.new(2022, 1, 1, 0, 0, 0, in: "Z").zone.should == "UTC"
+
+ Time.now(in: 'UTC').zone.should == "UTC"
+ Time.now(in: 'Z').zone.should == "UTC"
+
+ Time.at(Time.now, in: 'UTC').zone.should == "UTC"
+ Time.at(Time.now, in: 'Z').zone.should == "UTC"
+ end
end
platform_is_not :aix, :windows do