merch_calendar 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,41 +2,53 @@ require "date"
2
2
 
3
3
  module MerchCalendar
4
4
  class RetailCalendar
5
+
5
6
  QUARTER_1 = 1
6
7
  QUARTER_2 = 2
7
8
  QUARTER_3 = 3
8
9
  QUARTER_4 = 4
9
-
10
+
11
+ FOUR_WEEK_MONTHS = [2, 5, 8, 11]
12
+ FIVE_WEEK_MONTHS = [3, 6, 9, 12]
13
+
14
+ # The the first date of the retail year
15
+ #
16
+ # @param year [Fixnum] the retail year
17
+ # @return [Date] the first date of the retail year
10
18
  def end_of_year(year)
11
- year_end = Date.new((year + 1), 1, -1)
12
- wday = (year_end.wday + 1) % 7
19
+ year_end = Date.new((year + 1), 1, -1) # Jan 31st
20
+ wday = (year_end.wday + 1) % 7
13
21
 
14
22
  if wday > 3
15
23
  year_end += 7 - wday
16
- elsif wday > 0
24
+ else
17
25
  year_end -= wday
18
26
  end
19
27
  year_end
20
28
  end
21
29
 
22
- # The day after last years' end date
30
+ # The last date of the retail year
31
+ #
32
+ # @param year [Fixnum] the retail year
33
+ # @return [Date] the last date of the retail year
23
34
  def start_of_year(year)
24
35
  end_of_year(year - 1) + 1
25
36
  end
26
37
 
27
- # The starting date of a given month
28
- # THIS IS THE MERCH MONTH
29
- # 1 = feb
30
- #
38
+ # The starting date of the given merch month
39
+ #
40
+ # @param year [Fixnum] the retail year
41
+ # @param merch_month [Fixnum] the nth merch month of the retail calendar
42
+ # @return [Date] the start date of the merch month
31
43
  def start_of_month(year, merch_month)
32
44
  # 91 = number of days in a single 4-5-4 set
33
45
  start = start_of_year(year) + ((merch_month - 1) / 3).to_i * 91
34
46
 
35
47
  case merch_month
36
- when 2,5,8,11
48
+ when *FOUR_WEEK_MONTHS
37
49
  # 28 = 4 weeks
38
50
  start = start + 28
39
- when 3,6,9,12
51
+ when *FIVE_WEEK_MONTHS
40
52
  # The 5 week months
41
53
  # 63 = 4 weeks + 5 weeks
42
54
  start = start + 63
@@ -45,6 +57,11 @@ module MerchCalendar
45
57
  start
46
58
  end
47
59
 
60
+ # The ending date of the given merch month
61
+ #
62
+ # @param year [Fixnum] the retail year
63
+ # @param merch_month [Fixnum] the nth merch month of the retail calendar
64
+ # @return [Date] the end date of the merch month
48
65
  def end_of_month(year, merch_month)
49
66
  if merch_month == 12
50
67
  end_of_year(year)
@@ -54,16 +71,30 @@ module MerchCalendar
54
71
  end
55
72
 
56
73
  # Returns the date that corresponds to the first day in the merch week
74
+ #
75
+ # @param year [Fixnum] the retail year
76
+ # @param month [Fixnum] the nth merch month of the retail calendar
77
+ # @param merch_week [Fixnum] the nth week of the merch month
78
+ # @return [Date] the start date of the merch week
57
79
  def start_of_week(year, month, merch_week)
58
80
  start_of_month(year, month) + ((merch_week - 1) * 7)
59
81
  end
60
82
 
61
83
  # Returns the date that corresponds to the last day in the merch week
84
+ #
85
+ # @param year [Fixnum] the retail year
86
+ # @param month [Fixnum] the nth merch month of the retail calendar
87
+ # @param merch_week [Fixnum] the nth week of the merch month
88
+ # @return [Date] the end date of the merch week
62
89
  def end_of_week(year, month, merch_week)
63
90
  start_of_month(year, month) + (6 + ((merch_week - 1) * 7))
64
91
  end
65
92
 
66
93
  # Return the starting date for a particular quarter
94
+ #
95
+ # @param year [Fixnum] the retail year
96
+ # @param quarter [Fixnum] the quarter of the year, a number from 1 - 4
97
+ # @return [Date] the start date of the quarter
67
98
  def start_of_quarter(year, quarter)
68
99
  case quarter
69
100
  when QUARTER_1
@@ -74,10 +105,16 @@ module MerchCalendar
74
105
  start_of_month(year, 7)
75
106
  when QUARTER_4
76
107
  start_of_month(year, 10)
