timesteps 0.9.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,6 @@
1
1
 
2
+ autoload :TZInfo, "tzinfo"
3
+
2
4
  class TimeStep
3
5
 
4
6
  class Calendar
@@ -21,9 +23,13 @@ class TimeStep
21
23
  SYM_360_day => DateTime::Fixed360Day,
22
24
  }
23
25
 
24
- def initialize (calendar = "standard", bc: false)
26
+ # Construct the object
27
+ #
28
+ def initialize (calendar = "standard", tz: nil)
29
+ unless calendar.is_a?(String)
30
+ raise ArgumentError, "argument calendar '#{calendar}' should be string"
31
+ end
25
32
  @name = calendar
26
- @bc = bc
27
33
  case @name.downcase.intern
28
34
  when :standard, :gregorian
29
35
  @calendar = :standard
@@ -38,22 +44,30 @@ class TimeStep
38
44
  when SYM_360_day
39
45
  @calendar = SYM_360_day
40
46
  end
47
+ if tz
48
+ raise "'standard' calendar needed for tz" unless @calendar == :standard
49
+ case tz
50
+ when nil
51
+ when String
52
+ @tz = TZInfo::Timezone.get(tz)
53
+ when TZInfo::Timezone
54
+ @tz = tz
55
+ else
56
+ raise "invalid tz specification"
57
+ end
58
+ end
41
59
  end
42
60
 
43
- attr_reader :name, :calendar
44
-
61
+ attr_reader :name, :calendar, :tz
62
+
45
63
  def inspect
46
- "#<TimeStep::Calendar calendar='#{@calendar.to_s}' bc='#{@bc}'>"
64
+ "#<TimeStep::Calendar calendar='#{@calendar.to_s}'>"
47
65
  end
48
66
 
49
67
  def == (other)
50
- return @calendar == other.calendar && bc? == other.bc?
68
+ return @calendar == other.calendar
51
69
  end
52
-
53
- def bc?
54
- return @bc ? true : false
55
- end
56
-
70
+
57
71
  def valid_datetime_type? (time)
58
72
  return false unless time.is_a?(DateTimeType[@calendar])
59
73
  case @calendar
@@ -72,10 +86,31 @@ class TimeStep
72
86
  # and creates an date time instance.
73
87
  #
74
88
  # @return [DateTime object]
75
- def parse (timespec, format: nil)
76
- return DateTime.parse_timestamp(timespec, calendar: @calendar.to_s, bc: @bc, format: format)
89
+ def parse (timespec, format: nil, offset: nil)
90
+ return DateTime.parse_timestamp(timespec, calendar: @calendar.to_s, format: format, tz: @tz, offset: offset)
77
91
  end
78
-
92
+
93
+ def time_new (year, month, day, hour=0, min=0, sec=0, offset=0)
94
+ case @calendar
95
+ when :standard
96
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::ITALY)
97
+ when :proleptic_gregorian
98
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::GREGORIAN)
99
+ when :proleptic_julian
100
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::JULIAN)
101
+ when :noleap
102
+ time = DateTime::NoLeap.new(year, month, day, hour, min, sec, offset)
103
+ when :allleap
104
+ time = DateTime::AllLeap.new(year, month, day, hour, min, sec, offset)
105
+ when SYM_360_day
106
+ time = DateTime::Fixed360Day.new(year, month, day, hour, min, sec, offset)
107
+ end
108
+ if @tz
109
+ time = @tz.to_local(time)
110
+ end
111
+ return time
112
+ end
113
+
79
114
  def jday2date (jday)
80
115
  case @calendar
81
116
  when :standard
@@ -91,27 +126,17 @@ class TimeStep
91
126
  when SYM_360_day
92
127
  time = DateTime::Fixed360Day.jd(jday, 0, 0, 0, 0)
93
128
  end
129
+ if @tz
130
+ time = @tz.to_local(time)
131
+ end
94
132
  return time
95
133
  end
96
134
 
97
135
  def date2jday (year, month, day)
