Module: FatCore::Date

Included in:
Date
Defined in:
lib/fat_core/date.rb

Defined Under Namespace

Modules: ClassMethods

Federal Holidays and Workdays collapse

FED_DECREED_HOLIDAYS =

Holidays decreed by Presidential proclamation

[
  # Obama decree extra day before Christmas See
  # http://www.whitehouse.gov/the-press-office/2012/12/21
  ::Date.parse('2012-12-24'),
  # And Trump
  ::Date.parse('2018-12-24')
].freeze
PRESIDENTIAL_FUNERALS =

Presidential funeral since JFK

[
  # JKF Funeral
  ::Date.parse('1963-11-25'),
  # DWE Funeral
  ::Date.parse('1969-03-31'),
  # HST Funeral
  ::Date.parse('1972-12-28'),
  # LBJ Funeral
  ::Date.parse('1973-01-25'),
  # RMN Funeral
  ::Date.parse('1994-04-27'),
  # RWR Funeral
  ::Date.parse('2004-06-11'),
  # GTF Funeral
  ::Date.parse('2007-01-02'),
  # GHWBFuneral
  ::Date.parse('2018-12-05')
]

Constant Summary collapse

BOT =

Constant for Beginning of Time (BOT) outside the range of what we would ever want to find in commercial situations.

::Date.parse('1900-01-01')
EOT =

Constant for End of Time (EOT) outside the range of what we would ever want to find in commercial situations.

::Date.parse('3000-12-31')

Formatting collapse

Queries collapse

Relative ::Dates collapse

Federal Holidays and Workdays collapse

NYSE Holidays and Workdays collapse

Instance Method Details

#add_chunk(chunk, n = 1) ⇒ ::Date

Return the date that is n chunks later than self.

Parameters:

  • chunk (Symbol)

    one of +:year+, +:half+, +:quarter+, +:bimonth+, +:month+, +:semimonth+, +:biweek+, +:week+, or +:day+.

  • n (Integer) (defaults to: 1)

    the number of chunks to add, can be negative

Returns:

  • (::Date)

    the date n chunks from this date


678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
# File 'lib/fat_core/date.rb', line 678

def add_chunk(chunk, n = 1)
  case chunk
  when :year
    next_year(n)
  when :half
    next_month(6)
  when :quarter
    next_month(3)
  when :bimonth
    next_month(2)
  when :month
    next_month(n)
  when :semimonth
    next_semimonth(n)
  when :biweek
    next_biweek(n)
  when :week
    next_week(n)
  when :day
    next_day(n)
  else
    raise ArgumentError, "add_chunk unknown chunk: '#{chunk}'"
  end
end

#add_fed_workdays(n) ⇒ ::Date

Return the date that is n federal workdays after or before (if n < 0) this date.

Parameters:

  • n (Integer)

    number of federal workdays to add to this date

Returns:


870
871
872
873
874
875
876
877
878
879
880
# File 'lib/fat_core/date.rb', line 870

def add_fed_workdays(n)
  d = dup
  return d if n.zero?
  incr = n.negative? ? -1 : 1
  n = n.abs
  while n.positive?
    d += incr
    n -= 1 if d.fed_workday?
  end
  d
end

#add_nyse_workdays(n) ⇒ ::Date Also known as: add_trading_days

Return the date that is n NYSE trading days after or before (if n < 0) this date.

Parameters:

  • n (Integer)

    number of NYSE trading days to add to this date

Returns:


1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
# File 'lib/fat_core/date.rb', line 1047

def add_nyse_workdays(n)
  d = dup
  return d if n.zero?
  incr = n.negative? ? -1 : 1
  n = n.abs
  while n.positive?
    d += incr
    n -= 1 if d.nyse_workday?
  end
  d
end

#americanString

Format date in MM/DD/YYYY form, as typical for the short American form.

Returns:


104
105
106
# File 'lib/fat_core/date.rb', line 104

def american
  strftime '%-m/%-d/%Y'
end

#beginning_of_bimonth::Date

The date that is the first day of the bimonth in which self falls. A 'bimonth' is a two-month calendar period beginning on the first day of the odd-numbered months. E.g., 2014-01-01 to 2014-02-28 is the first bimonth of 2014.

