fat_core 0.1.10 → 0.1.11

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
  SHA1:
3
- metadata.gz: 41f3c535cff303356a43f0c50fd2f3628980b593
4
- data.tar.gz: 2ec1814ff9b4830de04e655575c36551e2a6034d
3
+ metadata.gz: 94d627be45140f03aae806d7efd270587f82fdd0
4
+ data.tar.gz: 6fd13f11a973858fb854d22e53c2cb74555e27fa
5
5
  SHA512:
6
- metadata.gz: 14112d1eca56bb81540764ba7cd128192ad3d100d887ff190bed388bac880615b89d2099bcd23de214e5aa42c221156ba2bcd7c8355885064eac38e3923a2cba
7
- data.tar.gz: 5c12d2e4fab0805bd87059918489420e4e1e97d8227e0c2f55bdfbc761fbc287289c45f0a9f374a58c02813897469664c56c8362d2b1f427348ad27c87dab153
6
+ metadata.gz: b82eef90d9fc99462877615c30880f51207de6d28a1cd3530a6b997677111d54996b9d302972b869f9c6225137803ec3eba2a0aa0f09a76f612da0a478bb75af
7
+ data.tar.gz: 6f284960e4c93e57daaa8b28ea34595b8a80fea89dc01234dc56b0c2722610fbedc25130fd0e58c21bc8ac3aaddcbd7ecf7933b09a5cda736bdb059cfb8f80e0
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_development_dependency 'simplecov'
21
22
  spec.add_development_dependency "bundler", "~> 1.5"
22
23
  spec.add_development_dependency "rake"
23
24
  spec.add_development_dependency "rspec"
@@ -164,34 +164,60 @@ class Date
164
164
  end # !> previous definition of length was here
165
165
  end
166
166
 
167
- def weekend?
168
- saturday? || sunday?
169
- end
170
-
171
- def weekday?
172
- !weekend?
173
- end
174
-
167
+ # Predecessor of self. Allows Date to work as a countable element
168
+ # of a Range.
175
169
  def pred
176
170
  self - 1.day
177
171
  end
178
172
 
173
+ # Successor of self. Allows Date to work as a countable element
174
+ # of a Range.
179
175
  def succ
180
176
  self + 1.day
181
177
  end
182
178
 
179
+ # Format as an ISO string.
183
180
  def iso
184
181
  strftime("%Y-%m-%d")
185
182
  end
186
183
 
184
+ # Format date to TeX documents as ISO strings
185
+ def tex_quote
186
+ iso
187
+ end
188
+
189
+ # Format as an all-numeric string, i.e. 'YYYYMMDD'
190
+ def num
191
+ strftime("%Y%m%d")
192
+ end
193
+
194
+ # Format as an inactive Org date (see emacs org-mode)
187
195
  def org
188
196
  strftime("[%Y-%m-%d %a]")
189
197
  end
190
198
 
199
+ # Format as an English string
191
200
  def eng
192
201
  strftime("%B %e, %Y")
193
202
  end
194
203
 
204
+ # Format date in MM/DD/YYYY form, as typical for the short American
205
+ # form.
206
+ def american
207
+ strftime "%-m/%-d/%Y"
208
+ end
209
+
210
+ # Does self fall on a weekend?
211
+ def weekend?
212
+ saturday? || sunday?
213
+ end
214
+
215
+ # Does self fall on a weekday?
216
+ def weekday?
217
+ !weekend?
218
+ end
219
+
220
+ # Self's calendar quarter: 1, 2, 3, or 4
195
221
  def quarter
196
222
  case month
197
223
  when (1..3)
@@ -205,6 +231,10 @@ class Date
205
231
  end
206
232
  end
207
233
 
234
+ # The date that is the first day of the bimonth in which self
235
+ # falls. A 'bimonth' is a two-month calendar period beginning on the
236
+ # first day of the odd-numbered months. E.g., 2014-01-01 to
237
+ # 2014-02-28 is the first bimonth of 2014.
208
238
  def beginning_of_bimonth