98
- case @calendar
99
- when :standard
100
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::ITALY)
101
- when :proleptic_gregorian
102
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::GREGORIAN)
103
- when :proleptic_julian
104
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::JULIAN)
105
- when :noleap
106
- time = DateTime::NoLeap.new(year, month, day, 0, 0, 0, 0)
107
- when :allleap
108
- time = DateTime::AllLeap.new(year, month, day, 0, 0, 0, 0)
109
- when SYM_360_day
110
- time = DateTime::Fixed360Day.new(year, month, day, 0, 0, 0, 0)
111
- end
112
- return time.jd
136
+ return time_new(year, month, day).jd
113
137
  end
114
138
 
115
139
  end
116
140
 
117
141
  end
142
+
@@ -2,16 +2,15 @@ require "timesteps"
2
2
 
3
3
  class TimeStep::Converter
4
4
 
5
- def initialize (reference, name: "reference", calendar: "standard", bc: false)
5
+ def initialize (reference, name: "reference", calendar: "standard")
6
6
  @calendar = calendar
7
- @bc = bc
8
7
  case reference
9
8
  when String
10
- @reference = TimeStep.new(reference, calendar: calendar, bc: bc)
9
+ @reference = TimeStep.new(reference, calendar: calendar)
11
10
  when TimeStep
12
11
  @reference = reference
13
12
  else
14
- raise "first argument should be TimeStep or string"
13
+ raise ArgumentError, "reference argument '#{reference}' should be time-step or string"
15
14
  end
16
15
  @pair = {}
17
16
  @target = {}
@@ -21,9 +20,9 @@ class TimeStep::Converter
21
20
  # Append or reset new target time step for the given name.
22
21
  #
23
22
  def add_timestep (spec, name:)
24
- @pair[name] = TimeStep::Pair.new(@reference, spec, calendar: @calendar, bc: @bc)
23
+ @pair[name] = TimeStep::Pair.new(@reference, spec, calendar: @calendar)
25
24
  @target[name] = @pair[name].to
26
- return @target[name]
25
+ return @pair[name]
27
26
  end
28
27
 
29
28
  # Append or reset new target time step for the given name.
@@ -35,14 +34,14 @@ class TimeStep::Converter
35
34
  # Return target time step of the given name.
36
35
  #
37
36
  def [] (name)
38
- return @target[name]
37
+ return @pair[name]
39
38
  end
40
39
 
41
40
  # Relete target of the given name.
42
41
  #
43
42
  def delete (name)
44
- @pair.delete(name)
45
43
  @target.delete(name)
44
+ @pair.delete(name)
46
45
  end
47
46
 
48
47
  # Returns the time represented by the given index as DateTime object
@@ -56,11 +55,18 @@ class TimeStep::Converter
56
55
 
57
56
  # Converts index refering `from` timestep to index refering `to`timestep.
58
57
  #
59
- def forward (*indices, with_time: false)
58
+ def forward (*indices, with_time: nil)
60
59
  if with_time
61
- hash = {
62
- "time" => time_at(*indices)
63
- }
60
+ case with_time
61
+ when String
62
+ hash = {
63
+ "time" => time_at(*indices).map{|t| t.strftime(with_time) }
64
+ }
65
+ else
66
+ hash = {
67
+ "time" => time_at(*indices)
68
+ }
69
+ end
64
70
  else
65
71
  hash = {}
66
72
  end
@@ -70,8 +76,8 @@ class TimeStep::Converter
70
76
  return hash
71
77
  end
72
78
 
73
- def range (ge: nil, gt: nil, lt: nil, le: nil, with_time: false)
74
- indices = @reference.range(ge: ge, gt: gt, lt: lt, le: le)
79
+ def range (other, with_time: false)
80
+ indices = @reference.range(other)
75
81
  return forward(*indices, with_time: with_time)
76
82
  end
77
83
 
@@ -80,14 +86,5 @@ class TimeStep::Converter
80
86
  return forward(*indices, with_time: with_time)
81
87
  end
82
88
 
83
- def valid? (*indices)
84
- hash = {}
85
- @pair.each do |name, conv|
86
- hash[name] = conv.to.valid?(*conv.forward(*indices))
87
- end
88
- return hash
89
- end
90
-
91
-
92
89
  end
93
90
 
@@ -46,28 +46,18 @@ class TimeStep
46
46
  #
47
47
  # @return [Integer]
48
48
  def difference_in_years (other)
