Skip to content
/ period Public

Smart-Period aims to simplify Time-range manipulation

License

Notifications You must be signed in to change notification settings

billaul/period

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Smart-Period Gem Version Code Climate Inline docs

⚠️ This gem will no longer receive any updates
⚠️ If you want to access to new awesome features, like endless-period support 😯
⚠️ Take a look to his successor ActivePeriod 🥳

Smart-Period aims to simplify Time-range manipulation.

Installation

Add this line to your application's Gemfile:

gem 'period'

And then execute:

$ bundle

Or install it yourself as:

$ gem install smart-period

Usage

Smart-Period was designed to simplify time-range manipulation, specialy with rails and user input

Warning :

  • A time-range take place between two date and it's different from an abstract duration of time
  • Smart-Period is limited at full day of time and will always round the starting and ending to the beginning and the ending of the day

Quick view (TL;DR)

require 'period'

# Get all user created today
User.where(created_at: Period.today)

# Get how many weeks there is from the beginning of time ?
Period.new('01/01/1970'..Time.now).weeks.count

# Is Trump still in charge ?
Time.now.in? Period.new('20/01/2017'...'20/01/2021')

# Get the week of an arbitrary date
Period.week('24/04/1990')

# Write a date for me (I18n supported)
Period.new('20/01/2017'...'20/01/2021').to_s
=> "From the 20 January 2017 to the 19 January 2021 included"

Detailed view

There's two way to create and manipulate a period of time FreePeriod and StandardPeriod

FreePeriod of time

You can declare FreePeriod as simply as :

# With Date objects
Period.new(3.month.ago..Date.today)
# or with Strings
Period.new('01/01/2000'...'01/02/2000')
# or with a mix
Period.new('01/01/2000'..1.week.ago)
# or in a rails Controller with params
Period.new(params[:start_date]..params[:end_date])

FreePeriod can be manipulated with + and -
Doing so will move the start and the end of the period

Period.new('01/01/2000'..'05/01/2000') + 3.day
# is equal to
Period.new('04/01/2000'..'08/01/2000')

Standard Period of time

Using StandardPeriod you are limited to strictly bordered periods of time
These periods are day, week, month, quarter and year

# To get the week, 42th day ago
Period.week(42.day.ago)
# To get the first month of 2020
Period.month('01/01/2020')
# or if you like it verbious
Period::Month.new('01/01/2020')
# or if you need the current week
Period.week(Time.now)

Note : If you ask for a month, quarter of year, the day part of your param doesn't matter 01/01/2020 give the same result as 14/01/2020 or 29/01/2020

StandardPeriod can be manipulated with + and - and will always return a StandardPeriod of the same type

# Subtraction are made from the start of the period
Period.month('10/02/2000') - 1.day
# Return the previous month
# Addition are made from the end
Period.month('10/02/2000') + 1.day
# Return the next month
Period.week('10/02/2000') + 67.day
# Return a week

StandardPeriod also respond to .next and .prev

Period.month('01/01/2000').next.next.next
# Return the month of April 2020

You can quickly access close period of time with .(last|this|next)_(day|week|month|quarter|year) and .yesterday .today .tomorrow

Period.this_week
# Same as Period.week(Time.now) but shorter
Period.next_month
# Return the next month
Period.last_year
# Return the last year
Period.today
# No comment

HasMany

FreePeriod and some StandardPeriod respond to .days, .weeks, .months, .quarters and .years
These methods return an array of StandardPeriod who are overlapping the current period

HasMany -> [<StandardPeriod>] .days .weeks .months .quarters .years
FreePeriod X X X X X
StandardPeriod::Day
StandardPeriod::Week X
StandardPeriod::Month X X
StandardPeriod::Quarter X X X
StandardPeriod::Year X X X X

Example

# Get how many weeks there is from the beginning of time ?
Period.new('01/01/1970'..Time.now).weeks.count
# How many day in the current quarter
Period.this_quarter.days.count
# Get all the quarters overlapping a Period of time
Period.new(...).quarters

BelongsTo

StandardPeriod respond to .day, .week, .month, .quarter and .year
These methods return a StandardPeriod who include the current period
FreePeriod does not respond to these methods

BelongTo -> StandardPeriod .day .week .month .quarter .year
FreePeriod
StandardPeriod::Day X X X X
StandardPeriod::Week X X X
StandardPeriod::Month X X
StandardPeriod::Quarter X
StandardPeriod::Year

Example with BelongTo and HasMany

# Get the first day, of the last week, of the second month, of the current year
Period.this_year.months.second.weeks.last.days.first

ActiveRecord

As Period inherite from Range, you can natively use them in ActiveRecord query

# Get all book published this year
Book.where(published_at: Period.this_year)

Rails Controller

In a Controller, use the error handling to validate the date for you

class BookController < ApplicationController
  def between # match via GET and POST
    # Default value for first display
    params[:from] ||= 1.month.ago
    params[:to]   ||= Time.now

    begin
      # Retrieve books from the DB
      @books = Book.where(published: Period.new(params[:from]..params[:to]))
    rescue ArgumentError => e
      # Period will handle mis-formatted date and incoherent period
      # I18n is supported for errors messages
      flash[:alert] = e.message
    end
  end
end

I18n and to_s

I18n is supported for en and fr

Period.new('01/01/2000'...'01/02/2001').to_s
=> "From the 01 January 2000 to the 31 January 2001 included"
I18n.locale = :fr
Period.new('01/01/2000'...'01/02/2001').to_s
=> "Du 01 janvier 2000 au 31 janvier 2001 inclus"

Errors are also supported

Period.new 'Foo'..'Bar'
#=> ArgumentError (The start date is invalid)
Period.new '01/02/3030'..'Bar'
#=> ArgumentError (The end date is invalid)
Period.new '01/02/3030'..'01/01/2020'
#=> ArgumentError (The start date is greater than the end date)

See locales/en.yml to implement your language support

If you need to change the format for a single call

  period.to_s(format: 'Your Format')
  # or
  period.strftime('Your Format')

For a FreePeriod or if you need to print the start and the end of your period differently, use .i18n

  period.i18n do |from, to|
    "You have from #{from.strftime(...)} until #{to.strftime(...)} to deliver the money !"
  end

The tricky case of Weeks

Weeks are implemented following the ISO 8601
So Period.this_month.weeks.first doesn't necessarily include the first days of the month

TimeZone

Time zone are supported, you have nothing to do
If you change the global Time.zone of your app, you have nothing to do
If your Period begin in a time zone and end in another, you have nothing to do

Bug reports

If you discover any bugs, feel free to create an issue on GitHub
Please add as much information as possible to help us in fixing the potential bug
We also encourage you to help even more by forking and sending us a pull request

No issues will be addressed outside GitHub

Maintainer

License

The gem is available as open source under the terms of the MIT License.

About

Smart-Period aims to simplify Time-range manipulation

Topics

Resources

License

Stars

Watchers

Forks