Returns:


395
396
397
398
399
400
401
# File 'lib/fat_core/date.rb', line 395

def beginning_of_bimonth
  if month.odd?
    beginning_of_month
  else
    (self - 1.month).beginning_of_month
  end
end

#beginning_of_bimonth?Boolean

Return whether the date falls on the first day of a calendar bi-monthly period, i.e., the beginning of an odd-numbered month.

Returns:

  • (Boolean)

211
212
213
# File 'lib/fat_core/date.rb', line 211

def beginning_of_bimonth?
  month.odd? && beginning_of_month == self
end

#beginning_of_biweek::Date

Return the date that is the first day of the commercial biweek in which self falls. A biweek is a period of two commercial weeks starting with an odd-numbered week and with each week starting in Monday and ending on Sunday.

Returns:


455
456
457
458
459
460
461
# File 'lib/fat_core/date.rb', line 455

def beginning_of_biweek
  if cweek.odd?
    beginning_of_week(:monday)
  else
    (self - 1.week).beginning_of_week(:monday)
  end
end

#beginning_of_biweek?Boolean

Return whether the date falls on the first day of a commercial bi-week, i.e., on /Monday/ in a commercial week that is an odd-numbered week. From ::Date: "The calendar week is a seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes January 4."

Returns:

  • (Boolean)

268
269
270
# File 'lib/fat_core/date.rb', line 268

def beginning_of_biweek?
  beginning_of_biweek == self
end

#beginning_of_chunk(chunk) ⇒ ::Date

Return the date that is the beginning of the +chunk+ in which this date falls.

Parameters:

  • chunk (Symbol)

    one of +:year+, +:half+, +:quarter+, +:bimonth+, +:month+, +:semimonth+, +:biweek+, +:week+, or +:day+.

Returns:

  • (::Date)

    the first date in the chunk-sized period in which this date falls


710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/fat_core/date.rb', line 710

def beginning_of_chunk(chunk)
  case chunk
  when :year
    beginning_of_year
  when :half
    beginning_of_half
  when :quarter
    beginning_of_quarter
  when :bimonth
    beginning_of_bimonth
  when :month
    beginning_of_month
  when :semimonth
    beginning_of_semimonth
  when :biweek
    beginning_of_biweek
  when :week
    beginning_of_week
  when :day
    self
  else
    raise ArgumentError, "unknown chunk sym: '#{chunk}'"
  end
end

#beginning_of_half::Date

The date that is the first day of the half-year in which self falls.

Returns:


364
365
366
367
368
369
370
371
372
# File 'lib/fat_core/date.rb', line 364

def beginning_of_half
  if month > 9
    (beginning_of_quarter - 15).beginning_of_quarter
  elsif month > 6
    beginning_of_quarter
  else
    beginning_of_year
  end
end

#beginning_of_half?Boolean

Return whether the date falls on the first day of a half-year.

Returns:

  • (Boolean)

178
179
180
# File 'lib/fat_core/date.rb', line 178

def beginning_of_half?
  beginning_of_half == self
end

#beginning_of_month?Boolean

Return whether the date falls on the first day of a calendar month.

Returns:

  • (Boolean)

228
229
230
# File 'lib/fat_core/date.rb', line 228

def beginning_of_month?
  beginning_of_month == self
end

#beginning_of_quarter?Boolean

Return whether the date falls on the first day of a calendar quarter.

Returns:

  • (Boolean)

194
195
196
# File 'lib/fat_core/date.rb', line 194

def beginning_of_quarter?
  beginning_of_quarter == self
end

#beginning_of_semimonth::Date

The date that is the first day of the semimonth in which self falls. A semimonth is a calendar period beginning on the 1st or 16th of each month and ending on the 15th or last day of the month respectively. So each year has exactly 24 semimonths.

Returns:


425
426
427
428
429
430
431
# File 'lib/fat_core/date.rb', line 425

def beginning_of_semimonth
  if day >= 16
    ::Date.new(year, month, 16)
  else
    beginning_of_month
  end
end

#beginning_of_semimonth?Boolean

Return whether the date falls on the first day of a calendar semi-monthly period, i.e., on the 1st or 15th of a month.