49
- extra = 0
50
- if self.year > other.year && self.compare_md(other) < 0
51
- extra = -1
52
- end
53
- if self.year < other.year && self.compare_md(other) > 0
54
- extra = 1
55
- end
56
- return self.year - other.year + extra
49
+ my = self
50
+ other = other.new_offset(self.offset)
51
+ return my.year - other.year + my.compare_md(other).quo(2)
57
52
  end
58
53
 
59
54
  # Calculate difference between the object and other object in months.
60
55
  #
61
56
  # @return [Integer]
62
57
  def difference_in_months (other)
63
- extra = 0
64
- if self.month > other.month && self.compare_d(other) < 0
65
- extra = -1
66
- end
67
- if self.month < other.month && self.compare_d(other) > 0
68
- extra = 1
69
- end
70
- return 12*(self.year - other.year) + self.month - other.month + extra
58
+ my = self
59
+ other = other.new_offset(self.offset)
60
+ return 12*(my.year - other.year) + my.month - other.month + my.compare_d(other).quo(2)
71
61
  end
72
62
 
73
63
  end
@@ -8,34 +8,32 @@ class TimeStep::Pair
8
8
  # The `from` and `to` arguments are either TimeStep or a string representing
9
9
  # the time step definition ("<INTERVAL> since <TIME>").
10
10
  # If a string representing the time step definition is given as an argument
11
- # `from` or `to`, the options `calendar` and `bc` are used to construct
11
+ # `from` or `to`, the options `calendar` are used to construct
12
12
  # TimeStep. The option `calendar` specifies the calendar for datetime
13
- # calculation. The option `bc` is a flag whether AD/BC or EC when parsing
14
- # timestamp for negative year.
13
+ # calculation.
15
14
  #
16
15
  # @param from [TimeStep, String]
17
16
  # @param to [TimeStep, String]
18
17
  # @option calendar [String, TimeStep::Calendar]
19
- # @option bc [Boolean]
20
18
  #
21
- def initialize (from, to, calendar: "standard", bc: false)
19
+ def initialize (from, to, calendar: "standard")
22
20
 
23
21
  case from
24
22
  when String
25
- @from = TimeStep.new(from, calendar: calendar, bc: bc)
23
+ @from = TimeStep.new(from, calendar: calendar)
26
24
  when TimeStep
27
25
  @from = from
28
26
  else
29
- raise "first argument should be TimeStep or string"
27
+ raise ArgumentError, "from argument '#{from}' should be a time-step or a string"
30
28
  end
31
29
 
32
30
  case to
33
31
  when String
34
- @to = TimeStep.new(to, calendar: calendar, bc: bc)
32
+ @to = TimeStep.new(to, calendar: calendar)
35
33
  when TimeStep
36
34
  @to = to
37
35
  else
38
- raise "second argument should be TimeStep or string"
36
+ raise ArgumentError, "to argument '#{to}' should be a time-step or a string"
39
37
  end
40
38
 
41
39
  @from_origin = @from.origin
@@ -66,13 +64,17 @@ class TimeStep::Pair
66
64
  "#<TimeStep::Pair from='#{from.inspect}' to='#{to.inspect}'>"
67
65
  end
68
66
 
67
+ def invert
68
+ return TimeStep::Pair.new(@to, @from)
69
+ end
70
+
69
71
  # Returns true if other has same contents of `from` and `to` as self has.
70
72
  #
71
73
  # @return [Boolean]
72
74
  def == (other)
73
75
  return @from == other.from && @to == other.to
74
76
  end
75
-
77
+
76
78
  # Returns the time represented by the given index as DateTime object
77
79
  #
78
80
  # @param indices [Numeric,Array<Numeric>]
@@ -85,10 +87,16 @@ class TimeStep::Pair
85
87
  # Converts index refering `from` timestep to index refering `to`timestep.
86
88
  #
87
89
  def forward (*indices, &block)
88
- if indices.size == 1
90
+ if indices.empty? || indices.size == 1 && indices.first.is_a?(Range)
91
+ return forward(*@from.range(*indices), &block)
92
+ elsif indices.size == 1
89
93
  index = indices.first.to_r
90
94
  if @numeric_conversion
91
- value = (index*@from_interval + @difference).quo(@to_interval)
95
+ if @to_interval == 0
96
+ value = 0
97
+ else
98
+ value = (index*@from_interval + @difference).quo(@to_interval)
99
+ end
92
100
  else