209
239
  if month % 2 == 1
210
240
  beginning_of_month
@@ -213,6 +243,10 @@ class Date
213
243
  end
214
244
  end
215
245
 
246
+ # The date that is the last day of the bimonth in which self falls.
247
+ # A 'bimonth' is a two-month calendar period beginning on the first
248
+ # day of the odd-numbered months. E.g., 2014-01-01 to 2014-02-28 is
249
+ # the first bimonth of 2014.
216
250
  def end_of_bimonth
217
251
  if month % 2 == 1
218
252
  (self + 1.month).end_of_month
@@ -221,6 +255,10 @@ class Date
221
255
  end
222
256
  end
223
257
 
258
+ # The date that is the first day of the semimonth in which self
259
+ # falls. A semimonth is a calendar period beginning on the 1st or
260
+ # 16th of each month and ending on the 15th or last day of the month
261
+ # respectively. So each year has exactly 24 semimonths.
224
262
  def beginning_of_semimonth
225
263
  if day >= 16
226
264
  Date.new(year, month, 16)
@@ -229,6 +267,10 @@ class Date
229
267
  end
230
268
  end
231
269
 
270
+ # The date that is the last day of the semimonth in which self
271
+ # falls. A semimonth is a calendar period beginning on the 1st or
272
+ # 16th of each month and ending on the 15th or last day of the month
273
+ # respectively. So each year has exactly 24 semimonths.
232
274
  def end_of_semimonth
233
275
  if day <= 15
234
276
  Date.new(year, month, 15)
@@ -237,8 +279,8 @@ class Date
237
279
  end
238
280
  end
239
281
 
240
- # Note: we use a monday start of the week in the next two methods because
241
- # commercial week counting assumes a monday start.
282
+ # Note: we use a Monday start of the week in the next two methods because
283
+ # commercial week counting assumes a Monday start.
242
284
  def beginning_of_biweek
243
285
  if cweek % 2 == 1
244
286
  beginning_of_week(:monday)
@@ -313,11 +355,6 @@ class Date
313
355
  self.end_of_week == self
314
356
  end
315
357
 
316
- # Format date in MM/DD/YYYY form
317
- def american
318
- strftime "%-m/%-d/%Y"
319
- end
320
-
321
358
  def expand_to_period(sym)
322
359
  require 'fat_core/period'
323
360
  Period.new(beginning_of_chunk(sym), end_of_chunk(sym))
@@ -342,7 +379,7 @@ class Date
342
379
  when :day
343
380
  self + 1.days
344
381
  else
345
- raise LogicError, "add_chunk unknown chunk: '#{chunk}'"
382
+ raise ArgumentError, "add_chunk unknown chunk: '#{chunk}'"
346
383
  end
347
384
  end
348
385
 
@@ -365,7 +402,7 @@ class Date
365
402
  when :day
366
403
  self
367
404
  else
368
- raise LogicError, "unknown chunk sym: '#{sym}'"
405
+ raise ArgumentError, "unknown chunk sym: '#{sym}'"
369
406
  end
370
407
  end
371
408
 
@@ -393,20 +430,37 @@ class Date
393
430
  end
394
431
 
395
432
  # Holidays decreed by executive order
396
- FED_DECLARED_HOLIDAYS =
433
+ # See http://www.whitehouse.gov/the-press-office/2012/12/21/
434
+ # executive-order-closing-executive-departments-and-agencies-federal-gover
435
+ FED_DECREED_HOLIDAYS =
397
436
  [
398
437
  Date.parse('2012-12-24')
399
438
  ]
400
439
 
401
440
  def self.days_in_month(y, m)
441
+ if m < 1 || m > 12
442
+ raise ArgumentError, "illegal month number"
443
+ end
402
444
  days = Time::COMMON_YEAR_DAYS_IN_MONTH[m]