Returns:

  • (Boolean)

245
246
247
# File 'lib/fat_core/date.rb', line 245

def beginning_of_semimonth?
  beginning_of_semimonth == self
end

#beginning_of_week?Boolean

Return whether the date falls on the first day of a commercial week, i.e., on /Monday/ in a commercial week. From ::Date: "The calendar week is a seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes January 4."

Returns:

  • (Boolean)

295
296
297
# File 'lib/fat_core/date.rb', line 295

def beginning_of_week?
  beginning_of_week == self
end

#beginning_of_year?Boolean

Return whether the date falls on the first day of a year.

Returns:

  • (Boolean)

162
163
164
# File 'lib/fat_core/date.rb', line 162

def beginning_of_year?
  beginning_of_year == self
end

#easter?Boolean

Return whether this date is Easter Sunday for the year in which it falls according to the Western Church. A few holidays key off this date as "moveable feasts."

Returns:

  • (Boolean)

332
333
334
335
# File 'lib/fat_core/date.rb', line 332

def easter?
  # Am I Easter?
  self == easter_this_year
end

#easter_this_year::Date

Return the date for Easter in the Western Church for the year in which this date falls.

Returns:


771
772
773
774
# File 'lib/fat_core/date.rb', line 771

def easter_this_year
  # Return the date of Easter in self's year
  ::Date.easter(year)
end

#end_of_bimonth::Date

The date that is the last day of the bimonth in which self falls. A 'bimonth' is a two-month calendar period beginning on the first day of the odd-numbered months. E.g., 2014-01-01 to 2014-02-28 is the first bimonth of 2014.

Returns:


410
411
412
413
414
415
416
# File 'lib/fat_core/date.rb', line 410

def end_of_bimonth
  if month.odd?
    (self + 1.month).end_of_month
  else
    end_of_month
  end
end

#end_of_bimonth?Boolean

Return whether the date falls on the last day of a calendar bi-monthly period, i.e., the end of an even-numbered month.

Returns:

  • (Boolean)

220
221
222
# File 'lib/fat_core/date.rb', line 220

def end_of_bimonth?
  month.even? && end_of_month == self
end

#end_of_biweek::Date

Return the date that is the last day of the commercial biweek in which self falls. A biweek is a period of two commercial weeks starting with an odd-numbered week and with each week starting in Monday and ending on Sunday. So this will always return a Sunday in an even-numbered week.

Returns:


470
471
472
473
474
475
476
# File 'lib/fat_core/date.rb', line 470

def end_of_biweek
  if cweek.odd?
    (self + 1.week).end_of_week(:monday)
  else
    end_of_week(:monday)
  end
end

#end_of_biweek?Boolean

Return whether the date falls on the last day of a commercial bi-week, i.e., on /Sunday/ in a commercial week that is an even-numbered week. From ::Date: "The calendar week is a seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes January 4."

Returns:

  • (Boolean)

282
283
284
# File 'lib/fat_core/date.rb', line 282

def end_of_biweek?
  end_of_biweek == self
end

#end_of_chunk(chunk) ⇒ ::Date

Return the date that is the end of the +chunk+ in which this date falls.

Parameters:

  • chunk (Symbol)

    one of +:year+, +:half+, +:quarter+, +:bimonth+, +:month+, +:semimonth+, +:biweek+, +:week+, or +:day+.

Returns:

  • (::Date)

    the first date in the chunk-sized period in which this date falls


742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
# File 'lib/fat_core/date.rb', line 742

def end_of_chunk(chunk)
  case chunk
  when :year
    end_of_year
  when :half
    end_of_half
  when :quarter
    end_of_quarter
  when :bimonth
    end_of_bimonth
  when :month
    end_of_month
  when :semimonth
    end_of_semimonth
  when :biweek
    end_of_biweek
  when :week
    end_of_week
  when :day
    self
  else
    raise ArgumentError, "unknown chunk: '#{chunk}'"
  end
end

#end_of_half::Date

The date that is the last day of the half-year in which self falls.

Returns:


378
379
380
381
382
383
384
385
386
# File 'lib/fat_core/date.rb', line 378

