Project

fat_period

0.0
A long-lived project that still receives updates
Implements a Period class as a Range of Dates.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

>= 5.4
 Project Readme

FatPeriod User Guide

https://travis-ci.org/ddoherty03/fat_table.svg?branch=master

Version

require 'fat_period'
include FatPeriod
"Current version is: #{FatPeriod::VERSION}"
Current version is: 3.0.0

Introduction

FatPeriod provides a Ruby Period class for dealing with time periods of days, that is ranges whose endpoints are of class Date. It’s target is financial applications, but it serves well for any application where periods of time are useful. It builds on the fat_date gem, which provides enhancements to the Date class, especially its class method Date.spec for interpreting a rich set of “specs” as the beginning or end of a variety of calendar-related periods.

In addition, set operations are provided for Period, as well as methods for breaking a larger periods into an array of smaller periods of various ‘chunk’ sizes that correspond to calendar-related periods such as days, weeks, months, and so forth.

Table of Contents

  • Version
  • Introduction
  • Installation
    • Installing the gem
  • Usage
    • Constant Period::FOREVER
    • Construction of Periods
      • Period.new
      • Period.parse
        • With Only a From-Spec
        • With Both a From-Spec and To-Spec
        • Using Skip Modifiers
      • Period.parse_phrase
      • Period.ensure
    • Conversion
      • To Range
      • To String
      • TeX Form
    • Comparison
    • Enumeration
    • Size
    • Chunking
    • Set Operations
      • Subset Determination
      • Superset Determination
      • Intersection
      • Difference
      • Union
    • Coverage
      • Contains?
      • Overlapping
      • Spanning
      • Gaps
  • Development
  • Contributing

Installation

Installing the gem

Add this line to your application’s Gemfile:

gem 'fat_period'

And then execute:

$ bundle

Or install it yourself as:

$ gem install fat_period

Usage

Constant Period::FOREVER

The Period class depends on the extensions to Date made by the fat_core gem, which you can read about here. It defines a constant, Period::FOREVER, which is defined as extending from Date::BOT to Date::EOT, which are defined in fat_date as 1900-01-01 and 3000-12-31, respectively and define the beginning of time and end of time for practical commercial purposes. The constant is not frozen, so you can re-define it to your liking.

Construction of Periods

Period.new

A Period is constructed with two arguments for the begin and end date. The begin date must be on or before the end date. Each argument can be (1) a Date, (2) a string parseable as a Date by the Date.parse method, or (3) an object that responds to #to_s and can be parsed as a Date by Date.parse:

p1 = Period.new(Date.today, Date.today + 30)
p2 = Period.new('Nov 22, 1963', Date.today)
p3 = Period.new('1961-01-21', '1963-11-22')
[[p1.to_s], [p2.to_s], [p3.to_s]]
["Camelot lasted #{p3.length} days"]

Period.parse

A more convenient way to construct a period is provided by Period.parse. It takes two strings as its arguments, a mandatory “from-spec” and an optional “to-spec”:

A “spec” is a string designating some period of time. There are many ways of specifying a period, which are detailed below.

With Only a From-Spec

If only a from-spec is given, it defines both the beginning and end of the overall period:

require 'fat_period'

tab = []
tab << ['From Spec', 'Result']
tab << nil
froms = ['2020', '2020-2Q', '2020-W15', '2020-09', '2020-09-A', '2020-09-iii']
froms.each do |f|
  tab << [f, Period.parse(f).inspect]
end
tab
From Spec Result
2020 Period(2020-01-01..2020-12-31)
2020-2Q Period(2020-04-01..2020-06-30)
2020-W45 Period(2020-11-02..2020-11-08)
2020-09 Period(2020-09-01..2020-09-30)
2020-09-A Period(2020-09-01..2020-09-15)
2020-09-iii Period(2020-09-14..2020-09-20)

With Both a From-Spec and To-Spec

But, if a to-spec is also given, the from-spec defines the beginning of the period and the to-spec defines the end of the period. In particular, the beginning of the period is the first day of the from-spec and the end of the period is the last day of the from-spec:

require 'fat_period'

tab = []
tab << ['From Spec', 'To Spec', 'Result']
tab << nil
from_tos = [['2020', '2020-2Q'], ['2020-2Q', '2020-W15'], ['2020-W15', '2020-09'], ['2020-09', '2020-09-A'], ['2020-09-A', '2020-09-iii']]
from_tos.each do |f, t|
  tab << [f, t, Period.parse(f, t).inspect]
end
tab
From Spec To Spec Result
2020 2020-2Q Period(2020-01-01..2020-06-30)
2020-2Q 2020-W15 Period(2020-04-01..2020-04-12)
2020-W15 2020-09 Period(2020-04-06..2020-09-30)
2020-09 2020-09-A Period(2020-09-01..2020-09-15)
2020-09-A 2020-09-iii Period(2020-09-01..2020-09-20)

Using Skip Modifiers

One new feature of FatDate is the ability to add a “skip modifier” to the end of a date spec to skip forward or backward to the first day-of-week either on or before/after the date given by the spec. For example, the following demonstrates that one can set the ‘to’ spec to the last Wednesday of 2025 or the last Wednesday before the end of 2025. Using ‘>’ or ‘>=’ specified skipping forward instead.

require 'fat_period'

