Logo

0x3d.Site

is designed for aggregating information.
Welcome
check repository here

biz

Gem Version repo-checks Code Climate Test Coverage

Time calculations using business hours.

Features

  • Support for:
    • Multiple intervals per day.
    • Multiple schedule configurations.
    • Intervals spanning the entire day.
    • Holidays.
    • Breaks (time-segment holidays).
    • Shifts (date-based intervals).
  • Second-level calculation precision.
  • Seamless Daylight Saving Time handling.
  • Schedule intersection.
  • Thread safety.

Anti-Features

  • No dependency on ActiveSupport.
  • No monkey patching by default.

Installation

Add this line to your application's Gemfile:

gem 'biz'

And then execute:

$ bundle

Or install it yourself as:

$ gem install biz

Configuration

Biz.configure do |config|
  config.hours = {
    mon: {'09:00' => '17:00'},
    tue: {'00:00' => '24:00'},
    wed: {'09:00' => '17:00'},
    thu: {'09:00' => '12:00', '13:00' => '17:00'},
    sat: {'10:00' => '14:00'}
  }

  config.shifts = {
    Date.new(2006, 1, 1) => {'09:00' => '12:00'},
    Date.new(2006, 1, 7) => {'08:00' => '10:00', '12:00' => '14:00'}
  }

  config.breaks = {
    Date.new(2006, 1, 2) => {'10:00' => '11:30'},
    Date.new(2006, 1, 3) => {'14:15' => '14:30', '15:40' => '15:50'}
  }

  config.holidays = [Date.new(2016, 1, 1), Date.new(2016, 12, 25)]

  config.time_zone = 'America/Los_Angeles'
end

Configured timestamps must be in either HH:MM or HH:MM:SS format.

Shifts act as exceptions to the hours configured for a particular date; that is, if a date is configured with both hours-based intervals and shifts, the shifts are in force and the intervals are disregarded.

Periods occurring on holidays are disregarded. Similarly, any segment of a period that overlaps with a break is treated as inactive.

If global configuration isn't your thing, configure an instance instead:

Biz::Schedule.new do |config|
  # ...
end

Note that times must be specified in 24-hour clock format and time zones must be IANA identifiers.

If you're operating in a threaded environment and want to use the same configuration across threads, save the configured schedule as a global variable:

$biz = Biz::Schedule.new

Usage

# Find the time an amount of business time *before* a specified starting time
Biz.time(30, :minutes).before(Time.utc(2015, 1, 1, 11, 45))

# Find the time an amount of business time *after* a specified starting time
Biz.time(2, :hours).after(Time.utc(2015, 12, 25, 9, 30))

# Calculations can be performed in seconds, minutes, hours, or days
Biz.time(1, :day).after(Time.utc(2015, 1, 8, 10))

# Find the previous business time
Biz.time(0, :hours).before(Time.utc(2016, 1, 8, 6))

# Find the next business time
Biz.time(0, :hours).after(Time.utc(2016, 1, 8, 20))

# Find the amount of business time between two times
Biz.within(Time.utc(2015, 3, 7), Time.utc(2015, 3, 14)).in_seconds

# Find the start of the business day
Biz.periods.on(Date.today).first.start_time

# Find the end of the business day
Biz.periods.on(Date.today).to_a.last.end_time

# Determine if a time is in business hours
Biz.in_hours?(Time.utc(2015, 1, 10, 9))

# Determine if a time is during a break
Biz.on_break?(Time.utc(2016, 6, 3))

# Determine if a time is during a holiday
Biz.on_holiday?(Time.utc(2014, 1, 1))

The same methods can be called on a configured instance:

schedule = Biz::Schedule.new

schedule.in_hours?(Time.utc(2015, 1, 1, 10))

All returned times are in UTC.

If a schedule will be configured with a large number of holidays and performance is a particular concern, it's recommended that holidays are filtered down to those relevant to the calculation(s) at hand before configuration to improve performance.

By dropping down a level, you can get access to the underlying time segments, which you can use to do your own custom calculations or just get a better idea of what's happening under the hood:

Biz.periods.after(Time.utc(2015, 1, 10, 10)).timeline
  .until(Time.utc(2015, 1, 17, 10)).to_a

#=> [#<Biz::TimeSegment start_time=2015-01-10 18:00:00 UTC end_time=2015-01-10 22:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-01-12 17:00:00 UTC end_time=2015-01-13 01:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-01-13 08:00:00 UTC end_time=2015-01-14 08:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-01-14 17:00:00 UTC end_time=2015-01-15 01:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-01-15 17:00:00 UTC end_time=2015-01-15 20:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-01-15 21:00:00 UTC end_time=2015-01-16 01:00:00 UTC>]

Biz.periods
  .before(Time.utc(2015, 5, 5, 12, 34, 57)).timeline
  .for(Biz::Duration.minutes(3_598)).to_a

#=> [#<Biz::TimeSegment start_time=2015-05-05 07:00:00 UTC end_time=2015-05-05 12:34:57 UTC>,
#  #<Biz::TimeSegment start_time=2015-05-04 16:00:00 UTC end_time=2015-05-05 00:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-05-02 17:00:00 UTC end_time=2015-05-02 21:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-04-30 20:00:00 UTC end_time=2015-05-01 00:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-04-30 16:00:00 UTC end_time=2015-04-30 19:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-04-29 16:00:00 UTC end_time=2015-04-30 00:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-04-28 07:00:00 UTC end_time=2015-04-29 07:00:00 UTC>,
#  #<Biz::TimeSegment start_time=2015-04-27 20:36:57 UTC end_time=2015-04-28 00:00:00 UTC>]

Day calculation semantics

Unlike seconds, minutes, or hours, a "day" is an ambiguous concept, particularly in relation to the vast number of potential schedule configurations. Because of that, day calculations are implemented with the principle of making the logic as straightforward as possible while knowing not all use cases will be satisfied out of the box.

Here's the logic that's followed:

Find the next day that contains business hours. Starting from the same minute on that day as the specified time, look forward (or back) to find the next moment in time that is in business hours.

Schedule intersection

An intersection of two schedules can be found using &:

schedule_1 = Biz::Schedule.new do |config|
  config.hours = {
    mon: {'09:00' => '17:00'},
    tue: {'10:00' => '16:00'},
    wed: {'09:00' => '17:00'},
    thu: {'10:00' => '16:00'},
    fri: {'09:00' => '17:00'},
    sat: {'11:00' => '14:30'}
  }

  config.shifts = {
    Date.new(2016, 7, 1) => {'10:00' => '13:00', '15:00' => '16:00'},
    Date.new(2016, 7, 2) => {'14:00' => '17:00'}
  }

  config.breaks = {
    Date.new(2016, 6, 2) => {'09:00' => '10:30', '16:00' => '16:30'},
    Date.new(2016, 6, 3) => {'12:15' => '12:45'}
  }

  config.holidays = [Date.new(2016, 1, 1), Date.new(2016, 12, 25)]

  config.time_zone = 'Etc/UTC'
end

schedule_2 = Biz::Schedule.new do |config|
  config.hours = {
    sun: {'10:00' => '12:00'},
    mon: {'08:00' => '10:00'},
    tue: {'11:00' => '15:00'},
    wed: {'16:00' => '18:00'},
    thu: {'11:00' => '12:00', '13:00' => '14:00'}
  }

  config.shifts = {
    Date.new(2016, 7, 1) => {'15:30' => '16:30'},
    Date.new(2016, 7, 5) => {'14:00' => '18:00'}
  }

  config.breaks = {
    Date.new(2016, 6, 3) => {'13:30' => '14:00'},
    Date.new(2016, 6, 4) => {'11:00' => '12:00'}
  }

  config.holidays = [
    Date.new(2016, 1, 1),
    Date.new(2016, 7, 4),
    Date.new(2016, 11, 24)
  ]

  config.time_zone = 'America/Los_Angeles'
end

schedule_1 & schedule_2

The resulting schedule will be a combination of the two schedules: an intersection of the intervals, a union of the breaks and holidays, and the time zone of the first schedule. Any configured shifts will be disregarded.