108
+ else
109
+ raise "invalid quarter"
77
110
  end
78
111
  end
79
112
 
80
113
  # Return the ending date for a particular quarter
114
+ #
115
+ # @param year [Fixnum] the retail year
116
+ # @param quarter [Fixnum] the quarter of the year, a number from 1 - 4
117
+ # @return [Date] the end date of the quarter
81
118
  def end_of_quarter(year, quarter)
82
119
  case quarter
83
120
  when QUARTER_1
@@ -88,14 +125,105 @@ module MerchCalendar
88
125
  end_of_month(year, 9)
89
126
  when QUARTER_4
90
127
  end_of_month(year, 12)
128
+ else
129
+ raise "invalid quarter"
130
+ end
131
+ end
132
+
133
+ # Returns the quarter that the merch month falls in
134
+ #
135
+ # @param merch_month [Fixnum] merch month
136
+ # @return [Date] the quarter that the merch_month falls in
137
+ def quarter(merch_month)
138
+ case merch_month
139
+ when 1,2,3
140
+ return QUARTER_1
141
+ when 4,5,6
142
+ return QUARTER_2
143
+ when 7,8,9
144
+ return QUARTER_3
145
+ when 10,11,12
146
+ return QUARTER_4
147
+ else
148
+ raise "invalid merch month"
149
+ end
150
+ end
151
+
152
+ #Returns the season given for the merch_month
153
+ #
154
+ # @param merch_month [Fixnum] the nth month of the retail calendar
155
+ # @return [String] the season that the merch_month falls under
156
+ def season(merch_month)
157
+ case merch_month
158
+ when 1,2,3,4,5,6
159
+ "Spring/Summer"
160
+ when 7,8,9,10,11,12
161
+ "Fall/Winter"
162
+ else
163
+ raise "invalid merch month"
91
164
  end
92
165
  end
93
166
 
94
- # Return the number of weeks in a particular year
167
+ # Returns the number of weeks in the retail year
168
+ #
169
+ # @param year [Fixnum] the retail year
170
+ # @return [Fixnum] the number of weeks within the retail year
95
171
  def weeks_in_year(year)
96
172
  ((start_of_year(year + 1) - start_of_year(year)) / 7).to_i
97
173
  end
98
174
 
175
+ # Given any julian date it will return what retail year it belongs to
176
+ #
177
+ # @param date [Date] the julian date to convert to its Retail Year
178
+ # @return [Fixnum] the retail year that the julian date falls into
179
+ def merch_year_from_date(date)
180
+ date_end_of_year = end_of_year(date.year)
181
+ date_start_of_year = start_of_year(date.year)
182
+ if date < date_start_of_year
183
+ date.year - 1
184
+ else
185
+ date.year
186
+ end
187
+ end
188
+
189
+
190
+ # Converts a merch month to the correct julian month
191
+ #
192
+ # @param merch_month [Fixnum] the merch month to convert
193
+ # @return [Fixnum] the julian month
194
+ def merch_to_julian(merch_month)
195
+ if merch_month > 12 || merch_month <= 0
196
+ raise ArgumentError
197
+ end
198
+
199
+ if merch_month == 12
200
+ 1
201
+ else
202
+ merch_month + 1
203
+ end
204
+ end
205
+
206
+ # Converts a julian month to a merch month
207
+ #
208
+ # @param julian_month [Fixnum] the julian month to convert
209
+ # @return [Fixnum] the merch month
210
+ def julian_to_merch(julian_month)
211
+ if julian_month > 12 || julian_month <= 0
212
+ raise ArgumentError
213
+ end
214
+
215
+ if julian_month == 1
216
+ 12
217
+ else
218
+ julian_month - 1
219
+ end
220
+ end
221
+
222
+ # Given beginning and end dates it will return an array of Retail Month's Start date
223
+ #
224
+ # @param start_date [Date] the starting date
225
+ # @param end_date [Date] the ending date
226
+ # @return [Array] Array of start dates of each Retail Month from given dates
99
227
  def merch_months_in(start_date, end_date)
100
228
  merch_months = []
101
229
  prev_date = start_date - 2
@@ -109,5 +237,50 @@ module MerchCalendar
109
237
  end
110
238
  merch_months
111
239
  end
