fat_core 4.7.0 → 4.7.3

Sign up to get free protection for your applications and to get access to all the features.
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