403
- return(days) unless m == 2
404
- return Date.new(y, m, 1).leap? ? 29 : 28
445
+ if m == 2
446
+ Date.new(y, m, 1).leap? ? 29 : 28
447
+ else
448
+ days
449
+ end
405
450
  end
406
451
 
407
452
  def self.nth_wday_in_year_month(n, wday, year, month)
408
453
  # Return the nth weekday in the given month
409
454
  # If n is negative, count from last day of month
455
+ wday = wday.to_i
456
+ if wday < 0 || wday > 6
457
+ raise ArgumentError, "illegal weekday number"
458
+ end
459
+ month = month.to_i
460
+ if month < 1 || month > 12
461
+ raise ArgumentError, "illegal month number"
462
+ end
463
+ n = n.to_i
410
464
  if n > 0
411
465
  # Set d to the 1st wday in month
412
466
  d = Date.new(year, month, 1)
@@ -419,12 +473,11 @@ class Date
419
473
  d += 7
420
474
  nd += 1
421
475
  end
422
- return d
476
+ d
423
477
  elsif n < 0
424
478
  n = -n
425
479
  # Set d to the last wday in month
426
- d = Date.new(year, month,
427
- Date.last_day_in_year_month(year, month))
480
+ d = Date.new(year, month, 1).end_of_month
428
481
  while d.wday != wday;
429
482
  d -= 1
430
483
  end
@@ -434,36 +487,22 @@ class Date
434
487
  d -= 7
435
488
  nd += 1
436
489
  end
437
- return d
490
+ d
438
491
  else
439
492
  raise ArgumentError,
440
493
  'Arg 1 to nth_wday_in_month_year cannot be zero'
441
494
  end
442
495
  end
443
496
 
444
- def self.last_day_in_year_month(year, month)
445
- days = [
446
- 31, # Dec
447
- 31, # Jan
448
- 28, # Feb
449
- 31, # Mar
450
- 30, # Apr
451
- 31, # May
452
- 30, # Jun
453
- 31, # Jul
454
- 31, # Aug
455
- 30, # Sep
456
- 31, # Oct
457
- 30, # Nov
458
- 31, # Dec
459
- ]
460
- days[2] = 29 if Date.new(year, month, 1).leap?
461
- days[month % 12]
497
+ def within_6mos_of?(d)
498
+ # Date 6 calendar months before self
499
+ start_date = self - 6.months + 2.days
500
+ end_date = self + 6.months - 2.days
501
+ (start_date..end_date).cover?(d)
462
502
  end
463
503
 
464
- def easter_this_year
465
- # Return the date of Easter in self's year
466
- y = self.year
504
+ def self.easter(year)
505
+ y = year
467
506
  a = y % 19
468
507
  b, c = y.divmod(100)
469
508
  d, e = b.divmod(4)
@@ -474,47 +513,45 @@ class Date
474
513
  l = (32 + 2*e + 2*i - h - k) % 7
475
514
  m = (a + 11*h + 22*l) / 451
476
515
  n, p = (h + l - 7*m + 114).divmod(31)
477
- return Date.new(y, n, p + 1)
516
+ Date.new(y, n, p + 1)
517
+ end
518
+
519
+ def easter_this_year
520
+ # Return the date of Easter in self's year
521
+ Date.easter(self.year)
478
522
  end
479
523
 
480
524
  def easter?
481
525
  # Am I Easter?
482
- # Easter is always in March or April
483
- return false unless [3, 4].include?(self.mon)
484
- return self == self.easter_this_year
526
+ self == self.easter_this_year
485
527
  end
486
528
 
487
529
  def nth_wday_in_month?(n, wday, month)
488
530
  # Is self the nth weekday in the given month of its year?
489
531
  # If n is negative, count from last day of month