For the above example, the resulting schedule would be equivalent to one with the following configuration:

Biz::Schedule.new do |config|
  config.hours = {
    mon: {'09:00' => '10:00'},
    tue: {'11:00' => '15:00'},
    wed: {'16:00' => '17:00'},
    thu: {'11:00' => '12:00', '13:00' => '14:00'}
  }

  config.shifts = {
    Date.new(2016, 7, 1) => {'15:30' => '16:00'},
    Date.new(2016, 7, 5) => {'14:00' => '16:00'}
  }

  config.breaks = {
    Date.new(2016, 6, 2) => {'09:00' => '10:30', '16:00' => '16:30'},
    Date.new(2016, 6, 3) => {'12:15' => '12:45', '13:30' => '14:00'},
    Date.new(2016, 6, 4) => {'11:00' => '12:00'}
  }

  config.holidays = [
    Date.new(2016, 1, 1),
    Date.new(2016, 7, 4),
    Date.new(2016, 11, 24),
    Date.new(2016, 12, 25)
  ]

  config.time_zone = 'Etc/UTC'
end

Core extensions

Optional extensions to core classes (Date, Integer, and Time) are available for additional expressiveness:

require 'biz/core_ext'

75.business_seconds.after(Time.utc(2015, 3, 5, 12, 30))

30.business_minutes.before(Time.utc(2015, 1, 1, 11, 45))

5.business_hours.after(Time.utc(2015, 4, 7, 8, 20))

3.business_days.before(Time.utc(2015, 5, 9, 4, 12))

Time.utc(2015, 8, 20, 9, 30).business_hours?

Time.utc(2016, 6, 3, 12).on_break?

Time.utc(2014, 1, 1, 12).on_holiday?

Date.new(2015, 12, 10).business_day?

Contributing

Pull requests are welcome, but consider asking for a feature or bug fix first through the issue tracker. When contributing code, please squash sloppy commits aggressively and follow Tim Pope's guidelines for commit messages.

There are a number of ways to get started after cloning the repository.

To set up your environment:

script/bootstrap

To run the spec suite:

script/spec

To open a console with the gem and sample schedule loaded:

script/console

Alternatives

Copyright and license

Copyright 2015-19 Zendesk

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this gem except in compliance with the License.

