Class: AIXM::Schedule::Date

Inherits:
Object show all
Extended by:
Forwardable
Includes:
Concerns::HashEquality, Comparable
Defined in:
lib/aixm/schedule/date.rb

Overview

Dates suitable for schedules

This class implements the bare minimum of stdlib Date and adds some extensions:

  • yearless dates

  • #covered_by? to check whether (yearless) date falls within (yearless) date range

Examples:

date = AIXM.date('2022-04-20')               # => 2022-04-20
from = AIXM.date('03-20')                    # => XXXX-03-20
date.covered_by?(from..AIXM.date('05-20'))   # => true

Constant Summary collapse

YEARLESS_YEAR =
0

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Concerns::HashEquality

#eql?

Constructor Details

#initialize(date) ⇒ Date

Parse the given representation of (yearless) date.

Parameters:

  • date (Date, Time, String)

    either stdlib Date/Time, “YYYY-MM-DD”, “XXXX-MM-DD” or “MM-DD” (yearless date)



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/aixm/schedule/date.rb', line 33

def initialize(date)
  @date = case date.to_s[0, 10]
  when /\A\d{4}-\d{2}-\d{2}\z/
    ::Date.strptime(date.to_s)
  when /\A(?:XXXX-)?(\d{2}-\d{2})\z/
    ::Date.strptime("#{YEARLESS_YEAR}-#{$1}")
  else
    fail ArgumentError
  end
rescue ::Date::Error
  raise ArgumentError
end

Instance Attribute Details

#dateObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



27
28
29
# File 'lib/aixm/schedule/date.rb', line 27

def date
  @date
end

Instance Method Details

#-(date) ⇒ Integer

Calculate difference in days between two dates.

Returns:

  • (Integer)


105
106
107
# File 'lib/aixm/schedule/date.rb', line 105

def -(date)
  (self.date - date.date).to_i
end

#<=>(other) ⇒ Object



134
135
136
137
# File 'lib/aixm/schedule/date.rb', line 134

def <=>(other)
  fail "not comparable" unless comparable_to? other
  @date.jd <=> other.to_date.jd
end

#at(year: nil, month: nil, day: nil, wrap: false) ⇒ AIXM::Schedule::Date

Creates a new date with the given parts altered.

Examples:

date = AIXM.date('2000-12-22')
date.at(month: 4)               # => 2000-04-22
date.at(year: 2020, day: 5)     # => 2020-12-05
date.at(month: 1)               # => 2020-01-22
date.at(month: 1, wrap: true)   # => 2021-01-22 (year incremented)

Parameters:

  • year (Integer) (defaults to: nil)

    new year

  • month (Integer) (defaults to: nil)

    new month

  • day (Integer) (defaults to: nil)

    new day

  • wrap (Boolean) (defaults to: false)

    whether to increment month when crossing month boundary and year when crossing year boundary

Returns:



77
78
79
80
81
82
83
84
# File 'lib/aixm/schedule/date.rb', line 77

def at(year: nil, month: nil, day: nil, wrap: false)
  return self unless year || month || day
  wrap_month, wrap_year = day&.<(date.day), month&.<(date.month)
  date = ::Date.new(year || self.year || YEARLESS_YEAR, month || self.month, day || self.day)
  date = date.next_month if wrap && wrap_month && !month
  date = date.next_year if wrap && wrap_year && !year
  self.class.new(date.strftime(yearless? ? '%m-%d' : '%F'))
end

#comparable_to?(other) ⇒ Boolean

Whether the other schedule date can be compared to this one.

Parameters:

Returns:

  • (Boolean)


130
131
132
# File 'lib/aixm/schedule/date.rb', line 130

def comparable_to?(other)
  other.instance_of?(self.class) && yearless? == other.yearless?
end

#covered_by?(other) ⇒ Boolean

Note:

It is possible to compare dates as well as days.

Whether this schedule date falls within the given range of schedule dates

Parameters:

Returns:

  • (Boolean)


177
178
179
180
181
182
183
184
185
186
187
# File 'lib/aixm/schedule/date.rb', line 177

def covered_by?(other)
  range = Range.from(other)
  case
  when range.first.instance_of?(AIXM::Schedule::Day)
    range.first.any? || to_day.covered_by?(range)
  when range.first.yearless?
    yearless? ? covered_by_yearless_date?(range) : to_yearless.covered_by?(range)
  else
    yearless? ? covered_by_yearless_date?(range.first.to_yearless..range.last.to_yearless) : range.cover?(self)
  end
end

#dayInteger

Returns day of month.

Returns:

  • (Integer)

    day of month



167
# File 'lib/aixm/schedule/date.rb', line 167

def_delegators :@date, :month, :day

#hashObject

See Also:

  • Object#hash


140
141
142
# File 'lib/aixm/schedule/date.rb', line 140

def hash
   [self.class, @date.jd].hash
end

#inspectObject



58
59
60
# File 'lib/aixm/schedule/date.rb', line 58

def inspect
  %Q(#<#{self.class} #{to_s}>)
end

#monthInteger

Returns:

  • (Integer)


167
# File 'lib/aixm/schedule/date.rb', line 167

def_delegators :@date, :month, :day

#predAIXM::Schedule::Date Also known as: prev

Create new date one day prior to this one.



89
90
91
# File 'lib/aixm/schedule/date.rb', line 89

def pred
  self.class.new(date.prev_day.to_s.sub(/^-/, '')).at(year: (YEARLESS_YEAR if yearless?))
end

#succAIXM::Schedule::Date Also known as: next

Create new date one day after this one.



97
98
99
# File 'lib/aixm/schedule/date.rb', line 97

def succ
  self.class.new(date.next_day).at(year: (YEARLESS_YEAR if yearless?))
end

#to_dateDate

Stdlib Date equivalent using the value of YEARLESS_YEAR to represent a yearless date.

Returns:



122
123
124
# File 'lib/aixm/schedule/date.rb', line 122

def to_date
  @date
end

#to_dayAIXM::Schedule::Day

Convert date to day

Returns:

Raises:

  • (RuntimeError)

    if date is yearless



113
114
115
116
# File 'lib/aixm/schedule/date.rb', line 113

def to_day
  fail "cannot convert yearless date" if yearless?
  AIXM.day(date.wday)
end

#to_s(format = '%Y-%m-%d') ⇒ String

Human readable representation such as “2002-05-19” or “XXXX-05-19”

All formats from strftime are supported, however, %Y is replaced with “XXXX” for yearless dates. Any other formats containing the year won’t do so and should be avoided!

Parameters:

  • format (String) (defaults to: '%Y-%m-%d')

    see strftime

Returns:

  • (String)


54
55
56
# File 'lib/aixm/schedule/date.rb', line 54

def to_s(format='%Y-%m-%d')
  @date.strftime(yearless? ? format.gsub('%Y', 'XXXX') : format)
end

#to_yearlessAIXM::Schedule::Date

Yearless duplicate of self



154
155
156
# File 'lib/aixm/schedule/date.rb', line 154

def to_yearless
  yearless? ? self : self.class.new(to_s[5..])
end

#yearInteger

Returns year or nil if yearless.

Returns:

  • (Integer)

    year or nil if yearless



159
160
161
# File 'lib/aixm/schedule/date.rb', line 159

def year
  @date.year unless yearless?
end

#yearless?Boolean

Whether this schedule date is yearless or not.

Returns:

  • (Boolean)


147
148
149
# File 'lib/aixm/schedule/date.rb', line 147

def yearless?
  @date.year == YEARLESS_YEAR
end