1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
|
# frozen_string_literal: true
require_relative "../abstract_unit"
require "active_support/inflector"
require "active_support/time"
require "active_support/json"
require_relative "../time_zone_test_helpers"
require "yaml"
class DurationTest < ActiveSupport::TestCase
include TimeZoneTestHelpers
def test_is_a
d = 1.day
assert d.is_a?(ActiveSupport::Duration)
assert_kind_of ActiveSupport::Duration, d
assert_kind_of Numeric, d
assert_kind_of Integer, d
assert_not d.is_a?(Hash)
k = Class.new
class << k; undef_method :== end
assert_not d.is_a?(k)
end
def test_instance_of
assert 1.minute.instance_of?(Integer)
assert 2.days.instance_of?(ActiveSupport::Duration)
assert_not 3.second.instance_of?(Numeric)
end
def test_threequals
assert ActiveSupport::Duration === 1.day
assert_not (ActiveSupport::Duration === 1.day.to_i)
assert_not (ActiveSupport::Duration === "foo")
end
def test_equals
assert 1.day == 1.day
assert 1.day == 1.day.to_i
assert 1.day.to_i == 1.day
assert_not (1.day == "foo")
end
def test_to_s
assert_equal "1", 1.second.to_s
end
def test_in_seconds
assert_equal 86400.0, 1.day.in_seconds
assert_equal 1.week.to_i, 1.week.in_seconds
end
def test_in_minutes
assert_in_delta 1440.0, 1.day.in_minutes
assert_in_delta 0.5, 30.seconds.in_minutes
end
def test_in_hours
assert_in_delta 24.0, 1.day.in_hours
assert_in_delta 336.0, 2.weeks.in_hours
end
def test_in_days
assert_in_delta 0.5, 12.hours.in_days
assert_in_delta 30.437, 1.month.in_days
end
def test_in_weeks
assert_in_delta 8.696, 2.months.in_weeks
assert_in_delta 52.178, 1.year.in_weeks
end
def test_in_months
assert_in_delta 2.07, 9.weeks.in_months
assert_in_delta 12.0, 1.year.in_months
end
def test_in_years
assert_in_delta 0.082, 30.days.in_years
assert_in_delta 1.0, 365.days.in_years
end
def test_eql
assert 1.minute.eql?(1.minute)
assert 1.minute.eql?(60.seconds)
assert 2.days.eql?(48.hours)
assert_not 1.second.eql?(1)
assert_not 1.eql?(1.second)
assert 1.minute.eql?(180.seconds - 2.minutes)
assert_not 1.minute.eql?(60)
assert_not 1.minute.eql?("foo")
end
def test_inspect
assert_equal "0 seconds", 0.seconds.inspect
assert_equal "0 days", 0.days.inspect
assert_equal "1 month", 1.month.inspect
assert_equal "1 month and 1 day", (1.month + 1.day).inspect
assert_equal "6 months and -2 days", (6.months - 2.days).inspect
assert_equal "10 seconds", 10.seconds.inspect
assert_equal "10 years, 2 months, and 1 day", (10.years + 2.months + 1.day).inspect
assert_equal "10 years, 2 months, and 1 day", (10.years + 1.month + 1.day + 1.month).inspect
assert_equal "10 years, 2 months, and 1 day", (1.day + 10.years + 2.months).inspect
assert_equal "7 days", 7.days.inspect
assert_equal "1 week", 1.week.inspect
assert_equal "2 weeks", 1.fortnight.inspect
assert_equal "0 seconds", (10 % 5.seconds).inspect
assert_equal "10 minutes", (10.minutes + 0.seconds).inspect
assert_equal "3600 seconds", (1.day / 24).inspect
end
def test_inspect_ignores_locale
current_locale = I18n.default_locale
I18n.default_locale = :de
I18n.backend.store_translations(:de, support: { array: { last_word_connector: " und " } })
assert_equal "10 years, 1 month, and 1 day", (10.years + 1.month + 1.day).inspect
ensure
I18n.default_locale = current_locale
end
def test_minus_with_duration_does_not_break_subtraction_of_date_from_date
assert_nothing_raised { Date.today - Date.today }
end
def test_unary_plus
assert_equal (+ 1.second), 1.second
assert_instance_of ActiveSupport::Duration, + 1.second
end
def test_plus
assert_equal 2.seconds, 1.second + 1.second
assert_instance_of ActiveSupport::Duration, 1.second + 1.second
assert_equal 2.seconds, 1.second + 1
assert_instance_of ActiveSupport::Duration, 1.second + 1
end
def test_minus
assert_equal 1.second, 2.seconds - 1.second
assert_instance_of ActiveSupport::Duration, 2.seconds - 1.second
assert_equal 1.second, 2.seconds - 1
assert_instance_of ActiveSupport::Duration, 2.seconds - 1
assert_equal 1.second, 2 - 1.second
assert_instance_of ActiveSupport::Duration, 2.seconds - 1
end
def test_multiply
assert_equal 7.days, 1.day * 7
assert_instance_of ActiveSupport::Duration, 1.day * 7
assert_equal 86400, 1.day * 1.second
end
def test_divide
assert_equal 1.day, 7.days / 7
assert_instance_of ActiveSupport::Duration, 7.days / 7
assert_equal 1.hour, 1.day / 24
assert_instance_of ActiveSupport::Duration, 1.day / 24
assert_equal 24, 86400 / 1.hour
assert_kind_of Integer, 86400 / 1.hour
assert_equal 24, 1.day / 1.hour
assert_kind_of Integer, 1.day / 1.hour
assert_equal 1, 1.day / 1.day
assert_kind_of Integer, 1.day / 1.hour
end
def test_modulo
assert_equal 1.minute, 5.minutes % 120
assert_instance_of ActiveSupport::Duration, 5.minutes % 120
assert_equal 1.minute, 5.minutes % 2.minutes
assert_instance_of ActiveSupport::Duration, 5.minutes % 2.minutes
assert_equal 1.minute, 5.minutes % 120.seconds
assert_instance_of ActiveSupport::Duration, 5.minutes % 120.seconds
assert_equal 5.minutes, 5.minutes % 1.hour
assert_instance_of ActiveSupport::Duration, 5.minutes % 1.hour
assert_equal 1.day, 36.days % 604800
assert_instance_of ActiveSupport::Duration, 36.days % 604800
assert_equal 1.day, 36.days % 7.days
assert_instance_of ActiveSupport::Duration, 36.days % 7.days
assert_equal 800.seconds, 8000 % 1.hour
assert_instance_of ActiveSupport::Duration, 8000 % 1.hour
assert_equal 1.month, 13.months % 1.year
assert_instance_of ActiveSupport::Duration, 13.months % 1.year
end
def test_date_added_with_zero_days
assert_equal Date.civil(2017, 1, 1), Date.civil(2017, 1, 1) + 0.days
assert_instance_of Date, Date.civil(2017, 1, 1) + 0.days
end
def test_date_added_with_multiplied_duration
assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 1.day * 2
assert_instance_of Date, Date.civil(2017, 1, 1) + 1.day * 2
end
def test_date_added_with_multiplied_duration_larger_than_one_month
assert_equal Date.civil(2017, 2, 15), Date.civil(2017, 1, 1) + 1.day * 45
assert_instance_of Date, Date.civil(2017, 1, 1) + 1.day * 45
end
def test_date_added_with_divided_duration
assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 4.days / 2
assert_instance_of Date, Date.civil(2017, 1, 1) + 4.days / 2
end
def test_date_added_with_divided_duration_larger_than_one_month
assert_equal Date.civil(2017, 2, 15), Date.civil(2017, 1, 1) + 90.days / 2
assert_instance_of Date, Date.civil(2017, 1, 1) + 90.days / 2
end
def test_plus_with_time
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
def test_time_plus_duration_returns_same_time_datatype
twz = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Moscow"], Time.utc(2016, 4, 28, 00, 45))
now = Time.now.utc
%w( second minute hour day week month year ).each do |unit|
assert_equal((now + 1.public_send(unit)).class, Time, "Time + 1.#{unit} must be Time")
assert_equal((twz + 1.public_send(unit)).class, ActiveSupport::TimeWithZone, "TimeWithZone + 1.#{unit} must be TimeWithZone")
end
end
def test_argument_error
e = assert_raise ArgumentError do
1.second.ago("")
end
assert_equal 'expected a time or date, got ""', e.message, "ensure ArgumentError is not being raised by dependencies.rb"
end
def test_fractional_weeks
assert_equal((86400 * 7) * 1.5, 1.5.weeks)
assert_equal((86400 * 7) * 1.7, 1.7.weeks)
end
def test_fractional_days
assert_equal 86400 * 1.5, 1.5.days
assert_equal 86400 * 1.7, 1.7.days
end
def test_since_and_ago
t = Time.local(2000)
assert_equal t + 1, 1.second.since(t)
assert_equal t + 1, (1.minute / 60).since(t)
assert_equal t - 1, 1.second.ago(t)
assert_equal t - 1, (1.minute / 60).ago(t)
end
def test_since_and_ago_without_argument
now = Time.now
assert 1.second.since >= now + 1
now = Time.now
assert 1.second.ago >= now - 1
end
def test_since_and_ago_with_fractional_days
t = Time.local(2000)
# since
assert_equal 36.hours.since(t), 1.5.days.since(t)
assert_in_delta((24 * 1.7).hours.since(t), 1.7.days.since(t), 1)
# ago
assert_equal 36.hours.ago(t), 1.5.days.ago(t)
assert_in_delta((24 * 1.7).hours.ago(t), 1.7.days.ago(t), 1)
end
def test_since_and_ago_with_fractional_weeks
t = Time.local(2000)
# since
assert_equal((7 * 36).hours.since(t), 1.5.weeks.since(t))
assert_in_delta((7 * 24 * 1.7).hours.since(t), 1.7.weeks.since(t), 1)
# ago
assert_equal((7 * 36).hours.ago(t), 1.5.weeks.ago(t))
assert_in_delta((7 * 24 * 1.7).hours.ago(t), 1.7.weeks.ago(t), 1)
end
def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set
Time.zone = nil
with_env_tz "US/Eastern" do
Time.stub(:now, Time.local(2000)) do
# since
assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
assert_equal Time.local(2000, 1, 1, 0, 0, 5), 5.seconds.since
# ago
assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
assert_equal Time.local(1999, 12, 31, 23, 59, 55), 5.seconds.ago
end
end
end
def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set
Time.zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
with_env_tz "US/Eastern" do
Time.stub(:now, Time.local(2000)) do
# since
assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
assert_equal Time.utc(2000, 1, 1, 0, 0, 5), 5.seconds.since.time
assert_equal "Eastern Time (US & Canada)", 5.seconds.since.time_zone.name
# ago
assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
assert_equal Time.utc(1999, 12, 31, 23, 59, 55), 5.seconds.ago.time
assert_equal "Eastern Time (US & Canada)", 5.seconds.ago.time_zone.name
end
end
ensure
Time.zone = nil
end
def test_before_and_after
t = Time.local(2000)
assert_equal t + 1, 1.second.after(t)
assert_equal t - 1, 1.second.before(t)
end
def test_before_and_after_without_argument
Time.stub(:now, Time.local(2000)) do
assert_equal Time.now - 1.second, 1.second.before
assert_equal Time.now + 1.second, 1.second.after
end
end
def test_adding_hours_across_dst_boundary
with_env_tz "CET" do
assert_equal Time.local(2009, 3, 29, 0, 0, 0) + 24.hours, Time.local(2009, 3, 30, 1, 0, 0)
end
end
def test_adding_day_across_dst_boundary
with_env_tz "CET" do
assert_equal Time.local(2009, 3, 29, 0, 0, 0) + 1.day, Time.local(2009, 3, 30, 0, 0, 0)
end
end
def test_delegation_with_block_works
counter = 0
assert_nothing_raised do
1.minute.times { counter += 1 }
end
assert_equal 60, counter
end
def test_as_json
assert_equal 172800, 2.days.as_json
end
def test_to_json
assert_equal "172800", 2.days.to_json
end
def test_case_when
cased = \
case 1.day
when 1.day
"ok"
end
assert_equal "ok", cased
end
def test_respond_to
assert_respond_to 1.day, :since
assert_respond_to 1.day, :zero?
end
def test_hash
assert_equal 1.minute.hash, 60.seconds.hash
end
def test_comparable
assert_equal(-1, (0.seconds <=> 1.second))
assert_equal(-1, (1.second <=> 1.minute))
assert_equal(-1, (1 <=> 1.minute))
assert_equal(0, (0.seconds <=> 0.seconds))
assert_equal(0, (0.seconds <=> 0.minutes))
assert_equal(0, (1.second <=> 1.second))
assert_equal(1, (1.second <=> 0.second))
assert_equal(1, (1.minute <=> 1.second))
assert_equal(1, (61 <=> 1.minute))
end
def test_implicit_coercion
assert_equal 2.days, 2 * 1.day
assert_instance_of ActiveSupport::Duration, 2 * 1.day
assert_equal Time.utc(2017, 1, 3), Time.utc(2017, 1, 1) + 2 * 1.day
assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 2 * 1.day
end
def test_scalar_coerce
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar
assert_instance_of ActiveSupport::Duration, 10.seconds + scalar
end
def test_scalar_delegations
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_kind_of Float, scalar.to_f
assert_kind_of Integer, scalar.to_i
assert_kind_of String, scalar.to_s
end
def test_scalar_unary_minus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal(-10, -scalar)
assert_instance_of ActiveSupport::Duration::Scalar, -scalar
end
def test_scalar_compare
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal(1, scalar <=> 5)
assert_equal(0, scalar <=> 10)
assert_equal(-1, scalar <=> 15)
assert_nil(scalar <=> "foo")
end
def test_scalar_plus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 20, 10 + scalar
assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar
assert_equal 20, scalar + 10
assert_instance_of ActiveSupport::Duration::Scalar, scalar + 10
assert_equal 20, 10.seconds + scalar
assert_instance_of ActiveSupport::Duration, 10.seconds + scalar
assert_equal 20, scalar + 10.seconds
assert_instance_of ActiveSupport::Duration, scalar + 10.seconds
exception = assert_raises(TypeError) do
scalar + "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_plus_parts
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal({ days: 1, seconds: 10 }, (scalar + 1.day).parts)
assert_equal({ days: -1, seconds: 10 }, (scalar + -1.day).parts)
end
def test_scalar_minus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 10, 20 - scalar
assert_instance_of ActiveSupport::Duration::Scalar, 20 - scalar
assert_equal 5, scalar - 5
assert_instance_of ActiveSupport::Duration::Scalar, scalar - 5
assert_equal 10, 20.seconds - scalar
assert_instance_of ActiveSupport::Duration, 20.seconds - scalar
assert_equal 5, scalar - 5.seconds
assert_instance_of ActiveSupport::Duration, scalar - 5.seconds
assert_equal({ days: -1, seconds: 10 }, (scalar - 1.day).parts)
assert_equal({ days: 1, seconds: 10 }, (scalar - -1.day).parts)
exception = assert_raises(TypeError) do
scalar - "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_minus_parts
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal({ days: -1, seconds: 10 }, (scalar - 1.day).parts)
assert_equal({ days: 1, seconds: 10 }, (scalar - -1.day).parts)
end
def test_scalar_multiply
scalar = ActiveSupport::Duration::Scalar.new(5)
assert_equal 10, 2 * scalar
assert_instance_of ActiveSupport::Duration::Scalar, 2 * scalar
assert_equal 10, scalar * 2
assert_instance_of ActiveSupport::Duration::Scalar, scalar * 2
assert_equal 10, 2.seconds * scalar
assert_instance_of ActiveSupport::Duration, 2.seconds * scalar
assert_equal 10, scalar * 2.seconds
assert_instance_of ActiveSupport::Duration, scalar * 2.seconds
exception = assert_raises(TypeError) do
scalar * "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_multiply_parts
scalar = ActiveSupport::Duration::Scalar.new(1)
assert_equal({ days: 2 }, (scalar * 2.days).parts)
assert_equal(172800, (scalar * 2.days).value)
assert_equal({ days: -2 }, (scalar * -2.days).parts)
assert_equal(-172800, (scalar * -2.days).value)
end
def test_scalar_divide
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 10, 100 / scalar
assert_instance_of ActiveSupport::Duration::Scalar, 100 / scalar
assert_equal 5, scalar / 2
assert_instance_of ActiveSupport::Duration::Scalar, scalar / 2
assert_equal 10, 100.seconds / scalar
assert_instance_of ActiveSupport::Duration, 100.seconds / scalar
assert_equal 5, scalar / 2.seconds
assert_kind_of Integer, scalar / 2.seconds
exception = assert_raises(TypeError) do
scalar / "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_modulo
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 1, 31 % scalar
assert_instance_of ActiveSupport::Duration::Scalar, 31 % scalar
assert_equal 1, scalar % 3
assert_instance_of ActiveSupport::Duration::Scalar, scalar % 3
assert_equal 1, 31.seconds % scalar
assert_instance_of ActiveSupport::Duration, 31.seconds % scalar
assert_equal 1, scalar % 3.seconds
assert_instance_of ActiveSupport::Duration, scalar % 3.seconds
exception = assert_raises(TypeError) do
scalar % "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_modulo_parts
scalar = ActiveSupport::Duration::Scalar.new(82800)
assert_equal({ hours: 1 }, (scalar % 2.hours).parts)
assert_equal(3600, (scalar % 2.hours).value)
end
def test_twelve_months_equals_one_year
assert_equal 12.months, 1.year
end
def test_thirty_days_does_not_equal_one_month
assert_not_equal 30.days, 1.month
end
def test_adding_one_month_maintains_day_of_month
(1..11).each do |month|
[1, 14, 28].each do |day|
assert_equal Date.civil(2016, month + 1, day), Date.civil(2016, month, day) + 1.month
end
end
assert_equal Date.civil(2017, 1, 1), Date.civil(2016, 12, 1) + 1.month
assert_equal Date.civil(2017, 1, 14), Date.civil(2016, 12, 14) + 1.month
assert_equal Date.civil(2017, 1, 28), Date.civil(2016, 12, 28) + 1.month
assert_equal Date.civil(2015, 2, 28), Date.civil(2015, 1, 31) + 1.month
assert_equal Date.civil(2016, 2, 29), Date.civil(2016, 1, 31) + 1.month
end
# ISO8601 string examples are taken from ISO8601 gem at https://github.com/arnau/ISO8601/blob/b93d466840/spec/iso8601/duration_spec.rb
# published under the conditions of MIT license at https://github.com/arnau/ISO8601/blob/b93d466840/LICENSE
#
# Copyright (c) 2012-2014 Arnau Siches
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
def test_iso8601_parsing_wrong_patterns_with_raise
invalid_patterns = ["", "P", "PT", "P1YT", "T", "PW", "P1Y1W", "~P1Y", ".P1Y", "P1.5Y0.5M", "P1.5Y1M", "P1.5MT10.5S"]
invalid_patterns.each do |pattern|
assert_raise ActiveSupport::Duration::ISO8601Parser::ParsingError, pattern.inspect do
ActiveSupport::Duration.parse(pattern)
end
end
end
def test_iso8601_output
expectations = [
["P1Y", 1.year ],
["P1W", 1.week ],
["P4W", 4.week ],
["P1Y7D", 1.year + 1.week ],
["P1Y1M21D", 1.year + 1.month + 3.week ],
["P1Y1M", 1.year + 1.month ],
["P1Y1M1D", 1.year + 1.month + 1.day ],
["P-1Y-1D", -1.year - 1.day ],
["P1Y-1DT-1S", 1.year - 1.day - 1.second ], # Parts with different signs are exists in PostgreSQL interval datatype.
["PT1S", 1.second ],
["PT1.4S", (1.4).seconds ],
["P1Y1M1DT1H", 1.year + 1.month + 1.day + 1.hour],
["PT0S", 0.minutes ],
["PT-0.2S", (-0.2).seconds ],
["PT1000000S", 1_000_000.seconds ],
]
expectations.each do |expected_output, duration|
assert_equal expected_output, duration.iso8601, expected_output.inspect
end
end
def test_iso8601_output_precision
expectations = [
[nil, "P1Y1MT8.55S", 1.year + 1.month + (8.55).seconds ],
[0, "P1Y1MT9S", 1.year + 1.month + (8.55).seconds ],
[1, "P1Y1MT8.6S", 1.year + 1.month + (8.55).seconds ],
[2, "P1Y1MT8.55S", 1.year + 1.month + (8.55).seconds ],
[3, "P1Y1MT8.550S", 1.year + 1.month + (8.55).seconds ],
[nil, "PT1S", 1.second ],
[2, "PT1.00S", 1.second ],
[nil, "PT1.4S", (1.4).seconds ],
[0, "PT1S", (1.4).seconds ],
[1, "PT1.4S", (1.4).seconds ],
[5, "PT1.40000S", (1.4).seconds ],
]
expectations.each do |precision, expected_output, duration|
assert_equal expected_output, duration.iso8601(precision: precision), expected_output.inspect
end
end
def test_iso8601_output_and_reparsing
patterns = %w[
P1Y P0.5Y P0,5Y P1Y1M P1Y0.5M P1Y0,5M P1Y1M1D P1Y1M0.5D P1Y1M0,5D P1Y1M1DT1H P1Y1M1DT0.5H P1Y1M1DT0,5H P1W +P1Y -P1Y P-1Y
P1Y1M1DT1H1M P1Y1M1DT1H0.5M P1Y1M1DT1H0,5M P1Y1M1DT1H1M1S P1Y1M1DT1H1M1.0S P1Y1M1DT1H1M1,0S P-1Y-2M3DT-4H-5M-6S
]
# That could be weird, but if we parse P1Y1M0.5D and output it to ISO 8601, we'll get P1Y1MT12.0H.
# So we check that initially parsed and reparsed duration added to time will result in the same time.
time = Time.current
patterns.each do |pattern|
duration = ActiveSupport::Duration.parse(pattern)
assert_equal time + duration, time + ActiveSupport::Duration.parse(duration.iso8601), pattern.inspect
end
end
def test_iso8601_parsing_across_spring_dst_boundary
with_env_tz eastern_time_zone do
with_tz_default "Eastern Time (US & Canada)" do
travel_to Time.utc(2016, 3, 11) do
assert_equal 604800, ActiveSupport::Duration.parse("P7D").to_i
assert_equal 604800, ActiveSupport::Duration.parse("P1W").to_i
end
end
end
end
def test_iso8601_parsing_across_autumn_dst_boundary
with_env_tz eastern_time_zone do
with_tz_default "Eastern Time (US & Canada)" do
travel_to Time.utc(2016, 11, 4) do
assert_equal 604800, ActiveSupport::Duration.parse("P7D").to_i
assert_equal 604800, ActiveSupport::Duration.parse("P1W").to_i
end
end
end
end
def test_iso8601_parsing_equivalence_with_numeric_extensions_over_long_periods
with_env_tz eastern_time_zone do
with_tz_default "Eastern Time (US & Canada)" do
assert_equal 3.months, ActiveSupport::Duration.parse("P3M")
assert_equal 3.months.to_i, ActiveSupport::Duration.parse("P3M").to_i
assert_equal 10.months, ActiveSupport::Duration.parse("P10M")
assert_equal 10.months.to_i, ActiveSupport::Duration.parse("P10M").to_i
assert_equal 3.years, ActiveSupport::Duration.parse("P3Y")
assert_equal 3.years.to_i, ActiveSupport::Duration.parse("P3Y").to_i
assert_equal 10.years, ActiveSupport::Duration.parse("P10Y")
assert_equal 10.years.to_i, ActiveSupport::Duration.parse("P10Y").to_i
end
end
end
def test_adding_durations_do_not_hold_prior_states
time = Time.parse("Nov 29, 2016")
# If the implementation adds and subtracts 3 months, the
# resulting date would have been in February so the day will
# change to the 29th.
d1 = 3.months - 3.months
d2 = 2.months - 2.months
assert_equal time + d1, time + d2
end
def test_durations_survive_yaml_serialization
payload = YAML.dump(10.minutes)
d1 = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(payload) : YAML.load(payload)
assert_equal 600, d1.to_i
assert_equal 660, (d1 + 60).to_i
end
def test_string_build_raises_error
error = assert_raises(TypeError) do
ActiveSupport::Duration.build("9")
end
assert_equal "can't build an ActiveSupport::Duration from a String", error.message
end
def test_non_numeric_build_raises_error
error = assert_raises(TypeError) do
ActiveSupport::Duration.build(nil)
end
assert_equal "can't build an ActiveSupport::Duration from a NilClass", error.message
end
def test_variable
assert_not 12.seconds.variable?
assert_not 12.minutes.variable?
assert_not 12.hours.variable?
assert_predicate 12.days, :variable?
assert_predicate 12.weeks, :variable?
assert_predicate 12.months, :variable?
assert_predicate 12.years, :variable?
assert_not (12.hours + 12.minutes).variable?
assert_predicate (12.hours + 1.day), :variable?
assert_predicate (1.day + 12.hours), :variable?
end
def test_duration_symmetry
time = Time.parse("Dec 7, 2021")
expected_time = Time.parse("2021-12-06 23:59:59")
assert_equal expected_time, time + -1.second
assert_equal expected_time, time + ActiveSupport::Duration.build(1) * -1
assert_equal expected_time, time + -ActiveSupport::Duration.build(1)
assert_equal expected_time, time + ActiveSupport::Duration::Scalar.new(-1)
assert_equal expected_time, time + ActiveSupport::Duration.build(-1)
end
private
def eastern_time_zone
if Gem.win_platform?
"EST5EDT"
else
"America/New_York"
end
end
end
|