fat_core 4.7.0 → 4.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 881fe55c54af6a96acb9ff0875df33063f4598d800785e5c46a3e2f4cd317cff
4
- data.tar.gz: 43bac870c480a6770a39640a61c51f236f891c648a109d4b85d70b1188150cff
3
+ metadata.gz: 66c2dafd995954b7775a200600f938570b7bc5ad0fd5e240fc8fabb1496b69ba
4
+ data.tar.gz: a3b3ff6b1dfd0a81afd0ed89b24501e7ade68e9b14e40e8a3da54a59e79f39a2
5
5
  SHA512:
6
- metadata.gz: 45fc3d2e6e09e59ff14cc4acaa4d2a3794f990a55422b8d2b68186262b2971d566a1651075363600ac9defb03705ddff971bf0b1eb09bf1ab7cb030ab6250910
7
- data.tar.gz: 95bc26f296c51bfcf25b15ab2e4c70f0e1ef28d053d36ee8f9917b6008e09ddbcd469dc2ab0e33e5da15809fb9ab8d749ba8e2250515e6515ecefea56e125a5f
6
+ metadata.gz: f5ada6fefbf2b7cd2a5adbc7f62bc207ad7b8efb55e52874ec0b6e0a8e46a7bd5bc4d5e35f135fe20ea8a6b619f28e14adecf38ea5a2b01b92dfc5aaaf9b5f31
7
+ data.tar.gz: 621a7ace7268390f3dd900bc0dc7ac38c2a33381cb54bda460a4f8b0f7d8b273b907007299db2c893518c99f846325312095dd7a24b63783da800909487ade51
data/README.org ADDED
@@ -0,0 +1,377 @@
1
+ [[https://travis-ci.org/ddoherty03/fat_core.svg?branch=master]]
2
+
3
+ * FatCore
4
+
5
+ fat-core is a simple gem to collect core extensions and a few new classes that
6
+ I find useful in multiple projects. The emphasis is on extending the Date
7
+ class to make it more useful in financial applications.
8
+
9
+ ** Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ #+begin_SRC ruby
14
+ gem 'fat_core', :git => 'https://github.com/ddoherty03/fat_core.git'
15
+ #+end_SRC
16
+
17
+ And then execute:
18
+
19
+ #+begin_src shell
20
+ $ bundle
21
+ #+end_src
22
+
23
+ Or install it yourself as:
24
+
25
+ #+begin_src shell
26
+ $ gem install fat_core
27
+ #+end_src
28
+
29
+ ** Usage
30
+
31
+ You can extend classes individually by requiring the corresponding file:
32
+
33
+ #+begin_SRC ruby
34
+ require 'fat_core/array'
35
+ require 'fat_core/bigdecimal'
36
+ require 'fat_core/date'
37
+ require 'fat_core/enumerable'
38
+ require 'fat_core/hash'
39
+ require 'fat_core/kernel'
40
+ require 'fat_core/numeric'
41
+ require 'fat_core/range'
42
+ require 'fat_core/string'
43
+ require 'fat_core/symbol'
44
+ #+end_SRC
45
+
46
+
47
+ Or, you can require them all:
48
+
49
+ #+begin_SRC ruby
50
+ require 'fat_core/all'
51
+ #+end_SRC
52
+
53
+ Many of these have little that is of general interest, but there are a few
54
+ goodies.
55
+
56
+ *** Date
57
+
58
+ **** Constants
59
+
60
+ ~FatCore~ adds two constants to the ~Date~ class, Date::BOT and Date::EOT.
61
+ These represent the earliest and latest dates of practical commercial
62
+ interest. The exact values are rather arbitrary, but they prove useful in
63
+ date ranges, for example. They are defined as:
64
+
65
+ - ~Date::BOT~ :: January 1, 1900
66
+ - ~Date::EOT~ :: December 31, 3000
67
+ - ~Date::FEDERAL_DECREED_HOLIDAYS~ :: an Array of dates declared as non-work
68
+ days for federal employees by presidential proclamation
69
+ - ~Date::PRESIDENTIAL_FUNERALS~ :: an Array of dates of presidential funerals,
70
+ which are observed with a closing of most federal agencies
71
+
72
+
73
+
74
+ **** Formatting
75
+
76
+ ~FatCore~ provides some concise methods for printing string versions of dates
77
+ that are often useful:
78
+
79
+ #+begin_SRC ruby :results output :wrap example :exports both
80
+ require 'fat_core/date'
81
+ d = Date.parse('1957-09-22')
82
+ puts "ISO: #{d.iso}"
83
+ puts "All Numbers: #{d.num}"
84
+ puts "Emacs Org Mode Inactive: #{d.org}"
85
+ puts "Emacs Org Mode Active: #{d.org(true)}"
86
+ puts "LaTeX: #{d.tex_quote}"
87
+ puts "English: #{d.eng}"
88
+ puts "American: #{d.american}"
89
+ #+end_SRC
90
+
91
+ #+RESULTS:
92
+ #+begin_example
93
+ ISO: 1957-09-22
94
+ All Numbers: 19570922
95
+ Emacs Org Mode Inactive: [1957-09-22 Sun]
96
+ Emacs Org Mode Active: <1957-09-22 Sun>
97
+ LaTeX: 1957--09--22
98
+ English: September 22, 1957
99
+ American: 9/22/1957
100
+ #+end_example
101
+
102
+ Most of these are self-explanatory, but a couple are not. The ~#org~ method
103
+ formats a date as an Emacs org-mode timestamp, by default an inactive
104
+ timestamp that does not show up in the org agenda, but can be made active with
105
+ the optional parameter set to a truthy value. See
106
+ [[https://orgmode.org/manual/Timestamps.html#Timestamps]].
107
+
108
+ The ~#tex_quote~ method formats the date in iso form but using TeX's
109
+ convention of using en-dashes to separate the components.
110
+
111
+ **** Chunks
112
+
113
+ Many of the methods provided by ~FatCore~ deal with various calendar periods
114
+ that are less common than those provided by the Ruby Standard Library or gems
115
+ such as ~active_suupor~. This documentation refers to these calendar periods
116
+ as "chunks", and they are the following:
117
+
118
+ - year,
119
+ - half,
120
+ - quarter,
121
+ - bimonth,
122
+ - month,
123
+ - semimonth,
124
+ - biweek,
125
+ - week, and
126
+ - day
127
+
128
+ ~FatCore~ provides methods that query whether the date falls on the beginning
129
+ or end of each of these chunks:
130
+
131
+ #+begin_SRC ruby :wrap example :exports both
132
+ require 'fat_core/date'
133
+
134
+ tab = []
135
+ d = Date.parse('2017-06-30')
136
+ %i[beginning end].each do |side|
137
+ %i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
138
+ meth = "#{side}_of_#{chunk}?".to_sym
139
+ tab << [d.iso, meth.to_s, "#{d.send(meth)}"]
140
+ end
141
+ end
142
+ tab
143
+ #+end_SRC
144
+
145
+ #+RESULTS:
146
+ #+begin_example
147
+ | 2017-06-30 | beginning_of_year? | false |
148
+ | 2017-06-30 | beginning_of_half? | false |
149
+ | 2017-06-30 | beginning_of_quarter? | false |
150
+ | 2017-06-30 | beginning_of_bimonth? | false |
151
+ | 2017-06-30 | beginning_of_month? | false |
152
+ | 2017-06-30 | beginning_of_semimonth? | false |
153
+ | 2017-06-30 | beginning_of_biweek? | false |
154
+ | 2017-06-30 | beginning_of_week? | false |
155
+ | 2017-06-30 | end_of_year? | false |
156
+ | 2017-06-30 | end_of_half? | true |
157
+ | 2017-06-30 | end_of_quarter? | true |
158
+ | 2017-06-30 | end_of_bimonth? | true |
159
+ | 2017-06-30 | end_of_month? | true |
160
+ | 2017-06-30 | end_of_semimonth? | true |
161
+ | 2017-06-30 | end_of_biweek? | false |
162
+ | 2017-06-30 | end_of_week? | false |
163
+ #+end_example
164
+
165
+ It also provides corresponding methods that return the date at the beginning
166
+ or end of the calendar chunk, starting at the given date:
167
+
168
+ #+begin_SRC ruby :wrap example :exports both
169
+ require 'fat_core/date'
170
+
171
+ tab = []
172
+ d = Date.parse('2017-04-21')
173
+ %i[beginning end].each do |side|
174
+ %i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
175
+ meth = "#{side}_of_#{chunk}".to_sym
176
+ tab << [d.iso, "d.#{meth}", "#{d.send(meth)}"]
177
+ end
178
+ end
179
+ tab
180
+ #+end_SRC
181
+
182
+ #+RESULTS:
183
+ #+begin_example
184
+ | 2017-04-21 | d.beginning_of_year | 2017-01-01 |
185
+ | 2017-04-21 | d.beginning_of_half | 2017-01-01 |
186
+ | 2017-04-21 | d.beginning_of_quarter | 2017-04-01 |
187
+ | 2017-04-21 | d.beginning_of_bimonth | 2017-03-01 |
188
+ | 2017-04-21 | d.beginning_of_month | 2017-04-01 |
189
+ | 2017-04-21 | d.beginning_of_semimonth | 2017-04-16 |
190
+ | 2017-04-21 | d.beginning_of_biweek | 2017-04-10 |
191
+ | 2017-04-21 | d.beginning_of_week | 2017-04-17 |
192
+ | 2017-04-21 | d.end_of_year | 2017-12-31 |
193
+ | 2017-04-21 | d.end_of_half | 2017-06-30 |
194
+ | 2017-04-21 | d.end_of_quarter | 2017-06-30 |
195
+ | 2017-04-21 | d.end_of_bimonth | 2017-04-30 |
196
+ | 2017-04-21 | d.end_of_month | 2017-04-30 |
197
+ | 2017-04-21 | d.end_of_semimonth | 2017-04-30 |
198
+ | 2017-04-21 | d.end_of_biweek | 2017-04-23 |
199
+ | 2017-04-21 | d.end_of_week | 2017-04-23 |
200
+ #+end_example
201
+
202
+ You can query which numerical half, quarter, etc. that a given date falls in:
203
+
204
+ #+begin_SRC ruby :exports both :wrap example
205
+ require 'fat_core/date'
206
+
207
+ tab = []
208
+ %i(year half quarter bimonth month semimonth biweek week ).each do |chunk|
209
+ d = Date.parse('2017-04-21') + rand(100)
210
+ meth = "#{chunk}".to_sym
211
+ tab << [d.iso, "d.#{meth}", "in #{chunk} number #{d.send(meth)}"]
212
+ end
213
+ tab
214
+ #+end_SRC
215
+
216
+ #+RESULTS:
217
+ #+begin_example
218
+ | 2017-07-05 | d.year | in year number 2017 |
219
+ | 2017-06-03 | d.half | in half number 1 |
220
+ | 2017-05-30 | d.quarter | in quarter number 2 |
221
+ | 2017-07-08 | d.bimonth | in bimonth number 4 |
222
+ | 2017-06-28 | d.month | in month number 6 |
223
+ | 2017-05-14 | d.semimonth | in semimonth number 9 |
224
+ | 2017-07-25 | d.biweek | in biweek number 15 |
225
+ | 2017-06-19 | d.week | in week number 25 |
226
+ #+end_example
227
+
228
+ **** Parsing
229
+
230
+ ~FatCore~ also adds some convenience methods for parsing strings as ~Date~
231
+ objects.
232
+
233
+ ***** American Dates
234
+
235
+ Americans often write dates in the form M/d/Y, and the normal parse method
236
+ will parse such a string as d/M/Y, often resulting in invalid date errors.
237
+ ~FatCore~ adds the specialty parsing method, ~Date.parse_american~ to handle
238
+ such strings.
239
+
240
+ #+begin_SRC ruby :results output :exports both :wrap example
241
+ require 'fat_core/date'
242
+
243
+ begin
244
+ ss = '9/22/1957'
245
+ Date.parse(ss)
246
+ rescue Date::Error => ex
247
+ puts "Date.parse('#{ss}') raises #{ex.class} (#{ex}), but"
248
+ puts "Date.parse_american('#{ss}') => #{Date.parse_american(ss)}"
249
+ end
250
+ #+end_SRC
251
+
252
+ #+RESULTS:
253
+ #+begin_example
254
+ Date.parse('9/22/1957') raises Date::Error (invalid date), but
255
+ Date.parse_american('9/22/1957') => 1957-09-22
256
+ #+end_example
257
+
258
+ ***** Date Specs
259
+
260
+
261
+
262
+ **** Holidays and Workdays
263
+
264
+ - weekend?
265
+ - weekday?
266
+
267
+ **** Weekdays in Month
268
+
269
+ **** Easter
270
+
271
+
272
+ The ~Date~ class extension adds two methods for determining whether a given
273
+ date is a US federal holiday as defined by federal law, including such things
274
+ as federal holidays established by executive decree:
275
+
276
+ #+begin_SRC ruby
277
+ require 'fat_core/date'
278
+ Date.parse('2014-05-18').fed_holiday? => true # It's a weekend
279
+ Date.parse('2014-01-01').fed_holiday? => true # It's New Years
280
+ #+end_SRC
281
+
282
+ Likewise, days on which the NYSE is closed can be gotten with:
283
+
284
+ #+begin_SRC ruby
285
+ Date.parse('2014-04-18').nyse_holiday? => true # It's Good Friday
286
+ #+end_SRC
287
+
288
+ Conversely, ~Date#fed_workday?~ and ~Date#nyse_workday?~ return true if the
289
+ federal government and the NYSE respectively are open for business on those
290
+ days.
291
+
292
+ In addition, the Date class, as extended by FatCore, adds ~#next_<chunk>~
293
+ methods for calendar periods in addition to those provided by the core Date
294
+ class: ~#next_half~, ~#next_quarter~, ~#next_bimonth~, and ~#next_semimonth~,
295
+ ~#next_biweek~. There are also ~#prior_<chunk>~ variants of these, as well as
296
+ methods for finding the end and beginning of all these periods (e.g.,
297
+ ~#beginning_of_bimonth~) and for querying whether a Date is at the beginning or
298
+ end of these periods (e.g., ~#beginning_of_bimonth?~, ~#end_of_bimonth?~, etc.).
299
+
300
+ FatCore also provides convenience formatting methods, such as ~Date#iso~ for
301
+ quickly converting a Date to a string of the form 'YYYY-MM-DD', ~Date#org~ for
302
+ formatting a Date as an Emacs org-mode timestamp, and several others.
303
+
304
+ Finally, it provides a ~#parse_spec~ method for parsing a string, typically
305
+ provided by a user, allowing all the period chunks to be conveniently and
306
+ tersely specified by a user. For example, the string '2Q' will be parsed as the
307
+ second calendar quarter of the current year, while '2014-3Q' will be parsed as
308
+ the third quarter of the year 2014.
309
+
310
+ *** Range
311
+
312
+ You can also extend the Range class with several useful methods that emphasize
313
+ coverage of one range by one or more others (~#spanned_by?~ and ~#gaps~),
314
+ contiguity of Ranges to one another (~#contiguous?~, ~#left_contiguous?~, and
315
+ ~#right_contiguous?~, ~#join~), and the testing of overlaps between ranges
316
+ (~#overlaps?~, ~#overlaps_among?~). These are put to good use in the
317
+ 'fat_period' ([[https://github.com/ddoherty03/fat_period]]) gem, which combines
318
+ fat_core's extended Range class with its extended Date class to make a useful
319
+ Period class for date ranges, and you may find fat_core's extended Range class
320
+ likewise useful.
321
+
322
+ For example, you can use the ~#gaps~ method to find the gaps left in the
323
+ coverage on one Range by an Array of other Ranges:
324
+
325
+ #+begin_SRC ruby
326
+ require 'fat_core/range'
327
+ (0..12).gaps([(0..2), (5..7), (10..12)]) => [(3..4), (8..9)]
328
+ #+end_SRC
329
+
330
+ **
331
+ * Enumerable
332
+
333
+ FatCore::Enumerable extends Enumerable with the ~#each_with_flags~ method that
334
+ yields the elements of the Enumerable but also yields two booleans, ~first~ and
335
+ ~last~ that are set to true on respectively, the first and last element of the
336
+ Enumerable. This makes it easy to treat these two cases specially without
337
+ testing the index as in ~#each_with_index~.
338
+
339
+ *** Hash
340
+
341
+ FatCore::Hash extends the Hash class with some useful methods for element
342
+ deletion (~#delete_with_value~) and for manipulating the keys
343
+ (~#keys_with_value~, ~#remap_keys~ and ~#replace_keys~) of a Hash. It also
344
+ provides ~#each_pair_with_flags~ as an analog to Enumerable's
345
+ ~#each_with_flags~.
346
+
347
+ *** String
348
+
349
+ FatCore::String has methods for performing matching of one string with another
350
+ (~#matches_with~, ~#fuzzy_match~), for converting a string to title-case as
351
+ might by used in the title of a book (~#entitle~), for converting a String into
352
+ a useable Symbol (~#as_sym~) and vice-versa (~#as_string~ also
353
+ ~Symbol#as_string~), for wrapping with an optional hanging indent (~#wrap~),
354
+ cleaning up errant spaces (~#clean~), and computing the Damerau-Levenshtein
355
+ distance between strings (~#distance~). And several others.
356
+
357
+ *** TeX Quoting
358
+
359
+ Several of the extension, most notably 'fat_core/string', provides a
360
+ ~#tex_quote~ method for quoting the string version of an object so as to allow
361
+ its inclusion in a TeX document and quote characters such as '$' or '%' that
362
+ have a special meaning for TeX.
363
+
364
+ *** Numbers
365
+
366
+ FatCore::Numeric has methods for inserting grouping commas into a number
367
+ (~#commas~ and ~#group~), for converting seconds to HH:MM:SS.dd format
368
+ (~#secs_to_hms~), for testing for integrality (~#whole?~ and ~#int_if_whole~), and
369
+ testing for sign (~#signum~).
370
+
371
+ ** Contributing
372
+
373
+ 1. Fork it ([[http://github.com/ddoherty03/fat_core/fork]] )
374
+ 2. Create your feature branch (~git checkout -b my-new-feature~)
375
+ 3. Commit your changes (~git commit -am 'Add some feature'~)
376
+ 4. Push to the branch (~git push origin my-new-feature~)
377
+ 5. Create new Pull Request
data/fat_core.gemspec CHANGED
@@ -10,8 +10,8 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Daniel E. Doherty']
11
11
  spec.email = ['ded@ddoherty.net']
12
12
  spec.summary = 'fat_core provides some useful core extensions'
13
- spec.description = 'Write a longer description. Optional.'
14
- spec.homepage = ''
13
+ spec.description = 'Useful extensions to Date, String, Range and other classes'
14
+ spec.homepage = 'https://github.com/ddoherty03/fat_core.git'
15
15
  spec.license = 'MIT'
16
16
  spec.required_ruby_version = '>= 2.2.2'
17
17
  spec.metadata['yard.run'] = 'yri' # use "yard" to build full HTML docs.
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ['lib']
24
24
 
25
25
  spec.add_development_dependency 'simplecov'
26
- spec.add_development_dependency 'bundler', '~> 1.5'
26
+ spec.add_development_dependency 'bundler'
27
27
  spec.add_development_dependency 'rake'
28
28
  spec.add_development_dependency 'rspec'
29
29
  spec.add_development_dependency 'byebug'
@@ -1,6 +1,7 @@
1
1
  require 'bigdecimal'
2
2
 
3
3
  module FatCore
4
+ # Extensions to BigDecimal class
4
5
  module BigDecimal
5
6
  # Provide a human-readable display for BigDecimal. e.g., while debugging.
6
7
  # The inspect method in BigDecimal is unreadable, as it exposes the
@@ -13,6 +14,7 @@ module FatCore
13
14
  end
14
15
  end
15
16
 
17
+ # Override the core inspect method.
16
18
  class BigDecimal
17
19
  prepend(FatCore::BigDecimal)
18
20
  # @!parse include FatCore::BigDecimal
data/lib/fat_core/date.rb CHANGED
@@ -84,8 +84,12 @@ module FatCore
84
84
  # Format as an inactive Org date timestamp of the form `[YYYY-MM-DD <dow>]`
85
85
  # (see Emacs org-mode)
86
86
  # @return [String]
87
- def org
88
- strftime('[%Y-%m-%d %a]')
87
+ def org(active = false)
88
+ if active
89
+ strftime('<%Y-%m-%d %a>')
90
+ else
91
+ strftime('[%Y-%m-%d %a]')
92
+ end
89
93
  end
90
94
 
91
95
  # :category: Formatting
@@ -108,20 +112,6 @@ module FatCore
108
112
  # :category: Queries
109
113
  # @group Queries
110
114
 
111
- # Does self fall on a weekend?
112
- # @return [Boolean]
113
- def weekend?
114
- saturday? || sunday?
115
- end
116
-
117
- # :category: Queries
118
-
119
- # Does self fall on a weekday?
120
- # @return [Boolean]
121
- def weekday?
122
- !weekend?
123
- end
124
-
125
115
  # :category: Queries
126
116
 
127
117
  # Self's calendar "half" by analogy to calendar quarters: 1 or 2, depending
@@ -155,6 +145,50 @@ module FatCore
155
145
  end
156
146
  end
157
147
 
148
+ # Self's calendar bimonth: 1, 2, 3, 4, 5, or 6 depending on which calendar
149
+ # bimonth the date falls in.
150
+ # @return [1, 2, 3, 4, 5, 6]
151
+ def bimonth
152
+ case month
153
+ when (1..2)
154
+ 1
155
+ when (3..4)
156
+ 2
157
+ when (5..6)
158
+ 3
159
+ when (7..8)
160
+ 4
161
+ when (9..10)
162
+ 5
163
+ when (11..12)
164
+ 6
165
+ end
166
+ end
167
+
168
+ # Self's calendar semimonth: 1, through 24 depending on which calendar
169
+ # semimonth the date falls in.
170
+ # @return [Integer]
171
+ def semimonth
172
+ (month - 1) * 2 + (day <= 15 ? 1 : 2)
173
+ end
174
+
175
+ # Self's calendar biweek: 1, through 24 depending on which calendar
176
+ # semimonth the date falls in.
177
+ # @return [Integer]
178
+ def biweek
179
+ if cweek.odd?
180
+ (cweek + 1) / 2
181
+ else
182
+ cweek / 2
183
+ end
184
+ end
185
+
186
+ # Self's calendar week: just calls cweek.
187
+ # @return [Integer]
188
+ def week
189
+ cweek
190
+ end
191
+
158
192
  # :category: Queries
159
193
 
160
194
  # Return whether the date falls on the first day of a year.
@@ -315,13 +349,13 @@ module FatCore
315
349
  # dates 2 days after date six months before and 2 days before the date six
316
350
  # months after the date `d`.
317
351
  #
318
- # @param d [::Date] the middle of the six-month range
352
+ # @param from_date [::Date] the middle of the six-month range
319
353
  # @return [Boolean]
320
- def within_6mos_of?(d)
354
+ def within_6mos_of?(from_date)
321
355
  # ::Date 6 calendar months before self
322
356
  start_date = self - 6.months + 2.days
323
357
  end_date = self + 6.months - 2.days
324
- (start_date..end_date).cover?(d)
358
+ (start_date..end_date).cover?(from_date)
325
359
  end
326
360
 
327
361
  # Return whether this date is Easter Sunday for the year in which it falls
@@ -337,15 +371,15 @@ module FatCore
337
371
  # Return whether this date is the `n`th weekday `wday` of the given `month` in
338
372
  # this date's year.
339
373
  #
340
- # @param n [Integer] number of wday in month, if negative count from end of
374
+ # @param nth [Integer] number of wday in month, if negative count from end of
341
375
  # the month
342
376
  # @param wday [Integer] day of week, 0 is Sunday, 1 Monday, etc.
343
377
  # @param month [Integer] the month number, 1 is January, 2 is February, etc.
344
378
  # @return [Boolean]
345
- def nth_wday_in_month?(n, wday, month)
379
+ def nth_wday_in_month?(nth, wday, month)
346
380
  # Is self the nth weekday in the given month of its year?
347
- # If n is negative, count from last day of month
348
- self == ::Date.nth_wday_in_year_month(n, wday, year, month)
381
+ # If nth is negative, count from last day of month
382
+ self == ::Date.nth_wday_in_year_month(nth, wday, year, month)
349
383
  end
350
384
 
351
385
  # :category: Relative ::Dates
@@ -478,61 +512,64 @@ module FatCore
478
512
  # Return the date that is +n+ calendar halves after this date, where a
479
513
  # calendar half is a period of 6 months.
480
514
  #
481
- # @param n [Integer] number of halves to advance, can be negative
515
+ # @param num [Integer] number of halves to advance, can be negative
482
516
  # @return [::Date] new date n halves after this date
483
- def next_half(n = 1)
484
- n = n.floor
485
- return self if n.zero?
486
- next_month(n * 6)
517
+ def next_half(num = 1)
518
+ num = num.floor
519
+ return self if num.zero?
520
+
521
+ next_month(num * 6)
487
522
  end
488
523
 
489
524
  # Return the date that is +n+ calendar halves before this date, where a
490
525
  # calendar half is a period of 6 months.
491
526
  #
492
- # @param n [Integer] number of halves to retreat, can be negative
527
+ # @param num [Integer] number of halves to retreat, can be negative
493
528
  # @return [::Date] new date n halves before this date
494
- def prior_half(n = 1)
495
- next_half(-n)
529
+ def prior_half(num = 1)
530
+ next_half(-num)
496
531
  end
497
532
 
498
533
  # Return the date that is +n+ calendar quarters after this date, where a
499
534
  # calendar quarter is a period of 3 months.
500
535
  #
501
- # @param n [Integer] number of quarters to advance, can be negative
536
+ # @param num [Integer] number of quarters to advance, can be negative
502
537
  # @return [::Date] new date n quarters after this date
503
- def next_quarter(n = 1)
504
- n = n.floor
505
- return self if n.zero?
506
- next_month(n * 3)
538
+ def next_quarter(num = 1)
539
+ num = num.floor
540
+ return self if num.zero?
541
+
542
+ next_month(num * 3)
507
543
  end
508
544
 
509
545
  # Return the date that is +n+ calendar quarters before this date, where a
510
546
  # calendar quarter is a period of 3 months.
511
547
  #
512
- # @param n [Integer] number of quarters to retreat, can be negative
548
+ # @param num [Integer] number of quarters to retreat, can be negative
513
549
  # @return [::Date] new date n quarters after this date
514
- def prior_quarter(n = 1)
515
- next_quarter(-n)
550
+ def prior_quarter(num = 1)
551
+ next_quarter(-num)
516
552
  end
517
553
 
518
554
  # Return the date that is +n+ calendar bimonths after this date, where a
519
555
  # calendar bimonth is a period of 2 months.
520
556
  #
521
- # @param n [Integer] number of bimonths to advance, can be negative
557
+ # @param num [Integer] number of bimonths to advance, can be negative
522
558
  # @return [::Date] new date n bimonths after this date
523
- def next_bimonth(n = 1)
524
- n = n.floor
525
- return self if n.zero?
526
- next_month(n * 2)
559
+ def next_bimonth(num = 1)
560
+ num = num.floor
561
+ return self if num.zero?
562
+
563
+ next_month(num * 2)
527
564
  end
528
565
 
529
566
  # Return the date that is +n+ calendar bimonths before this date, where a
530
567
  # calendar bimonth is a period of 2 months.
531
568
  #
532
- # @param n [Integer] number of bimonths to retreat, can be negative
569
+ # @param num [Integer] number of bimonths to retreat, can be negative
533
570
  # @return [::Date] new date n bimonths before this date
534
- def prior_bimonth(n = 1)
535
- next_bimonth(-n)
571
+ def prior_bimonth(num = 1)
572
+ next_bimonth(-num)
536
573
  end
537
574
 
538
575
  # Return the date that is +n+ semimonths after this date. Each semimonth begins
@@ -542,15 +579,16 @@ module FatCore
542
579
  # as far into the next month past the 1st as the current date is past the
543
580
  # 16th, but never past the 15th of the next month.
544
581
  #
545
- # @param n [Integer] number of semimonths to advance, can be negative
582
+ # @param num [Integer] number of semimonths to advance, can be negative
546
583
  # @return [::Date] new date n semimonths after this date
547
- def next_semimonth(n = 1)
548
- n = n.floor
549
- return self if n.zero?
550
- factor = n.negative? ? -1 : 1
551
- n = n.abs
552
- if n.even?
553
- next_month(n / 2)
584
+ def next_semimonth(num = 1)
585
+ num = num.floor
586
+ return self if num.zero?
587
+
588
+ factor = num.negative? ? -1 : 1
589
+ num = num.abs
590
+ if num.even?
591
+ next_month(num / 2)
554
592
  else
555
593
  # Advance or retreat one semimonth
556
594
  next_sm =
@@ -577,23 +615,21 @@ module FatCore
577
615
  [prior_month.beginning_of_month + 16.days + (day - 1).days,
578
616
  prior_month.end_of_month].min
579
617
  end
580
- else
618
+ elsif factor.positive?
581
619
  # In the second half of the month (17th to the 31st), go as many
582
620
  # days into the next month as we are past the 16th. Thus, as many as
583
621
  # 15 days. But we don't want to go past the first half of the next
584
622
  # month, so we only go so far as the 15th of the next month.
585
623
  # ::Date.parse('2015-02-18').next_semimonth should be the 3rd of the
586
624
  # following month.
587
- if factor.positive?
588
- next_month.beginning_of_month + [(day - 16), 15].min
589
- else
590
- beginning_of_month + [(day - 16), 15].min
591
- end
625
+ next_month.beginning_of_month + [(day - 16), 15].min
626
+ else
627
+ beginning_of_month + [(day - 16), 15].min
592
628
  end
593
- n -= 1
629
+ num -= 1
594
630
  # Now that n is even, advance (or retreat) n / 2 months unless we're done.
595
- if n >= 2
596
- next_sm.next_month(factor * n / 2)
631
+ if num >= 2
632
+ next_sm.next_month(factor * num / 2)
597
633
  else
598
634
  next_sm
599
635
  end
@@ -608,30 +644,31 @@ module FatCore
608
644
  # as the current date is past the 15th, but never past the 14th of the the
609
645
  # current month.
610
646
  #
611
- # @param n [Integer] number of semimonths to retreat, can be negative
647
+ # @param num [Integer] number of semimonths to retreat, can be negative
612
648
  # @return [::Date] new date n semimonths before this date
613
- def prior_semimonth(n = 1)
614
- next_semimonth(-n)
649
+ def prior_semimonth(num = 1)
650
+ next_semimonth(-num)
615
651
  end
616
652
 
617
653
  # Return the date that is +n+ biweeks after this date where each biweek is 14
618
654
  # days.
619
655
  #
620
- # @param n [Integer] number of biweeks to advance, can be negative
656
+ # @param num [Integer] number of biweeks to advance, can be negative
621
657
  # @return [::Date] new date n biweeks after this date
622
- def next_biweek(n = 1)
623
- n = n.floor
624
- return self if n.zero?
625
- self + (14 * n)
658
+ def next_biweek(num = 1)
659
+ num = num.floor
660
+ return self if num.zero?
661
+
662
+ self + (14 * num)
626
663
  end
627
664
 
628
665
  # Return the date that is +n+ biweeks before this date where each biweek is 14
629
666
  # days.
630
667
  #
631
- # @param n [Integer] number of biweeks to retreat, can be negative
668
+ # @param num [Integer] number of biweeks to retreat, can be negative
632
669
  # @return [::Date] new date n biweeks before this date
633
- def prior_biweek(n = 1)
634
- next_biweek(-n)
670
+ def prior_biweek(num = 1)
671
+ next_biweek(-num)
635
672
  end
636
673
 
637
674
  # Return the date that is +n+ weeks after this date where each week is 7 days.
@@ -639,21 +676,22 @@ module FatCore
639
676
  # goes to the first day of the week in the next week and does not take an
640
677
  # argument +n+ to go multiple weeks.
641
678
  #
642
- # @param n [Integer] number of weeks to advance
679
+ # @param num [Integer] number of weeks to advance
643
680
  # @return [::Date] new date n weeks after this date
644
- def next_week(n = 1)
645
- n = n.floor
646
- return self if n.zero?
647
- self + (7 * n)
681
+ def next_week(num = 1)
682
+ num = num.floor
683
+ return self if num.zero?
684
+
685
+ self + (7 * num)
648
686
  end
649
687
 
650
688
  # Return the date that is +n+ weeks before this date where each week is 7
651
689
  # days.
652
690
  #
653
- # @param n [Integer] number of weeks to retreat
691
+ # @param num [Integer] number of weeks to retreat
654
692
  # @return [::Date] new date n weeks from this date
655
- def prior_week(n)
656
- next_week(-n)
693
+ def prior_week(num)
694
+ next_week(-num)
657
695
  end
658
696
 
659
697
  # NOTE: #next_day is defined in active_support.
@@ -661,10 +699,10 @@ module FatCore
661
699
  # Return the date that is +n+ weeks before this date where each week is 7
662
700
  # days.
663
701
  #
664
- # @param n [Integer] number of days to retreat
702
+ # @param num [Integer] number of days to retreat
665
703
  # @return [::Date] new date n days before this date
666
- def prior_day(n)
667
- next_day(-n)
704
+ def prior_day(num)
705
+ next_day(-num)
668
706
  end
669
707
 
670
708
  # :category: Relative ::Dates
@@ -673,28 +711,28 @@ module FatCore
673
711
  #
674
712
  # @param chunk [Symbol] one of +:year+, +:half+, +:quarter+, +:bimonth+,
675
713
  # +:month+, +:semimonth+, +:biweek+, +:week+, or +:day+.
676
- # @param n [Integer] the number of chunks to add, can be negative
714
+ # @param num [Integer] the number of chunks to add, can be negative
677
715
  # @return [::Date] the date n chunks from this date
678
- def add_chunk(chunk, n = 1)
716
+ def add_chunk(chunk, num = 1)
679
717
  case chunk
680
718
  when :year
681
- next_year(n)
719
+ next_year(num)
682
720
  when :half
683
- next_month(6)
721
+ next_month(6 * num)
684
722
  when :quarter
685
- next_month(3)
723
+ next_month(3 * num)
686
724
  when :bimonth
687
- next_month(2)
725
+ next_month(2 * num)
688
726
  when :month
689
- next_month(n)
727
+ next_month(num)
690
728
  when :semimonth
691
- next_semimonth(n)
729
+ next_semimonth(num)
692
730
  when :biweek
693
- next_biweek(n)
731
+ next_biweek(num)
694
732
  when :week
695
- next_week(n)
733
+ next_week(num)
696
734
  when :day
697
- next_day(n)
735
+ next_day(num)
698
736
  else
699
737
  raise ArgumentError, "add_chunk unknown chunk: '#{chunk}'"
700
738
  end
@@ -764,6 +802,22 @@ module FatCore
764
802
  end
765
803
  end
766
804
 
805
+ # @group Holidays and Workdays
806
+
807
+ # Does self fall on a weekend?
808
+ # @return [Boolean]
809
+ def weekend?
810
+ saturday? || sunday?
811
+ end
812
+
813
+ # :category: Queries
814
+
815
+ # Does self fall on a weekday?
816
+ # @return [Boolean]
817
+ def weekday?
818
+ !weekend?
819
+ end
820
+
767
821
  # Return the date for Easter in the Western Church for the year in which this
768
822
  # date falls.
769
823
  #
@@ -773,8 +827,6 @@ module FatCore
773
827
  ::Date.easter(year)
774
828
  end
775
829
 
776
- # @group Federal Holidays and Workdays
777
-
778
830
  # Holidays decreed by Presidential proclamation
779
831
  FED_DECREED_HOLIDAYS =
780
832
  [
@@ -782,7 +834,8 @@ module FatCore
782
834
  # http://www.whitehouse.gov/the-press-office/2012/12/21
783
835
  ::Date.parse('2012-12-24'),
784
836
  # And Trump
785
- ::Date.parse('2018-12-24')
837
+ ::Date.parse('2018-12-24'),
838
+ ::Date.parse('2019-12-24')
786
839
  ].freeze
787
840
 
788
841
  # Presidential funeral since JFK
@@ -803,7 +856,7 @@ module FatCore
803
856
  ::Date.parse('2007-01-02'),
804
857
  # GHWBFuneral
805
858
  ::Date.parse('2018-12-05')
806
- ]
859
+ ].freeze
807
860
 
808
861
  # Return whether this date is a United States federal holiday.
809
862
  #
@@ -865,16 +918,17 @@ module FatCore
865
918
  # Return the date that is n federal workdays after or before (if n < 0) this
866
919
  # date.
867
920
  #
868
- # @param n [Integer] number of federal workdays to add to this date
921
+ # @param num [Integer] number of federal workdays to add to this date
869
922
  # @return [::Date]
870
- def add_fed_workdays(n)
923
+ def add_fed_workdays(num)
871
924
  d = dup
872
- return d if n.zero?
873
- incr = n.negative? ? -1 : 1
874
- n = n.abs
875
- while n.positive?
925
+ return d if num.zero?
926
+
927
+ incr = num.negative? ? -1 : 1
928
+ num = num.abs
929
+ while num.positive?
876
930
  d += incr
877
- n -= 1 if d.fed_workday?
931
+ num -= 1 if d.fed_workday?
878
932
  end
879
933
  d
880
934
  end
@@ -975,8 +1029,10 @@ module FatCore
975
1029
  # Calculations for NYSE holidays are from Rule 51 and supplementary materials
976
1030
  # for the Rules of the New York Stock Exchange, Inc.
977
1031
  #
978
- # * General Rule 1: if a regular holiday falls on Saturday, observe it on the preceding Friday.
979
- # * General Rule 2: if a regular holiday falls on Sunday, observe it on the following Monday.
1032
+ # * General Rule 1: if a regular holiday falls on Saturday, observe it on
1033
+ # the preceding Friday.
1034
+ # * General Rule 2: if a regular holiday falls on Sunday, observe it on
1035
+ # the following Monday.
980
1036
  #
981
1037
  # These are the regular holidays:
982
1038
  #
@@ -1042,16 +1098,17 @@ module FatCore
1042
1098
  # Return the date that is n NYSE trading days after or before (if n < 0) this
1043
1099
  # date.
1044
1100
  #
1045
- # @param n [Integer] number of NYSE trading days to add to this date
1101
+ # @param num [Integer] number of NYSE trading days to add to this date
1046
1102
  # @return [::Date]
1047
- def add_nyse_workdays(n)
1103
+ def add_nyse_workdays(num)
1048
1104
  d = dup
1049
- return d if n.zero?
1050
- incr = n.negative? ? -1 : 1
1051
- n = n.abs
1052
- while n.positive?
1105
+ return d if num.zero?
1106
+
1107
+ incr = num.negative? ? -1 : 1
1108
+ num = num.abs
1109
+ while num.positive?
1053
1110
  d += incr
1054
- n -= 1 if d.nyse_workday?
1111
+ num -= 1 if d.nyse_workday?
1055
1112
  end
1056
1113
  d
1057
1114
  end
@@ -1095,8 +1152,6 @@ module FatCore
1095
1152
  date
1096
1153
  end
1097
1154
 
1098
- protected
1099
-
1100
1155
  # Return whether this date is a fixed holiday for the NYSE, that is, a holiday
1101
1156
  # that falls on the same date each year.
1102
1157
  #
@@ -1209,7 +1264,9 @@ module FatCore
1209
1264
  # @return [Boolean]
1210
1265
  def nyse_special_holiday?
1211
1266
  return false unless self > ::Date.parse('1960-01-01')
1267
+
1212
1268
  return true if PRESIDENTIAL_FUNERALS.include?(self)
1269
+
1213
1270
  case self
1214
1271
  when ::Date.parse('1961-05-29')
1215
1272
  # Day before Decoaration Day
@@ -1281,9 +1338,11 @@ module FatCore
1281
1338
  # @param str [String, #to_s] a stringling of the form MM/DD/YYYY
1282
1339
  # @return [::Date] the date represented by the str paramenter.
1283
1340
  def parse_american(str)
1284
- unless str.to_s =~ %r{\A\s*(\d\d?)\s*/\s*(\d\d?)\s*/\s*((\d\d)?\d\d)\s*\z}
1341
+ re = %r{\A\s*(\d\d?)\s*[-/]\s*(\d\d?)\s*[-/]\s*((\d\d)?\d\d)\s*\z}
1342
+ unless str.to_s =~ re
1285
1343
  raise ArgumentError, "date string must be of form 'MM?/DD?/YY(YY)?'"
1286
1344
  end
1345
+
1287
1346
  year = $3.to_i
1288
1347
  month = $1.to_i
1289
1348
  day = $2.to_i
@@ -1345,13 +1404,13 @@ module FatCore
1345
1404
  # designated by spec
1346
1405
  def parse_spec(spec, spec_type = :from)
1347
1406
  spec = spec.to_s.strip
1348
- unless [:from, :to].include?(spec_type)
1407
+ unless %i[from to].include?(spec_type)
1349
1408
  raise ArgumentError, "invalid date spec type: '#{spec_type}'"
1350
1409
  end
1351
1410
 
1352
1411
  today = ::Date.current
1353
1412
  case spec.clean
1354
- when /\A(\d\d\d\d)[-\/](\d\d?)[-\/](\d\d?)\z/
1413
+ when %r{\A(\d\d\d\d)[-/](\d\d?)[-/](\d\d?)\z}
1355
1414
  # A specified date
1356
1415
  ::Date.new($1.to_i, $2.to_i, $3.to_i)
1357
1416
  when /\AW(\d\d?)\z/, /\A(\d\d?)W\z/
@@ -1359,29 +1418,32 @@ module FatCore
1359
1418
  if week_num < 1 || week_num > 53
1360
1419
  raise ArgumentError, "invalid week number (1-53): '#{spec}'"
1361
1420
  end
1421
+
1362
1422
  if spec_type == :from
1363
1423
  ::Date.commercial(today.year, week_num).beginning_of_week
1364
1424
  else
1365
1425
  ::Date.commercial(today.year, week_num).end_of_week
1366
1426
  end
1367
- when /\A(\d\d\d\d)[-\/]W(\d\d?)\z/, /\A(\d\d\d\d)[-\/](\d\d?)W\z/
1427
+ when %r{\A(\d\d\d\d)[-/]W(\d\d?)\z}, %r{\A(\d\d\d\d)[-/](\d\d?)W\z}
1368
1428
  year = $1.to_i
1369
1429
  week_num = $2.to_i
1370
1430
  if week_num < 1 || week_num > 53
1371
1431
  raise ArgumentError, "invalid week number (1-53): '#{spec}'"
1372
1432
  end
1433
+
1373
1434
  if spec_type == :from
1374
1435
  ::Date.commercial(year, week_num).beginning_of_week
1375
1436
  else
1376
1437
  ::Date.commercial(year, week_num).end_of_week
1377
1438
  end
1378
- when /^(\d\d\d\d)[-\/](\d)[Qq]$/, /^(\d\d\d\d)[-\/][Qq](\d)$/
1439
+ when %r{^(\d\d\d\d)[-/](\d)[Qq]$}, %r{^(\d\d\d\d)[-/][Qq](\d)$}
1379
1440
  # Year-Quarter
1380
1441
  year = $1.to_i
1381
1442
  quarter = $2.to_i
1382
1443
  unless [1, 2, 3, 4].include?(quarter)
1383
1444
  raise ArgumentError, "invalid quarter number (1-4): '#{spec}'"
1384
1445
  end
1446
+
1385
1447
  month = quarter * 3
1386
1448
  if spec_type == :from
1387
1449
  ::Date.new(year, month, 1).beginning_of_quarter
@@ -1395,19 +1457,20 @@ module FatCore
1395
1457
  unless [1, 2, 3, 4].include?(quarter)
1396
1458
  raise ArgumentError, "invalid quarter number (1-4): '#{spec}'"
1397
1459
  end
1460
+
1398
1461
  date = ::Date.new(this_year, quarter * 3, 15)
1399
1462
  if spec_type == :from
1400
1463
  date.beginning_of_quarter
1401
1464
  else
1402
1465
  date.end_of_quarter
1403
1466
  end
1404
- when /^(\d\d\d\d)[-\/](\d)[Hh]$/, /^(\d\d\d\d)[-\/][Hh](\d)$/
1467
+ when %r{^(\d\d\d\d)[-/](\d)[Hh]$}, %r{^(\d\d\d\d)[-/][Hh](\d)$}
1405
1468
  # Year-Half
1406
1469
  year = $1.to_i
1407
1470
  half = $2.to_i
1408
- unless [1, 2].include?(half)
1409
- raise ArgumentError, "invalid half number: '#{spec}'"
1410
- end
1471
+ msg = "invalid half number: '#{spec}'"
1472
+ raise ArgumentError, msg unless [1, 2].include?(half)
1473
+
1411
1474
  month = half * 6
1412
1475
  if spec_type == :from
1413
1476
  ::Date.new(year, month, 15).beginning_of_half
@@ -1418,34 +1481,36 @@ module FatCore
1418
1481
  # Half only
1419
1482
  this_year = today.year
1420
1483
  half = $1.to_i
1421
- unless [1, 2].include?(half)
1422
- raise ArgumentError, "invalid half number: '#{spec}'"
1423
- end
1484
+ msg = "invalid half number: '#{spec}'"
1485
+ raise ArgumentError, msg unless [1, 2].include?(half)
1486
+
1424
1487
  date = ::Date.new(this_year, half * 6, 15)
1425
1488
  if spec_type == :from
1426
1489
  date.beginning_of_half
1427
1490
  else
1428
1491
  date.end_of_half
1429
1492
  end
1430
- when /^(\d\d\d\d)[-\/](\d\d?)*$/
1493
+ when %r{^(\d\d\d\d)[-/](\d\d?)*$}
1431
1494
  # Year-Month only
1432
1495
  year = $1.to_i
1433
1496
  month = $2.to_i
1434
1497
  unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month)
1435
1498
  raise ArgumentError, "invalid month number (1-12): '#{spec}'"
1436
1499
  end
1500
+
1437
1501
  if spec_type == :from
1438
1502
  ::Date.new(year, month, 1)
1439
1503
  else
1440
1504
  ::Date.new(year, month, 1).end_of_month
1441
1505
  end
1442
- when /^(\d\d?)[-\/](\d\d?)*$/
1506
+ when %r{^(\d\d?)[-/](\d\d?)*$}
1443
1507
  # Month-Day only
1444
1508
  month = $1.to_i
1445
1509
  day = $2.to_i
1446
1510
  unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month)
1447
1511
  raise ArgumentError, "invalid month number (1-12): '#{spec}'"
1448
1512
  end
1513
+
1449
1514
  if spec_type == :from
1450
1515
  ::Date.new(today.year, month, day)
1451
1516
  else
@@ -1457,6 +1522,7 @@ module FatCore
1457
1522
  unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month)
1458
1523
  raise ArgumentError, "invalid month number (1-12): '#{spec}'"
1459
1524
  end
1525
+
1460
1526
  if spec_type == :from
1461
1527
  ::Date.new(today.year, month, 1)
1462
1528
  else
@@ -1580,11 +1646,12 @@ module FatCore
1580
1646
  # starting with January = 1, etc.
1581
1647
  COMMON_YEAR_DAYS_IN_MONTH = [31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31,
1582
1648
  30, 31].freeze
1583
- def days_in_month(y, m)
1584
- raise ArgumentError, 'illegal month number' if m < 1 || m > 12
1585
- days = COMMON_YEAR_DAYS_IN_MONTH[m]
1586
- if m == 2
1587
- ::Date.new(y, m, 1).leap? ? 29 : 28
1649
+ def days_in_month(year, month)
1650
+ raise ArgumentError, 'illegal month number' if month < 1 || month > 12
1651
+
1652
+ days = COMMON_YEAR_DAYS_IN_MONTH[month]
1653
+ if month == 2
1654
+ ::Date.new(year, month, 1).leap? ? 29 : 28
1588
1655
  else
1589
1656
  days
1590
1657
  end
@@ -1593,41 +1660,43 @@ module FatCore
1593
1660
  # Return the nth weekday in the given month. If n is negative, count from
1594
1661
  # last day of month.
1595
1662
  #
1596
- # @param n [Integer] the ordinal number for the weekday
1663
+ # @param nth [Integer] the ordinal number for the weekday
1597
1664
  # @param wday [Integer] the weekday of interest with Monday 0 to Sunday 6
1598
1665
  # @param year [Integer] the year of interest
1599
1666
  # @param month [Integer] the month of interest with January 1 to December 12
1600
- def nth_wday_in_year_month(n, wday, year, month)
1667
+ def nth_wday_in_year_month(nth, wday, year, month)
1601
1668
  wday = wday.to_i
1602
- raise ArgumentError, 'illegal weekday number' if wday < 0 || wday > 6
1669
+ raise ArgumentError, 'illegal weekday number' if wday.negative? || wday > 6
1670
+
1603
1671
  month = month.to_i
1604
1672
  raise ArgumentError, 'illegal month number' if month < 1 || month > 12
1605
- n = n.to_i
1606
- if n.positive?
1673
+
1674
+ nth = nth.to_i
1675
+ if nth.positive?
1607
1676
  # Set d to the 1st wday in month
1608
1677
  d = ::Date.new(year, month, 1)
1609
1678
  d += 1 while d.wday != wday
1610
1679
  # Set d to the nth wday in month
1611
1680
  nd = 1
1612
- while nd != n
1681
+ while nd != nth
1613
1682
  d += 7
1614
1683
  nd += 1
1615
1684
  end
1616
1685
  d
1617
- elsif n.negative?
1618
- n = -n
1686
+ elsif nth.negative?
1687
+ nth = -nth
1619
1688
  # Set d to the last wday in month
1620
1689
  d = ::Date.new(year, month, 1).end_of_month
1621
1690
  d -= 1 while d.wday != wday
1622
1691
  # Set d to the nth wday in month
1623
1692
  nd = 1
1624
- while nd != n
1693
+ while nd != nth
1625
1694
  d -= 7
1626
1695
  nd += 1
1627
1696
  end
1628
1697
  d
1629
1698
  else
1630
- raise ArgumentError, 'Argument n cannot be zero'
1699
+ raise ArgumentError, 'Argument nth cannot be zero'
1631
1700
  end
1632
1701
  end
1633
1702
 
@@ -1669,7 +1738,6 @@ module FatCore
1669
1738
  end
1670
1739
  end
1671
1740
 
1672
- # @private
1673
1741
  def self.included(base)
1674
1742
  base.extend(ClassMethods)
1675
1743
  end