def end_of_half
  if month < 4
    (end_of_quarter + 15).end_of_quarter
  elsif month < 7
    end_of_quarter
  else
    end_of_year
  end
end

#end_of_half?Boolean

Return whether the date falls on the last day of a half-year.

Returns:

  • (Boolean)

186
187
188
# File 'lib/fat_core/date.rb', line 186

def end_of_half?
  end_of_half == self
end

#end_of_month?Boolean

Return whether the date falls on the last day of a calendar month.

Returns:

  • (Boolean)

236
237
238
# File 'lib/fat_core/date.rb', line 236

def end_of_month?
  end_of_month == self
end

#end_of_quarter?Boolean

Return whether the date falls on the last day of a calendar quarter.

Returns:

  • (Boolean)

202
203
204
# File 'lib/fat_core/date.rb', line 202

def end_of_quarter?
  end_of_quarter == self
end

#end_of_semimonth::Date

The date that is the last day of the semimonth in which self falls. A semimonth is a calendar period beginning on the 1st or 16th of each month and ending on the 15th or last day of the month respectively. So each year has exactly 24 semimonths.

Returns:


440
441
442
443
444
445
446
# File 'lib/fat_core/date.rb', line 440

def end_of_semimonth
  if day <= 15
    ::Date.new(year, month, 15)
  else
    end_of_month
  end
end

#end_of_semimonth?Boolean

Return whether the date falls on the last day of a calendar semi-monthly period, i.e., on the 14th or the last day of a month.

Returns:

  • (Boolean)

254
255
256
# File 'lib/fat_core/date.rb', line 254

def end_of_semimonth?
  end_of_semimonth == self
end

#end_of_week?Boolean

Return whether the date falls on the first day of a commercial week, i.e., on /Sunday/ in a commercial week. From ::Date: "The calendar week is a seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes January 4."

Returns:

  • (Boolean)

308
309
310
# File 'lib/fat_core/date.rb', line 308

def end_of_week?
  end_of_week == self
end

#end_of_year?Boolean

Return whether the date falls on the last day of a year.

Returns:

  • (Boolean)

170
171
172
# File 'lib/fat_core/date.rb', line 170

def end_of_year?
  end_of_year == self
end

#engString

Format as an English string, like 'January 12, 2016'

Returns:


95
96
97
# File 'lib/fat_core/date.rb', line 95

def eng
  strftime('%B %-d, %Y')
end

#fed_holiday?Boolean

Return whether this date is a United States federal holiday.

Calculations for Federal holidays are based on 5 USC 6103, include all weekends, Presidential funerals, and holidays decreed executive orders.

Returns:

  • (Boolean)

814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
# File 'lib/fat_core/date.rb', line 814

def fed_holiday?
  # All Saturdays and Sundays are "holidays"
  return true if weekend?

  # Some days are holidays by executive decree
  return true if FED_DECREED_HOLIDAYS.include?(self)

  # Presidential funerals
  return true if PRESIDENTIAL_FUNERALS.include?(self)

  # Is self a fixed holiday
  return true if fed_fixed_holiday? || fed_moveable_feast?

  if friday? && month == 12 && day == 26
    # If Christmas falls on a Thursday, apparently, the Friday after is
    # treated as a holiday as well.  See 2003, 2008, for example.
    true
  elsif friday?
    # A Friday is a holiday if a fixed-date holiday
    # would fall on the following Saturday
    (self + 1).fed_fixed_holiday? || (self + 1).fed_moveable_feast?
  elsif monday?
    # A Monday is a holiday if a fixed-date holiday
    # would fall on the preceding Sunday
    (self - 1).fed_fixed_holiday? || (self - 1).fed_moveable_feast?
  elsif (year % 4 == 1) && year > 1965 && mon == 1 && mday == 20
    # Inauguration Day after 1965 is a federal holiday, but if it falls on a
    # Sunday, the following Monday is observed, but if it falls on a
    # Saturday, the prior Friday is /not/ observed. So, we can't just count
    # this as a regular fixed holiday.
    true
  elsif monday? && (year % 4 == 1) && year > 1965 && mon == 1 && mday == 21
    # Inauguration Day after 1965 is a federal holiday, but if it falls on a
    # Sunday, the following Monday is observed, but if it falls on a
    # Saturday, the prior Friday is /not/ observed.
    true
  else
    false
  end