490
- if self.wday != wday
491
- return false
492
- elsif self.mon != month
493
- return false
494
- else
495
- return self == Date.nth_wday_in_year_month(n, wday, self.year, month)
496
- end
532
+ self == Date.nth_wday_in_year_month(n, wday, self.year, month)
497
533
  end
498
534
 
535
+ #######################################################
536
+ # Calculations for Federal holidays
537
+ # 5 USC 6103
538
+ #######################################################
499
539
  def fed_fixed_holiday?
500
540
  # Fixed-date holidays on weekdays
501
541
  if self.mon == 1 && self.mday == 1
502
542
  # New Years (January 1),
503
- return true
543
+ true
504
544
  elsif self.mon == 7 && self.mday == 4
505
545
  # Independence Day (July 4),
506
- return true
546
+ true
507
547
  elsif self.mon == 11 && self.mday == 11
508
548
  # Veterans Day (November 11),
509
- return true
549
+ true
510
550
  elsif self.mon == 12 && self.mday == 25
511
551
  # Christmas (December 25), and
512
- return true
513
- elsif self.mon == 12 && self.mday == 31
514
- # New Year's Eve (December 31)
515
- return true;
552
+ true
516
553
  else
517
- return false
554
+ false
518
555
  end
519
556
  end
520
557
 
@@ -524,66 +561,58 @@ class Date
524
561
 
525
562
  # No moveable feasts in certain months
526
563
  if [ 3, 4, 6, 7, 8, 12 ].include?(self.month)
527
- return false
564
+ false
528
565
  elsif self.wday == 1
566
+ moveable_mondays = []
529
567
  # MLK's Birthday (Third Monday in Jan)
530
- return true if self.nth_wday_in_month?(3, 1, 1)
568
+ moveable_mondays << self.nth_wday_in_month?(3, 1, 1)
531
569
  # Washington's Birthday (Third Monday in Feb)
532
- return true if self.nth_wday_in_month?(3, 1, 2)
570
+ moveable_mondays << self.nth_wday_in_month?(3, 1, 2)
533
571
  # Memorial Day (Last Monday in May)
534
- return true if self.nth_wday_in_month?(-1, 1, 5)
572
+ moveable_mondays << self.nth_wday_in_month?(-1, 1, 5)
535
573
  # Labor Day (First Monday in Sep)
536
- return true if self.nth_wday_in_month?(1, 1, 9)
574
+ moveable_mondays << self.nth_wday_in_month?(1, 1, 9)
537
575
  # Columbus Day (Second Monday in Oct)
538
- return true if self.nth_wday_in_month?(2, 1, 10)
576
+ moveable_mondays << self.nth_wday_in_month?(2, 1, 10)
539
577
  # Other Mondays
540
- return false
578
+ moveable_mondays.any?
541
579
  elsif self.wday == 4
542
580
  # Thanksgiving Day (Fourth Thur in Nov)
543
- return false unless self.month == 11
544
- return self.nth_wday_in_month?(4, 4, 11)
581
+ self.nth_wday_in_month?(4, 4, 11)
545
582
  else
546
- return false
583
+ false
547
584
  end
548
585
  end
549
586
 
550
587
  def fed_holiday?
551
- if FED_DECLARED_HOLIDAYS.include?(self)
552
- return true
553
- end
554
-
555
588
  # All Saturdays and Sundays are "holidays"
556
- if self.weekend? then return true end
557
-
558
- # Is self a fixed holiday
559
- return true if self.fed_fixed_holiday?
589
+ return true if self.weekend?
560
590
 
561
- # A Friday is a holiday if a fixed-date holiday
562
- # would fall on the following Saturday
563
- if self.wday == 5
564
- td = self + 1
565
- return true if td.fed_fixed_holiday?
566
- end
591
+ # Some days are holidays by executive decree
592
+ return true if FED_DECREED_HOLIDAYS.include?(self)
567
593
 
