require "spec" require "spec/helpers/iterate" private def expect_overflow(&) expect_raises ArgumentError, "Time::Span too big or too small" do yield end end describe Time::Span do it "initializes" do t1 = Time::Span.new nanoseconds: 123_456_789_123 t1.to_s.should eq("00:02:03.456789123") t1 = Time::Span.new hours: 1, minutes: 2, seconds: 3 t1.to_s.should eq("01:02:03") t1 = Time::Span.new minutes: 2, seconds: 3 t1.to_s.should eq("00:02:03") t1 = Time::Span.new seconds: 3 t1.to_s.should eq("00:00:03") t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4 t1.to_s.should eq("1.02:03:04") t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t1.to_s.should eq("1.02:03:04.005000000") t1 = Time::Span.new days: -1, hours: 2, minutes: -3, seconds: 4, nanoseconds: -5_000_000 t1.to_s.should eq("-22:02:56.005000000") t1 = Time::Span.new weeks: 1, days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000 t1.to_s.should eq("9.03:04:05.006000000") t1 = Time::Span.new hours: 25 t1.to_s.should eq("1.01:00:00") end it "initializes with type restrictions" do t = Time::Span.new seconds: 1_u8, nanoseconds: 1_u8 t.should eq(Time::Span.new seconds: 1, nanoseconds: 1) t = Time::Span.new seconds: 127_i8, nanoseconds: 1_000_000_000 t.should eq(Time::Span.new seconds: 128) t = Time::Span.new seconds: -128_i8, nanoseconds: -1_000_000_000 t.should eq(Time::Span.new seconds: -129) t = Time::Span.new seconds: 255_u8, nanoseconds: 1_000_000_000 t.should eq(Time::Span.new seconds: 256) t = Time::Span.new seconds: 0_u8, nanoseconds: -1_000_000_000 t.should eq(Time::Span.new seconds: -1) end it "initializes with big seconds value" do t = Time::Span.new hours: 0, minutes: 0, seconds: 1231231231231 t.total_seconds.should eq(1231231231231) end it "days overflows" do expect_overflow do days = 106751991167301 Time::Span.new days: days end end it "max days" do expect_overflow do Int64::MAX.days end end it "min days" do expect_overflow do Int64::MIN.days end end it "max seconds" do ts = Int32::MAX.seconds ts.days.should eq(24855) ts.hours.should eq(3) ts.minutes.should eq(14) ts.seconds.should eq(7) ts.milliseconds.should eq(0) ts.nanoseconds.should eq(0) end it "min seconds" do ts = Int32::MIN.seconds ts.days.should eq(-24855) ts.hours.should eq(-3) ts.minutes.should eq(-14) ts.seconds.should eq(-8) ts.milliseconds.should eq(0) end it "max milliseconds" do ts = Int32::MAX.milliseconds ts.days.should eq(24) ts.hours.should eq(20) ts.minutes.should eq(31) ts.seconds.should eq(23) ts.milliseconds.should eq(647) end it "min milliseconds" do ts = Int32::MIN.milliseconds ts.days.should eq(-24) ts.hours.should eq(-20) ts.minutes.should eq(-31) ts.seconds.should eq(-23) ts.milliseconds.should eq(-648) end it "negative timespan" do ts = Time::Span.new hours: -23, minutes: -59, seconds: -59 ts.days.should eq(0) ts.hours.should eq(-23) ts.minutes.should eq(-59) ts.seconds.should eq(-59) ts.milliseconds.should eq(0) end it "test properties" do t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t2 = -t1 t1.days.should eq(1) t1.hours.should eq(2) t1.minutes.should eq(3) t1.seconds.should eq(4) t1.milliseconds.should eq(5) t1.microseconds.should eq(5_000) t1.nanoseconds.should eq(5_000_000) t2.days.should eq(-1) t2.hours.should eq(-2) t2.minutes.should eq(-3) t2.seconds.should eq(-4) t2.milliseconds.should eq(-5) t2.microseconds.should eq(-5_000) t2.nanoseconds.should eq(-5_000_000) end it "test compare" do t1 = Time::Span.new nanoseconds: -1 t2 = Time::Span.new nanoseconds: 1 (t1 <=> t2).should eq(-1) (t2 <=> t1).should eq(1) (t2 <=> t2).should eq(0) (Time::Span::MIN <=> Time::Span::MAX).should eq(-1) (t1 == t2).should be_false (t1 > t2).should be_false (t1 >= t2).should be_false (t1 != t2).should be_true (t1 < t2).should be_true (t1 <= t2).should be_true end it "test equals" do t1 = Time::Span.new nanoseconds: 1 t2 = Time::Span.new nanoseconds: 2 (t1 == t1).should be_true (t1 == t2).should be_false (t1 == "hello").should be_false end describe "#step" do it_iterates "basic", [1.day, 2.days, 3.days, 4.days, 5.days], 1.days.step(to: 5.days, by: 1.day) end it "test int extension methods" do 1_000_000.days.to_s.should eq("1000000.00:00:00") 12.microseconds.to_s.should eq("00:00:00.000012000") -12.microseconds.to_s.should eq("-00:00:00.000012000") end it "test float extension methods" do 12.345.days.to_s.should eq("12.08:16:48") 12.345.hours.to_s.should eq("12:20:42") 12.345.minutes.to_s.should eq("00:12:20.700000000") 12.345.seconds.to_s.should eq("00:00:12.345000000") 12.345.milliseconds.to_s.should eq("00:00:00.012345000") -0.5.milliseconds.to_s.should eq("-00:00:00.000500000") 0.5.milliseconds.to_s.should eq("00:00:00.000500000") -2.5.milliseconds.to_s.should eq("-00:00:00.002500000") 2.5.milliseconds.to_s.should eq("00:00:00.002500000") -2.5.microseconds.to_s.should eq("-00:00:00.000002500") 2.5.microseconds.to_s.should eq("00:00:00.000002500") 0.0005.seconds.to_s.should eq("00:00:00.000500000") 1_000_000.5.days.to_s.should eq("1000000.12:00:00") end it "test negate and abs" do (-Time::Span.new(nanoseconds: 1234500)).to_s.should eq("-00:00:00.001234500") Time::Span.new(nanoseconds: -1234500).abs.to_s.should eq("00:00:00.001234500") (-Time::Span.new(nanoseconds: 7700)).to_s.should eq("-00:00:00.000007700") (+Time::Span.new(nanoseconds: 7700)).to_s.should eq("00:00:00.000007700") end it "test hash code" do t1 = Time::Span.new(nanoseconds: 77) t2 = Time::Span.new(nanoseconds: 77) t1.hash.should eq(t2.hash) end describe "arithmetic" do it "#+" do t1 = Time::Span.new days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000 t2 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t3 = t1 + t2 t3.days.should eq(3) t3.hours.should eq(5) t3.minutes.should eq(7) t3.seconds.should eq(9) t3.milliseconds.should eq(11) t3.nanoseconds.should eq(11_000_000) t3.to_s.should eq("3.05:07:09.011000000") expect_raises(OverflowError) do Time::Span::MAX + Time::Span.new(seconds: 1) end expect_raises(OverflowError) do Time::Span.new(seconds: Int64::MAX) + Time::Span.new(seconds: 1) end (Time::Span.new(nanoseconds: Int64::MAX) + Time::Span.new(nanoseconds: 1)).should eq Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775808 end it "#-" do t1 = Time::Span.new days: 2, hours: 3, minutes: 4, seconds: 5, nanoseconds: 6_000_000 t2 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t3 = t1 - t2 t3.to_s.should eq("1.01:01:01.001000000") expect_raises(OverflowError) do Time::Span::MIN - Time::Span.new(seconds: 1) end expect_raises(OverflowError) do Time::Span.new(seconds: Int64::MIN) - Time::Span.new(seconds: 1) end (Time::Span.new(nanoseconds: Int64::MIN) - Time::Span.new(nanoseconds: 1)).should eq -Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775809 end it "#*" do t1 = Time::Span.new days: 5, hours: 4, minutes: 3, seconds: 2, nanoseconds: 1_000_000 t2 = t1 * 61 t3 = t1 * 0.5 t2.should eq(Time::Span.new days: 315, hours: 7, minutes: 5, seconds: 2, nanoseconds: 61_000_000) t3.should eq(Time::Span.new days: 2, hours: 14, minutes: 1, seconds: 31, nanoseconds: 500_000) expect_raises(OverflowError) do Time::Span::MAX * 2 end t = Time::Span.new(seconds: Int64::MAX // 2 + 1) expect_raises(OverflowError) do t * 2 end t = Time::Span.new(nanoseconds: Int64::MAX // 2 + 1) (t * 2).should eq Time::Span.new days: 106751, hours: 23, minutes: 47, seconds: 16, nanoseconds: 854775808 end it "#/(Number)" do t1 = Time::Span.new days: 3, hours: 3, minutes: 3, seconds: 3, nanoseconds: 3_000_000 t2 = t1 / 2 t3 = t1 / 1.5 t2.should eq(Time::Span.new(days: 1, hours: 13, minutes: 31, seconds: 31, nanoseconds: 501_000_000) + Time::Span.new(nanoseconds: 500_000)) t3.should eq(Time::Span.new days: 2, hours: 2, minutes: 2, seconds: 2, nanoseconds: 2_000_000) expect_raises(DivisionByZeroError) do Time::Span::MAX / 0 end end it "#/(self)" do ratio = 20.minutes / 15.seconds ratio.should eq(80.0) ratio2 = 45.seconds / 1.minute ratio2.should eq(0.75) end it "#sign" do Time::Span.new(days: 2).sign.should eq 1 Time::Span.new(days: -2).sign.should eq -1 Time::Span.new.sign.should eq 0 Time::Span.new(nanoseconds: -2).sign.should eq -1 Time::Span.new(nanoseconds: 2).sign.should eq 1 end end it "test to_s" do t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t2 = -t1 t1.to_s.should eq("1.02:03:04.005000000") t2.to_s.should eq("-1.02:03:04.005000000") Time::Span::MAX.to_s.should eq("106751991167300.15:30:07.999999999") Time::Span::MIN.to_s.should eq("-106751991167300.15:30:08.999999999") Time::Span::ZERO.to_s.should eq("00:00:00") end it "test totals" do t1 = Time::Span.new days: 1, hours: 2, minutes: 3, seconds: 4, nanoseconds: 5_000_000 t1.total_days.should be_close(1.08546, 1e-05) t1.total_hours.should be_close(26.0511, 1e-04) t1.total_minutes.should be_close(1563.07, 1e-02) t1.total_seconds.should be_close(93784, 1e-01) t1.total_milliseconds.should be_close(9.3784e+07, 1e+01) t1.total_microseconds.should be_close(9.3784e+10, 1e+04) t1.total_nanoseconds.should be_close(9.3784e+13, 1e+07) t1.to_f.should be_close(93784, 1e-01) t1.to_i.should eq(93784) t2 = Time::Span.new nanoseconds: 123456 t2.total_seconds.should be_close(0.000123456, 1e-06) end it "should sum" do [1.second, 5.seconds].sum.should eq(6.seconds) end it "#zero?" do Time::Span.zero.zero?.should be_true Time::Span::ZERO.zero?.should be_true Time::Span.new(nanoseconds: 123456789).zero?.should be_false end it "#positive?" do Time::Span.new(nanoseconds: 123456789).positive?.should be_true Time::Span.zero.positive?.should be_false Time::Span.new(nanoseconds: -123456789).positive?.should be_false end it "#negative?" do Time::Span.new(nanoseconds: 123456789).negative?.should be_false Time::Span.zero.negative?.should be_false Time::Span.new(nanoseconds: -123456789).negative?.should be_true end it "converts units" do 1.nanoseconds.should eq(Time::Span.new(nanoseconds: 1)) 1.millisecond.should eq(1_000_000.nanoseconds) 1.milliseconds.should eq(1_000_000.nanoseconds) 1.second.should eq(1000.milliseconds) 1.seconds.should eq(1000.milliseconds) 1.minute.should eq(60.seconds) 1.minutes.should eq(60.seconds) 1.hour.should eq(60.minutes) 1.hours.should eq(60.minutes) 1.week.should eq(7.days) 2.weeks.should eq(14.days) 1.1.weeks.should eq(7.7.days) end it "can subtract big amount using microseconds" do jan_1_2k = Time.utc(2000, 1, 1) past = Time.utc(5, 2, 3, 0, 0, 0) delta = (past - jan_1_2k).total_microseconds.to_i64 past2 = jan_1_2k + delta.microseconds past2.should eq(past) end it "can subtract big amount using milliseconds" do jan_1_2k = Time.utc(2000, 1, 1) past = Time.utc(5, 2, 3, 0, 0, 0) delta = (past - jan_1_2k).total_milliseconds.to_i64 past2 = jan_1_2k + delta.milliseconds past2.should eq(past) end end