240
+
241
+ # Returns an array of Merch Weeks that pertains to the Julian Month of a Retail Year
242
+ #
243
+ # @param year [Fixnum] the Retail year
244
+ # @param month_param [Fixnum] the julian month
245
+ # @return [Array] Array of MerchWeeks
246
+ def weeks_for_month(year, month_param)
247
+ merch_month = get_merch_month_param(month_param)
248
+
249
+ start_date = start_of_month(year, merch_month)
250
+
251
+ weeks = (end_of_month(year, merch_month) - start_date + 1) / 7
252
+
253
+ (1..weeks).map do |week_num|
254
+ week_start = start_date + ((week_num - 1) * 7)
255
+ week_end = week_start + 6
256
+
257
+ MerchWeek.new(week_start, {
258
+ start_of_week: week_start,
259
+ end_of_week: week_end,
260
+ week: week_num,
261
+ calendar: RetailCalendar.new
262
+ })
263
+ end
264
+ end
265
+
266
+ private
267
+
268
+ def get_merch_month_param(param)
269
+ if param.is_a? Fixnum
270
+ return julian_to_merch(param)
271
+ elsif param.is_a? Hash
272
+ julian_month = param.delete(:julian_month) || param.delete(:month)
273
+ merch_month = param.delete(:merch_month)
274
+
275
+ if merch_month
276
+ return merch_month
277
+ elsif julian_month
278
+ return julian_to_merch(julian_month)
279
+ end
280
+ end
281
+
282
+ raise ArgumentError
283
+ end
284
+
112
285
  end
113
286
  end
