![Create React App Officially Deprecated Amid React 19 Compatibility Issues](https://cdn.sanity.io/images/cgdhsj6q/production/04fa08cf844d798abc0e1a6391c129363cc7e2ab-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Create React App Officially Deprecated Amid React 19 Compatibility Issues
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
TimeMath2 is a small, no-dependencies library attempting to make time arithmetics easier. It provides you with simple, easy-to-remember API, without any monkey-patching of core Ruby classes, so it can be used alongside Rails or without it, for any purpose.
TimeMath
is the best name I know for the task library does, yet
it is already taken. So, with no
other thoughts I came with the ugly solution.
(BTW, the previous version had some dumb "funny" name for gem and all helper classes, and nobody liked it.)
You frequently need to calculate things like "exact midnight of the next day", but you don't want to monkey-patch all of your integers, tug in 5K LOC of ActiveSupport and you like to have things clean and readable.
Install it like always:
$ gem install time_math2
or add to your Gemfile
gem 'time_math2', require: 'time_math'
and bundle install
it.
First, you take time unit you want:
TimeMath[:day] # => #<TimeMath::Units::Day>
# or
TimeMath.day # => #<TimeMath::Units::Day>
# List of units supported:
TimeMath.units
# => [:sec, :min, :hour, :day, :week, :month, :year]
Then you use this unit for any math you want:
TimeMath.day.floor(Time.now) # => 2016-05-28 00:00:00 +0300
TimeMath.day.ceil(Time.now) # => 2016-05-29 00:00:00 +0300
TimeMath.day.advance(Time.now, +10) # => 2016-06-07 14:06:57 +0300
# ...and so on
<unit>.floor(tm)
-- rounds down to nearest <unit>
;<unit>.ceil(tm)
-- rounds up to nearest <unit>
;<unit>.round(tm)
-- rounds to nearest <unit>
(up or down);<unit>.round?(tm)
-- checks if tm
is already round to <unit>
;<unit>.prev(tm)
-- like floor
, but always decreases:
2015-06-27 13:30
would be converted to 2015-06-27 00:00
by both
floor
and prev
, but2015-06-27 00:00
would be left intact on floor
, but would be
decreased to 2015-06-26 00:00
by prev
;<unit>.next(tm)
-- like ceil
, but always increases;<unit>.advance(tm, amount)
-- increases tm by integer amount of <unit>
s;<unit>.decrease(tm, amount)
-- decreases tm by integer amount of <unit>
s;<unit>.range(tm, amount)
-- creates range of tm ... tm + amount <units>
;<unit>.range_back(tm, amount)
-- creates range of tm - amount <units> ... tm
.Things to note:
floor
, ceil
and company) support optional second
argument—amount of units to round to, like "each 3 hours": hour.floor(tm, 3)
;hour.advance(tm, 1/2r)
and this would
work as you may expect. Non-integer arguments are only supported for
units less than week (because "half of month" have no exact mathematical
sense).See also Units::Base.
For example, you want "10 am at next monday". By using atomic time unit operations, you'll need the code like:
TimeMath.hour.advance(TimeMath.week.ceil(Time.now), 10)
...which is not really readable, to say the least. So, TimeMath
provides
one top-level method allowing to chain any operations you want:
TimeMath(Time.now).ceil(:week).advance(:hour, 10).call
Much more readable, huh?
The best thing about it, that you can prepare "operations list" value object, and then use it (or pass to methods, or serialize to YAML and deserialize in some Sidekiq task and so on):
op = TimeMath().ceil(:week).advance(:hour, 10)
# => #<TimeMath::Op ceil(:week).advance(:hour, 10)>
op.call(Time.now)
# => 2016-06-27 10:00:00 +0300
# It also can be called on several arguments/array of arguments:
op.call(tm1, tm2, tm3)
op.call(array_of_timestamps)
# ...or even used as a block-ish object:
array_of_timestamps.map(&op)
See also TimeMath() and underlying TimeMath::Op class docs.
Time sequence allows you to generate an array of time values between some points:
to = Time.now
# => 2016-05-28 17:47:30 +0300
from = TimeMath.day.floor(to)
# => 2016-05-28 00:00:00 +0300
seq = TimeMath.hour.sequence(from...to)
# => #<TimeMath::Sequence(:hour, 2016-05-28 00:00:00 +0300...2016-05-28 17:47:30 +0300)>
p(*seq)
# 2016-05-28 00:00:00 +0300
# 2016-05-28 01:00:00 +0300
# 2016-05-28 02:00:00 +0300
# 2016-05-28 03:00:00 +0300
# 2016-05-28 04:00:00 +0300
# 2016-05-28 05:00:00 +0300
# 2016-05-28 06:00:00 +0300
# 2016-05-28 07:00:00 +0300
# ...and so on
Note that sequence also play well with operation chain described above, so you can
seq = TimeMath.day.sequence(Time.parse('2016-05-01')...Time.parse('2016-05-04')).advance(:hour, 10).decrease(:min, 5)
# => #<TimeMath::Sequence(:day, 2016-05-01 00:00:00 +0300...2016-05-04 00:00:00 +0300).advance(:hour, 10).decrease(:min, 5)>
seq.to_a
# => [2016-05-01 09:55:00 +0300, 2016-05-02 09:55:00 +0300, 2016-05-03 09:55:00 +0300]
See also Sequence YARD docs.
Simple measure: just "how many <unit>
s from date A to date B":
TimeMath.week.measure(Time.parse('2016-05-01'), Time.parse('2016-06-01'))
# => 4
Measure with remaineder: returns number of <unit>
s between dates and
the date when this number would be exact:
TimeMath.week.measure_rem(Time.parse('2016-05-01'), Time.parse('2016-06-01'))
# => [4, 2016-05-29 00:00:00 +0300]
(on May 29 there would be exactly 4 weeks since May 1).
Multi-unit measuring:
# My real birthday, in fact!
birthday = Time.parse('1983-02-14 13:30')
# My full age
TimeMath.measure(birthday, Time.now)
# => {:years=>33, :months=>3, :weeks=>2, :days=>0, :hours=>1, :minutes=>25, :seconds=>52}
# NB: you can use this output with String#format or String%:
puts "%{years}y %{months}m %{weeks}w %{days}d %{hours}h %{minutes}m %{seconds}s" %
TimeMath.measure(birthday, Time.now)
# 33y 3m 2w 0d 1h 26m 15s
# Option: measure without weeks
TimeMath.measure(birthday, Time.now, weeks: false)
# => {:years=>33, :months=>3, :days=>14, :hours=>1, :minutes=>26, :seconds=>31}
# My full age in days, hours, minutes
TimeMath.measure(birthday, Time.now, upto: :day)
# => {:days=>12157, :hours=>2, :minutes=>26, :seconds=>55}
Resampling is useful for situations when you have some timestamped data (with variable holes between values), and wantto make it regular, e.g. for charts drawing.
The most simple (and not very useful) resampling just turns array of irregular timestamps into regular one:
dates = %w[2016-06-01 2016-06-03 2016-06-06].map(&Date.method(:parse))
# => [#<Date: 2016-06-01>, #<Date: 2016-06-03>, #<Date: 2016-06-06>]
TimeMath.day.resample(dates)
# => [#<Date: 2016-06-01>, #<Date: 2016-06-02>, #<Date: 2016-06-03>, #<Date: 2016-06-04>, #<Date: 2016-06-05>, #<Date: 2016-06-06>]
TimeMath.week.resample(dates)
# => [#<Date: 2016-05-30>, #<Date: 2016-06-06>]
TimeMath.month.resample(dates)
# => [#<Date: 2016-06-01>]
Much more useful is hash resampling: when you have a hash of {timestamp => value}
and...
data = {Date.parse('2016-06-01') => 18, Date.parse('2016-06-03') => 8, Date.parse('2016-06-06') => -4}
# => {#<Date: 2016-06-01>=>18, #<Date: 2016-06-03>=>8, #<Date: 2016-06-06>=>-4}
TimeMath.day.resample(data)
# => {#<Date: 2016-06-01>=>[18], #<Date: 2016-06-02>=>[], #<Date: 2016-06-03>=>[8], #<Date: 2016-06-04>=>[], #<Date: 2016-06-05>=>[], #<Date: 2016-06-06>=>[-4]}
TimeMath.week.resample(data)
# => {#<Date: 2016-05-30>=>[18, 8], #<Date: 2016-06-06>=>[-4]}
TimeMath.month.resample(data)
# => {#<Date: 2016-06-01>=>[18, 8, -4]}
For values grouping strategy, resample
accepts symbol and block arguments:
TimeMath.week.resample(data, :first)
# => {#<Date: 2016-05-30>=>18, #<Date: 2016-06-06>=>-4}
TimeMath.week.resample(data) { |vals| vals.inject(:+) }
=> {#<Date: 2016-05-30>=>26, #<Date: 2016-06-06>=>-4}
The functionality currently considered experimental, please notify me about your ideas and use cases via GitHub issues!
TimeMath tries its best to preserve timezones of original values. Currently, it means:
Time
instances, symbolic timezone is preserved; when jumping over
DST border, UTC offset will change and everything remains as expected;DateTime
Ruby not provides symbolic timezone, only numeric offset;
it is preserved by TimeMath (but be careful about jumping around DST,
offset would not change).TimeMath is known to work on MRI Ruby >= 2.0 and JRuby >= 9.0.0.0.
On Rubinius, some of tests fail and I haven't time to investigate it. If somebody still uses Rubinius and wants TimeMath to be working properly on it, please let me know.
There's pretty small and useful AS::Duration by Janko Marohnić, which is time durations, extracted from ActiveSupport, but without any ActiveSupport bloat.
MIT.
FAQs
Unknown package
We found that time_math2 demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.