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/_dump_spec.rb55
-rw-r--r--spec/ruby/core/time/_load_spec.rb51
-rw-r--r--spec/ruby/core/time/asctime_spec.rb6
-rw-r--r--spec/ruby/core/time/at_spec.rb316
-rw-r--r--spec/ruby/core/time/ceil_spec.rb44
-rw-r--r--spec/ruby/core/time/comparison_spec.rb130
-rw-r--r--spec/ruby/core/time/ctime_spec.rb6
-rw-r--r--spec/ruby/core/time/day_spec.rb6
-rw-r--r--spec/ruby/core/time/deconstruct_keys_spec.rb43
-rw-r--r--spec/ruby/core/time/dst_spec.rb6
-rw-r--r--spec/ruby/core/time/dup_spec.rb46
-rw-r--r--spec/ruby/core/time/eql_spec.rb29
-rw-r--r--spec/ruby/core/time/fixtures/classes.rb105
-rw-r--r--spec/ruby/core/time/floor_spec.rb36
-rw-r--r--spec/ruby/core/time/friday_spec.rb11
-rw-r--r--spec/ruby/core/time/getgm_spec.rb6
-rw-r--r--spec/ruby/core/time/getlocal_spec.rb206
-rw-r--r--spec/ruby/core/time/getutc_spec.rb6
-rw-r--r--spec/ruby/core/time/gm_spec.rb10
-rw-r--r--spec/ruby/core/time/gmt_offset_spec.rb6
-rw-r--r--spec/ruby/core/time/gmt_spec.rb8
-rw-r--r--spec/ruby/core/time/gmtime_spec.rb6
-rw-r--r--spec/ruby/core/time/gmtoff_spec.rb6
-rw-r--r--spec/ruby/core/time/hash_spec.rb11
-rw-r--r--spec/ruby/core/time/hour_spec.rb17
-rw-r--r--spec/ruby/core/time/inspect_spec.rb33
-rw-r--r--spec/ruby/core/time/isdst_spec.rb6
-rw-r--r--spec/ruby/core/time/iso8601_spec.rb6
-rw-r--r--spec/ruby/core/time/local_spec.rb11
-rw-r--r--spec/ruby/core/time/localtime_spec.rb203
-rw-r--r--spec/ruby/core/time/mday_spec.rb6
-rw-r--r--spec/ruby/core/time/min_spec.rb17
-rw-r--r--spec/ruby/core/time/minus_spec.rb121
-rw-r--r--spec/ruby/core/time/mktime_spec.rb11
-rw-r--r--spec/ruby/core/time/mon_spec.rb6
-rw-r--r--spec/ruby/core/time/monday_spec.rb11
-rw-r--r--spec/ruby/core/time/month_spec.rb6
-rw-r--r--spec/ruby/core/time/new_spec.rb753
-rw-r--r--spec/ruby/core/time/now_spec.rb181
-rw-r--r--spec/ruby/core/time/nsec_spec.rb31
-rw-r--r--spec/ruby/core/time/plus_spec.rb118
-rw-r--r--spec/ruby/core/time/round_spec.rb35
-rw-r--r--spec/ruby/core/time/saturday_spec.rb11
-rw-r--r--spec/ruby/core/time/sec_spec.rb7
-rw-r--r--spec/ruby/core/time/shared/asctime.rb6
-rw-r--r--spec/ruby/core/time/shared/day.rb15
-rw-r--r--spec/ruby/core/time/shared/getgm.rb9
-rw-r--r--spec/ruby/core/time/shared/gm.rb70
-rw-r--r--spec/ruby/core/time/shared/gmt_offset.rb59
-rw-r--r--spec/ruby/core/time/shared/gmtime.rb40
-rw-r--r--spec/ruby/core/time/shared/inspect.rb21
-rw-r--r--spec/ruby/core/time/shared/isdst.rb8
-rw-r--r--spec/ruby/core/time/shared/local.rb42
-rw-r--r--spec/ruby/core/time/shared/month.rb15
-rw-r--r--spec/ruby/core/time/shared/now.rb33
-rw-r--r--spec/ruby/core/time/shared/time_params.rb271
-rw-r--r--spec/ruby/core/time/shared/to_i.rb16
-rw-r--r--spec/ruby/core/time/shared/xmlschema.rb31
-rw-r--r--spec/ruby/core/time/strftime_spec.rb91
-rw-r--r--spec/ruby/core/time/subsec_spec.rb27
-rw-r--r--spec/ruby/core/time/sunday_spec.rb11
-rw-r--r--spec/ruby/core/time/thursday_spec.rb11
-rw-r--r--spec/ruby/core/time/time_spec.rb7
-rw-r--r--spec/ruby/core/time/to_a_spec.rb12
-rw-r--r--spec/ruby/core/time/to_f_spec.rb7
-rw-r--r--spec/ruby/core/time/to_i_spec.rb6
-rw-r--r--spec/ruby/core/time/to_r_spec.rb11
-rw-r--r--spec/ruby/core/time/to_s_spec.rb6
-rw-r--r--spec/ruby/core/time/tuesday_spec.rb11
-rw-r--r--spec/ruby/core/time/tv_nsec_spec.rb5
-rw-r--r--spec/ruby/core/time/tv_sec_spec.rb6
-rw-r--r--spec/ruby/core/time/tv_usec_spec.rb5
-rw-r--r--spec/ruby/core/time/usec_spec.rb43
-rw-r--r--spec/ruby/core/time/utc_offset_spec.rb6
-rw-r--r--spec/ruby/core/time/utc_spec.rb66
-rw-r--r--spec/ruby/core/time/wday_spec.rb9
-rw-r--r--spec/ruby/core/time/wednesday_spec.rb11
-rw-r--r--spec/ruby/core/time/xmlschema_spec.rb6
-rw-r--r--spec/ruby/core/time/yday_spec.rb12
-rw-r--r--spec/ruby/core/time/year_spec.rb17
-rw-r--r--spec/ruby/core/time/zone_spec.rb111
81 files changed, 3878 insertions, 0 deletions
diff --git a/spec/ruby/core/time/_dump_spec.rb b/spec/ruby/core/time/_dump_spec.rb
new file mode 100644
index 0000000000..852f9a07ab
--- /dev/null
+++ b/spec/ruby/core/time/_dump_spec.rb
@@ -0,0 +1,55 @@
+# encoding: binary
+require_relative '../../spec_helper'
+
+describe "Time#_dump" do
+ before :each do
+ @local = Time.at(946812800)
+ @t = Time.at(946812800)
+ @t = @t.gmtime
+ @s = @t.send(:_dump)
+ end
+
+ it "is a private method" do
+ Time.should have_private_instance_method(:_dump, false)
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/627
+ it "preserves the GMT flag" do
+ @t.should.gmt?
+ dump = @t.send(:_dump).unpack("VV").first
+ ((dump >> 30) & 0x1).should == 1
+
+ @local.should_not.gmt?
+ dump = @local.send(:_dump).unpack("VV").first
+ ((dump >> 30) & 0x1).should == 0
+ end
+
+ it "dumps a Time object to a bytestring" do
+ @s.should be_an_instance_of(String)
+ @s.should == [3222863947, 2235564032].pack("VV")
+ end
+
+ it "dumps an array with a date as first element" do
+ high = 1 << 31 |
+ (@t.gmt? ? 1 : 0) << 30 |
+ (@t.year - 1900) << 14 |
+ (@t.mon - 1) << 10 |
+ @t.mday << 5 |
+ @t.hour
+
+ high.should == @s.unpack("VV").first
+ end
+
+ it "dumps an array with a time as second element" do
+ low = @t.min << 26 |
+ @t.sec << 20 |
+ @t.usec
+ low.should == @s.unpack("VV").last
+ end
+
+ it "dumps like MRI's marshaled time format" do
+ t = Time.utc(2000, 1, 15, 20, 1, 1, 203).localtime
+
+ t.send(:_dump).should == "\364\001\031\200\313\000\020\004"
+ end
+end
diff --git a/spec/ruby/core/time/_load_spec.rb b/spec/ruby/core/time/_load_spec.rb
new file mode 100644
index 0000000000..30899de262
--- /dev/null
+++ b/spec/ruby/core/time/_load_spec.rb
@@ -0,0 +1,51 @@
+# encoding: binary
+require_relative '../../spec_helper'
+
+describe "Time._load" do
+ it "is a private method" do
+ Time.should have_private_method(:_load, false)
+ end
+
+ # http://redmine.ruby-lang.org/issues/show/627
+ it "loads a time object in the new format" do
+ t = Time.local(2000, 1, 15, 20, 1, 1)
+ t = t.gmtime
+
+ high = 1 << 31 |
+ (t.gmt? ? 1 : 0) << 30 |
+ (t.year - 1900) << 14 |
+ (t.mon - 1) << 10 |
+ t.mday << 5 |
+ t.hour
+
+ low = t.min << 26 |
+ t.sec << 20 |
+ t.usec
+
+ Time.send(:_load, [high, low].pack("VV")).should == t
+ end
+
+ it "loads a time object in the old UNIX timestamp based format" do
+ t = Time.local(2000, 1, 15, 20, 1, 1, 203)
+ timestamp = t.to_i
+
+ high = timestamp & ((1 << 31) - 1)
+
+ low = t.usec
+
+ Time.send(:_load, [high, low].pack("VV")).should == t
+ end
+
+ it "loads MRI's marshaled time format" do
+ t = Marshal.load("\004\bu:\tTime\r\320\246\e\200\320\001\r\347")
+ t.utc
+
+ t.to_s.should == "2010-10-22 16:57:48 UTC"
+ end
+
+ it "treats the data as binary data" do
+ 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
+end
diff --git a/spec/ruby/core/time/asctime_spec.rb b/spec/ruby/core/time/asctime_spec.rb
new file mode 100644
index 0000000000..a41ee531e4
--- /dev/null
+++ b/spec/ruby/core/time/asctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/asctime'
+
+describe "Time#asctime" do
+ it_behaves_like :time_asctime, :asctime
+end
diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb
new file mode 100644
index 0000000000..97906b8c8c
--- /dev/null
+++ b/spec/ruby/core/time/at_spec.rb
@@ -0,0 +1,316 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Time.at" do
+ describe "passed Numeric" do
+ it "returns a Time object representing the given number of Integer seconds since 1970-01-01 00:00:00 UTC" do
+ Time.at(1184027924).getgm.asctime.should == "Tue Jul 10 00:38:44 2007"
+ end
+
+ it "returns a Time object representing the given number of Float seconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10.5)
+ t.usec.should == 500000.0
+ t.should_not == Time.at(10)
+ end
+
+ it "returns a non-UTC Time" do
+ Time.at(1184027924).should_not.utc?
+ end
+
+ it "returns a subclass instance on a Time subclass" do
+ c = Class.new(Time)
+ t = c.at(0)
+ t.should be_an_instance_of(c)
+ end
+
+ it "roundtrips a Rational produced by #to_r" do
+ t = Time.now()
+ t2 = Time.at(t.to_r)
+
+ t2.should == t
+ t2.usec.should == t.usec
+ t2.nsec.should == t.nsec
+ 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))
+ t.usec.should == 539_759
+ t.nsec.should == 539_759_000
+ end
+
+ it "returns Time with correct nanoseconds" do
+ t = Time.at(Rational(1_486_570_508_539_759_123, 1_000_000_000))
+ t.usec.should == 539_759
+ t.nsec.should == 539_759_123
+ end
+ end
+ end
+
+ describe "passed Time" do
+ it "creates a new time object with the value given by time" do
+ t = Time.now
+ Time.at(t).inspect.should == t.inspect
+ end
+
+ it "creates a dup time object with the value given by time" do
+ t1 = Time.new
+ t2 = Time.at(t1)
+ t1.should_not equal t2
+ end
+
+ it "returns a UTC time if the argument is UTC" do
+ t = Time.now.getgm
+ Time.at(t).should.utc?
+ end
+
+ it "returns a non-UTC time if the argument is non-UTC" do
+ t = Time.now
+ Time.at(t).should_not.utc?
+ end
+
+ it "returns a subclass instance" do
+ c = Class.new(Time)
+ t = c.at(Time.now)
+ t.should be_an_instance_of(c)
+ end
+ end
+
+ describe "passed non-Time, non-Numeric" do
+ it "raises a TypeError with a String argument" do
+ -> { Time.at("0") }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError with a nil argument" do
+ -> { Time.at(nil) }.should raise_error(TypeError)
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(0)
+ Time.at(o).should == Time.at(0)
+ end
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.at(o).should == Time.at(Rational(5, 2))
+ end
+
+ it "needs for the argument to respond to #to_int too" do
+ o = mock('rational-but-no-to_int')
+ def o.to_r; Rational(5, 2) end
+ -> { Time.at(o) }.should raise_error(TypeError, "can't convert MockObject into an exact number")
+ end
+ end
+ end
+
+ describe "passed [Integer, Numeric]" do
+ it "returns a Time object representing the given number of seconds and Integer microseconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10, 500000)
+ t.tv_sec.should == 10
+ t.tv_usec.should == 500000
+ end
+
+ it "returns a Time object representing the given number of seconds and Float microseconds since 1970-01-01 00:00:00 UTC" do
+ t = Time.at(10, 500.500)
+ t.tv_sec.should == 10
+ t.tv_nsec.should == 500500
+ end
+ end
+
+ describe "with a second argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(10)
+ Time.at(0, o).should == Time.at(0, 10)
+ end
+ end
+
+ describe "with a second argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.at(0, o).should == Time.at(0, Rational(5, 2))
+ end
+ end
+
+ describe "passed [Integer, nil]" do
+ it "raises a TypeError" do
+ -> { Time.at(0, nil) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed [Integer, String]" do
+ it "raises a TypeError" do
+ -> { Time.at(0, "0") }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed [Time, Integer]" do
+ # #8173
+ it "raises a TypeError" do
+ -> { Time.at(Time.now, 500000) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "passed [Time, Numeric, format]" do
+ context ":nanosecond format" do
+ it "treats second argument as nanoseconds" do
+ Time.at(0, 123456789, :nanosecond).nsec.should == 123456789
+ end
+ end
+
+ context ":nsec format" do
+ it "treats second argument as nanoseconds" do
+ Time.at(0, 123456789, :nsec).nsec.should == 123456789
+ end
+ end
+
+ context ":microsecond format" do
+ it "treats second argument as microseconds" do
+ Time.at(0, 123456, :microsecond).nsec.should == 123456000
+ end
+ end
+
+ context ":usec format" do
+ it "treats second argument as microseconds" do
+ Time.at(0, 123456, :usec).nsec.should == 123456000
+ end
+ end
+
+ context ":millisecond format" do
+ it "treats second argument as milliseconds" do
+ Time.at(0, 123, :millisecond).nsec.should == 123000000
+ end
+ end
+
+ context "not supported format" do
+ it "raises ArgumentError" do
+ -> { Time.at(0, 123456, 2) }.should raise_error(ArgumentError)
+ -> { Time.at(0, 123456, nil) }.should raise_error(ArgumentError)
+ -> { Time.at(0, 123456, :invalid) }.should raise_error(ArgumentError)
+ end
+
+ it "does not try to convert format to Symbol with #to_sym" do
+ format = +"usec"
+ format.should_not_receive(:to_sym)
+ -> { Time.at(0, 123456, format) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "supports Float second argument" do
+ Time.at(0, 123456789.500, :nanosecond).nsec.should == 123456789
+ Time.at(0, 123456789.500, :nsec).nsec.should == 123456789
+ Time.at(0, 123456.500, :microsecond).nsec.should == 123456500
+ Time.at(0, 123456.500, :usec).nsec.should == 123456500
+ Time.at(0, 123.500, :millisecond).nsec.should == 123500000
+ end
+ 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")
+
+ 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.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+
+ time = Time.at(@epoch_time, in: "-09:00:01")
+
+ time.utc_offset.should == -(9*60*60 + 1)
+ 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)
+
+ 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.utc_offset.should == -9*60*60
+ time.zone.should == nil
+ time.to_i.should == @epoch_time
+ end
+
+ it "could be UTC offset as a 'UTC' String" do
+ time = Time.at(@epoch_time, in: "UTC")
+
+ time.utc_offset.should == 0
+ time.zone.should == "UTC"
+ time.to_i.should == @epoch_time
+ end
+
+ it "could be UTC offset as a military zone A-Z" do
+ time = Time.at(@epoch_time, in: "B")
+
+ 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
+
+ it "raises ArgumentError if hours greater than 23" do # TODO
+ -> { Time.at(@epoch_time, in: "+24:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.at(@epoch_time, in: "+2400") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.at(@epoch_time, in: "+99:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.at(@epoch_time, in: "+9900") }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+
+ it "raises ArgumentError if minutes greater than 59" do # TODO
+ -> { Time.at(@epoch_time, in: "+00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:60')
+ -> { Time.at(@epoch_time, in: "+0060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0060')
+
+ -> { Time.at(@epoch_time, in: "+00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:99')
+ -> { Time.at(@epoch_time, in: "+0099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0099')
+ end
+
+ ruby_bug '#20797', ''...'3.4' do
+ it "raises ArgumentError if seconds greater than 59" do
+ -> { Time.at(@epoch_time, in: "+00:00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:60')
+ -> { Time.at(@epoch_time, in: "+000060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000060')
+
+ -> { Time.at(@epoch_time, in: "+00:00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:99')
+ -> { Time.at(@epoch_time, in: "+000099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000099')
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/ceil_spec.rb b/spec/ruby/core/time/ceil_spec.rb
new file mode 100644
index 0000000000..9d624a1ed0
--- /dev/null
+++ b/spec/ruby/core/time/ceil_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../spec_helper'
+
+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
+
+ 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 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 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 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 "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 "copies own timezone to the returning value" do
+ @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/comparison_spec.rb b/spec/ruby/core/time/comparison_spec.rb
new file mode 100644
index 0000000000..866fbea72e
--- /dev/null
+++ b/spec/ruby/core/time/comparison_spec.rb
@@ -0,0 +1,130 @@
+require_relative '../../spec_helper'
+
+describe "Time#<=>" do
+ it "returns 1 if the first argument is a point in time after the second argument" do
+ (Time.now <=> Time.at(0)).should == 1
+ end
+
+ it "returns 1 if the first argument is a point in time after the second argument (down to a millisecond)" do
+ (Time.at(0, 1000) <=> Time.at(0, 0)).should == 1
+ (Time.at(1202778512, 1000) <=> Time.at(1202778512, 999)).should == 1
+ end
+
+ it "returns 1 if the first argument is a point in time after the second argument (down to a microsecond)" do
+ (Time.at(0, 100) <=> Time.at(0, 0)).should == 1
+ (Time.at(1202778512, 100) <=> Time.at(1202778512, 99)).should == 1
+ end
+
+ it "returns 0 if time is the same as other" do
+ (Time.at(1202778513) <=> Time.at(1202778513)).should == 0
+ (Time.at(100, 100) <=> Time.at(100, 100)).should == 0
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument" do
+ (Time.at(0) <=> Time.now).should == -1
+ (Time.at(100, 100) <=> Time.at(101, 100)).should == -1
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument (down to a millisecond)" do
+ (Time.at(0, 0) <=> Time.at(0, 1000)).should == -1
+ end
+
+ it "returns -1 if the first argument is a point in time before the second argument (down to a microsecond)" do
+ (Time.at(0, 0) <=> Time.at(0, 100)).should == -1
+ end
+
+ it "returns 1 if the first argument is a fraction of a microsecond after the second argument" do
+ (Time.at(100, Rational(1,1000)) <=> Time.at(100, 0)).should == 1
+ end
+
+ it "returns 0 if time is the same as other, including fractional microseconds" do
+ (Time.at(100, Rational(1,1000)) <=> Time.at(100, Rational(1,1000))).should == 0
+ end
+
+ it "returns -1 if the first argument is a fraction of a microsecond before the second argument" do
+ (Time.at(100, 0) <=> Time.at(100, Rational(1,1000))).should == -1
+ end
+
+ it "returns nil when compared to an Integer because Time does not respond to #coerce" do
+ time = Time.at(1)
+ time.respond_to?(:coerce).should == false
+ time.should_receive(:respond_to?).exactly(2).and_return(false)
+ -> {
+ (time <=> 2).should == nil
+ (2 <=> time).should == nil
+ }.should_not complain
+ end
+
+ context "given different timezones" do
+ it "returns 0 if time is the same as other" do
+ # three timezones, all at the same timestamp
+ time_utc = Time.new(2000, 1, 1, 0, 0, 0, 0)
+ time_cet = Time.new(2000, 1, 1, 1, 0, 0, '+01:00')
+ time_brt = Time.new(1999, 12, 31, 21, 0, 0, '-03:00')
+ (time_utc <=> time_cet).should == 0
+ (time_utc <=> time_brt).should == 0
+ (time_cet <=> time_brt).should == 0
+ end
+
+ it "returns -1 if the first argument is before the second argument" do
+ # time_brt is later, even though the date is earlier
+ time_utc = Time.new(2000, 1, 1, 0, 0, 0, 0)
+ time_brt = Time.new(1999, 12, 31, 23, 0, 0, '-03:00')
+ (time_utc <=> time_brt).should == -1
+ end
+
+ it "returns 1 if the first argument is before the second argument" do
+ # time_brt is later, even though the date is earlier
+ time_utc = Time.new(2000, 1, 1, 0, 0, 0, 0)
+ time_brt = Time.new(1999, 12, 31, 23, 0, 0, '-03:00')
+ (time_brt <=> time_utc).should == 1
+ end
+ end
+
+ describe "given a non-Time argument" do
+ it "returns nil if argument <=> self returns nil" do
+ t = Time.now
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(nil)
+ (t <=> obj).should == nil
+ end
+
+ it "returns -1 if argument <=> self is greater than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(true)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == -1
+ end
+
+ it "returns 1 if argument <=> self is not greater than 0 and is less than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(false)
+ r.should_receive(:<).with(0).and_return(true)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == 1
+ end
+
+ it "returns 0 if argument <=> self is neither greater than 0 nor less than 0" do
+ t = Time.now
+ r = mock('r')
+ r.should_receive(:>).with(0).and_return(false)
+ r.should_receive(:<).with(0).and_return(false)
+ obj = mock('time')
+ obj.should_receive(:<=>).with(t).and_return(r)
+ (t <=> obj).should == 0
+ end
+
+ it "returns nil if argument also uses an inverse comparison for <=>" do
+ t = Time.now
+ r = mock('r')
+ def r.<=>(other); other <=> self; end
+ r.should_receive(:<=>).once
+
+ (t <=> r).should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/core/time/ctime_spec.rb b/spec/ruby/core/time/ctime_spec.rb
new file mode 100644
index 0000000000..57e7cfd9ce
--- /dev/null
+++ b/spec/ruby/core/time/ctime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/asctime'
+
+describe "Time#ctime" do
+ it_behaves_like :time_asctime, :ctime
+end
diff --git a/spec/ruby/core/time/day_spec.rb b/spec/ruby/core/time/day_spec.rb
new file mode 100644
index 0000000000..895bcd7a86
--- /dev/null
+++ b/spec/ruby/core/time/day_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/day'
+
+describe "Time#day" do
+ it_behaves_like :time_day, :day
+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..b5cfdaa93f
--- /dev/null
+++ b/spec/ruby/core/time/deconstruct_keys_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+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
diff --git a/spec/ruby/core/time/dst_spec.rb b/spec/ruby/core/time/dst_spec.rb
new file mode 100644
index 0000000000..436240aae5
--- /dev/null
+++ b/spec/ruby/core/time/dst_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/isdst'
+
+describe "Time#dst?" do
+ it_behaves_like :time_isdst, :dst?
+end
diff --git a/spec/ruby/core/time/dup_spec.rb b/spec/ruby/core/time/dup_spec.rb
new file mode 100644
index 0000000000..5d6621beaa
--- /dev/null
+++ b/spec/ruby/core/time/dup_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../../spec_helper'
+
+describe "Time#dup" do
+ it "returns a Time object that represents the same time" do
+ t = Time.at(100)
+ t.dup.tv_sec.should == t.tv_sec
+ end
+
+ it "copies the gmt state flag" do
+ Time.now.gmtime.dup.should.gmt?
+ end
+
+ it "returns an independent Time object" do
+ t = Time.now
+ t2 = t.dup
+ t.gmtime
+
+ t2.should_not.gmt?
+ end
+
+ it "returns a subclass instance" do
+ c = Class.new(Time)
+ t = c.now
+
+ t.should be_an_instance_of(c)
+ t.dup.should be_an_instance_of(c)
+ end
+
+ it "returns a clone of Time instance" do
+ c = Time.dup
+ t = c.now
+
+ t.should be_an_instance_of(c)
+ t.should_not be_an_instance_of(Time)
+
+ t.dup.should be_an_instance_of(c)
+ t.dup.should_not be_an_instance_of(Time)
+ end
+
+ it "does not copy frozen status from the original" do
+ t = Time.now
+ t.freeze
+ t2 = t.dup
+ t2.frozen?.should be_false
+ end
+end
diff --git a/spec/ruby/core/time/eql_spec.rb b/spec/ruby/core/time/eql_spec.rb
new file mode 100644
index 0000000000..2ffb4eec96
--- /dev/null
+++ b/spec/ruby/core/time/eql_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+
+describe "Time#eql?" do
+ it "returns true if self and other have the same whole number of seconds" do
+ Time.at(100).should eql(Time.at(100))
+ end
+
+ it "returns false if self and other have differing whole numbers of seconds" do
+ Time.at(100).should_not eql(Time.at(99))
+ end
+
+ it "returns true if self and other have the same number of microseconds" do
+ Time.at(100, 100).should eql(Time.at(100, 100))
+ end
+
+ it "returns false if self and other have differing numbers of microseconds" do
+ Time.at(100, 100).should_not eql(Time.at(100, 99))
+ end
+
+ it "returns false if self and other have differing fractional microseconds" do
+ Time.at(100, Rational(100,1000)).should_not eql(Time.at(100, Rational(99,1000)))
+ end
+
+ it "returns false when given a non-time value" do
+ Time.at(100, 100).should_not eql("100")
+ Time.at(100, 100).should_not eql(100)
+ Time.at(100, 100).should_not eql(100.1)
+ end
+end
diff --git a/spec/ruby/core/time/fixtures/classes.rb b/spec/ruby/core/time/fixtures/classes.rb
new file mode 100644
index 0000000000..21c4e1effb
--- /dev/null
+++ b/spec/ruby/core/time/fixtures/classes.rb
@@ -0,0 +1,105 @@
+module TimeSpecs
+
+ class SubTime < Time; end
+
+ class MethodHolder
+ class << self
+ define_method(:now, &Time.method(:now))
+ define_method(:new, &Time.method(:new))
+ end
+ end
+
+ class Timezone
+ def initialize(options)
+ @offset = options[:offset]
+ end
+
+ def local_to_utc(t)
+ t - @offset
+ end
+
+ def utc_to_local(t)
+ t + @offset
+ end
+ end
+
+ class TimezoneMethodCallRecorder < Timezone
+ def initialize(options, &blk)
+ super(options)
+ @blk = blk
+ end
+
+ def local_to_utc(t)
+ @blk.call(t)
+ super
+ end
+
+ def utc_to_local(t)
+ @blk.call(t)
+ super
+ end
+ end
+
+ class TimeLikeArgumentRecorder
+ def self.result
+ arguments = []
+
+ zone = TimeSpecs::TimezoneMethodCallRecorder.new(offset: 0) do |obj|
+ arguments << obj
+ end
+
+ # ensure timezone's methods are called at least once
+ Time.new(2000, 1, 1, 12, 0, 0, zone)
+
+ return arguments[0]
+ end
+ end
+
+ Z = Struct.new(:offset, :abbr)
+ Zone = Struct.new(:std, :dst, :dst_range)
+ Zones = {
+ "Asia/Colombo" => Zone[Z[5*3600+30*60, "MMT"], nil, nil],
+ "PST" => Zone[Z[(-9*60*60), "PST"], nil, nil],
+ }
+
+ class TimezoneWithName < Timezone
+ attr_reader :name
+
+ def initialize(options)
+ @name = options[:name]
+ @std, @dst, @dst_range = *Zones[@name]
+ end
+
+ def dst?(t)
+ @dst_range&.cover?(t.mon)
+ end
+
+ def zone(t)
+ (dst?(t) ? @dst : @std)
+ end
+
+ def utc_offset(t)
+ zone(t)&.offset || 0
+ end
+
+ def abbr(t)
+ zone(t)&.abbr
+ end
+
+ def local_to_utc(t)
+ t - utc_offset(t)
+ end
+
+ def utc_to_local(t)
+ t + utc_offset(t)
+ end
+ end
+
+ class TimeWithFindTimezone < Time
+ def self.find_timezone(name)
+ TimezoneWithName.new(name: name.to_s)
+ end
+ end
+
+ TimezoneWithAbbr = TimezoneWithName
+end
diff --git a/spec/ruby/core/time/floor_spec.rb b/spec/ruby/core/time/floor_spec.rb
new file mode 100644
index 0000000000..b0003469c9
--- /dev/null
+++ b/spec/ruby/core/time/floor_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+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
+
+ 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 "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 "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 "copies own timezone to the returning value" do
+ @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/friday_spec.rb b/spec/ruby/core/time/friday_spec.rb
new file mode 100644
index 0000000000..8bee7f7558
--- /dev/null
+++ b/spec/ruby/core/time/friday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#friday?" do
+ it "returns true if time represents Friday" do
+ Time.local(2000, 1, 7).should.friday?
+ end
+
+ it "returns false if time doesn't represent Friday" do
+ Time.local(2000, 1, 1).should_not.friday?
+ end
+end
diff --git a/spec/ruby/core/time/getgm_spec.rb b/spec/ruby/core/time/getgm_spec.rb
new file mode 100644
index 0000000000..b5d45b1d9f
--- /dev/null
+++ b/spec/ruby/core/time/getgm_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getgm'
+
+describe "Time#getgm" do
+ it_behaves_like :time_getgm, :getgm
+end
diff --git a/spec/ruby/core/time/getlocal_spec.rb b/spec/ruby/core/time/getlocal_spec.rb
new file mode 100644
index 0000000000..398596f400
--- /dev/null
+++ b/spec/ruby/core/time/getlocal_spec.rb
@@ -0,0 +1,206 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Time#getlocal" do
+ it "returns a new time which is the local representation of time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime.should == Time.local(2007, 1, 9, 6, 0, 0)
+ end
+ end
+
+ it "returns a Time with UTC offset specified as an Integer number of seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(3630)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ t.zone.should be_nil
+ end
+
+ platform_is_not :windows do
+ it "returns a new time with the correct utc_offset according to the set timezone" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+ t.utc_offset.should == -3600
+
+ with_timezone("America/New_York") do
+ t.getlocal.utc_offset.should == -18000
+ end
+ end
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(3630)
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(Rational(7201, 2))
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(7201, 2))
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("+01:00")
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM:SS" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("+01:00:01")
+ t.should == Time.new(2007, 1, 9, 13, 0, 1, 3601)
+ t.utc_offset.should == 3601
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("-01:00")
+ t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600)
+ t.utc_offset.should == -3600
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM:SS" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("-01:00:01")
+ t.should == Time.new(2007, 1, 9, 10, 59, 59, -3601)
+ t.utc_offset.should == -3601
+ end
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+01:00")
+ t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ t = Time.now
+ -> { t.getlocal("3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ t = Time.now
+ -> { t.getlocal("-01:00".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
+ t = Time.new
+ t.getlocal(-86400 + 1).utc_offset.should == (-86400 + 1)
+ -> { t.getlocal(-86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ t = Time.new
+ t.getlocal(86400 - 1).utc_offset.should == (86400 - 1)
+ -> { t.getlocal(86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if String argument and hours greater than 23" do
+ -> { Time.now.getlocal("+24:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now.getlocal("+2400") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.now.getlocal("+99:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now.getlocal("+9900") }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+
+ it "raises ArgumentError if String argument and minutes greater than 59" do
+ -> { Time.now.getlocal("+00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:60')
+ -> { Time.now.getlocal("+0060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0060')
+
+ -> { Time.now.getlocal("+00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:99')
+ -> { Time.now.getlocal("+0099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0099')
+ end
+
+ ruby_bug '#20797', ''...'3.4' do
+ it "raises ArgumentError if String argument and seconds greater than 59" do
+ -> { Time.now.getlocal("+00:00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:60')
+ -> { Time.now.getlocal("+000060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000060')
+
+ -> { Time.now.getlocal("+00:00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:99')
+ -> { Time.now.getlocal("+000099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000099')
+ end
+ 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)
+
+ time.zone.should == zone
+ time.utc_offset.should == 5*3600+30*60
+ end
+
+ 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.utc(2000, 1, 1, 12, 0, 0).getlocal(zone).should be_kind_of(Time)
+ }.should_not raise_error
+ 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 raise_error(TypeError, /can't convert \w+ into an exact number/)
+ 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 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
+
+ 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
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/getutc_spec.rb b/spec/ruby/core/time/getutc_spec.rb
new file mode 100644
index 0000000000..0cd9c17b00
--- /dev/null
+++ b/spec/ruby/core/time/getutc_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/getgm'
+
+describe "Time#getutc" do
+ it_behaves_like :time_getgm, :getutc
+end
diff --git a/spec/ruby/core/time/gm_spec.rb b/spec/ruby/core/time/gm_spec.rb
new file mode 100644
index 0000000000..26dffbcedc
--- /dev/null
+++ b/spec/ruby/core/time/gm_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gm'
+require_relative 'shared/time_params'
+
+describe "Time.gm" do
+ it_behaves_like :time_gm, :gm
+ it_behaves_like :time_params, :gm
+ it_behaves_like :time_params_10_arg, :gm
+ it_behaves_like :time_params_microseconds, :gm
+end
diff --git a/spec/ruby/core/time/gmt_offset_spec.rb b/spec/ruby/core/time/gmt_offset_spec.rb
new file mode 100644
index 0000000000..df417e6e4e
--- /dev/null
+++ b/spec/ruby/core/time/gmt_offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#gmt_offset" do
+ it_behaves_like :time_gmt_offset, :gmt_offset
+end
diff --git a/spec/ruby/core/time/gmt_spec.rb b/spec/ruby/core/time/gmt_spec.rb
new file mode 100644
index 0000000000..840f59e0e8
--- /dev/null
+++ b/spec/ruby/core/time/gmt_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+
+describe "Time#gmt?" do
+ it "returns true if time represents a time in UTC (GMT)" do
+ Time.now.should_not.gmt?
+ Time.now.gmtime.should.gmt?
+ end
+end
diff --git a/spec/ruby/core/time/gmtime_spec.rb b/spec/ruby/core/time/gmtime_spec.rb
new file mode 100644
index 0000000000..d965cd541d
--- /dev/null
+++ b/spec/ruby/core/time/gmtime_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmtime'
+
+describe "Time#gmtime" do
+ it_behaves_like :time_gmtime, :gmtime
+end
diff --git a/spec/ruby/core/time/gmtoff_spec.rb b/spec/ruby/core/time/gmtoff_spec.rb
new file mode 100644
index 0000000000..fa28520e9e
--- /dev/null
+++ b/spec/ruby/core/time/gmtoff_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#gmtoff" do
+ it_behaves_like :time_gmt_offset, :gmtoff
+end
diff --git a/spec/ruby/core/time/hash_spec.rb b/spec/ruby/core/time/hash_spec.rb
new file mode 100644
index 0000000000..4f4d11a2cd
--- /dev/null
+++ b/spec/ruby/core/time/hash_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#hash" do
+ it "returns an Integer" do
+ Time.at(100).hash.should be_an_instance_of(Integer)
+ end
+
+ it "is stable" do
+ Time.at(1234).hash.should == Time.at(1234).hash
+ end
+end
diff --git a/spec/ruby/core/time/hour_spec.rb b/spec/ruby/core/time/hour_spec.rb
new file mode 100644
index 0000000000..ca69c25adb
--- /dev/null
+++ b/spec/ruby/core/time/hour_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#hour" do
+ it "returns the hour of the day (0..23) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1, 1).hour.should == 1
+ end
+ end
+
+ it "returns the hour of the day for a UTC Time" do
+ Time.utc(1970, 1, 1, 0).hour.should == 0
+ end
+
+ it "returns the hour of the day for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).hour.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/inspect_spec.rb b/spec/ruby/core/time/inspect_spec.rb
new file mode 100644
index 0000000000..c3a4519a24
--- /dev/null
+++ b/spec/ruby/core/time/inspect_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../../spec_helper'
+require_relative 'shared/inspect'
+
+describe "Time#inspect" do
+ it_behaves_like :inspect, :inspect
+
+ 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 "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 "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/isdst_spec.rb b/spec/ruby/core/time/isdst_spec.rb
new file mode 100644
index 0000000000..173230ca07
--- /dev/null
+++ b/spec/ruby/core/time/isdst_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/isdst'
+
+describe "Time#isdst" do
+ it_behaves_like :time_isdst, :isdst
+end
diff --git a/spec/ruby/core/time/iso8601_spec.rb b/spec/ruby/core/time/iso8601_spec.rb
new file mode 100644
index 0000000000..ad60c3bb32
--- /dev/null
+++ b/spec/ruby/core/time/iso8601_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/xmlschema'
+
+describe "Time#iso8601" do
+ it_behaves_like :time_xmlschema, :iso8601
+end
diff --git a/spec/ruby/core/time/local_spec.rb b/spec/ruby/core/time/local_spec.rb
new file mode 100644
index 0000000000..581ed171d5
--- /dev/null
+++ b/spec/ruby/core/time/local_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.local" do
+ it_behaves_like :time_local, :local
+ it_behaves_like :time_local_10_arg, :local
+ it_behaves_like :time_params, :local
+ it_behaves_like :time_params_10_arg, :local
+ it_behaves_like :time_params_microseconds, :local
+end
diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb
new file mode 100644
index 0000000000..71c0dfebde
--- /dev/null
+++ b/spec/ruby/core/time/localtime_spec.rb
@@ -0,0 +1,203 @@
+require_relative '../../spec_helper'
+
+describe "Time#localtime" do
+ it "converts self to local time, modifying the receiver" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime
+ t.should == Time.local(2007, 1, 9, 6, 0, 0)
+ end
+ end
+
+ it "returns self" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime.should equal(t)
+ end
+
+ it "converts time to the UTC offset specified as an Integer number of seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(3630)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+
+ describe "on a frozen time" do
+ it "does not raise an error if already in the right time zone" do
+ time = Time.now
+ time.freeze
+ time.localtime.should equal(time)
+ end
+
+ 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(FrozenError)
+ end
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(3630)
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630)
+ t.utc_offset.should == 3630
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(Rational(7201, 2))
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(7201, 2))
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2))
+ t.utc_offset.should eql(Rational(7201, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("+01:00")
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM:SS" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("+01:00:01")
+ t.should == Time.new(2007, 1, 9, 13, 0, 1, 3601)
+ t.utc_offset.should == 3601
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("-01:00")
+ t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600)
+ t.utc_offset.should == -3600
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM:SS" do
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime("-01:00:01")
+ t.should == Time.new(2007, 1, 9, 10, 59, 59, -3601)
+ t.utc_offset.should == -3601
+ 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
+
+ it "raises ArgumentError if String argument and hours greater than 23" do
+ -> { Time.now.localtime("+24:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now.localtime("+2400") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.now.localtime("+99:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now.localtime("+9900") }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+
+ it "raises ArgumentError if String argument and minutes greater than 59" do
+ -> { Time.now.localtime("+00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:60')
+ -> { Time.now.localtime("+0060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0060')
+
+ -> { Time.now.localtime("+00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:99')
+ -> { Time.now.localtime("+0099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0099')
+ end
+
+ ruby_bug '#20797', ''...'3.4' do
+ it "raises ArgumentError if String argument and seconds greater than 59" do
+ -> { Time.now.localtime("+00:00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:60')
+ -> { Time.now.localtime("+000060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000060')
+
+ -> { Time.now.localtime("+00:00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:99')
+ -> { Time.now.localtime("+000099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000099')
+ end
+ 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)
+ t.utc_offset.should == -3600
+
+ with_timezone("America/New_York") do
+ t.localtime
+ end
+
+ t.utc_offset.should == -18000
+ end
+
+ it "does nothing if already in a local time zone" do
+ time = with_timezone("America/New_York") do
+ Time.new(2005, 2, 27, 22, 50, 0)
+ end
+ zone = time.zone
+
+ with_timezone("Europe/Amsterdam") do
+ time.localtime
+ end
+
+ time.zone.should == zone
+ end
+ end
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+01:00")
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+ end
+
+ describe "with an argument that responds to #utc_to_local" do
+ it "coerces using #utc_to_local" do
+ o = mock('string')
+ o.should_receive(:utc_to_local).and_return(Time.new(2007, 1, 9, 13, 0, 0, 3600))
+ t = Time.gm(2007, 1, 9, 12, 0, 0)
+ t.localtime(o)
+ t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600)
+ t.utc_offset.should == 3600
+ end
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ t = Time.now
+ -> { t.localtime("3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ t = Time.now
+ -> { t.localtime("-01:00".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
+ t = Time.new
+ t.localtime(-86400 + 1).utc_offset.should == (-86400 + 1)
+ -> { t.localtime(-86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ t = Time.new
+ t.localtime(86400 - 1).utc_offset.should == (86400 - 1)
+ -> { t.localtime(86400) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/core/time/mday_spec.rb b/spec/ruby/core/time/mday_spec.rb
new file mode 100644
index 0000000000..3c21939890
--- /dev/null
+++ b/spec/ruby/core/time/mday_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/day'
+
+describe "Time#mday" do
+ it_behaves_like :time_day, :mday
+end
diff --git a/spec/ruby/core/time/min_spec.rb b/spec/ruby/core/time/min_spec.rb
new file mode 100644
index 0000000000..7d087d4046
--- /dev/null
+++ b/spec/ruby/core/time/min_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#min" do
+ it "returns the minute of the hour (0..59) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1, 0, 0).min.should == 0
+ end
+ end
+
+ it "returns the minute of the hour for a UTC Time" do
+ Time.utc(1970, 1, 1, 0, 0).min.should == 0
+ end
+
+ it "returns the minute of the hour for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).min.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/minus_spec.rb b/spec/ruby/core/time/minus_spec.rb
new file mode 100644
index 0000000000..9182d99652
--- /dev/null
+++ b/spec/ruby/core/time/minus_spec.rb
@@ -0,0 +1,121 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Time#-" do
+ it "decrements the time by the specified amount" do
+ (Time.at(100) - 100).should == Time.at(0)
+ (Time.at(100) - Time.at(99)).should == 1.0
+ end
+
+ it "understands negative subtractions" do
+ t = Time.at(100) - -1.3
+ t.usec.should == 300000
+ t.to_i.should == 101
+ end
+
+ #see [ruby-dev:38446]
+ it "accepts arguments that can be coerced into Rational" do
+ (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10))
+ (Time.at(100) - obj).should == Time.at(90)
+ end
+
+ it "raises a TypeError if given argument is a coercible String" do
+ -> { Time.now - "1" }.should raise_error(TypeError)
+ -> { Time.now - "0.1" }.should raise_error(TypeError)
+ -> { Time.now - "1/3" }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on argument that can't be coerced" do
+ -> { Time.now - Object.new }.should raise_error(TypeError)
+ -> { Time.now - "stuff" }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on nil argument" do
+ -> { Time.now - nil }.should raise_error(TypeError)
+ end
+
+ it "tracks microseconds" do
+ time = Time.at(0.777777)
+ time -= 0.654321
+ time.usec.should == 123456
+ time -= 1
+ time.usec.should == 123456
+ end
+
+ it "tracks microseconds from a Rational" do
+ time = Time.at(Rational(777_777, 1_000_000))
+ time -= Rational(654_321, 1_000_000)
+ time.usec.should == 123_456
+ time -= Rational(123_456, 1_000_000)
+ time.usec.should == 0
+ end
+
+ it "tracks nanoseconds" do
+ time = Time.at(Rational(999_999_999, 1_000_000_000))
+ time -= Rational(876_543_210, 1_000_000_000)
+ time.nsec.should == 123_456_789
+ time -= Rational(123_456_789, 1_000_000_000)
+ time.nsec.should == 0
+ end
+
+ it "maintains precision" do
+ time = Time.at(10) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000)
+ time.should_not == Time.at(9)
+ end
+
+ it "maintains microseconds precision" do
+ time = Time.at(10) - Rational(1, 1_000_000)
+ time.usec.should == 999_999
+ end
+
+ it "maintains nanoseconds precision" do
+ time = Time.at(10) - Rational(1, 1_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains subseconds precision" do
+ time = Time.at(0) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000)
+ time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000)
+ end
+
+ it "returns a UTC time if self is UTC" do
+ (Time.utc(2012) - 10).should.utc?
+ end
+
+ it "returns a non-UTC time if self is non-UTC" do
+ (Time.local(2012) - 10).should_not.utc?
+ end
+
+ it "returns a time with the same fixed offset as self" do
+ (Time.new(2012, 1, 1, 0, 0, 0, 3600) - 10).utc_offset.should == 3600
+ end
+
+ it "preserves time zone" do
+ time_with_zone = Time.now.utc
+ time_with_zone.zone.should == (time_with_zone - 1).zone
+
+ time_with_zone = Time.now
+ time_with_zone.zone.should == (time_with_zone - 1).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
+
+ it "does not return a subclass instance" do
+ c = Class.new(Time)
+ x = c.now - 1
+ x.should be_an_instance_of(Time)
+ end
+
+ it "returns a time with nanoseconds precision between two time objects" do
+ time1 = Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000))
+ time2 = Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ (time1 - time2).should == 86_399.999999998
+ end
+end
diff --git a/spec/ruby/core/time/mktime_spec.rb b/spec/ruby/core/time/mktime_spec.rb
new file mode 100644
index 0000000000..78a6a6e772
--- /dev/null
+++ b/spec/ruby/core/time/mktime_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.mktime" do
+ it_behaves_like :time_local, :mktime
+ it_behaves_like :time_local_10_arg, :mktime
+ it_behaves_like :time_params, :mktime
+ it_behaves_like :time_params_10_arg, :mktime
+ it_behaves_like :time_params_microseconds, :mktime
+end
diff --git a/spec/ruby/core/time/mon_spec.rb b/spec/ruby/core/time/mon_spec.rb
new file mode 100644
index 0000000000..f41b39648b
--- /dev/null
+++ b/spec/ruby/core/time/mon_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/month'
+
+describe "Time#mon" do
+ it_behaves_like :time_month, :mon
+end
diff --git a/spec/ruby/core/time/monday_spec.rb b/spec/ruby/core/time/monday_spec.rb
new file mode 100644
index 0000000000..47ecaeb1db
--- /dev/null
+++ b/spec/ruby/core/time/monday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#monday?" do
+ it "returns true if time represents Monday" do
+ Time.local(2000, 1, 3).should.monday?
+ end
+
+ it "returns false if time doesn't represent Monday" do
+ Time.local(2000, 1, 1).should_not.monday?
+ end
+end
diff --git a/spec/ruby/core/time/month_spec.rb b/spec/ruby/core/time/month_spec.rb
new file mode 100644
index 0000000000..81e20384ab
--- /dev/null
+++ b/spec/ruby/core/time/month_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/month'
+
+describe "Time#month" do
+ it_behaves_like :time_month, :month
+end
diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb
new file mode 100644
index 0000000000..dc3ccbdc00
--- /dev/null
+++ b/spec/ruby/core/time/new_spec.rb
@@ -0,0 +1,753 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/now'
+require_relative 'shared/local'
+require_relative 'shared/time_params'
+
+describe "Time.new" do
+ it_behaves_like :time_now, :new
+end
+
+describe "Time.new" do
+ it_behaves_like :time_local, :new
+ it_behaves_like :time_params, :new
+end
+
+describe "Time.new with a utc_offset argument" do
+ it "returns a non-UTC time" do
+ Time.new(2000, 1, 1, 0, 0, 0, 0).should_not.utc?
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Integer seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, 123).utc_offset.should == 123
+ end
+
+ describe "with an argument that responds to #to_int" do
+ it "coerces using #to_int" do
+ o = mock('integer')
+ o.should_receive(:to_int).and_return(123)
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 123
+ end
+ end
+
+ it "returns a Time with a UTC offset of the specified number of Rational seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, Rational(5, 2)).utc_offset.should eql(Rational(5, 2))
+ end
+
+ describe "with an argument that responds to #to_r" do
+ it "coerces using #to_r" do
+ o = mock_numeric('rational')
+ o.should_receive(:to_r).and_return(Rational(5, 2))
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should eql(Rational(5, 2))
+ end
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05:30").utc_offset.should == 19800
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10").utc_offset.should == -15000
+ end
+
+ it "returns a Time with a UTC offset specified as +HH:MM:SS" do
+ Time.new(2000, 1, 1, 0, 0, 0, "+05:30:37").utc_offset.should == 19837
+ end
+
+ it "returns a Time with a UTC offset specified as -HH:MM" do
+ Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043
+ 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 -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
+
+ describe "with an argument that responds to #to_str" do
+ it "coerces using #to_str" do
+ o = mock('string')
+ o.should_receive(:to_str).and_return("+05:30")
+ Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 19800
+ 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
+
+ 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
+
+ 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)
+ t.utc_offset.should == -28800
+ t.zone.should == "PST"
+ end
+ end
+
+ # [Bug #8679], r47676
+ it "disallows a value for minutes greater than 59" do
+ -> {
+ Time.new(2000, 1, 1, 0, 0, 0, "+01:60")
+ }.should raise_error(ArgumentError)
+ -> {
+ Time.new(2000, 1, 1, 0, 0, 0, "+01:99")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "3600") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the hour value is greater than 23" do
+ -> { Time.new(2000, 1, 1, 0, 0, 0, "+24:00") }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do
+ # 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
+ Time.new(2000, 1, 1, 0, 0, 0, -86400 + 1).utc_offset.should == (-86400 + 1)
+ -> { Time.new(2000, 1, 1, 0, 0, 0, -86400) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do
+ Time.new(2000, 1, 1, 0, 0, 0, 86400 - 1).utc_offset.should == (86400 - 1)
+ -> { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.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
+
+# The method #local_to_utc is tested only here because Time.new is the only method that calls #local_to_utc.
+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)
+
+ time.zone.should == zone
+ time.utc_offset.should == 5*3600+30*60
+ time.wday.should == 6
+ time.yday.should == 1
+ end
+
+ 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.new(2000, 1, 1, 12, 0, 0, zone).should be_kind_of(Time)
+ 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
+
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone)
+ }.should raise_error(TypeError, /can't convert Object 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)
+ end
+
+ # The result also should be a Time or Time-like object (not necessary to be the same class)
+ # or respond to #to_int method. 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 = Time.utc(t.year, t.mon, t.day, t.hour, t.min, t.sec)
+ time - 60 * 60 # - 1 hour
+ 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
+ end
+
+ it "could be Time subclass instance" do
+ zone = Object.new
+ def zone.local_to_utc(t)
+ time = Time.utc(t.year, t.mon, t.day, t.hour, t.min, t.sec)
+ time -= 60 * 60 # - 1 hour
+ Class.new(Time).utc(time.year, time.mon, time.day, time.hour, 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
+ end
+
+ it "could be any object with #to_i method" do
+ zone = Object.new
+ def zone.local_to_utc(time)
+ obj = Object.new
+ obj.singleton_class.define_method(:to_i) { time.to_i - 60*60 }
+ obj
+ 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
+ end
+
+ it "could have any #zone and #utc_offset because they are ignored if it isn't an instance of Time" 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
+
+ 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
+
+ it "cannot have arbitrary #utc_offset if it is an instance of Time" do
+ zone = Object.new
+ def zone.local_to_utc(t)
+ Time.new(t.year, t.mon, t.mday, t.hour, t.min, t.sec, 9*60*60)
+ end
+ Time.new(2000, 1, 1, 12, 0, 0, zone).utc_offset.should == 9*60*60
+ end
+
+ it "raises ArgumentError 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
+
+ -> {
+ Time.new(2000, 1, 1, 12, 0, 0, zone)
+ }.should raise_error(ArgumentError, "utc_offset out of range")
+ 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
+
+ it "implements subset of Time methods" do
+ # List only methods that are explicitly documented.
+ [
+ :year, :mon, :mday, :hour, :min, :sec, :to_i, :isdst
+ ].each do |name|
+ @obj.respond_to?(name).should == true
+ end
+ 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
+
+ @obj.utc_offset.should == 0
+ @obj.zone.should == "UTC"
+ @obj.isdst.should == Time.new.utc.isdst
+ end
+ 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|
+ -> {
+ 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
+
+ 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")
+
+ 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
+
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: "-09:00:01")
+
+ time.utc_offset.should == -(9*60*60 + 1)
+ time.zone.should == nil
+ end
+
+ 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)
+
+ 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
+
+ it "could be a timezone object" do
+ zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo")
+ time = Time.new(2000, 1, 1, 12, 0, 0, in: zone)
+
+ 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 "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
+ -> {
+ 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
+
+ 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
+
+ Time.new("2020-12-25T00:56:17.123456+09:00").should == Time.utc(2020, 12, 24, 15, 56, 17, 123456)
+ 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
+
+ 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
+
+ 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 "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
+
+ it "returns Time of Jan 1 for string with just year" do
+ Time.new("2021").should == Time.new(2021, 1, 1)
+ Time.new("2021").zone.should == Time.new(2021, 1, 1).zone
+ Time.new("2021").utc_offset.should == Time.new(2021, 1, 1).utc_offset
+ end
+
+ it "returns Time of Jan 1 for string with just year in timezone specified with in keyword argument" do
+ Time.new("2021", in: "+17:00").to_s.should == "2021-01-01 00:00:00 +1700"
+ end
+
+ 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 "returns Time with correct subseconds when given seconds fraction is shorted than 6 digits" do
+ Time.new("2020-12-25T00:56:17.123 +09:00").nsec.should == 123000000
+ Time.new("2020-12-25T00:56:17.123 +09:00").usec.should == 123000
+ Time.new("2020-12-25T00:56:17.123 +09:00").subsec.should == 0.123
+ end
+
+ it "returns Time with correct subseconds when given seconds fraction is milliseconds" do
+ Time.new("2020-12-25T00:56:17.123456 +09:00").nsec.should == 123456000
+ Time.new("2020-12-25T00:56:17.123456 +09:00").usec.should == 123456
+ Time.new("2020-12-25T00:56:17.123456 +09:00").subsec.should == 0.123456
+ end
+
+ it "returns Time with correct subseconds when given seconds fraction is longer that 6 digits but shorted than 9 digits" do
+ Time.new("2020-12-25T00:56:17.12345678 +09:00").nsec.should == 123456780
+ Time.new("2020-12-25T00:56:17.12345678 +09:00").usec.should == 123456
+ Time.new("2020-12-25T00:56:17.12345678 +09:00").subsec.should == 0.12345678
+ end
+
+ it "returns Time with correct subseconds when given seconds fraction is nanoseconds" do
+ Time.new("2020-12-25T00:56:17.123456789 +09:00").nsec.should == 123456789
+ Time.new("2020-12-25T00:56:17.123456789 +09:00").usec.should == 123456
+ Time.new("2020-12-25T00:56:17.123456789 +09:00").subsec.should == 0.123456789
+ end
+
+ it "returns Time with correct subseconds when given seconds fraction is longer than 9 digits" do
+ Time.new("2020-12-25T00:56:17.123456789876 +09:00").nsec.should == 123456789
+ Time.new("2020-12-25T00:56:17.123456789876 +09:00").usec.should == 123456
+ Time.new("2020-12-25T00:56:17.123456789876 +09:00").subsec.should == 0.123456789
+ 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 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 |can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00 +09:00")
+ }.should raise_error(ArgumentError, /missing min part: 00 |can't parse:/)
+ end
+
+ ruby_version_is "3.2.3" do
+ it "raises ArgumentError if the time part is missing" do
+ -> {
+ Time.new("2020-12-25")
+ }.should raise_error(ArgumentError, /no time information|can't parse:/)
+ end
+
+ it "raises ArgumentError if day is missing" do
+ -> {
+ Time.new("2020-12")
+ }.should raise_error(ArgumentError, /no time information|can't parse:/)
+ end
+ 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. |can't parse:/)
+ 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|can't parse:/)
+
+ -> {
+ Time.new("2020-012-25 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -012-25 00:\z|can't parse:/)
+
+ -> {
+ Time.new("2020-2-25 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mon is expected after [`']-': -2-25 00:56\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-215 00:56:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits mday is expected after [`']-': -215 00:56:\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 000:56:17 +0900")
+ }.should raise_error(ArgumentError, /two digits hour is expected: 000:56:17 |can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 0:56:17 +0900")
+ }.should raise_error(ArgumentError, /two digits hour is expected: 0:56:17 \+0|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00:516:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :516:17 \+09\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00:6:17 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits min is expected after [`']:': :6:17 \+0900\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00:56:137 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :137 \+0900\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00:56:7 +0900")
+ }.should raise_error(ArgumentError, /\Atwo digits sec is expected after [`']:': :7 \+0900\z|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00:56. +0900")
+ }.should raise_error(ArgumentError, /fraction min is not supported: 00:56\.|can't parse:/)
+
+ -> {
+ Time.new("2020-12-25 00. +0900")
+ }.should raise_error(ArgumentError, /fraction hour is not supported: 00\.|can't parse:/)
+ 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|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-32 00:56:17 +09:00")
+ }.should raise_error(ArgumentError, /(mday|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-25 25:56:17 +09:00")
+ }.should raise_error(ArgumentError, /(hour|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-25 00:61:17 +09:00")
+ }.should raise_error(ArgumentError, /(min|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-25 00:56:61 +09:00")
+ }.should raise_error(ArgumentError, /(sec|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +23:59:60")
+ }.should raise_error(ArgumentError, /utc_offset|argument out of range/)
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +24:00")
+ }.should raise_error(ArgumentError, /(utc_offset|argument) out of range/)
+
+ -> {
+ Time.new("2020-12-25 00:56:17 +23:61")
+ }.should raise_error(ArgumentError, /utc_offset/)
+
+ ruby_bug '#20797', ''...'3.4' do
+ -> {
+ Time.new("2020-12-25 00:56:17 +00:23:61")
+ }.should raise_error(ArgumentError, /utc_offset/)
+ end
+ end
+
+ it "raises ArgumentError if utc offset parts are not valid" do
+ -> { 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 +2400") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.new("2020-12-25 00:56:17 +99:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.new("2020-12-25 00:56:17 +9900") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.new("2020-12-25 00:56:17 +00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:60')
+ -> { Time.new("2020-12-25 00:56:17 +0060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0060')
+
+ -> { Time.new("2020-12-25 00:56:17 +00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:99')
+ -> { Time.new("2020-12-25 00:56:17 +0099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0099')
+
+ ruby_bug '#20797', ''...'3.4' do
+ -> { Time.new("2020-12-25 00:56:17 +00:00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:60')
+ -> { Time.new("2020-12-25 00:56:17 +000060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000060')
+
+ -> { Time.new("2020-12-25 00:56:17 +00:00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:99')
+ -> { Time.new("2020-12-25 00:56:17 +000099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000099')
+ end
+ 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
+
+ it "raises ArgumentError if string doesn't start with year" do
+ -> {
+ Time.new("a\nb")
+ }.should raise_error(ArgumentError, "can't parse: \"a\\nb\"")
+ end
+
+ it "raises ArgumentError if string has extra characters after offset" do
+ -> {
+ Time.new("2021-11-31 00:00:59 +09:00 abc")
+ }.should raise_error(ArgumentError, /can't parse.+ abc/)
+ end
+
+ ruby_version_is "3.2.3" do
+ it "raises ArgumentError when there are leading space characters" do
+ -> { Time.new(" 2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("\t2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("\n2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("\v2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("\f2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("\r2020-12-02 00:00:00") }.should raise_error(ArgumentError, /can't parse/)
+ end
+
+ it "raises ArgumentError when there are trailing whitespaces" do
+ -> { Time.new("2020-12-02 00:00:00 ") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("2020-12-02 00:00:00\t") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("2020-12-02 00:00:00\n") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("2020-12-02 00:00:00\v") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("2020-12-02 00:00:00\f") }.should raise_error(ArgumentError, /can't parse/)
+ -> { Time.new("2020-12-02 00:00:00\r") }.should raise_error(ArgumentError, /can't parse/)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/now_spec.rb b/spec/ruby/core/time/now_spec.rb
new file mode 100644
index 0000000000..e3fe6edad6
--- /dev/null
+++ b/spec/ruby/core/time/now_spec.rb
@@ -0,0 +1,181 @@
+require_relative '../../spec_helper'
+require_relative 'shared/now'
+
+describe "Time.now" do
+ it_behaves_like :time_now, :now
+
+ 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
+
+ time = Time.now(in: "-09:00:01")
+
+ time.utc_offset.should == -(9*60*60 + 1)
+ 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
+
+ it "raises ArgumentError if String argument and hours greater than 23" do
+ -> { Time.now(in: "+24:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now(in: "+2400") }.should raise_error(ArgumentError, "utc_offset out of range")
+
+ -> { Time.now(in: "+99:00") }.should raise_error(ArgumentError, "utc_offset out of range")
+ -> { Time.now(in: "+9900") }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+
+ it "raises ArgumentError if String argument and minutes greater than 59" do
+ -> { Time.now(in: "+00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:60')
+ -> { Time.now(in: "+0060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0060')
+
+ -> { Time.now(in: "+00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:99')
+ -> { Time.now(in: "+0099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +0099')
+ end
+
+ ruby_bug '#20797', ''...'3.4' do
+ it "raises ArgumentError if String argument and seconds greater than 59" do
+ -> { Time.now(in: "+00:00:60") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:60')
+ -> { Time.now(in: "+000060") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000060')
+
+ -> { Time.now(in: "+00:00:99") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +00:00:99')
+ -> { Time.now(in: "+000099") }.should raise_error(ArgumentError, '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: +000099')
+ end
+ end
+ end
+
+ describe "Timezone object" do # https://bugs.ruby-lang.org/issues/17485
+ 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.now(in: zone)
+ }.should raise_error(TypeError, /can't convert Object into an exact number/)
+ 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.now(in: zone).should be_kind_of(Time)
+ end
+
+ # The result also should be a Time or Time-like object (not necessary to be the same class)
+ # or Integer. 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(t)
+ time = Time.new(t.year, t.mon, t.day, t.hour, t.min, t.sec, t.utc_offset)
+ time + 60 * 60 # + 1 hour
+ end
+
+ Time.now(in: zone).should be_kind_of(Time)
+ Time.now(in: zone).utc_offset.should == 3600
+ end
+
+ it "could be Time subclass instance" do
+ zone = Object.new
+ def zone.utc_to_local(t)
+ time = Time.new(t.year, t.mon, t.day, t.hour, t.min, t.sec, t.utc_offset)
+ time += 60 * 60 # + 1 hour
+
+ Class.new(Time).new(time.year, time.mon, time.day, time.hour, time.min, time.sec, time.utc_offset)
+ end
+
+ Time.now(in: zone).should be_kind_of(Time)
+ Time.now(in: zone).utc_offset.should == 3600
+ end
+
+ it "could be Integer" do
+ zone = Object.new
+ def zone.utc_to_local(time)
+ time.to_i + 60*60
+ end
+
+ Time.now(in: zone).should be_kind_of(Time)
+ Time.now(in: zone).utc_offset.should == 60*60
+ end
+
+ it "could have any #zone and #utc_offset because they are ignored" do
+ zone = Object.new
+ def zone.utc_to_local(t)
+ Struct.new(:year, :mon, :mday, :hour, :min, :sec, :isdst, :to_i, :zone, :utc_offset) # rubocop:disable Lint/StructNewOverride
+ .new(t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.isdst, t.to_i, 'America/New_York', -5*60*60)
+ end
+ Time.now(in: zone).utc_offset.should == 0
+
+ zone = Object.new
+ def zone.utc_to_local(t)
+ Struct.new(:year, :mon, :mday, :hour, :min, :sec, :isdst, :to_i, :zone, :utc_offset) # rubocop:disable Lint/StructNewOverride
+ .new(t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.isdst, t.to_i, 'Asia/Tokyo', 9*60*60)
+ end
+ Time.now(in: zone).utc_offset.should == 0
+
+ zone = Object.new
+ def zone.utc_to_local(t)
+ Time.new(t.year, t.mon, t.mday, t.hour, t.min, t.sec, 9*60*60)
+ end
+ Time.now(in: zone).utc_offset.should == 0
+ end
+
+ it "raises ArgumentError if difference between argument and result is too large" do
+ zone = Object.new
+ def zone.utc_to_local(t)
+ time = Time.utc(t.year, t.mon, t.day, t.hour, t.min, t.sec, t.utc_offset)
+ time -= 24 * 60 * 60 # - 1 day
+ Time.utc(time.year, time.mon, time.day, time.hour, time.min, time.sec, time.utc_offset)
+ end
+
+ -> {
+ Time.now(in: zone)
+ }.should raise_error(ArgumentError, "utc_offset out of range")
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/nsec_spec.rb b/spec/ruby/core/time/nsec_spec.rb
new file mode 100644
index 0000000000..9338eb435a
--- /dev/null
+++ b/spec/ruby/core/time/nsec_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+describe "Time#nsec" do
+ it "returns 0 for a Time constructed with a whole number of seconds" do
+ Time.at(100).nsec.should == 0
+ end
+
+ it "returns the nanoseconds part of a Time constructed with a Float number of seconds" do
+ Time.at(10.75).nsec.should == 750_000_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999_999).nsec.should == 999_999_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Float number of microseconds" do
+ Time.at(0, 3.75).nsec.should == 3750
+ end
+
+ it "returns the nanoseconds part of a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).nsec.should == 500_000_000
+ end
+
+ it "returns the nanoseconds part of a Time constructed with an Rational number of microseconds" do
+ Time.at(0, Rational(99, 10)).nsec.should == 9900
+ end
+
+ it "returns a positive value for dates before the epoch" do
+ Time.utc(1969, 11, 12, 13, 18, 57, 404240).nsec.should == 404240000
+ end
+end
diff --git a/spec/ruby/core/time/plus_spec.rb b/spec/ruby/core/time/plus_spec.rb
new file mode 100644
index 0000000000..642393b615
--- /dev/null
+++ b/spec/ruby/core/time/plus_spec.rb
@@ -0,0 +1,118 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Time#+" do
+ it "increments the time by the specified amount" do
+ (Time.at(0) + 100).should == Time.at(100)
+ end
+
+ it "is a commutative operator" do
+ (Time.at(1.1) + 0.9).should == Time.at(0.9) + 1.1
+ end
+
+ it "adds a negative Float" do
+ t = Time.at(100) + -1.3
+ t.usec.should == 699999
+ t.to_i.should == 98
+ end
+
+ it "raises a TypeError if given argument is a coercible String" do
+ -> { Time.now + "1" }.should raise_error(TypeError)
+ -> { Time.now + "0.1" }.should raise_error(TypeError)
+ -> { Time.now + "1/3" }.should raise_error(TypeError)
+ end
+
+ it "increments the time by the specified amount as rational numbers" do
+ (Time.at(Rational(11, 10)) + Rational(9, 10)).should == Time.at(2)
+ end
+
+ it "accepts arguments that can be coerced into Rational" do
+ (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10))
+ (Time.at(100) + obj).should == Time.at(110)
+ end
+
+ it "raises TypeError on argument that can't be coerced into Rational" do
+ -> { Time.now + Object.new }.should raise_error(TypeError)
+ -> { Time.now + "stuff" }.should raise_error(TypeError)
+ end
+
+ it "returns a UTC time if self is UTC" do
+ (Time.utc(2012) + 10).should.utc?
+ end
+
+ it "returns a non-UTC time if self is non-UTC" do
+ (Time.local(2012) + 10).should_not.utc?
+ end
+
+ it "returns a time with the same fixed offset as self" do
+ (Time.new(2012, 1, 1, 0, 0, 0, 3600) + 10).utc_offset.should == 3600
+ end
+
+ it "preserves time zone" do
+ time_with_zone = Time.now.utc
+ time_with_zone.zone.should == (time_with_zone + 1).zone
+
+ time_with_zone = Time.now
+ time_with_zone.zone.should == (time_with_zone + 1).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
+
+ it "does not return a subclass instance" do
+ c = Class.new(Time)
+ x = c.now + 1
+ x.should be_an_instance_of(Time)
+ end
+
+ it "raises TypeError on Time argument" do
+ -> { Time.now + Time.now }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError on nil argument" do
+ -> { Time.now + nil }.should raise_error(TypeError)
+ end
+
+ #see [ruby-dev:38446]
+ it "tracks microseconds" do
+ time = Time.at(0)
+ time += Rational(123_456, 1_000_000)
+ time.usec.should == 123_456
+ time += Rational(654_321, 1_000_000)
+ time.usec.should == 777_777
+ end
+
+ it "tracks nanoseconds" do
+ time = Time.at(0)
+ time += Rational(123_456_789, 1_000_000_000)
+ time.nsec.should == 123_456_789
+ time += Rational(876_543_210, 1_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains precision" do
+ t = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ t.should_not == Time.at(9)
+ end
+
+ it "maintains microseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.usec.should == 999_999
+ end
+
+ it "maintains nanoseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.nsec.should == 999_999_999
+ end
+
+ it "maintains subseconds precision" do
+ time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000)
+ time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000)
+ end
+end
diff --git a/spec/ruby/core/time/round_spec.rb b/spec/ruby/core/time/round_spec.rb
new file mode 100644
index 0000000000..0cbed04ade
--- /dev/null
+++ b/spec/ruby/core/time/round_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../../spec_helper'
+
+describe "Time#round" do
+ before do
+ @time = Time.utc(2010, 3, 30, 5, 43, "25.123456789".to_r)
+ end
+
+ it "defaults to rounding to 0 places" do
+ @time.round.should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
+
+ it "rounds to 0 decimal places with an explicit argument" do
+ @time.round(0).should == Time.utc(2010, 3, 30, 5, 43, 25.to_r)
+ end
+
+ it "rounds to 7 decimal places with an explicit argument" do
+ @time.round(7).should == Time.utc(2010, 3, 30, 5, 43, "25.1234568".to_r)
+ end
+
+ it "returns an instance of Time, even if #round is called on a subclass" do
+ subclass = Class.new(Time)
+ instance = subclass.at(0)
+ instance.class.should equal subclass
+ instance.round.should be_an_instance_of(Time)
+ end
+
+ it "copies own timezone to the returning value" do
+ @time.zone.should == @time.round.zone
+
+ with_timezone "JST-9" do
+ time = Time.at 0, 1
+ time.zone.should == time.round.zone
+ end
+ end
+end
diff --git a/spec/ruby/core/time/saturday_spec.rb b/spec/ruby/core/time/saturday_spec.rb
new file mode 100644
index 0000000000..0e51407366
--- /dev/null
+++ b/spec/ruby/core/time/saturday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#saturday?" do
+ it "returns true if time represents Saturday" do
+ Time.local(2000, 1, 1).should.saturday?
+ end
+
+ it "returns false if time doesn't represent Saturday" do
+ Time.local(2000, 1, 2).should_not.saturday?
+ end
+end
diff --git a/spec/ruby/core/time/sec_spec.rb b/spec/ruby/core/time/sec_spec.rb
new file mode 100644
index 0000000000..73fc5ce1fc
--- /dev/null
+++ b/spec/ruby/core/time/sec_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time#sec" do
+ it "returns the second of the minute(0..60) for time" do
+ Time.at(0).sec.should == 0
+ end
+end
diff --git a/spec/ruby/core/time/shared/asctime.rb b/spec/ruby/core/time/shared/asctime.rb
new file mode 100644
index 0000000000..d096666863
--- /dev/null
+++ b/spec/ruby/core/time/shared/asctime.rb
@@ -0,0 +1,6 @@
+describe :time_asctime, shared: true do
+ it "returns a canonical string representation of time" do
+ t = Time.now
+ t.send(@method).should == t.strftime("%a %b %e %H:%M:%S %Y")
+ end
+end
diff --git a/spec/ruby/core/time/shared/day.rb b/spec/ruby/core/time/shared/day.rb
new file mode 100644
index 0000000000..472dc959c1
--- /dev/null
+++ b/spec/ruby/core/time/shared/day.rb
@@ -0,0 +1,15 @@
+describe :time_day, shared: true do
+ it "returns the day of the month (1..n) for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1, 1).send(@method).should == 1
+ end
+ end
+
+ it "returns the day of the month for a UTC Time" do
+ Time.utc(1970, 1, 1).send(@method).should == 1
+ end
+
+ it "returns the day of the month for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1
+ end
+end
diff --git a/spec/ruby/core/time/shared/getgm.rb b/spec/ruby/core/time/shared/getgm.rb
new file mode 100644
index 0000000000..3576365772
--- /dev/null
+++ b/spec/ruby/core/time/shared/getgm.rb
@@ -0,0 +1,9 @@
+describe :time_getgm, shared: true do
+ it "returns a new time which is the utc representation of time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 6, 0, 0)
+ t.send(@method).should == Time.gm(2007, 1, 9, 12, 0, 0)
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gm.rb b/spec/ruby/core/time/shared/gm.rb
new file mode 100644
index 0000000000..0ee602c837
--- /dev/null
+++ b/spec/ruby/core/time/shared/gm.rb
@@ -0,0 +1,70 @@
+describe :time_gm, shared: true do
+ it "creates a time based on given values, interpreted as UTC (GMT)" do
+ Time.send(@method, 2000,"jan",1,20,15,1).inspect.should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "creates a time based on given C-style gmtime arguments, interpreted as UTC (GMT)" do
+ time = Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ time.inspect.should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "interprets pre-Gregorian reform dates using Gregorian proleptic calendar" do
+ Time.send(@method, 1582, 10, 4, 12).to_i.should == -12220200000 # 2299150j
+ end
+
+ it "interprets Julian-Gregorian gap dates using Gregorian proleptic calendar" do
+ Time.send(@method, 1582, 10, 14, 12).to_i.should == -12219336000 # 2299160j
+ end
+
+ it "interprets post-Gregorian reform dates using Gregorian calendar" do
+ Time.send(@method, 1582, 10, 15, 12).to_i.should == -12219249600 # 2299161j
+ end
+
+ it "handles fractional usec close to rounding limit" do
+ time = Time.send(@method, 2000, 1, 1, 12, 30, 0, 9999r/10000)
+
+ time.usec.should == 0
+ time.nsec.should == 999
+ end
+
+ guard -> {
+ with_timezone 'right/UTC' do
+ (Time.gm(1972, 6, 30, 23, 59, 59) + 1).sec == 60
+ end
+ } do
+ it "handles real leap seconds in zone 'right/UTC'" do
+ with_timezone 'right/UTC' do
+ time = Time.send(@method, 1972, 6, 30, 23, 59, 60)
+
+ time.sec.should == 60
+ time.min.should == 59
+ time.hour.should == 23
+ time.day.should == 30
+ time.month.should == 6
+ end
+ end
+ end
+
+ it "handles bad leap seconds by carrying values forward" do
+ with_timezone 'UTC' do
+ time = Time.send(@method, 2017, 7, 5, 23, 59, 60)
+ time.sec.should == 0
+ time.min.should == 0
+ time.hour.should == 0
+ time.day.should == 6
+ time.month.should == 7
+ end
+ end
+
+ it "handles a value of 60 for seconds by carrying values forward in zone 'UTC'" do
+ with_timezone 'UTC' do
+ time = Time.send(@method, 1972, 6, 30, 23, 59, 60)
+
+ time.sec.should == 0
+ time.min.should == 0
+ time.hour.should == 0
+ time.day.should == 1
+ time.month.should == 7
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gmt_offset.rb b/spec/ruby/core/time/shared/gmt_offset.rb
new file mode 100644
index 0000000000..839566c249
--- /dev/null
+++ b/spec/ruby/core/time/shared/gmt_offset.rb
@@ -0,0 +1,59 @@
+describe :time_gmt_offset, shared: true do
+ it "returns the offset in seconds between the timezone of time and UTC" do
+ with_timezone("AST", 3) do
+ Time.new.send(@method).should == 10800
+ end
+ end
+
+ it "returns 0 when the date is UTC" do
+ with_timezone("AST", 3) do
+ Time.new.utc.send(@method).should == 0
+ end
+ end
+
+ platform_is_not :windows do
+ it "returns the correct offset for US Eastern time zone around daylight savings time change" do
+ # "2010-03-14 01:59:59 -0500" + 1 ==> "2010-03-14 03:00:00 -0400"
+ with_timezone("EST5EDT") do
+ t = Time.local(2010,3,14,1,59,59)
+ t.send(@method).should == -5*60*60
+ (t + 1).send(@method).should == -4*60*60
+ end
+ end
+
+ it "returns the correct offset for Hawaii around daylight savings time change" do
+ # "2010-03-14 01:59:59 -1000" + 1 ==> "2010-03-14 02:00:00 -1000"
+ with_timezone("Pacific/Honolulu") do
+ t = Time.local(2010,3,14,1,59,59)
+ t.send(@method).should == -10*60*60
+ (t + 1).send(@method).should == -10*60*60
+ end
+ end
+
+ it "returns the correct offset for New Zealand around daylight savings time change" do
+ # "2010-04-04 02:59:59 +1300" + 1 ==> "2010-04-04 02:00:00 +1200"
+ with_timezone("Pacific/Auckland") do
+ t = Time.local(2010,4,4,1,59,59) + (60 * 60)
+ t.send(@method).should == 13*60*60
+ (t + 1).send(@method).should == 12*60*60
+ end
+ end
+ end
+
+ it "returns offset as Rational" do
+ Time.new(2010,4,4,1,59,59,7245).send(@method).should == 7245
+ Time.new(2010,4,4,1,59,59,7245.5).send(@method).should == Rational(14491,2)
+ end
+
+ context 'given positive offset' do
+ it 'returns a positive offset' do
+ Time.new(2013,3,17,nil,nil,nil,"+03:00").send(@method).should == 10800
+ end
+ end
+
+ context 'given negative offset' do
+ it 'returns a negative offset' do
+ Time.new(2013,3,17,nil,nil,nil,"-03:00").send(@method).should == -10800
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb
new file mode 100644
index 0000000000..7b4f65f0b7
--- /dev/null
+++ b/spec/ruby/core/time/shared/gmtime.rb
@@ -0,0 +1,40 @@
+describe :time_gmtime, shared: true do
+ it "converts self to UTC, modifying the receiver" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 6, 0, 0)
+ t.send(@method)
+ # Time#== compensates for time zones, so check all parts separately
+ t.year.should == 2007
+ t.month.should == 1
+ t.mday.should == 9
+ t.hour.should == 12
+ t.min.should == 0
+ t.sec.should == 0
+ t.zone.should == "UTC"
+ end
+ end
+
+ it "returns self" do
+ with_timezone("CST", -6) do
+ t = Time.local(2007, 1, 9, 12, 0, 0)
+ t.send(@method).should equal(t)
+ end
+ end
+
+ describe "on a frozen time" do
+ it "does not raise an error if already in UTC" do
+ time = Time.gm(2007, 1, 9, 12, 0, 0)
+ time.freeze
+ time.send(@method).should equal(time)
+ end
+
+ 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(FrozenError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/inspect.rb b/spec/ruby/core/time/shared/inspect.rb
new file mode 100644
index 0000000000..4133671924
--- /dev/null
+++ b/spec/ruby/core/time/shared/inspect.rb
@@ -0,0 +1,21 @@
+# -*- encoding: us-ascii -*-
+
+describe :inspect, shared: true do
+ it "formats the local time following the pattern 'yyyy-MM-dd HH:mm:ss Z'" do
+ with_timezone("PST", +1) do
+ Time.local(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 +0100"
+ end
+ end
+
+ it "formats the UTC time following the pattern 'yyyy-MM-dd HH:mm:ss UTC'" do
+ Time.utc(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 UTC"
+ end
+
+ it "formats the fixed offset time following the pattern 'yyyy-MM-dd HH:mm:ss +/-HHMM'" do
+ Time.new(2000, 1, 1, 20, 15, 01, 3600).send(@method).should == "2000-01-01 20:15:01 +0100"
+ end
+
+ it "returns a US-ASCII encoded string" do
+ Time.now.send(@method).encoding.should equal(Encoding::US_ASCII)
+ end
+end
diff --git a/spec/ruby/core/time/shared/isdst.rb b/spec/ruby/core/time/shared/isdst.rb
new file mode 100644
index 0000000000..bc6d139230
--- /dev/null
+++ b/spec/ruby/core/time/shared/isdst.rb
@@ -0,0 +1,8 @@
+describe :time_isdst, shared: true do
+ it "dst? returns whether time is during daylight saving time" do
+ with_timezone("America/Los_Angeles") do
+ Time.local(2007, 9, 9, 0, 0, 0).send(@method).should == true
+ Time.local(2007, 1, 9, 0, 0, 0).send(@method).should == false
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/local.rb b/spec/ruby/core/time/shared/local.rb
new file mode 100644
index 0000000000..068e314999
--- /dev/null
+++ b/spec/ruby/core/time/shared/local.rb
@@ -0,0 +1,42 @@
+describe :time_local, shared: true do
+ it "creates a time based on given values, interpreted in the local time zone" do
+ with_timezone("PST", -8) do
+ Time.send(@method, 2000, "jan", 1, 20, 15, 1).to_a.should ==
+ [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"]
+ end
+ end
+
+ platform_is_not :windows do
+ 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
+end
+
+describe :time_local_10_arg, shared: true do
+ it "creates a time based on given C-style gmtime arguments, interpreted in the local time zone" do
+ with_timezone("PST", -8) do
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored).to_a.should ==
+ [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"]
+ end
+ end
+
+ platform_is_not :windows do
+ it "creates the correct time just before dst change" do
+ with_timezone("America/New_York") do
+ time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, true, ENV['TZ'])
+ time.utc_offset.should == -4 * 3600
+ end
+ end
+
+ it "creates the correct time just after dst change" do
+ with_timezone("America/New_York") do
+ time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, false, ENV['TZ'])
+ time.utc_offset.should == -5 * 3600
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/shared/month.rb b/spec/ruby/core/time/shared/month.rb
new file mode 100644
index 0000000000..31ca679557
--- /dev/null
+++ b/spec/ruby/core/time/shared/month.rb
@@ -0,0 +1,15 @@
+describe :time_month, shared: true do
+ it "returns the month of the year for a local Time" do
+ with_timezone("CET", 1) do
+ Time.local(1970, 1).send(@method).should == 1
+ end
+ end
+
+ it "returns the month of the year for a UTC Time" do
+ Time.utc(1970, 1).send(@method).should == 1
+ end
+
+ it "returns the four digit year for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1
+ end
+end
diff --git a/spec/ruby/core/time/shared/now.rb b/spec/ruby/core/time/shared/now.rb
new file mode 100644
index 0000000000..f4018d72f4
--- /dev/null
+++ b/spec/ruby/core/time/shared/now.rb
@@ -0,0 +1,33 @@
+require_relative '../fixtures/classes'
+
+describe :time_now, shared: true do
+ it "creates a subclass instance if called on a subclass" do
+ TimeSpecs::SubTime.send(@method).should be_an_instance_of(TimeSpecs::SubTime)
+ TimeSpecs::MethodHolder.send(@method).should be_an_instance_of(Time)
+ end
+
+ it "sets the current time" do
+ now = TimeSpecs::MethodHolder.send(@method)
+ now.to_f.should be_close(Process.clock_gettime(Process::CLOCK_REALTIME), TIME_TOLERANCE)
+ end
+
+ it "uses the local timezone" do
+ with_timezone("PDT", -8) do
+ now = TimeSpecs::MethodHolder.send(@method)
+ now.utc_offset.should == (-8 * 60 * 60)
+ end
+ end
+
+ it "has at least microsecond precision" do
+ # The clock should not be less accurate than expected (times should
+ # not all be a multiple of the next precision up, assuming precisions
+ # are multiples of ten.)
+ expected = 1_000
+ t = 0
+ 10_000.times.find do
+ t = Time.now.nsec
+ t % (expected * 10) != 0
+ end
+ (t % (expected * 10)).should != 0
+ end
+end
diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb
new file mode 100644
index 0000000000..9832fd17fe
--- /dev/null
+++ b/spec/ruby/core/time/shared/time_params.rb
@@ -0,0 +1,271 @@
+describe :time_params, shared: true do
+ it "accepts 1 argument (year)" do
+ Time.send(@method, 2000).should ==
+ Time.send(@method, 2000, 1, 1, 0, 0, 0)
+ end
+
+ it "accepts 2 arguments (year, month)" do
+ Time.send(@method, 2000, 2).should ==
+ Time.send(@method, 2000, 2, 1, 0, 0, 0)
+ end
+
+ it "accepts 3 arguments (year, month, day)" do
+ Time.send(@method, 2000, 2, 3).should ==
+ Time.send(@method, 2000, 2, 3, 0, 0, 0)
+ end
+
+ it "accepts 4 arguments (year, month, day, hour)" do
+ Time.send(@method, 2000, 2, 3, 4).should ==
+ Time.send(@method, 2000, 2, 3, 4, 0, 0)
+ end
+
+ it "accepts 5 arguments (year, month, day, hour, minute)" do
+ Time.send(@method, 2000, 2, 3, 4, 5).should ==
+ Time.send(@method, 2000, 2, 3, 4, 5, 0)
+ end
+
+ it "accepts a too big day of the month by going to the next month" do
+ Time.send(@method, 1999, 2, 31).should ==
+ Time.send(@method, 1999, 3, 3)
+ end
+
+ it "raises a TypeError if the year is nil" do
+ -> { Time.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "accepts nil month, day, hour, minute, and second" do
+ Time.send(@method, 2000, nil, nil, nil, nil, nil).should ==
+ Time.send(@method, 2000)
+ end
+
+ it "handles a String year" do
+ Time.send(@method, "2000").should ==
+ Time.send(@method, 2000)
+ end
+
+ it "coerces the year with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, m).should == Time.send(@method, 1)
+ end
+
+ it "handles a String month given as a numeral" do
+ Time.send(@method, 2000, "12").should ==
+ Time.send(@method, 2000, 12)
+ end
+
+ it "handles a String month given as a short month name" do
+ Time.send(@method, 2000, "dec").should ==
+ Time.send(@method, 2000, 12)
+ end
+
+ it "coerces the month with #to_str" do
+ (obj = mock('12')).should_receive(:to_str).and_return("12")
+ Time.send(@method, 2008, obj).should ==
+ Time.send(@method, 2008, 12)
+ end
+
+ it "coerces the month with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, m).should == Time.send(@method, 2008, 1)
+ end
+
+ it "handles a String day" do
+ Time.send(@method, 2000, 12, "15").should ==
+ Time.send(@method, 2000, 12, 15)
+ end
+
+ it "coerces the day with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, m).should == Time.send(@method, 2008, 1, 1)
+ end
+
+ it "handles a String hour" do
+ Time.send(@method, 2000, 12, 1, "5").should ==
+ Time.send(@method, 2000, 12, 1, 5)
+ end
+
+ it "coerces the hour with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, m).should == Time.send(@method, 2008, 1, 1, 1)
+ end
+
+ it "handles a String minute" do
+ Time.send(@method, 2000, 12, 1, 1, "8").should ==
+ Time.send(@method, 2000, 12, 1, 1, 8)
+ end
+
+ it "coerces the minute with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 1)
+ end
+
+ it "handles a String second" do
+ Time.send(@method, 2000, 12, 1, 1, 1, "8").should ==
+ Time.send(@method, 2000, 12, 1, 1, 1, 8)
+ end
+
+ it "coerces the second with #to_int" do
+ m = mock(:int)
+ m.should_receive(:to_int).and_return(1)
+ Time.send(@method, 2008, 1, 1, 0, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 0, 1)
+ end
+
+ it "interprets all numerals as base 10" do
+ Time.send(@method, "2000", "08", "08", "08", "08", "08").should == Time.send(@method, 2000, 8, 8, 8, 8, 8)
+ Time.send(@method, "2000", "09", "09", "09", "09", "09").should == Time.send(@method, 2000, 9, 9, 9, 9, 9)
+ end
+
+ it "handles fractional seconds as a Float" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75)
+ t.sec.should == 1
+ t.usec.should == 750000
+ end
+
+ it "handles fractional seconds as a Rational" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, Rational(99, 10))
+ t.sec.should == 9
+ t.usec.should == 900000
+ end
+
+ it "handles years from 0 as such" do
+ 0.upto(2100) do |year|
+ t = Time.send(@method, year)
+ t.year.should == year
+ end
+ end
+
+ it "accepts various year ranges" do
+ Time.send(@method, 1801, 12, 31, 23, 59, 59).wday.should == 4
+ Time.send(@method, 3000, 12, 31, 23, 59, 59).wday.should == 3
+ 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, 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
+ -> {
+ Time.send(@method, 2008, 12, 32, 23, 59, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range hour" do
+ -> {
+ Time.send(@method, 2008, 12, 31, 25, 59, 59)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for out of range minute" do
+ -> {
+ Time.send(@method, 2008, 12, 31, 23, 61, 59)
+ }.should raise_error(ArgumentError)
+ 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, /(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 8 arguments" do
+ -> { Time.send(@method, *[0]*8) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when given 9 arguments" do
+ -> { Time.send(@method, *[0]*9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError when given 11 arguments" do
+ -> { Time.send(@method, *[0]*11) }.should raise_error(ArgumentError)
+ end
+
+ it "returns subclass instances" do
+ c = Class.new(Time)
+ c.send(@method, 2008, "12").should be_an_instance_of(c)
+ end
+end
+
+describe :time_params_10_arg, shared: true do
+ it "handles string arguments" do
+ Time.send(@method, "1", "15", "20", "1", "1", "2000", :ignored, :ignored,
+ :ignored, :ignored).should ==
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ end
+
+ it "handles float arguments" do
+ Time.send(@method, 1.0, 15.0, 20.0, 1.0, 1.0, 2000.0, :ignored, :ignored,
+ :ignored, :ignored).should ==
+ Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored)
+ end
+
+ it "raises an ArgumentError for out of range values" do
+ -> {
+ Time.send(@method, 61, 59, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # sec
+
+ -> {
+ Time.send(@method, 59, 61, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # min
+
+ -> {
+ Time.send(@method, 59, 59, 25, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # hour
+
+ -> {
+ Time.send(@method, 59, 59, 23, 32, 12, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # day
+
+ -> {
+ Time.send(@method, 59, 59, 23, 31, 13, 2008, :ignored, :ignored, :ignored, :ignored)
+ }.should raise_error(ArgumentError) # month
+ end
+end
+
+describe :time_params_microseconds, shared: true do
+ it "handles microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 123)
+ t.usec.should == 123
+ end
+
+ it "raises an ArgumentError for out of range microsecond" do
+ -> { Time.send(@method, 2000, 1, 1, 20, 15, 1, 1000000) }.should raise_error(ArgumentError)
+ end
+
+ it "handles fractional microseconds as a Float" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 1.75)
+ t.usec.should == 1
+ t.nsec.should == 1750
+ end
+
+ it "handles fractional microseconds as a Rational" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1, Rational(99, 10))
+ t.usec.should == 9
+ t.nsec.should == 9900
+ end
+
+ it "ignores fractional seconds if a passed whole number of microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, 2)
+ t.sec.should == 1
+ t.usec.should == 2
+ t.nsec.should == 2000
+ end
+
+ it "ignores fractional seconds if a passed fractional number of microseconds" do
+ t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, Rational(99, 10))
+ t.sec.should == 1
+ t.usec.should == 9
+ t.nsec.should == 9900
+ end
+end
diff --git a/spec/ruby/core/time/shared/to_i.rb b/spec/ruby/core/time/shared/to_i.rb
new file mode 100644
index 0000000000..06c966b708
--- /dev/null
+++ b/spec/ruby/core/time/shared/to_i.rb
@@ -0,0 +1,16 @@
+describe :time_to_i, shared: true do
+ it "returns the value of time as an integer number of seconds since epoch" do
+ Time.at(0).send(@method).should == 0
+ end
+
+ it "doesn't return an actual number of seconds in time" do
+ Time.at(65.5).send(@method).should == 65
+ end
+
+ it "rounds fractional seconds toward zero" do
+ t = Time.utc(1960, 1, 1, 0, 0, 0, 999_999)
+
+ t.to_f.to_i.should == -315619199
+ t.to_i.should == -315619200
+ end
+end
diff --git a/spec/ruby/core/time/shared/xmlschema.rb b/spec/ruby/core/time/shared/xmlschema.rb
new file mode 100644
index 0000000000..d68c18df36
--- /dev/null
+++ b/spec/ruby/core/time/shared/xmlschema.rb
@@ -0,0 +1,31 @@
+describe :time_xmlschema, shared: true do
+ ruby_version_is "3.4" do
+ it "generates ISO-8601 strings in Z for UTC times" do
+ t = Time.utc(1985, 4, 12, 23, 20, 50, 521245)
+ t.send(@method).should == "1985-04-12T23:20:50Z"
+ t.send(@method, 2).should == "1985-04-12T23:20:50.52Z"
+ t.send(@method, 9).should == "1985-04-12T23:20:50.521245000Z"
+ end
+
+ it "generates ISO-8601 string with timeone offset for non-UTC times" do
+ t = Time.new(1985, 4, 12, 23, 20, 50, "+02:00")
+ t.send(@method).should == "1985-04-12T23:20:50+02:00"
+ t.send(@method, 2).should == "1985-04-12T23:20:50.00+02:00"
+ end
+
+ it "year is always at least 4 digits" do
+ t = Time.utc(12, 4, 12)
+ t.send(@method).should == "0012-04-12T00:00:00Z"
+ end
+
+ it "year can be more than 4 digits" do
+ t = Time.utc(40_000, 4, 12)
+ t.send(@method).should == "40000-04-12T00:00:00Z"
+ end
+
+ it "year can be negative" do
+ t = Time.utc(-2000, 4, 12)
+ t.send(@method).should == "-2000-04-12T00:00:00Z"
+ end
+ end
+end
diff --git a/spec/ruby/core/time/strftime_spec.rb b/spec/ruby/core/time/strftime_spec.rb
new file mode 100644
index 0000000000..fd233f3577
--- /dev/null
+++ b/spec/ruby/core/time/strftime_spec.rb
@@ -0,0 +1,91 @@
+# encoding: utf-8
+
+require_relative '../../spec_helper'
+require_relative '../../shared/time/strftime_for_date'
+require_relative '../../shared/time/strftime_for_time'
+
+describe "Time#strftime" do
+ before :all do
+ @new_date = -> y, m, d { Time.gm(y,m,d) }
+ @new_time = -> *args { Time.gm(*args) }
+ @new_time_in_zone = -> zone, offset, *args {
+ with_timezone(zone, offset) do
+ Time.new(*args)
+ end
+ }
+ @new_time_with_offset = -> y, m, d, h, min, s, offset {
+ Time.new(y,m,d,h,min,s,offset)
+ }
+
+ @time = @new_time[2001, 2, 3, 4, 5, 6]
+ end
+
+ it_behaves_like :strftime_date, :strftime
+ it_behaves_like :strftime_time, :strftime
+
+ # Differences with date
+ it "requires an argument" do
+ -> { @time.strftime }.should raise_error(ArgumentError)
+ end
+
+ # %Z is zone name or empty for Time
+ it "should be able to show the timezone if available" do
+ @time.strftime("%Z").should == @time.zone
+ with_timezone("UTC", 0) do
+ Time.gm(2000).strftime("%Z").should == "UTC"
+ end
+
+ Time.new(2000, 1, 1, 0, 0, 0, 42).strftime("%Z").should == ""
+ end
+
+ # %v is %e-%^b-%Y for Time
+ it "should be able to show the commercial week" do
+ @time.strftime("%v").should == " 3-FEB-2001"
+ @time.strftime("%v").should == @time.strftime('%e-%^b-%Y')
+ end
+
+ # Date/DateTime round at creation time, but Time does it in strftime.
+ it "rounds an offset to the nearest second when formatting with %z" do
+ time = @new_time_with_offset[2012, 1, 1, 0, 0, 0, Rational(36645, 10)]
+ time.strftime("%::z").should == "+01:01:05"
+ end
+
+ 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
diff --git a/spec/ruby/core/time/subsec_spec.rb b/spec/ruby/core/time/subsec_spec.rb
new file mode 100644
index 0000000000..0f2c4eb856
--- /dev/null
+++ b/spec/ruby/core/time/subsec_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+describe "Time#subsec" do
+ it "returns 0 as an Integer for a Time with a whole number of seconds" do
+ Time.at(100).subsec.should eql(0)
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).subsec.should eql(Rational(1, 2))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with a Float number of seconds" do
+ Time.at(10.75).subsec.should eql(Rational(3, 4))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999999).subsec.should eql(Rational(999999, 1000000))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Rational number of microseconds" do
+ Time.at(0, Rational(9, 10)).subsec.should eql(Rational(9, 10000000))
+ end
+
+ it "returns the fractional seconds as a Rational for a Time constructed with an Float number of microseconds" do
+ Time.at(0, 0.75).subsec.should eql(Rational(3, 4000000))
+ end
+end
diff --git a/spec/ruby/core/time/sunday_spec.rb b/spec/ruby/core/time/sunday_spec.rb
new file mode 100644
index 0000000000..0d46421132
--- /dev/null
+++ b/spec/ruby/core/time/sunday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#sunday?" do
+ it "returns true if time represents Sunday" do
+ Time.local(2000, 1, 2).should.sunday?
+ end
+
+ it "returns false if time doesn't represent Sunday" do
+ Time.local(2000, 1, 1).should_not.sunday?
+ end
+end
diff --git a/spec/ruby/core/time/thursday_spec.rb b/spec/ruby/core/time/thursday_spec.rb
new file mode 100644
index 0000000000..c11e79d2fa
--- /dev/null
+++ b/spec/ruby/core/time/thursday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#thursday?" do
+ it "returns true if time represents Thursday" do
+ Time.local(2000, 1, 6).should.thursday?
+ end
+
+ it "returns false if time doesn't represent Thursday" do
+ Time.local(2000, 1, 1).should_not.thursday?
+ end
+end
diff --git a/spec/ruby/core/time/time_spec.rb b/spec/ruby/core/time/time_spec.rb
new file mode 100644
index 0000000000..b0803a7f21
--- /dev/null
+++ b/spec/ruby/core/time/time_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time" do
+ it "includes Comparable" do
+ Time.include?(Comparable).should == true
+ end
+end
diff --git a/spec/ruby/core/time/to_a_spec.rb b/spec/ruby/core/time/to_a_spec.rb
new file mode 100644
index 0000000000..3728b8c526
--- /dev/null
+++ b/spec/ruby/core/time/to_a_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_a" do
+ platform_is_not :windows do
+ it "returns a 10 element array representing the deconstructed time" do
+ # Testing with America/Regina here because it doesn't have DST.
+ with_timezone("America/Regina") do
+ Time.at(0).to_a.should == [0, 0, 18, 31, 12, 1969, 3, 365, false, "CST"]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/time/to_f_spec.rb b/spec/ruby/core/time/to_f_spec.rb
new file mode 100644
index 0000000000..6101dcf871
--- /dev/null
+++ b/spec/ruby/core/time/to_f_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_f" do
+ it "returns the float number of seconds + usecs since the epoch" do
+ Time.at(100, 100).to_f.should == 100.0001
+ end
+end
diff --git a/spec/ruby/core/time/to_i_spec.rb b/spec/ruby/core/time/to_i_spec.rb
new file mode 100644
index 0000000000..54929d1e18
--- /dev/null
+++ b/spec/ruby/core/time/to_i_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Time#to_i" do
+ it_behaves_like :time_to_i, :to_i
+end
diff --git a/spec/ruby/core/time/to_r_spec.rb b/spec/ruby/core/time/to_r_spec.rb
new file mode 100644
index 0000000000..6af2d9b7ea
--- /dev/null
+++ b/spec/ruby/core/time/to_r_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#to_r" do
+ it "returns the a Rational representing seconds and subseconds since the epoch" do
+ Time.at(Rational(11, 10)).to_r.should eql(Rational(11, 10))
+ end
+
+ it "returns a Rational even for a whole number of seconds" do
+ Time.at(2).to_r.should eql(Rational(2))
+ end
+end
diff --git a/spec/ruby/core/time/to_s_spec.rb b/spec/ruby/core/time/to_s_spec.rb
new file mode 100644
index 0000000000..ac6c0908ac
--- /dev/null
+++ b/spec/ruby/core/time/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/inspect'
+
+describe "Time#to_s" do
+ it_behaves_like :inspect, :to_s
+end
diff --git a/spec/ruby/core/time/tuesday_spec.rb b/spec/ruby/core/time/tuesday_spec.rb
new file mode 100644
index 0000000000..0e7b9e7506
--- /dev/null
+++ b/spec/ruby/core/time/tuesday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#tuesday?" do
+ it "returns true if time represents Tuesday" do
+ Time.local(2000, 1, 4).should.tuesday?
+ end
+
+ it "returns false if time doesn't represent Tuesday" do
+ Time.local(2000, 1, 1).should_not.tuesday?
+ end
+end
diff --git a/spec/ruby/core/time/tv_nsec_spec.rb b/spec/ruby/core/time/tv_nsec_spec.rb
new file mode 100644
index 0000000000..feb7998b16
--- /dev/null
+++ b/spec/ruby/core/time/tv_nsec_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Time#tv_nsec" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/time/tv_sec_spec.rb b/spec/ruby/core/time/tv_sec_spec.rb
new file mode 100644
index 0000000000..f83e1fbfdd
--- /dev/null
+++ b/spec/ruby/core/time/tv_sec_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/to_i'
+
+describe "Time#tv_sec" do
+ it_behaves_like :time_to_i, :tv_sec
+end
diff --git a/spec/ruby/core/time/tv_usec_spec.rb b/spec/ruby/core/time/tv_usec_spec.rb
new file mode 100644
index 0000000000..f0de4f4a9c
--- /dev/null
+++ b/spec/ruby/core/time/tv_usec_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+describe "Time#tv_usec" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/time/usec_spec.rb b/spec/ruby/core/time/usec_spec.rb
new file mode 100644
index 0000000000..6ea52f5e79
--- /dev/null
+++ b/spec/ruby/core/time/usec_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+
+describe "Time#usec" do
+ it "returns 0 for a Time constructed with a whole number of seconds" do
+ Time.at(100).usec.should == 0
+ end
+
+ it "returns the microseconds part of a Time constructed with a Float number of seconds" do
+ Time.at(10.75).usec.should == 750_000
+ end
+
+ it "returns the microseconds part of a Time constructed with an Integer number of microseconds" do
+ Time.at(0, 999_999).usec.should == 999_999
+ end
+
+ it "returns the microseconds part of a Time constructed with an Float number of microseconds > 1" do
+ Time.at(0, 3.75).usec.should == 3
+ end
+
+ it "returns 0 for a Time constructed with an Float number of microseconds < 1" do
+ Time.at(0, 0.75).usec.should == 0
+ end
+
+ it "returns the microseconds part of a Time constructed with a Rational number of seconds" do
+ Time.at(Rational(3, 2)).usec.should == 500_000
+ end
+
+ it "returns the microseconds part of a Time constructed with an Rational number of microseconds > 1" do
+ Time.at(0, Rational(99, 10)).usec.should == 9
+ end
+
+ it "returns 0 for a Time constructed with an Rational number of microseconds < 1" do
+ Time.at(0, Rational(9, 10)).usec.should == 0
+ end
+
+ it "returns the microseconds for time created by Time#local" do
+ Time.local(1,2,3,4,5,Rational(6.78)).usec.should == 780000
+ end
+
+ it "returns a positive value for dates before the epoch" do
+ Time.utc(1969, 11, 12, 13, 18, 57, 404240).usec.should == 404240
+ end
+end
diff --git a/spec/ruby/core/time/utc_offset_spec.rb b/spec/ruby/core/time/utc_offset_spec.rb
new file mode 100644
index 0000000000..17c031b8c6
--- /dev/null
+++ b/spec/ruby/core/time/utc_offset_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gmt_offset'
+
+describe "Time#utc_offset" do
+ it_behaves_like :time_gmt_offset, :utc_offset
+end
diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb
new file mode 100644
index 0000000000..ab3c0df657
--- /dev/null
+++ b/spec/ruby/core/time/utc_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../../spec_helper'
+require_relative 'shared/gm'
+require_relative 'shared/gmtime'
+require_relative 'shared/time_params'
+
+describe "Time#utc?" do
+ 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
+
+ Time.new(2022, 1, 1, 0, 0, 0, in: "UTC").utc?.should == true
+ Time.now(in: "UTC").utc?.should == true
+ 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
+
+ Time.new(2022, 1, 1, 0, 0, 0, in: "Z").utc?.should == true
+ Time.now(in: "Z").utc?.should == true
+ end
+
+ 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
+
+ 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
+ Time.now.localtime("+00:00").utc?.should == false
+ Time.at(Time.now, in: "+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
+ Time.now.localtime(0).utc?.should == false
+ Time.at(Time.now, in: 0).utc?.should == false
+ end
+end
+
+describe "Time.utc" do
+ it_behaves_like :time_gm, :utc
+ it_behaves_like :time_params, :utc
+ it_behaves_like :time_params_10_arg, :utc
+ it_behaves_like :time_params_microseconds, :utc
+end
+
+describe "Time#utc" do
+ it_behaves_like :time_gmtime, :utc
+end
diff --git a/spec/ruby/core/time/wday_spec.rb b/spec/ruby/core/time/wday_spec.rb
new file mode 100644
index 0000000000..9f63f67de9
--- /dev/null
+++ b/spec/ruby/core/time/wday_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Time#wday" do
+ it "returns an integer representing the day of the week, 0..6, with Sunday being 0" do
+ with_timezone("GMT", 0) do
+ Time.at(0).wday.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/core/time/wednesday_spec.rb b/spec/ruby/core/time/wednesday_spec.rb
new file mode 100644
index 0000000000..cc686681d7
--- /dev/null
+++ b/spec/ruby/core/time/wednesday_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../../spec_helper'
+
+describe "Time#wednesday?" do
+ it "returns true if time represents Wednesday" do
+ Time.local(2000, 1, 5).should.wednesday?
+ end
+
+ it "returns false if time doesn't represent Wednesday" do
+ Time.local(2000, 1, 1).should_not.wednesday?
+ end
+end
diff --git a/spec/ruby/core/time/xmlschema_spec.rb b/spec/ruby/core/time/xmlschema_spec.rb
new file mode 100644
index 0000000000..bdf1dc7923
--- /dev/null
+++ b/spec/ruby/core/time/xmlschema_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/xmlschema'
+
+describe "Time#xmlschema" do
+ it_behaves_like :time_xmlschema, :xmlschema
+end
diff --git a/spec/ruby/core/time/yday_spec.rb b/spec/ruby/core/time/yday_spec.rb
new file mode 100644
index 0000000000..e920c2e28d
--- /dev/null
+++ b/spec/ruby/core/time/yday_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/time/yday'
+
+describe "Time#yday" do
+ it "returns an integer representing the day of the year, 1..366" do
+ with_timezone("UTC") do
+ Time.at(9999999).yday.should == 116
+ end
+ end
+
+ it_behaves_like :time_yday, -> year, month, day { Time.new(year, month, day).yday }
+end
diff --git a/spec/ruby/core/time/year_spec.rb b/spec/ruby/core/time/year_spec.rb
new file mode 100644
index 0000000000..d2d50062c5
--- /dev/null
+++ b/spec/ruby/core/time/year_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+
+describe "Time#year" do
+ it "returns the four digit year for a local Time as an Integer" do
+ with_timezone("CET", 1) do
+ Time.local(1970).year.should == 1970
+ end
+ end
+
+ it "returns the four digit year for a UTC Time as an Integer" do
+ Time.utc(1970).year.should == 1970
+ end
+
+ it "returns the four digit year for a Time with a fixed offset" do
+ Time.new(2012, 1, 1, 0, 0, 0, -3600).year.should == 2012
+ end
+end
diff --git a/spec/ruby/core/time/zone_spec.rb b/spec/ruby/core/time/zone_spec.rb
new file mode 100644
index 0000000000..9a15bd569b
--- /dev/null
+++ b/spec/ruby/core/time/zone_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../../spec_helper'
+
+describe "Time#zone" do
+ platform_is_not :windows do
+ it "returns the time zone used for time" do
+ with_timezone("America/New_York") do
+ Time.new(2001, 1, 1, 0, 0, 0).zone.should == "EST"
+ Time.new(2001, 7, 1, 0, 0, 0).zone.should == "EDT"
+ %w[EST EDT].should include Time.now.zone
+ end
+ end
+ end
+
+ it "returns nil for a Time with a fixed offset" do
+ Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil
+ end
+
+ platform_is_not :windows do
+ it "returns the correct timezone for a local time" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal.zone.should == "EST"
+ end
+ end
+ end
+
+ it "returns nil when getting the local time with a fixed offset" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal("+05:00").zone.should be_nil
+ end
+ end
+
+ describe "Encoding.default_internal is set" do
+ before :each do
+ @encoding = Encoding.default_internal
+ Encoding.default_internal = Encoding::UTF_8
+ end
+
+ after :each do
+ Encoding.default_internal = @encoding
+ end
+
+ it "returns an ASCII string" do
+ t = Time.new(2005, 2, 27, 22, 50, 0, -3600)
+
+ with_timezone("America/New_York") do
+ t.getlocal.zone.encoding.should == Encoding::US_ASCII
+ end
+ 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 == 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"
+
+ 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"
+
+ 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
+
+ platform_is_not :aix, :windows do
+ it "defaults to UTC when bad zones given" do
+ with_timezone("hello-foo") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("1,2") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("Sun,Fri,2") do
+ Time.now.utc_offset.should == 0
+ end
+ end
+ end
+
+ platform_is :windows do
+ # See https://bugs.ruby-lang.org/issues/13591#note-11
+ it "defaults to UTC when bad zones given" do
+ with_timezone("1,2") do
+ Time.now.utc_offset.should == 0
+ end
+ with_timezone("12") do
+ Time.now.utc_offset.should == 0
+ end
+ end
+ end
+end