end

#fed_workday?Boolean

Return whether this date is a date on which the US federal government is open for business. It is the opposite of #fed_holiday?

Returns:

  • (Boolean)

859
860
861
# File 'lib/fat_core/date.rb', line 859

def fed_workday?
  !fed_holiday?
end

#half1, 2

Self's calendar "half" by analogy to calendar quarters: 1 or 2, depending on whether the date falls in the first or second half of the calendar year.

Returns:

  • (1, 2)

131
132
133
134
135
136
137
138
# File 'lib/fat_core/date.rb', line 131

def half
  case month
  when (1..6)
    1
  when (7..12)
    2
  end
end

#isoString

Format as an ISO string of the form YYYY-MM-DD.

Returns:


62
63
64
# File 'lib/fat_core/date.rb', line 62

def iso
  strftime('%Y-%m-%d')
end

#next_bimonth(n = 1) ⇒ ::Date

Return the date that is +n+ calendar bimonths after this date, where a calendar bimonth is a period of 2 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of bimonths to advance, can be negative

Returns:

  • (::Date)

    new date n bimonths after this date


523
524
525
526
527
# File 'lib/fat_core/date.rb', line 523

def next_bimonth(n = 1)
  n = n.floor
  return self if n.zero?
  next_month(n * 2)
end

#next_biweek(n = 1) ⇒ ::Date

Return the date that is +n+ biweeks after this date where each biweek is 14 days.

Parameters:

  • n (Integer) (defaults to: 1)

    number of biweeks to advance, can be negative

Returns:

  • (::Date)

    new date n biweeks after this date


622
623
624
625
626
# File 'lib/fat_core/date.rb', line 622

def next_biweek(n = 1)
  n = n.floor
  return self if n.zero?
  self + (14 * n)
end

#next_fed_workday::Date

Return the next federal workday after this date. The date returned is always a date at least one day after this date, never this date.

Returns:


886
887
888
# File 'lib/fat_core/date.rb', line 886

def next_fed_workday
  add_fed_workdays(1)
end

#next_half(n = 1) ⇒ ::Date

Return the date that is +n+ calendar halves after this date, where a calendar half is a period of 6 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of halves to advance, can be negative

Returns:

  • (::Date)

    new date n halves after this date


483
484
485
486
487
# File 'lib/fat_core/date.rb', line 483

def next_half(n = 1)
  n = n.floor
  return self if n.zero?
  next_month(n * 6)
end

#next_nyse_workday::Date Also known as: next_trading_day

Return the next NYSE trading day after this date. The date returned is always a date at least one day after this date, never this date.

Returns:


1064
1065
1066
# File 'lib/fat_core/date.rb', line 1064

def next_nyse_workday
  add_nyse_workdays(1)
end

#next_quarter(n = 1) ⇒ ::Date

Return the date that is +n+ calendar quarters after this date, where a calendar quarter is a period of 3 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of quarters to advance, can be negative

Returns:

  • (::Date)

    new date n quarters after this date


503
504
505
506
507
# File 'lib/fat_core/date.rb', line 503

def next_quarter(n = 1)
  n = n.floor
  return self if n.zero?
  next_month(n * 3)
end

#next_semimonth(n = 1) ⇒ ::Date

Return the date that is +n+ semimonths after this date. Each semimonth begins on the 1st or 16th of the month, and advancing one semimonth from the first half of a month means to go as far past the 16th as the current date is past the 1st; advancing one semimonth from the second half of a month means to go as far into the next month past the 1st as the current date is past the 16th, but never past the 15th of the next month.

Parameters:

  • n (Integer) (defaults to: 1)

    number of semimonths to advance, can be negative

Returns:

  • (::Date)

    new date n semimonths after this date


547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/fat_core/date.rb', line 547