@@ -0,0 +1,280 @@
1
+ module MerchCalendar
2
+ class StitchFixFiscalYearCalendar
3
+
4
+ QUARTER_1 = 1
5
+ QUARTER_2 = 2
6
+ QUARTER_3 = 3
7
+ QUARTER_4 = 4
8
+
9
+ FOUR_WEEK_MONTHS = [2, 5, 8, 11]
10
+ FIVE_WEEK_MONTHS = [3, 6, 9, 12]
11
+
12
+ # The date of the first day of the fiscal year
13
+ #
14
+ # @param year [Fixnum] the fiscal year
15
+ # @return [Date] the first date of the fiscal year
16
+ def start_of_year(year)
17
+ end_of_year(year - 1) + 1
18
+ end
19
+
20
+ # The date of the last day of the fiscal year
21
+ #
22
+ # @param year [Fixnum] the fiscal year
23
+ # @return [Date] the last date of the fiscal year
24
+ def end_of_year(year)
25
+ year_end = Date.new((year), 7, -1) # Jul 31st
26
+ wday = (year_end.wday + 1) % 7
27
+
28
+ if wday > 3
29
+ year_end += 7 - wday
30
+ else
31
+ year_end -= wday
32
+ end
33
+ year_end
34
+ end
35
+
36
+ # Return the starting date for a particular quarter
37
+ #
38
+ # @param year [Fixnum] the fiscal year
39
+ # @param quarter [Fixnum] the quarter of the year, a number from 1 - 4
40
+ # @return [Date] the start date of the quarter
41
+ def start_of_quarter(year, quarter)
42
+ case quarter
43
+ when QUARTER_1
44
+ start_of_month(year, 1)
45
+ when QUARTER_2
46
+ start_of_month(year, 4)
47
+ when QUARTER_3
48
+ start_of_month(year, 7)
49
+ when QUARTER_4
50
+ start_of_month(year, 10)
51
+ else
52
+ raise "invalid quarter"
53
+ end
54
+ end
55
+
56
+ # Returns the quarter that the merch month falls in
57
+ #
58
+ # @param merch_month [Fixnum] merch month
59
+ # @return [Date] the quarter that the merch_month falls in
60
+ def quarter(merch_month)
61
+ case merch_month
62
+ when 1,2,3
63
+ return QUARTER_1
64
+ when 4,5,6
65
+ return QUARTER_2
66
+ when 7,8,9
67
+ return QUARTER_3
68
+ when 10,11,12
69
+ return QUARTER_4
70
+ else
71
+ raise "invalid merch month"
72
+ end
73
+ end
74
+
75
+ # Return the ending date for a particular quarter
76
+ #
77
+ # @param year [Fixnum] the fiscal year
78
+ # @param quarter [Fixnum] the quarter of the year, a number from 1 - 4
79
+ # @return [Date] the end date of the quarter
80
+ def end_of_quarter(year, quarter)
81
+ case quarter
82
+ when QUARTER_1
83
+ end_of_month(year, 3)
84
+ when QUARTER_2
85
+ end_of_month(year, 6)
86
+ when QUARTER_3
87
+ end_of_month(year, 9)
88
+ when QUARTER_4
89
+ end_of_month(year, 12)
90
+ else
91
+ raise "invalid quarter"
92
+ end
93
+ end
94
+
95
+ #Returns the season given for the merch_month
96
+ #
97
+ # @param merch_month [Fixnum] the nth merch month of the retail calendar
98
+ # @return [String] the season that the merch month falls under
99
+ def season(merch_month)
100
+ case merch_month
101
+ when 1,2,3,4,5,6
102
+ "Fall/Winter"
103
+ when 7,8,9,10,11,12
104
+ "Spring/Summer"
105
+ else
106
+ raise "invalid merch month"
107
+ end
108
+ end
109
+
110
+ # The starting date of the given merch month
111
+ #
112
+ # @param year [Fixnum] the fiscal year
113
+ # @param merch_month [Fixnum] the nth merch month of the fiscal calendar
114
+ # @return [Date] the start date of the merch month
115
+ def start_of_month(year, merch_month)
116
+ # 91 = number of days in a single 4-5-4 set
117
+ start = start_of_year(year) + ((merch_month - 1) / 3).to_i * 91
118
+
119
+ case merch_month
120
+ when *FOUR_WEEK_MONTHS
121
+ # 28 = 4 weeks
122
+ start = start + 28
123
+ when *FIVE_WEEK_MONTHS
124
+ # The 5 week months
125
+ # 63 = 4 weeks + 5 weeks
126
+ start = start + 63
127
+ end
128
+
129
+ start
130
+ end
131
+
132
+ # The ending date of the given merch month
133
+ #
134
+ # @param year [Fixnum] the fiscal year
135
+ # @param merch_month [Fixnum] the nth merch month of the fiscal calendar
136
+ # @return [Date] the end date of the merch month
137
+ def end_of_month(year, merch_month)
138
+ if merch_month == 12
139
+ end_of_year(year)
140
+ else
141
+ start_of_month(year, merch_month + 1) - 1
142
+ end
143
+ end
144
+
145
+ # Returns the date that corresponds to the first day in the merch week
146
+ #
147
+ # @param year [Fixnum] the fiscal year
148
+ # @param merch_month [Fixnum] the nth month of the fiscal calendar
149
+ # @param merch_week [Fixnum] the nth week of the merch month
150
+ # @return [Date] the start date of the merch week
151
+ def start_of_week(year, month, merch_week)
152
+ start_of_month(year, month) + ((merch_week - 1) * 7)
153
+ end
154
+
155
+ # Returns the date that corresponds to the last day in the merch week
156
+ #
157
+ # @param year [Fixnum] the fiscal year
158
+ # @param merch_month [Fixnum] the nth merch month of the fiscal calendar
159
+ # @param merch_week [Fixnum] the nth week of the merch month
160
+ # @return [Date] the end date of the merch week
161
+ def end_of_week(year, month, merch_week)
162
+ start_of_month(year, month) + (6 + ((merch_week - 1) * 7))
163
+ end
164
+
165
+ # Returns the number of weeks in the fiscal year
166
+ #
167
+ # @param year [Fixnum] the fiscal year
168
+ # @return [Fixnum] the number of weeks within the fiscal year
169
+ def weeks_in_year(year)
170
+ ((start_of_year(year + 1) - start_of_year(year)) / 7).to_i
171
+ end
172
+
173
+ # Given any julian date it will return what Fiscal Year it belongs to
174
+ #
175
+ # @param date [Date] the julian date to convert to its Fiscal Year
176
+ # @return [Fixnum] the fiscal year that the julian date falls into
177
+ def merch_year_from_date(date)
178
+ if end_of_year(date.year) >= date
179
+ return date.year
180
+ else
181
+ return date.year + 1
182
+ end
183
+ end
184
+
185
+ # Converts a merch month to the correct julian month
186
+ #
187
+ # @param merch_month [Fixnum] the merch month to convert
188
+ # @return [Fixnum] the julian month
189
+ def merch_to_julian(merch_month)
190
+ if merch_month > 12 || merch_month <= 0
191
+ raise ArgumentError
192
+ end
193
+
194
+ if merch_month <= 5
195
+ merch_month + 7
196
+ else
197
+ merch_month - 5
198
+ end
199
+ end
200
+
201
+ # Converts a julian month to a fiscal month
202
+ #
203
+ # @param julian_month [Fixnum] the julian month to convert
204
+ # @return [Fixnum] the merch month
205
+ def julian_to_merch(julian_month)
206
+ if julian_month > 12 || julian_month <= 0
207
+ raise ArgumentError
208
+ end
209
+
210
+ if julian_month <= 7
211
+ julian_month + 5
212
+ else
213
+ julian_month - 7
214
+ end
215
+ end
216
+
217
+ # Given beginning and end dates it will return an array of Fiscal Month's Start date
218
+ #
219
+ # @param start_date [Date] the starting date
220
+ # @param end_date [Date] the ending date
221
+ # @return [Array] Array of start dates of each Fiscal Month from given dates
222
+ def merch_months_in(start_date, end_date)
223
+ merch_months_combos = merch_year_and_month_from_dates(start_date, end_date)
224
+ merch_months_combos.map { | merch_month_combo | start_of_month(merch_month_combo[0], merch_month_combo[1]) }
225
+ end
226
+
227
+ # Returns an array of Merch Weeks that pertains to the Julian Month of a Fiscal Year
228
+ #
229
+ # @param year [Fixnum] the fiscal year
230
+ # @param month_param [Fixnum] the julian month
231
+ # @return [Array] Array of MerchWeeks that falls within that julian month
232
+ def weeks_for_month(year, month_param)
233
+ merch_month = julian_to_merch(month_param)
234
+
235
+ start_date = start_of_month(year, merch_month)
236
+
237
+ weeks = (end_of_month(year, merch_month) - start_date + 1) / 7
238
+
239
+ (1..weeks).map do |week_num|
240
+ week_start = start_date + ((week_num - 1) * 7)
241
+ week_end = week_start + 6
242
+
243
+ MerchWeek.new(week_start, {
244
+ start_of_week: week_start,
245
+ end_of_week: week_end,
246
+ week: week_num,
247
+ calendar: StitchFixFiscalYearCalendar.new
248
+ })
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ # Returns an array of merch_months and year combination that falls in and between the start and end date
255
+ #
256
+ # Ex: if start_date = August 1, 2018 and end_date = October 1, 2018
257
+ # it returns [[2019, 1], [ 2019, 2], [2019, 3]]
258
+ def merch_year_and_month_from_dates(start_date, end_date)
259
+ merch_months = []
260
+
261
+ middle_of_start_month = Date.new(start_date.year, start_date.month, 14)
262
+ middle_of_end_month = Date.new(end_date.year, end_date.month, 14)
263
+ date = middle_of_start_month
264
+
265
+ while date <= middle_of_end_month do
266
+ merch_months.push(date_conversion(date))
267
+ date = date >> 1
268
+ end
269
+ merch_months
270
+ end
271
+
272
+ # This isn't a true date conversion, only used for merch_year_and_month_from_dates
273
+ # when its julian month actually falls in the wrong merch year
274
+ # EX: The true date_conversion of July 1, 2018 => [ 2019, 1 ]
275
+ # BUT this method here will return [2019, 12] because July is merch_month 12 for fiscal year
276
+ def date_conversion(date)
277
+ [ merch_year_from_date(date), julian_to_merch(date.month) ]
278
+ end
279
+ end
280
+ end
@@ -1,5 +1,5 @@
1
1
  require "merch_calendar/retail_calendar"