93
101
  case @from_symbol
94
102
  when :years, :months
@@ -97,12 +105,14 @@ class TimeStep::Pair
97
105
  target = @from_origin + index*(@from_interval.quo(86400))
98
106
  end
99
107
  case @to_symbol
100
- when :years
101
- value = target.difference_in_years(@to_origin).quo(@to.numeric)
108
+ when :years
109
+ diff = target.difference_in_years(@to_origin)
110
+ value = diff.quo(@to.numeric)
102
111
  when :months
103
- value = target.difference_in_months(@to_origin).quo(@to.numeric)
112
+ diff = target.difference_in_months(@to_origin)
113
+ value = diff.quo(@to.numeric)
104
114
  else
105
- value = (target.ajd - @to_origin.ajd).quo(@to_interval)
115
+ value = (target.ajd - @to_origin.ajd).quo(@to_interval)*86400
106
116
  end
107
117
  end
108
118
  value = value.to_i if value.denominator == 1
@@ -126,18 +136,19 @@ class TimeStep::Pair
126
136
  return forward(*indices) {|index| @to.time_at(index) }
127
137
  end
128
138
 
129
- def range (ge: nil, gt: nil, lt: nil, le: nil)
130
- indices = @from.range(ge: ge, gt: gt, lt: lt, le: le)
131
- return forward(*indices)
132
- end
133
-
134
139
  # Converts index refering `to` timestep to index refering `from` timestep.
135
140
  #
136
141
  def inverse (*indices, &block)
137
- if indices.size == 1
142
+ if indices.empty? || indices.size == 1 && indices.first.is_a?(Range)
143
+ return inverse(*@to.range(*indices), &block)
144
+ elsif indices.size == 1
138
145
  index = indices.first.to_r
139
146
  if @numeric_conversion
140
- value = (index*@to_interval - @difference).quo(@from_interval)
147
+ if @from_interval == 0
148
+ value = 0
149
+ else
150
+ value = (index*@to_interval - @difference).quo(@from_interval)
151
+ end
141
152
  else
142
153
  case @to_symbol
143
154
  when :years, :months
@@ -147,11 +158,13 @@ class TimeStep::Pair
147
158
  end
148
159
  case @from_symbol
149
160
  when :years
150
- value = target.difference_in_years(@from_origin).quo(@from.numeric)
161
+ diff = target.difference_in_years(@from_origin)
162
+ value = diff.quo(@from.numeric)
151
163
  when :months
152
- value = target.difference_in_months(@from_origin).quo(@from.numeric)
164
+ diff = target.difference_in_months(@from_origin)
165
+ value = diff.quo(@from.numeric)
153
166
  else
154
- value = (target.ajd - @from_origin.ajd).quo(@from_interval)
167
+ value = (target.ajd - @from_origin.ajd).quo(@from_interval)*86400
155
168
  end
156
169
  end
157
170
  value = value.to_i if value.denominator == 1
