Method: Date.parse_spec

Defined in:
lib/fat_core/date.rb

.parse_spec(spec, spec_type = :from) ⇒ Object

Convert a ‘date spec’ to a Date. A date spec is a short-hand way of

specifying a date, relative to the computer clock.  A date spec can
interpreted as either a 'from spec' or a 'to spec'.

@example
Assuming that Date.current at the time of execution is 2014-07-26 and
using the default spec_type of :from.  The return values are actually Date
objects, but are shown below as textual dates.

A fully specified date returns that date:
    Date.parse_spec('2001-09-11')  # =>

Commercial weeks can be specified using, for example W32 or 32W, with the
week beginning on Monday, ending on Sunday.
    Date.parse_spec('2012-W32')    # =>
    Date.parse_spec('2012-W32', :to) # =>
    Date.parse_spec('W32') # =>

A spec of the form Q3 or 3Q returns the beginning or end of calendar
quarters.
    Date.parse_spec('Q3')         # =>

@param spec [#to_s] a stringling containing the spec to be interpreted
@param spec_type [:from, :to] interpret the spec as a from- or to-spec
  respectively, defaulting to interpretation as a to-spec.
@return [Date] a date object equivalent to the date spec


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
# File 'lib/fat_core/date.rb', line 66

def self.parse_spec(spec, spec_type = :from)
  spec = spec.to_s.strip
  unless [:from, :to].include?(spec_type)
    raise ArgumentError "invalid date spec type: '#{spec_type}'"
  end

  today = Date.current
  case spec
  when /^(\d\d\d\d)-(\d\d?)-(\d\d?)*$/
    # A specified date
    Date.new($1.to_i, $2.to_i, $3.to_i)
  when /\AW(\d\d?)\z/, /\A(\d\d?)W\z/
    week_num = $1.to_i
    if week_num < 1 || week_num > 53
      raise ArgumentError, "invalid week number (1-53): 'W#{week_num}'"
    end
    spec_type == :from ? Date.commercial(today.year, week_num).beginning_of_week :
      Date.commercial(today.year, week_num).end_of_week
  when /\A(\d\d\d\d)-W(\d\d?)\z/, /\A(\d\d\d\d)-(\d\d?)W\z/
    year = $1.to_i
    week_num = $2.to_i
    if week_num < 1 || week_num > 53
      raise ArgumentError, "invalid week number (1-53): 'W#{week_num}'"
    end
    spec_type == :from ? Date.commercial(year, week_num).beginning_of_week :
      Date.commercial(year, week_num).end_of_week
  when /^(\d\d\d\d)-(\d)[Qq]$/, /^(\d\d\d\d)-[Qq](\d)$/
    # Year-Quarter
    year = $1.to_i
    quarter = $2.to_i
    unless [1, 2, 3, 4].include?(quarter)
      raise ArgumentError, "bad date format: #{spec}"
    end
    month = quarter * 3
    spec_type == :from ? Date.new(year, month, 1).beginning_of_quarter :
      Date.new(year, month, 1).end_of_quarter
  when /^([1234])[qQ]$/, /^[qQ]([1234])$/
    # Quarter only
    this_year = today.year
    quarter = $1.to_i
    date = Date.new(this_year, quarter * 3, 15)
    spec_type == :from ? date.beginning_of_quarter : date.end_of_quarter
  when /^(\d\d\d\d)-(\d\d?)*$/
    # Year-Month only
    spec_type == :from ? Date.new($1.to_i, $2.to_i, 1) :
      Date.new($1.to_i, $2.to_i, 1).end_of_month
  when /\A(\d\d?)\z/
    # Month only
    spec_type == :from ? Date.new(today.year, $1.to_i, 1) :
      Date.new(today.year, $1.to_i, 1).end_of_month
  when /^(\d\d\d\d)$/
    # Year only
    spec_type == :from ? Date.new($1.to_i, 1, 1) : Date.new($1.to_i, 12, 31)
  when /^(to|this_?)?day/
    today
  when /^(yester|last_?)?day/
    today - 1.day
  when /^(this_?)?week/
    spec_type == :from ? today.beginning_of_week : today.end_of_week
  when /last_?week/
    spec_type == :from ? (today - 1.week).beginning_of_week :
      (today - 1.week).end_of_week
  when /^(this_?)?biweek/
    spec_type == :from ? today.beginning_of_biweek : today.end_of_biweek
  when /last_?biweek/
    spec_type == :from ? (today - 2.week).beginning_of_biweek :
      (today - 2.week).end_of_biweek
  when /^(this_?)?semimonth/
    spec_type == :from ? today.beginning_of_semimonth : today.end_of_semimonth
  when /^last_?semimonth/
    spec_type == :from ? (today - 15.days).beginning_of_semimonth :
      (today - 15.days).end_of_semimonth
  when /^(this_?)?month/
    spec_type == :from ? today.beginning_of_month : today.end_of_month
  when /^last_?month/
    spec_type == :from ? (today - 1.month).beginning_of_month :
      (today - 1.month).end_of_month
  when /^(this_?)?bimonth/
    spec_type == :from ? today.beginning_of_bimonth : today.end_of_bimonth
  when /^last_?bimonth/
    spec_type == :from ? (today - 2.month).beginning_of_bimonth :
      (today - 2.month).end_of_bimonth
  when /^(this_?)?quarter/
    spec_type == :from ? today.beginning_of_quarter : today.end_of_quarter
  when /^last_?quarter/
    spec_type == :from ? (today - 3.months).beginning_of_quarter :
      (today - 3.months).end_of_quarter
  when /^(this_?)?year/
    spec_type == :from ? today.beginning_of_year : today.end_of_year
  when /^last_?year/
    spec_type == :from ? (today - 1.year).beginning_of_year :
      (today - 1.year).end_of_year
  when /^forever/
    spec_type == :from ? Date::BOT : Date::EOT
  when /^never/
    nil
  else
    raise ArgumentError, "bad date spec: '#{spec}''"
  end # !> previous definition of length was here
end