def next_semimonth(n = 1)
  n = n.floor
  return self if n.zero?
  factor = n.negative? ? -1 : 1
  n = n.abs
  if n.even?
    next_month(n / 2)
  else
    # Advance or retreat one semimonth
    next_sm =
      if day == 1
        if factor.positive?
          beginning_of_month + 16.days
        else
          prior_month.beginning_of_month + 16.days
        end
      elsif day == 16
        if factor.positive?
          next_month.beginning_of_month
        else
          beginning_of_month
        end
      elsif day < 16
        # In the first half of the month (the 2nd to the 15th), go as far past
        # the 16th as the date is past the 1st. Thus, as many as 14 days past
        # the 16th, so at most to the 30th of the month unless there are less
        # than 30 days in the month, then go to the end of the month.
        if factor.positive?
          [beginning_of_month + 16.days + (day - 1).days, end_of_month].min
        else
          [prior_month.beginning_of_month + 16.days + (day - 1).days,
           prior_month.end_of_month].min
        end
      else
        # In the second half of the month (17th to the 31st), go as many
        # days into the next month as we are past the 16th. Thus, as many as
        # 15 days.  But we don't want to go past the first half of the next
        # month, so we only go so far as the 15th of the next month.
        # ::Date.parse('2015-02-18').next_semimonth should be the 3rd of the
        # following month.
        if factor.positive?
          next_month.beginning_of_month + [(day - 16), 15].min
        else
          beginning_of_month + [(day - 16), 15].min
        end
      end
    n -= 1
    # Now that n is even, advance (or retreat) n / 2 months unless we're done.
    if n >= 2
      next_sm.next_month(factor * n / 2)
    else
      next_sm
    end
  end
end

#next_until_fed_workday::Date

Return this date if its a federal workday, otherwise skip forward to the first later federal workday.

Returns:


902
903
904
905
906
# File 'lib/fat_core/date.rb', line 902

def next_until_fed_workday
  date = dup
  date += 1 until date.fed_workday?
  date
end

#next_until_trading_day::Date

Return this date if its a trading day, otherwise skip forward to the first later trading day.

Returns:


1082
1083
1084
1085
1086
# File 'lib/fat_core/date.rb', line 1082

def next_until_trading_day
  date = dup
  date += 1 until date.trading_day?
  date
end

#next_week(n = 1) ⇒ ::Date

Return the date that is +n+ weeks after this date where each week is 7 days. This is different from the #next_week method in active_support, which goes to the first day of the week in the next week and does not take an argument +n+ to go multiple weeks.

Parameters:

  • n (Integer) (defaults to: 1)

    number of weeks to advance

Returns:

  • (::Date)

    new date n weeks after this date


644
645
646
647
648
# File 'lib/fat_core/date.rb', line 644

def next_week(n = 1)
  n = n.floor
  return self if n.zero?
  self + (7 * n)
end

#nth_wday_in_month?(n, wday, month) ⇒ Boolean

Return whether this date is the nth weekday wday of the given month in this date's year.

Parameters:

  • n (Integer)

    number of wday in month, if negative count from end of the month

  • wday (Integer)

    day of week, 0 is Sunday, 1 Monday, etc.

  • month (Integer)

    the month number, 1 is January, 2 is February, etc.

Returns:

  • (Boolean)

345
346
347
348
349
# File 'lib/fat_core/date.rb', line 345

def nth_wday_in_month?(n, wday, month)
  # Is self the nth weekday in the given month of its year?
  # If n is negative, count from last day of month
  self == ::Date.nth_wday_in_year_month(n, wday, year, month)
end

#numString

Format as an all-numeric string of the form YYYYMMDD

Returns:


78
79
80
# File 'lib/fat_core/date.rb', line 78

def num
  strftime('%Y%m%d')
end

#nyse_holiday?Boolean

Returns whether this date is one on which the NYSE was or is expected to be closed for business.

Calculations for NYSE holidays are from Rule 51 and supplementary materials for the Rules of the New York Stock Exchange, Inc.

  • General Rule 1: if a regular holiday falls on Saturday, observe it on the preceding Friday.
  • General Rule 2: if a regular holiday falls on Sunday, observe it on the following Monday.

These are the regular holidays:

  • New Year's Day, January 1.
  • Birthday of Martin Luther King, Jr., the third Monday in January.
  • Washington's Birthday, the third Monday in February.
  • Good Friday Friday before Easter Sunday. NOTE: this is not a fed holiday
  • Memorial Day, the last Monday in May.
  • Independence Day, July 4.
  • Labor Day, the first Monday in September.
  • Thanksgiving Day, the fourth Thursday in November.
  • Christmas Day, December 25.