@@ -0,0 +1,57 @@
1
+ require "timesteps"
2
+
3
+ class TimeStep::Query
4
+
5
+ def initialize (timestep, format: nil)
6
+ @timestep = timestep
7
+ @format = format
8
+ end
9
+
10
+ def __format__ (time)
11
+ return ( @format.nil? ) ? time : time.strftime(@format)
12
+ end
13
+
14
+ private :__format__
15
+
16
+ def just_time? (time)
17
+ return @timestep.index_at(time).denominator == 1
18
+ end
19
+
20
+ def just_before_time_of (time)
21
+ idx = @timestep.index_at(time)
22
+ if idx.denominator == 1
23
+ time0 = time
24
+ else
25
+ time0 = @timestep.time_at(idx.floor)
26
+ end
27
+ return __format__(time0)
28
+ end
29
+
30
+ def just_after_time_of (time)
31
+ idx = @timestep.index_at(time)
32
+ if idx.denominator == 1
33
+ time0 = time
34
+ else
35
+ time0 = @timestep.time_at(idx.ceil)
36
+ end
37
+ return __format__(time0)
38
+ end
39
+
40
+ def prev_time_of (time)
41
+ return __format__(@timestep.prev_time_of(time))
42
+ end
43
+
44
+ def next_time_of (time)
45
+ return __format__(@timestep.next_time_of(time))
46
+ end
47
+
48
+ end
49
+
50
+ class TimeStep
51
+
52
+ def query (format = nil)
53
+ return TimeStep::Query.new(self, format: format)
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,153 @@
1
+ require "forwardable"
2
+
3
+ class TimeStep::Range
4
+
5
+ extend Forwardable
6
+
7
+ def initialize (timestep, start = nil, last = nil, count: nil, ends: "[]")
8
+
9
+ raise "'ends' option should be one of '[]', '()', '(]', '[)" unless ends =~ /\A[\[\(][\]\)]\z/
10
+ include_start = ends[0] == "["
11
+ include_last = ends[1] == "]"
12
+
13
+ if last
14
+ case start
15
+ when Numeric
16
+ when Time
17
+ start = timestep.index_at(start.to_datetime)
18
+ when DateTime, DateTimeLike, String
19
+ start = timestep.index_at(start)
20
+ else
21
+ raise "unknown type of argument"
22
+ end
23
+ case last
24
+ when Numeric
25
+ when Time
26
+ last = timestep.index_at(last.to_datetime)
27
+ when DateTime, DateTimeLike, String
28
+ last = timestep.index_at(last)
29
+ else
30
+ raise "unknown argument"
31
+ end
32
+ else
33
+ case start
34
+ when Integer
35
+ count = start
36
+ start = 0
37
+ last = count - 1
38
+ when String
39
+ raise "count should be set" unless count.is_a?(Integer)
40
+ start = timestep.index_at(start)
41
+ last = start + count - 1
42
+ when TimePeriod
43
+ period = range
44
+ return initialize(timestep, period.origin..period.last, ends: period.ends)
45
+ else
46
+ raise "unknown argument"
47
+ end
48
+ end
49
+
50
+ if include_start
51
+ @start = start.ceil
52
+ else
53
+ @start = start.floor + 1
54
+ end
55
+
56
+ if include_last
57
+ @last = last.floor
58
+ else
59
+ @last = last.ceil - 1
60
+ end
61
+
62
+ @timestep = timestep.new_origin(timestep.time_at(@start))
63
+ @count = @last - @start + 1
64
+
65
+ @start_time = @timestep.time_at(@start)
66
+ @last_time = @timestep.time_at(@last)
67
+ end
68
+
69
+ attr_reader :timestep, :count, :start, :last, :start_time, :last_time
70
+
71
+ def_delegators :@timestep, :numeric, :symbol, :interval,
72
+ :origin, :offset, :calendar, :definition,
73
+ :parse, :time_at, :index_at, :duration_at, :[]
74
+
75
+
76
+ # FIXME
77
+ def valid? (*indices)
78
+ if @count
79
+ if indices.size == 1
80
+ index = indices.first
81
+ if index >= 0 and index < @count
82
+ return true
83
+ else
84
+ return false
85
+ end
86
+ else
87
+ return indices.map{|index| valid?(index) }
88
+ end
89
+ else
90
+ if indices.size == 1
91
+ return true
92
+ else
93
+ return indices.map{|index| true }
94
+ end
95
+ end
96
+ end
97
+
98
+ # Returns TimeStep object which has origin time determined
99
+ # by the given `index`.
100
+ #
101
+ # @param index [Numeric]
102
+ #
103
+ # @return [TimeStep]
104
+ def shift_origin (index)
105
+ case with
106
+ when :index, "index"
107
+ timestep = @timestep.shift_origin(index)
108
+ return TimeStep::Range.new(timestep, count: @count)
109
+ when :duration, "duration", :days, "days"
110
+ timestep = @timestep.shift_origin(index, with: "duration")
111
+ return TimeStep::Range.new(timestep, count: @count)
112
+ end
113
+ end
114
+
115
+ # Returns TimeStep object which has origin time specified
116
+ # by the given `time`.
117
+ #
118
+ # @param time [DateTime]
119
+ #
120
+ # @return [TimeStep]
121
+ def new_origin (time)
122
+ timestep = @timestep.new_origin(time)
123
+ return TimeStep::Range.new(timestep, count: @count)
124
+ end
125
+
126
+ include Enumerable
127
+
128
+ def each_time (&block)
129
+ if block
130
+ @start.upto(@last) do |k|
131
+ block.call(@timestep.time_at(k))
132
+ end
133
+ else
134
+ return Enumerator.new {|y|
135
+ @start.upto(@last) do |k|
136
+ y << @timestep.time_at(k)
137
+ end
138
+ }
139
+ end
140
+ end
141
+
142
+ alias each each_time
143
+
144
+ def each_index (&block)
145
+ (@start..@last).each(&block)
146
+ end
147
+
148
+ def map_index (&block)
149
+ (@start..@last).map(&block)
150
+ end
151
+
152
+ end
153
+
data/lib/timesteps.rb CHANGED
@@ -1,13 +1,18 @@
1
1
 