568
- # A Monday is a holiday if a fixed-date holiday
569
- # would fall on the preceding Sunday
570
- if self.wday == 1
571
- td = self - 1
572
- return true if td.fed_fixed_holiday?
573
- end
594
+ # Is self a fixed holiday
595
+ return true if (self.fed_fixed_holiday? || self.fed_moveable_feast?)
574
596
 
575
- # If Christmas falls on a Thursday, apparently, the Friday after is
576
- # treated as a holiday as well. See 2003, 2008, for example.
577
597
  if self.wday == 5 and self.month == 12 and self.day == 26
578
- return true
598
+ # If Christmas falls on a Thursday, apparently, the Friday after is
599
+ # treated as a holiday as well. See 2003, 2008, for example.
600
+ true
601
+ elsif self.wday == 5
602
+ # A Friday is a holiday if a fixed-date holiday
603
+ # would fall on the following Saturday
604
+ (self + 1).fed_fixed_holiday? || (self + 1).fed_moveable_feast?
605
+ elsif self.wday == 1
606
+ # A Monday is a holiday if a fixed-date holiday
607
+ # would fall on the preceding Sunday
608
+ (self - 1).fed_fixed_holiday? || (self - 1).fed_moveable_feast?
609
+ else
610
+ false
579
611
  end
580
-
581
- # It's last chance is if its a movable feast
582
- return self.fed_moveable_feast?;
583
612
  end
584
613
 
585
614
  #######################################################
586
- # Calulations for NYSE holidays
615
+ # Calculations for NYSE holidays
587
616
  # Rule 51 and supplementary material
588
617
  #######################################################
589
618
 
@@ -605,15 +634,15 @@ class Date
605
634
  # Fixed-date holidays
606
635
  if self.mon == 1 && self.mday == 1
607
636
  # New Years (January 1),
608
- return true
637
+ true
609
638
  elsif self.mon == 7 && self.mday == 4
610
639
  # Independence Day (July 4),
611
- return true
640
+ true
612
641
  elsif self.mon == 12 && self.mday == 25
613
642
  # Christmas (December 25), and
614
- return true
643
+ true
615
644
  else
616
- return false
645
+ false
617
646
  end
618
647
  end
619
648
 
@@ -623,7 +652,7 @@ class Date
623
652
 
624
653
  # No moveable feasts in certain months
625
654
  if [ 6, 7, 8, 10, 12 ].include?(self.month)
626
- return false
655
+ false
627
656
  elsif self.wday == 1
628
657
  # MLK's Birthday (Third Monday in Jan)
629
658
  return true if self.nth_wday_in_month?(3, 1, 1)
@@ -634,17 +663,15 @@ class Date
634
663
  # Labor Day (First Monday in Sep)
635
664
  return true if self.nth_wday_in_month?(1, 1, 9)
636
665
  # Other Mondays
637
- return false
666
+ false
638
667
  elsif self.wday == 4
639
668
  # Thanksgiving Day (Fourth Thur in Nov)
640
- return false unless self.month == 11
641
- return self.nth_wday_in_month?(4, 4, 11)
669
+ self.nth_wday_in_month?(4, 4, 11)
642
670
  elsif self.wday == 5
643
671
  # Good Friday, the Friday before Easter
644
- td = self + 2
645
- return td.easter?
672
+ (self + 2).easter?
646
673
  else
647
- return false
674
+ false
648
675
  end
649
676
  end
650
677
 
@@ -653,105 +680,70 @@ class Date
653
680
  return true if self.weekend?
654
681
 
655
682
  # Is self a fixed holiday
656
- return true if self.nyse_fixed_holiday?
683
+ return true if self.nyse_fixed_holiday? || self.nyse_moveable_feast?
657
684
 
658
- # A Friday is a holiday if a fixed-date holiday
659
- # would fall on the following Saturday
660
685
  if self.wday == 5