Columbus and Veterans days not observed.

In addition, there have been several days on which the exchange has been closed for special events such as Presidential funerals, the 9-11 attacks, the paper-work crisis in the 1960's, hurricanes, etc. All of these are considered holidays for purposes of this method.

In addition, every weekend is considered a holiday.

Returns:

  • (Boolean)

1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
# File 'lib/fat_core/date.rb', line 1003

def nyse_holiday?
  # All Saturdays and Sundays are "holidays"
  return true if weekend?

  # Presidential funerals, observed by NYSE as well.
  return true if PRESIDENTIAL_FUNERALS.include?(self)

  # Is self a fixed holiday
  return true if nyse_fixed_holiday? || nyse_moveable_feast?

  return true if nyse_special_holiday?

  if friday? && (self >= ::Date.parse('1959-07-03'))
    # A Friday is a holiday if a holiday would fall on the following
    # Saturday.  The rule does not apply if the Friday "ends a monthly or
    # yearly accounting period." Adopted July 3, 1959. E.g, December 31,
    # 2010, fell on a Friday, so New Years was on Saturday, but the NYSE
    # opened because it ended a yearly accounting period.  I believe 12/31
    # is the only date to which the exception can apply since only New
    # Year's can fall on the first of the month.
    !end_of_quarter? &&
      ((self + 1).nyse_fixed_holiday? || (self + 1).nyse_moveable_feast?)
  elsif monday?
    # A Monday is a holiday if a holiday would fall on the
    # preceding Sunday.  This has apparently always been the rule.
    (self - 1).nyse_fixed_holiday? || (self - 1).nyse_moveable_feast?
  else
    false
  end
end

#nyse_workday?Boolean Also known as: trading_day?

Return whether the NYSE is open for trading on this date.

Returns:

  • (Boolean)

1037
1038
1039
# File 'lib/fat_core/date.rb', line 1037

def nyse_workday?
  !nyse_holiday?
end

#orgString

Format as an inactive Org date timestamp of the form [YYYY-MM-DD <dow>] (see Emacs org-mode)

Returns:


87
88
89
# File 'lib/fat_core/date.rb', line 87

def org
  strftime('[%Y-%m-%d %a]')
end

#pred::Date

Predecessor of self, opposite of #succ.

Returns:


356
357
358
# File 'lib/fat_core/date.rb', line 356

def pred
  self - 1.day
end

#prior_bimonth(n = 1) ⇒ ::Date

Return the date that is +n+ calendar bimonths before this date, where a calendar bimonth is a period of 2 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of bimonths to retreat, can be negative

Returns:

  • (::Date)

    new date n bimonths before this date


534
535
536
# File 'lib/fat_core/date.rb', line 534

def prior_bimonth(n = 1)
  next_bimonth(-n)
end

#prior_biweek(n = 1) ⇒ ::Date

Return the date that is +n+ biweeks before this date where each biweek is 14 days.

Parameters:

  • n (Integer) (defaults to: 1)

    number of biweeks to retreat, can be negative

Returns:

  • (::Date)

    new date n biweeks before this date


633
634
635
# File 'lib/fat_core/date.rb', line 633

def prior_biweek(n = 1)
  next_biweek(-n)
end

#prior_day(n) ⇒ ::Date

Return the date that is +n+ weeks before this date where each week is 7 days.

Parameters:

  • n (Integer)

    number of days to retreat

Returns:

  • (::Date)

    new date n days before this date


666
667
668
# File 'lib/fat_core/date.rb', line 666

def prior_day(n)
  next_day(-n)
end

#prior_fed_workday::Date

Return the last federal workday before this date. The date returned is always a date at least one day before this date, never this date.

Returns:


894
895
896
# File 'lib/fat_core/date.rb', line 894

def prior_fed_workday
  add_fed_workdays(-1)
end

#prior_half(n = 1) ⇒ ::Date

Return the date that is +n+ calendar halves before this date, where a calendar half is a period of 6 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of halves to retreat, can be negative

Returns:

  • (::Date)

    new date n halves before this date