2
2
  require "date"
3
+ require "timesteps/time"
3
4
  require "timesteps/datetimelike"
5
+ require "timesteps/datetimelike_format"
4
6
  require "timesteps/datetime_noleap"
5
7
  require "timesteps/datetime_allleap"
6
8
  require "timesteps/datetime_360day"
7
- require "timesteps/parse_timestamp"
8
- require "timesteps/format"
9
+ require "timesteps/datetime_parse_timestamp"
9
10
  require "timesteps/timestep_datetime_ext"
10
- require "timesteps/calendar"
11
+ require "timesteps/timestep_calendar"
11
12
  require "timesteps/timestep"
12
13
  require "timesteps/timestep_pair"
13
14
  require "timesteps/timestep_converter"
15
+ require "timesteps/timeperiod"
16
+ require "timesteps/timestep_range"
17
+ require "timesteps/datetime_timestep"
18
+ require "timesteps/timestep_query"
data/spec/allleap_spec.rb CHANGED
@@ -123,17 +123,6 @@ describe "AllLeap.parse_timestamp" do
123
123
 
124
124
  end
125
125
 
126
- example "with bc option" do
127
-
128
- d1 = DateTime::AllLeap.new(0, 1, 1, 0, 0, 0, 0)
129
- d2 = DateTime.parse_timestamp('BC 0001-1-1 00:00:00', calendar: "allleap")
130
- d3 = DateTime.parse_timestamp('-0001-1-1 00:00:00', calendar: "allleap", bc: true)
131
-
132
- is_asserted_by { d2 == d1 }
133
- is_asserted_by { d3 == d1 }
134
-
135
- end
136
-
137
126
  example '2000-12-31 24:00:00' do
138
127
 
139
128
  d1 = DateTime::AllLeap.new(2001, 1, 1, 0, 0, 0, 0)
@@ -301,8 +290,8 @@ describe "AllLeap#difference_in_years" do
301
290
  d3 = DateTime::AllLeap.new(2002, 3, 5, 9, 35, 11.5, 9.quo(24))
302
291
  d4 = DateTime::AllLeap.new(2002, 3, 5, 9, 35, 12.5, 9.quo(24))
303
292
 
304
- is_asserted_by { d2.difference_in_years(d1) == 0 }
305
- is_asserted_by { d3.difference_in_years(d1) == 0 }
293
+ is_asserted_by { d2.difference_in_years(d1) == 1.quo(2) }
294
+ is_asserted_by { d3.difference_in_years(d1) == 1.quo(2) }
306
295
  is_asserted_by { d4.difference_in_years(d1) == 1 }
307
296
 
308
297
  end
@@ -319,8 +308,8 @@ describe "AllLeap#difference_in_months" do
319
308
  d4 = DateTime::AllLeap.new(2001, 4, 5, 9, 35, 12.5, 9.quo(24))
320
309
  d5 = DateTime::AllLeap.new(2002, 3, 5, 9, 35, 12.5, 9.quo(24))
321
310
 
322
- is_asserted_by { d2.difference_in_months(d1) == 0 }
323
- is_asserted_by { d3.difference_in_months(d1) == 0 }
311
+ is_asserted_by { d2.difference_in_months(d1) == 1.quo(2) }
312
+ is_asserted_by { d3.difference_in_months(d1) == 1.quo(2) }
324
313
  is_asserted_by { d4.difference_in_months(d1) == 1 }
325
314
  is_asserted_by { d5.difference_in_months(d1) == 12 }
326
315