661
- td = self + 1
662
- return true if td.nyse_fixed_holiday?
663
- end
664
-
665
- # A Monday is a holiday if a fixed-date holiday
666
- # would fall on the preceding Sunday
667
- if self.wday == 1
668
- td = self - 1
669
- return true if td.nyse_fixed_holiday?
686
+ # A Friday is a holiday if a fixed-date holiday
687
+ # would fall on the following Saturday
688
+ (self + 1).nyse_fixed_holiday? || (self + 1).nyse_moveable_feast?
689
+ elsif self.wday == 1
690
+ # A Monday is a holiday if a fixed-date holiday
691
+ # would fall on the preceding Sunday
692
+ (self - 1).nyse_fixed_holiday? || (self - 1).nyse_moveable_feast?
693
+ else
694
+ false
670
695
  end
671
-
672
- # It's last chance is if its a movable feast
673
- return self.nyse_moveable_feast?;
674
696
  end
675
697
 
676
698
  def fed_workday?
677
- return ! self.fed_holiday?;
699
+ !self.fed_holiday?
678
700
  end
679
701
 
680
702
  def nyse_workday?
681
- return ! self.nyse_holiday?;
682
- end
683
-
684
- def next_fed_workday
685
- result = self + 1
686
- while result.fed_holiday?
687
- result += 1;
688
- end
689
- return result
703
+ !self.nyse_holiday?
690
704
  end
691
705
 
692
706
  def add_fed_business_days(n)
693
707
  d = self.dup
694
- if n < 0
695
- n.abs.times { d = d.prior_fed_workday }
696
- elsif n > 0
697
- n.times { d = d.next_fed_workday }
698
- end
708
+ return d if n == 0
709
+ incr = n < 0 ? -1 : 1
710
+ n = n.abs
711
+ while n > 0
712
+ d += incr
713
+ if d.fed_workday?
714
+ n -= 1
715
+ end
716
+ end
699
717
  d
700
718
  end
701
719
 
702
- def next_nyse_workday
703
- result = self.dup
704
- result += 1
705
- while result.nyse_holiday?
706
- result += 1;
707
- end
708
- return result
720
+ def next_fed_workday
721
+ self.add_fed_business_days(1)
722
+ end
723
+
724
+ def prior_fed_workday
725
+ self.add_fed_business_days(-1)
709
726
  end
710
727
 
711
728
  def add_nyse_business_days(n)
712
729
  d = self.dup
713
- if n < 0
714
- n.abs.times { d = d.prior_nyse_workday }
715
- elsif n > 0
716
- n.times { d = d.next_nyse_workday }
717
- end
730
+ return d if n == 0
731
+ incr = n < 0 ? -1 : 1
732
+ n = n.abs
733
+ while n > 0
734
+ d += incr
735
+ if d.nyse_workday?
736
+ n -= 1
737
+ end
738
+ end
718
739
  d
719
740
  end
720
741
 
721
- def prior_fed_workday
722
- result = self - 1
723
- while result.fed_holiday?
724
- result -= 1;
725
- end
726
- return result
742
+ def next_nyse_workday
743
+ self.add_nyse_business_days(1)
727
744
  end
728
745
 
729
746
  def prior_nyse_workday
730
- result = self.dup
731
- result -= 1
732
- while result.nyse_holiday?
733
- result -= 1;
734
- end
735
- return result
736
- end
737
-
738
- def iso
739
- strftime("%Y-%m-%d")
740
- end
741
-
742
- def num
743
- strftime("%Y%m%d")
744
- end
745
-
746
- def within_6mos_of?(d)
747
- # Date 6 calendar months before self
748
- start_date = self - 6.months + 2.days
749
- end_date = self + 6.months - 2.days
750
- (start_date..end_date).cover?(d)
751
- end
752
-
753
- # Allow erb documents can directly interpolate dates
754
- def tex_quote
755
- iso
747
+ self.add_nyse_business_days(-1)
756
748
  end
757
749
  end