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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +47 -12
- data/lib/merch_calendar/merch_week.rb +74 -90
- data/lib/merch_calendar/retail_calendar.rb +185 -12
- data/lib/merch_calendar/stitch_fix_fiscal_year_calendar.rb +280 -0
- data/lib/merch_calendar/util.rb +3 -3
- data/lib/merch_calendar/version.rb +1 -1
- data/lib/merch_calendar.rb +1 -2
- data/spec/merch_calendar/merch_week_spec.rb +204 -52
- data/spec/merch_calendar/retail_calendar_spec.rb +141 -2
- data/spec/merch_calendar/stitch_fix_fiscal_year_calendar_spec.rb +382 -0
- metadata +5 -5
- data/lib/merch_calendar/fiscal_year_calendar.rb +0 -118
- data/spec/merch_calendar/fiscal_year_calendar_spec.rb +0 -145
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13a41aecf0152a0c3bffeed5ecf51f15dd5d6927
|
4
|
+
data.tar.gz: 482637d57739642817b9c8729f6598fdd4ba4ff7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cee1f7ae6cf8817c0616eb46eb572f3a39cf6deb087ed6515335ad693030ae5106353915199be23b471d53e494a7f190ba7ccf021523c66674fd96e52ac71085
|
7
|
+
data.tar.gz: c49c4924295a06b57a71c164a2f6c57a6b433ef25b0eb3359e42da3cec27f8b3f25affc94009c57337cf4d872747deddff78585a4a95c65e57b3093aa7bd6e03
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -24,13 +24,13 @@ gem "merch_calendar"
|
|
24
24
|
For converting a date into a `MerchWeek` object.
|
25
25
|
|
26
26
|
```ruby
|
27
|
-
merch_week = MerchCalendar::MerchWeek.from_date("2014-01-01")
|
27
|
+
merch_week = MerchCalendar::MerchWeek.from_date("2014-01-01", calendar: MerchCalendar::RetailCalendar.new)
|
28
28
|
|
29
29
|
merch_week.year # 2013 (the merch year associated with this date)
|
30
30
|
merch_week.month # 12 (the julian month that the date falls in)
|
31
31
|
merch_week.week # 5 (the week number within the month)
|
32
|
-
merch_week.year_week # 48 (the week number within the year)
|
33
|
-
merch_week.quarter #
|
32
|
+
merch_week.year_week # 48 (the week number within the retail calendar year)
|
33
|
+
merch_week.quarter # 4
|
34
34
|
|
35
35
|
merch_week.start_of_week # <Date: 2013-12-29>
|
36
36
|
merch_week.end_of_week # <Date: 2014-01-04>
|
@@ -41,6 +41,9 @@ merch_week.end_of_month # <Date: 2014-01-04>
|
|
41
41
|
merch_week.start_of_year # <Date: 2013-02-03>
|
42
42
|
merch_week.end_of_year # <Date: 2014-02-01>
|
43
43
|
|
44
|
+
merch_week.calendar # <MerchCalendar::RetailCalendar> (the calendar we're using)
|
45
|
+
# if you don't initialize a calendar, it defaults to RetailCalendar
|
46
|
+
|
44
47
|
# Formatting
|
45
48
|
merch_week.to_s # "Dec W5"
|
46
49
|
merch_week.to_s(:short) # "Dec W5"
|
@@ -51,7 +54,7 @@ merch_week.to_s(:elasticsearch) # "2013-12w05"
|
|
51
54
|
|
52
55
|
### Merch retail calendar
|
53
56
|
|
54
|
-
|
57
|
+
Retail calendars have their first month in February, and the last (12th) month is in January of the
|
55
58
|
following year.
|
56
59
|
|
57
60
|
```ruby
|
@@ -102,18 +105,17 @@ retail_calendar.end_of_week(2017, 4, 1)
|
|
102
105
|
#=> #<Date: 2017-05-06 ((2457880j,0s,0n),+0s,2299161j)>
|
103
106
|
```
|
104
107
|
|
105
|
-
###
|
106
|
-
Some companies, one of which being Stitch Fix, operate on a fiscal year calendar that
|
107
|
-
the traditional retail calendar. The `MerchCalendar::
|
108
|
-
|
108
|
+
### Stitch Fix Fiscal Year Calendars
|
109
|
+
Some companies, one of which being Stitch Fix, operate on a fiscal year calendar that instead starts in August
|
110
|
+
rather than in February in a the traditional retail calendar. The `MerchCalendar::StitchFixFiscalYearCalendar` class
|
111
|
+
allows you to easily translate Gregorian dates to a Stitch Fix Fiscal Year date.
|
109
112
|
|
110
113
|
```ruby
|
111
|
-
fiscal_calendar = MerchCalendar::
|
114
|
+
fiscal_calendar = MerchCalendar::StitchFixFiscalYearCalendar.new
|
112
115
|
|
113
|
-
# 52 or 53 (depending on leap year)
|
114
|
-
fiscal_calendar.weeks_in_year(2017)
|
115
|
-
# => 52
|
116
116
|
fiscal_calendar.weeks_in_year(2018)
|
117
|
+
# => 52
|
118
|
+
fiscal_calendar.weeks_in_year(2019)
|
117
119
|
# => 53
|
118
120
|
|
119
121
|
# get the start date of a given merch week
|
@@ -125,6 +127,39 @@ fiscal_calendar.end_of_week(2017, 4, 1)
|
|
125
127
|
#=> #<Date: 2017-05-06 ((2457880j,0s,0n),+0s,2299161j)>
|
126
128
|
```
|
127
129
|
|
130
|
+
#### Stitch Fix Fiscal Year Calendar in Relation to MerchWeek object
|
131
|
+
You can set MerchWeek object respond to the Stitch Fix Fiscal Calendar, by passing in `StitchFixFiscalYearCalendar.new` in the calendar parameter:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
merch_week = MerchCalendar::MerchWeek.from_date("2018-07-02", calendar: MerchCalendar::StitchFixFiscalYearCalendar.new)
|
135
|
+
|
136
|
+
merch_week.year # 2018 (the merch year associated with this date)
|
137
|
+
merch_week.month # 7 (the julian month that the date falls in)
|
138
|
+
merch_week.merch_month # 12 (this fiscal month the date falls in)
|
139
|
+
merch_week.week # 1 (the week number within the month)
|
140
|
+
merch_week.year_week # 49 (the week number within the retail calendar year)
|
141
|
+
merch_week.quarter # 4
|
142
|
+
|
143
|
+
merch_week.start_of_week # <Date: 2018-07-01>
|
144
|
+
merch_week.end_of_week # <Date: 2018-07-07>
|
145
|
+
|
146
|
+
merch_week.start_of_month # <Date: 2018-07-01>
|
147
|
+
merch_week.end_of_month # <Date: 2018-07-28>
|
148
|
+
|
149
|
+
merch_week.start_of_year # <Date: 2017-07-30>
|
150
|
+
merch_week.end_of_year # <Date: 2018-07-28>
|
151
|
+
|
152
|
+
merch_week.calendar # <MerchCalendar::StitchFixFiscalYearCalendar> (the calendar
|
153
|
+
# we're using)
|
154
|
+
# if you don't initialize a calendar, it defaults to RetailCalendar
|
155
|
+
|
156
|
+
# Formatting
|
157
|
+
merch_week.to_s # "Jul W1"
|
158
|
+
merch_week.to_s(:short) # "Jul W1"
|
159
|
+
merch_week.to_s(:long) # "2018:49 Jul W1"
|
160
|
+
merch_week.to_s(:elasticsearch) # "2018-07w01"
|
161
|
+
```
|
162
|
+
|
128
163
|
## Documentation
|
129
164
|
You can view the documentation for this gem on [RubyDoc.info](http://www.rubydoc.info/github/stitchfix/merch_calendar/master).
|
130
165
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module MerchCalendar
|
2
2
|
|
3
|
-
# Represents the Merch Week for a specified date
|
3
|
+
# Represents the Merch Week for a specified date and calendar
|
4
4
|
class MerchWeek
|
5
5
|
|
6
6
|
MONTHS = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec).freeze
|
@@ -10,56 +10,68 @@ module MerchCalendar
|
|
10
10
|
# @!attribute [r] date
|
11
11
|
# @return [Date] the date for this merch week
|
12
12
|
attr_reader :date
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
13
|
+
|
14
|
+
# The Merch Calendar that is being represented, either Fiscal or Retail
|
15
|
+
#
|
16
|
+
# @!attribute [r] calendar
|
17
|
+
# @return [Class] the calendar that determines the merch week
|
18
|
+
attr_reader :calendar
|
19
|
+
|
20
|
+
# Locates the +MerchWeek+ for a given Julian date.
|
21
|
+
#
|
22
|
+
# @overload from_date(String)
|
23
|
+
# @param julian_date [String] a julian date in the format of +YYYY-MM-DD+
|
24
|
+
# @param options [Hash] opts the options to set your calendar, if none it will default to RetailCalendar
|
25
|
+
# # @option opts [Class] :calendar The Calendar Class
|
26
|
+
# @overload from_date(Date)
|
27
|
+
# @param julian_date [Date] julian_date a +Date+ object
|
28
|
+
# @param options [Hash] opts the options to set your calendar, if none it will default to RetailCalendar
|
29
|
+
# # @option opts [Class] :calendar The Calendar Class
|
30
|
+
# @return [MerchWeek]
|
31
|
+
def self.from_date(julian_date, options = {})
|
32
|
+
MerchWeek.new(Date.parse("#{julian_date}"), options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns an array of merch weeks for a month, or a specific week.
|
36
|
+
#
|
37
|
+
# @overload find(year, julian_month, week_number=nil, options)
|
38
|
+
# Returns an array of +MerchWeek+s for a given month
|
39
|
+
# @param year [Fixnum] the merch year to locate
|
40
|
+
# @param julian_month [Fixnum] the month to find merch months for
|
41
|
+
# @param week_number [Nil] set week_number to nil
|
42
|
+
# @param options [Hash] options to set your calendar, if none it will default to RetailCalendar
|
43
|
+
# @return [Array<MerchWeek>]
|
44
|
+
# @overload find(year, julian_month, week_number)
|
45
|
+
# @param year [Fixnum] the merch year to locate
|
46
|
+
# @param julian_month [Fixnum] the month to find merch months for
|
47
|
+
# @param week_number [Fixnum] the specific week number.
|
48
|
+
# @param options [Hash] options to set your calendar, if none it will default to RetailCalendar
|
49
|
+
# @return [MerchWeek] the specific merch week based on the week number
|
50
|
+
def self.find(year, julian_month, week_number=nil, options={})
|
51
|
+
calendar = options.fetch(:calendar, RetailCalendar.new)
|
52
|
+
if week_number.nil?
|
53
|
+
calendar.weeks_for_month(year, julian_month)
|
54
|
+
else
|
55
|
+
calendar.weeks_for_month(year, julian_month)[week_number-1]
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
56
|
-
|
59
|
+
# Returns the +MerchWeek+ for today's date
|
60
|
+
#
|
61
|
+
# @param options [Hash] opts the options to set your calendar, if none it will default to RetailCalendar
|
62
|
+
# # @option opts [Class] :calendar The Calendar Class
|
63
|
+
# @return [MerchWeek]
|
64
|
+
def self.today(options={})
|
65
|
+
MerchWeek.from_date(Date.today, options)
|
66
|
+
end
|
57
67
|
|
58
68
|
# Pass in a date, make sure it is a valid date object
|
59
69
|
# @private
|
60
70
|
def initialize(date, options = {})
|
61
71
|
@date = date
|
62
|
-
|
72
|
+
|
73
|
+
#defaults to Retail Calendar if no other calendar is defined
|
74
|
+
@calendar = options.fetch(:calendar, RetailCalendar.new)
|
63
75
|
# Placeholders. These should be populated by functions if nil
|
64
76
|
# week_start: nil, week_end: nil, week_number: nil
|
65
77
|
@start_of_year = options[:start_of_year]
|
@@ -81,62 +93,55 @@ module MerchCalendar
|
|
81
93
|
end
|
82
94
|
|
83
95
|
# This returns the "merch month" number for a date
|
84
|
-
#
|
96
|
+
# Month 1 is Feb for the retail calendar
|
97
|
+
# Month 1 is August for the fiscal calendar
|
85
98
|
#
|
86
99
|
# @return [Fixnum]
|
87
100
|
def merch_month
|
88
101
|
# TODO: This is very inefficient, but less complex than strategic guessing
|
89
102
|
# maybe switch to a binary search or something
|
103
|
+
merch_year = calendar.merch_year_from_date(date)
|
90
104
|
@merch_month ||= (1..12).detect do |num|
|
91
|
-
|
105
|
+
calendar.end_of_month(merch_year, num) >= date && date >= calendar.start_of_month(merch_year, num)
|
92
106
|
end
|
93
107
|
end
|
94
108
|
|
95
|
-
#
|
109
|
+
# Returns the Merch year depending whether it is from the Retail or Fiscal calendar
|
96
110
|
#
|
97
111
|
# @return [Fixnum]
|
98
112
|
def year
|
99
|
-
|
113
|
+
@year ||= calendar.merch_year_from_date(date)
|
100
114
|
end
|
101
115
|
|
102
|
-
#
|
116
|
+
# Returns julian month where the merch week falls
|
103
117
|
#
|
104
118
|
# @return [Fixnum]
|
105
119
|
def month
|
106
|
-
@month ||=
|
120
|
+
@month ||= calendar.merch_to_julian(merch_month)
|
107
121
|
end
|
108
122
|
|
109
|
-
#
|
123
|
+
# Returns the quarter that this merch week falls
|
110
124
|
#
|
111
125
|
# @return [Fixnum]
|
112
126
|
def quarter
|
113
|
-
|
114
|
-
when 7,8,9
|
115
|
-
return 1
|
116
|
-
when 10,11,12
|
117
|
-
return 2
|
118
|
-
when 1,2,3
|
119
|
-
return 3
|
120
|
-
else
|
121
|
-
return 4
|
122
|
-
end
|
127
|
+
@quarter ||= calendar.quarter(merch_month)
|
123
128
|
end
|
124
129
|
|
125
|
-
# Returns the date of
|
130
|
+
# Returns the start date of this week
|
126
131
|
#
|
127
132
|
# @return [Date]
|
128
133
|
def start_of_week
|
129
134
|
@start_of_week ||= (start_of_month + (7 * (week - 1)))
|
130
135
|
end
|
131
136
|
|
132
|
-
# Returns the date of
|
137
|
+
# Returns the end date of this week
|
133
138
|
#
|
134
139
|
# @return [Date]
|
135
140
|
def end_of_week
|
136
141
|
@end_of_week ||= (start_of_week + 6)
|
137
142
|
end
|
138
143
|
|
139
|
-
#
|
144
|
+
# The number of the week within the given merch month
|
140
145
|
# will be between 1 and 5
|
141
146
|
#
|
142
147
|
# @return [Fixnum]
|
@@ -144,32 +149,32 @@ module MerchCalendar
|
|
144
149
|
@week ||= (((date-start_of_month)+1)/7.0).ceil
|
145
150
|
end
|
146
151
|
|
147
|
-
# The date of the
|
152
|
+
# The start date of the corresponding merch year
|
148
153
|
#
|
149
154
|
# @return [Date]
|
150
155
|
def start_of_year
|
151
|
-
@start_of_year ||=
|
156
|
+
@start_of_year ||= calendar.start_of_year(year)
|
152
157
|
end
|
153
158
|
|
154
159
|
# The end date of the corresponding merch year
|
155
160
|
#
|
156
161
|
# @return [Date]
|
157
162
|
def end_of_year
|
158
|
-
@end_of_year ||=
|
163
|
+
@end_of_year ||= calendar.end_of_year(year)
|
159
164
|
end
|
160
165
|
|
161
166
|
# The start date of the merch month
|
162
167
|
#
|
163
168
|
# @return [Date]
|
164
169
|
def start_of_month
|
165
|
-
@start_of_month ||=
|
170
|
+
@start_of_month ||= calendar.start_of_month(year, merch_month)
|
166
171
|
end
|
167
172
|
|
168
173
|
# The end date of the merch month
|
169
174
|
#
|
170
175
|
# @return [Date]
|
171
176
|
def end_of_month
|
172
|
-
@end_of_month ||=
|
177
|
+
@end_of_month ||= calendar.end_of_month(year, merch_month)
|
173
178
|
end
|
174
179
|
|
175
180
|
# The merch season this date falls under.
|
@@ -177,12 +182,7 @@ module MerchCalendar
|
|
177
182
|
#
|
178
183
|
# @return [String]
|
179
184
|
def season
|
180
|
-
|
181
|
-
when 1,2,3,4,5,6
|
182
|
-
"Spring/Summer"
|
183
|
-
when 7,8,9,10,11,12
|
184
|
-
"Fall/Winter"
|
185
|
-
end
|
185
|
+
@season ||= calendar.season(merch_month)
|
186
186
|
end
|
187
187
|
|
188
188
|
# Outputs a text representation of this merch week
|
@@ -193,7 +193,6 @@ module MerchCalendar
|
|
193
193
|
# * +:elasticsearch+ (default) "2012-12w05"
|
194
194
|
#
|
195
195
|
# @param format [Symbol] the format identifier to return. Default is +:short+
|
196
|
-
#
|
197
196
|
# @return [Date]
|
198
197
|
def to_s(format = :short)
|
199
198
|
case format
|
@@ -205,20 +204,5 @@ module MerchCalendar
|
|
205
204
|
"#{MONTHS[month - 1]} W#{week}"
|
206
205
|
end
|
207
206
|
end
|
208
|
-
|
209
|
-
private
|
210
|
-
|
211
|
-
def year_start_date
|
212
|
-
start_date = retail_calendar.start_of_year(date.year)
|
213
|
-
if start_date > date
|
214
|
-
start_date = retail_calendar.start_of_year(date.year - 1)
|
215
|
-
end
|
216
|
-
start_date
|
217
|
-
end
|
218
|
-
|
219
|
-
def retail_calendar
|
220
|
-
@retail_calendar ||= RetailCalendar.new
|
221
|
-
end
|
222
|
-
|
223
207
|
end
|
224
208
|
end
|