494
495
496
# File 'lib/fat_core/date.rb', line 494

def prior_half(n = 1)
  next_half(-n)
end

#prior_nyse_workday::Date Also known as: prior_trading_day

Return the last NYSE trading day before this date. The date returned is always a date at least one day before this date, never this date.

Returns:


1073
1074
1075
# File 'lib/fat_core/date.rb', line 1073

def prior_nyse_workday
  add_nyse_workdays(-1)
end

#prior_quarter(n = 1) ⇒ ::Date

Return the date that is +n+ calendar quarters before this date, where a calendar quarter is a period of 3 months.

Parameters:

  • n (Integer) (defaults to: 1)

    number of quarters to retreat, can be negative

Returns:

  • (::Date)

    new date n quarters after this date


514
515
516
# File 'lib/fat_core/date.rb', line 514

def prior_quarter(n = 1)
  next_quarter(-n)
end

#prior_semimonth(n = 1) ⇒ ::Date

Return the date that is +n+ semimonths before this date. Each semimonth begins on the 1st or 15th of the month, and retreating one semimonth from the first half of a month means to go as far past the 15th of the prior month as the current date is past the 1st; retreating one semimonth from the second half of a month means to go as far past the 1st of the current month as the current date is past the 15th, but never past the 14th of the the current month.

Parameters:

  • n (Integer) (defaults to: 1)

    number of semimonths to retreat, can be negative

Returns:

  • (::Date)

    new date n semimonths before this date


613
614
615
# File 'lib/fat_core/date.rb', line 613

def prior_semimonth(n = 1)
  next_semimonth(-n)
end

#prior_until_fed_workdayObject

Return this if its a federal workday, otherwise skip back to the first prior federal workday.


910
911
912
913
914
# File 'lib/fat_core/date.rb', line 910

def prior_until_fed_workday
  date = dup
  date -= 1 until date.fed_workday?
  date
end

#prior_until_trading_day::Date

Return this date if its a trading day, otherwise skip back to the first prior trading day.

Returns:


1092
1093
1094
1095
1096
# File 'lib/fat_core/date.rb', line 1092

def prior_until_trading_day
  date = dup
  date -= 1 until date.trading_day?
  date
end

#prior_week(n) ⇒ ::Date

Return the date that is +n+ weeks before this date where each week is 7 days.

Parameters:

  • n (Integer)

    number of weeks to retreat

Returns:

  • (::Date)

    new date n weeks from this date


655
656
657
# File 'lib/fat_core/date.rb', line 655

def prior_week(n)
  next_week(-n)
end

#quarter1, ...

Self's calendar quarter: 1, 2, 3, or 4, depending on which calendar quarter the date falls in.

Returns:

  • (1, 2, 3, 4)

145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/fat_core/date.rb', line 145

def quarter
  case month
  when (1..3)
    1
  when (4..6)
    2
  when (7..9)
    3
  when (10..12)
    4
  end
end

#tex_quoteString

Format date to TeX documents as ISO strings but with en-dashes

Returns:


70
71
72
# File 'lib/fat_core/date.rb', line 70

def tex_quote
  strftime('%Y--%m--%d')
end

#weekday?Boolean

Does self fall on a weekday?

Returns:

  • (Boolean)

121
122
123
# File 'lib/fat_core/date.rb', line 121

def weekday?
  !weekend?
end

#weekend?Boolean

Does self fall on a weekend?

Returns:

  • (Boolean)

113
114
115
# File 'lib/fat_core/date.rb', line 113

def weekend?
  saturday? || sunday?
end

#within_6mos_of?(d) ⇒ Boolean

Return whether this date falls within a period of less than six months from the date d using the Stella v. Graham Page Motors convention that "less" than six months is true only if this date falls within the range of dates 2 days after date six months before and 2 days before the date six months after the date d.

Parameters:

  • d (::Date)

    the middle of the six-month range

Returns:

  • (Boolean)

320
321
322
323
324
325
# File 'lib/fat_core/date.rb', line 320

def within_6mos_of?(d)
  # ::Date 6 calendar months before self
  start_date = self - 6.months + 2.days
  end_date = self + 6.months - 2.days
  (start_date..end_date).cover?(d)
end