class Time
def to_minidate
MiniDate.new(self.month, self.day)
end
end
class Season
attr_reader :start, :end
def initialize(myStart, myEnd)
@start = myStart
@end = myEnd
end
def self.find_next_season(season, pointer)
lookup = {:spring => 0, :summer => 1, :autumn => 2, :winter => 3}
next_season_num = (lookup[season]+1*pointer) % 4
lookup.invert[next_season_num]
end
def self.season_after(season); find_next_season(season, +1); end
def self.season_before(season); find_next_season(season, -1); end
end
class MiniDate
attr_accessor :month, :day
def initialize(month, day)
@month = month
@day = day
end
def is_between?(md_start, md_end)
return true if (@month == md_start.month and @day >= md_start.day) ||
(@month == md_end.month and @day <= md_end.day)
i = md_start.month + 1
until i == md_end.month
return true if @month == i
i = (i+1) % 12
end
return false
end
def equals?(other)
@month == other.month and day == other.day
end
end
class Chronic::RepeaterSeason < Chronic::Repeater #:nodoc:
YEAR_SEASONS = 4
SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
SEASONS = { :spring => Season.new( MiniDate.new(3,20),MiniDate.new(6,20) ),
:summer => Season.new( MiniDate.new(6,21),MiniDate.new(9,22) ),
:autumn => Season.new( MiniDate.new(9,23),MiniDate.new(12,21) ),
:winter => Season.new( MiniDate.new(12,22),MiniDate.new(3,19) ) }
def initialize(type)
super
@next_season_start = nil
end
def next(pointer)
super
direction = pointer == :future ? 1 : -1
next_season = Season.find_next_season(find_current_season(@now.to_minidate), direction)
find_next_season_span(direction, next_season)
end
def this(pointer = :future)
super
direction = pointer == :future ? 1 : -1
today = Time.construct(@now.year, @now.month, @now.day)
this_ssn = find_current_season(@now.to_minidate)
case pointer
when :past
this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
this_ssn_end = today
when :future
this_ssn_start = today + Chronic::RepeaterDay::DAY_SECONDS
this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
when :none
this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
end
Chronic::Span.new(this_ssn_start, this_ssn_end)
end
def offset(span, amount, pointer)
Chronic::Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
end
def offset_by(time, amount, pointer)
direction = pointer == :future ? 1 : -1
time + amount * direction * SEASON_SECONDS
end
def width
SEASON_SECONDS
end
def to_s
super << '-season'
end
private
def find_next_season_span(direction, next_season)
if !@next_season_start or !@next_season_end
@next_season_start = Time.construct(@now.year, @now.month, @now.day)
@next_season_end = Time.construct(@now.year, @now.month, @now.day)
end
@next_season_start += direction * num_seconds_til_start(next_season, direction)
@next_season_end += direction * num_seconds_til_end(next_season, direction)
Chronic::Span.new(@next_season_start, @next_season_end)
end
def find_current_season(md)
[:spring, :summer, :autumn, :winter].each do |season|
return season if md.is_between?(SEASONS[season].start, SEASONS[season].end)
end
end
def num_seconds_til(goal, direction)
start = Time.construct(@now.year, @now.month, @now.day)
seconds = 0
until (start + direction * seconds).to_minidate.equals?(goal)
seconds += Chronic::RepeaterDay::DAY_SECONDS
end
seconds
end
def num_seconds_til_start(season_symbol, direction)
num_seconds_til(SEASONS[season_symbol].start, direction)
end
def num_seconds_til_end(season_symbol, direction)
num_seconds_til(SEASONS[season_symbol].end, direction)
end
end