tab = []
tab << ['From Spec', 'To Spec', 'Result', 'Description']
tab << nil
from_to_descs = [['2025-2Q', '2025<=Wed', 'From 2q to last Wednesday of 2025'],
                 ['2025-2Q', '2025<Wed', 'From 2q to last Wednesday /before/ the end of 2025'],
                 ['2012-11', '2012-11<=Thur', 'November 2012 through last Thursday'],
                 ['2012-11', '2012-11-4Thur', 'And through Thanksgiving (not always the /last/ Thursday!)']
                ]
from_to_descs.each do |f, t, d|
  tab << [f, t, Period.parse(f, t).inspect, d]
end
tab
From Spec To Spec Result Description
2025-2Q 2025<=Wed Period(2025-04-01..2025-12-31) From 2q to last Wednesday of 2025
2025-2Q 2025<Wed Period(2025-04-01..2025-12-31) From 2q to last Wednesday before the end of 2025
2012-11 2012-11<=Thur Period(2012-11-01..2012-11-29) November 2012 through last Thursday
2012-11 2012-11-4Thur Period(2012-11-01..2012-11-22) And through Thanksgiving (not always the last Thursday!)

Period.parse_phrase

For example:

The Period.parse_phrase method will take a string having a ‘from’, ‘to’, and ‘per’ clause and return an Array of Periods encompassing the same period as Period.parse, but optionally broken into sub-periods each having the length specified by the ‘per’ clause. Period.parse_phrase always returns an Array of Periods even if there is no ‘per’ clause and the Array has only one member. If there is no ‘to’ clause, the returned period is from the start of the ‘from’ period to its end. If there is neither a ‘from’ or a ‘to’ clause, it tries to interpret the beginning of the phrase as a valid spec and uses it as a ‘from’ clause.

require 'fat_period'

tab = []
tab << ['k', 'Sub Period']
tab << nil
pds = Period.parse_phrase('from 2025 to 2025-3Q per month')
pds.each_with_index do |pd, k|
  tab << [k, pd.inspect]
end
tab
k Sub Period
0 Period(2025-01-01..2025-01-31)
1 Period(2025-02-01..2025-02-28)
2 Period(2025-03-01..2025-03-31)
3 Period(2025-04-01..2025-04-30)
4 Period(2025-05-01..2025-05-31)
5 Period(2025-06-01..2025-06-30)
6 Period(2025-07-01..2025-07-31)
7 Period(2025-08-01..2025-08-31)
8 Period(2025-09-01..2025-09-30)

The period named in the ‘per’ clause is called a ‘chunk’ and there are several valid chunk names in FatPeriod:

Chunk Name
year
half
quarter
bimonth
month
semimonth
biweek
week
day

Here is the same period broken into weeks. Notice that the first and last “weeks” are not whole weeks because parts of them fall outside the boundaries of the overall period.

require 'fat_period'

tab = []
tab << ['k', 'Sub Period']
tab << nil
pds = Period.parse_phrase('from 2025 to 2025-3Q per week')
pds.each_with_index do |pd, k|
  tab << [k, pd.inspect]
end
tab
k Sub Period
0 Period(2025-01-01..2025-01-05)
1 Period(2025-01-06..2025-01-12)
2 Period(2025-01-13..2025-01-19)
3 Period(2025-01-20..2025-01-26)
4 Period(2025-01-27..2025-02-02)
5 Period(2025-02-03..2025-02-09)
6 Period(2025-02-10..2025-02-16)
7 Period(2025-02-17..2025-02-23)
8 Period(2025-02-24..2025-03-02)
9 Period(2025-03-03..2025-03-09)
10 Period(2025-03-10..2025-03-16)
11 Period(2025-03-17..2025-03-23)
12 Period(2025-03-24..2025-03-30)
13 Period(2025-03-31..2025-04-06)
14 Period(2025-04-07..2025-04-13)
15 Period(2025-04-14..2025-04-20)
16 Period(2025-04-21..2025-04-27)
17 Period(2025-04-28..2025-05-04)
18 Period(2025-05-05..2025-05-11)
19 Period(2025-05-12..2025-05-18)
20 Period(2025-05-19..2025-05-25)
21 Period(2025-05-26..2025-06-01)
22 Period(2025-06-02..2025-06-08)
23 Period(2025-06-09..2025-06-15)
24 Period(2025-06-16..2025-06-22)
25 Period(2025-06-23..2025-06-29)
26 Period(2025-06-30..2025-07-06)
27 Period(2025-07-07..2025-07-13)
28 Period(2025-07-14..2025-07-20)
29 Period(2025-07-21..2025-07-27)
30 Period(2025-07-28..2025-08-03)
31 Period(2025-08-04..2025-08-10)
32 Period(2025-08-11..2025-08-17)
33 Period(2025-08-18..2025-08-24)
34 Period(2025-08-25..2025-08-31)
35 Period(2025-09-01..2025-09-07)
36 Period(2025-09-08..2025-09-14)
37 Period(2025-09-15..2025-09-21)
38 Period(2025-09-22..2025-09-28)
39 Period(2025-09-29..2025-09-30)

Period.ensure

Period.ensure tries to interpret its argument as a Period and returns it; otherwise it throws an ArgumentError exception:

  • if the argument responds to the to_period method, it invokes that on the argument and returns it;
  • if the argument is a String, it uses Period.parse_phrase to try to interepret it as a Period;
  • if it is already a Period, it just returns the argument;
  • otherwise, it throws an ArgumentError exception.

Conversion

To Range

To String

TeX Form

Comparison

Enumeration

Size

Chunking

Set Operations

Subset Determination

Superset Determination

Intersection

Difference

Union

Coverage

Contains?

Overlapping

Spanning

Gaps

Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ddoherty03/fat_table.