You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Ruby
Ruby
Ruby is a dynamic, object-oriented programming language known for its simplicity and productivity. It powers the Ruby on Rails framework, making it popular for web application development. Ruby emphasizes clean and readable code.
GitHub - TrestleAdmin/trestle: A modern, responsive admin framework for Ruby on Rails
GitHub - TrestleAdmin/trestle: A modern, responsive admin framework for Ruby on Rails
GitHub - ElMassimo/vite_ruby: ⚡️ Vite.js in Ruby, bringing joy to your JavaScript experience
GitHub - ElMassimo/vite_ruby: ⚡️ Vite.js in Ruby, bringing joy to your JavaScript experience
Rails Assets
Rails Assets
Avo
Avo
GitHub - mina-deploy/mina: Blazing fast deployer and server automation tool
GitHub - mina-deploy/mina: Blazing fast deployer and server automation tool
GitHub - rails/actionpack-action_caching: Action caching for Action Pack (removed from core in Rails 4.0)
GitHub - rails/actionpack-action_caching: Action caching for Action Pack (removed from core in Rails 4.0)
Awesome Ruby | LibHunt
Awesome Ruby | LibHunt
GitHub - palkan/action_policy: Authorization framework for Ruby/Rails applications
GitHub - palkan/action_policy: Authorization framework for Ruby/Rails applications
GitHub - pickhardt/betty: Friendly English-like interface for your command line. Don't remember a command? Ask Betty.
GitHub - pickhardt/betty: Friendly English-like interface for your command line. Don't remember a command? Ask Betty.
GitHub - metaskills/less-rails: :-1: Less.js For Rails
GitHub - metaskills/less-rails: :-1: Less.js For Rails
GitHub - codeplant/simple-navigation: A ruby gem for creating navigations (with multiple levels) for your Rails, Sinatra or Padrino applications.  Render your navigation as html list, link list or breadcrumbs.
GitHub - codeplant/simple-navigation: A ruby gem for creating navigations (with multiple levels) for your Rails, Sinatra or Padrino applications. Render your navigation as html list, link list or breadcrumbs.
GitHub - ruby/rake: A make-like build utility for Ruby.
GitHub - ruby/rake: A make-like build utility for Ruby.
GitHub - lazaronixon/authentication-zero: An authentication system generator for Rails applications.
GitHub - lazaronixon/authentication-zero: An authentication system generator for Rails applications.
GitHub - sorentwo/readthis: :newspaper: Pooled active support compliant caching with redis
GitHub - sorentwo/readthis: :newspaper: Pooled active support compliant caching with redis
GitHub - crepe/crepe: 🥞 The thin API stack.
GitHub - crepe/crepe: 🥞 The thin API stack.
GitHub - geemus/formatador: STDOUT text formatting
GitHub - geemus/formatador: STDOUT text formatting
GitHub - CanCanCommunity/cancancan: The authorization Gem for Ruby on Rails.
GitHub - CanCanCommunity/cancancan: The authorization Gem for Ruby on Rails.
GitHub - dry-rb/dry-cli: General purpose Command Line Interface (CLI) framework for Ruby
GitHub - dry-rb/dry-cli: General purpose Command Line Interface (CLI) framework for Ruby
The Ruby Toolbox - Know your options!
The Ruby Toolbox - Know your options!
GitHub - rharriso/bower-rails: Bundler-like DSL + rake tasks for Bower on Rails
GitHub - rharriso/bower-rails: Bundler-like DSL + rake tasks for Bower on Rails
GitHub - joshfrench/rakismet: Easy Akismet and TypePad AntiSpam integration for Rails
GitHub - joshfrench/rakismet: Easy Akismet and TypePad AntiSpam integration for Rails
GitHub - torba-rb/torba: Bundler for Sprockets
GitHub - torba-rb/torba: Bundler for Sprockets
GitHub - artsy/garner: A set of Rack middleware and cache helpers that implement various caching strategies.
GitHub - artsy/garner: A set of Rack middleware and cache helpers that implement various caching strategies.
GitHub - matthutchinson/acts_as_textcaptcha: Text-based logic question captcha's for Rails 🚫🤖
GitHub - matthutchinson/acts_as_textcaptcha: Text-based logic question captcha's for Rails 🚫🤖
GitHub - ai/autoprefixer-rails: Autoprefixer for Ruby and Ruby on Rails
GitHub - ai/autoprefixer-rails: Autoprefixer for Ruby and Ruby on Rails
GitHub - apneadiving/waterfall: A slice of functional programming to chain ruby services and blocks, thus providing a new approach to flow control. Make them flow!
GitHub - apneadiving/waterfall: A slice of functional programming to chain ruby services and blocks, thus providing a new approach to flow control. Make them flow!
GitHub - chaps-io/access-granted: Multi-role and whitelist based authorization gem for Rails (and not only Rails!)
GitHub - chaps-io/access-granted: Multi-role and whitelist based authorization gem for Rails (and not only Rails!)
MidiSmtpServer - brief profile
MidiSmtpServer - brief profile
GitHub - mdub/clamp: a Ruby command-line application framework
GitHub - mdub/clamp: a Ruby command-line application framework
GitHub - thoughtbot/administrate: A Rails engine that helps you put together a super-flexible admin dashboard.
GitHub - thoughtbot/administrate: A Rails engine that helps you put together a super-flexible admin dashboard.
Ruby
More on Ruby

Programming Tips & Tricks

Code smarter, not harder—insider tips and tricks for developers.

Error Solutions

Turn frustration into progress—fix errors faster than ever.

Shortcuts

The art of speed—shortcuts to supercharge your workflow.
  1. Collections 😎
  2. Frequently Asked Question's 🤯

Tools

available to use.

Made with ❤️

to provide resources in various ares.