2
- require "merch_calendar/fiscal_year_calendar"
2
+ require "merch_calendar/stitch_fix_fiscal_year_calendar"
3
3
 
4
4
  module MerchCalendar
5
5
 
@@ -179,7 +179,7 @@ module MerchCalendar
179
179
  end
180
180
 
181
181
  def fiscal_year_calendar
182
- @fiscal_year_calendar ||= FiscalYearCalendar.new
182
+ @fiscal_year_calendar ||= StitchFixFiscalYearCalendar.new
183
183
  end
184
184
 
185
185
  # Reads the provided parameter and converts the value
@@ -225,7 +225,7 @@ module MerchCalendar
225
225
  :start_of_quarter,
226
226
  :end_of_quarter
227
227
  ].each do |method|
228
- deprecate method, "#{MerchCalendar::FiscalYearCalendar}##{method}", DEPRECATION_DATE.year, DEPRECATION_DATE.month
228
+ deprecate method, "#{MerchCalendar::StitchFixFiscalYearCalendar}##{method}", DEPRECATION_DATE.year, DEPRECATION_DATE.month
229
229
  end
230
230
 
231
231
  end
@@ -1,3 +1,3 @@
1
1
  module MerchCalendar
2
- VERSION = '0.1.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -1,6 +1,5 @@
1
1
  require "date"
2
2
 
3
- #
4
3
  module MerchCalendar
5
4
  DEPRECATION_DATE = Date.new(2018, 1, 1)
6
5
  end
@@ -9,4 +8,4 @@ require_relative 'merch_calendar/version'
9
8
  require_relative 'merch_calendar/util'
10
9
  require_relative 'merch_calendar/merch_week'
11
10
  require_relative 'merch_calendar/retail_calendar'
12
- require_relative 'merch_calendar/fiscal_year_calendar'
11
+ require_relative 'merch_calendar/stitch_fix_fiscal_year_calendar'