time_slice 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/time_slice/duration.rb +62 -0
- data/lib/time_slice/version.rb +5 -0
- data/lib/time_slice.rb +126 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4851aac99fd986d96c2c2a739abdfea9cb81dbacf77ffed24ad2cd30bc3eb800
|
4
|
+
data.tar.gz: dabebf80b0466d88855f12761278754253052500c6cbd7e34a8ab4dafe2408b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a28092c1182a9caf9c9cf773d166d78729ee41da41f6e8867461e71895db7ed5227d78c15b54358f57e7eb63cf1ada2ac47fc47ce3cd06574525a23f4fa1d1da
|
7
|
+
data.tar.gz: 475bf4fd88fa5f5077e36c9f943ae67cbfe68774190e51a0e28f002a39c6a59a7bd73e728d9146aff352502e9b84fa69c30c8c542e11653c0b38d35ed4e74767
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Duration class extends ActiveSupport::Duration to provide a more
|
5
|
+
# flexible way of specifying and working with time durations.
|
6
|
+
#
|
7
|
+
# It allows creation of duration objects from string representations
|
8
|
+
# and provides methods for working with these durations.
|
9
|
+
#
|
10
|
+
# == Supported Units
|
11
|
+
#
|
12
|
+
# * s: seconds
|
13
|
+
# * m: minutes
|
14
|
+
# * h: hours
|
15
|
+
# * d: days
|
16
|
+
# * w: weeks
|
17
|
+
# * mo: months
|
18
|
+
# * y: years
|
19
|
+
#
|
20
|
+
# == Usage
|
21
|
+
#
|
22
|
+
# Duration.new("5m") # => 5 minutes
|
23
|
+
# Duration.new("2h") # => 2 hours
|
24
|
+
# Duration.new("1d") # => 1 day
|
25
|
+
#
|
26
|
+
# == Examples
|
27
|
+
#
|
28
|
+
# duration = Duration.new("3h")
|
29
|
+
# duration.unit # => :hours
|
30
|
+
# duration.period # => "3h"
|
31
|
+
# duration * 2 # => 21600 (number of seconds in 6 hours)
|
32
|
+
#
|
33
|
+
class TimeSlice
|
34
|
+
class Duration < ActiveSupport::Duration
|
35
|
+
MAP = {
|
36
|
+
"s" => :seconds,
|
37
|
+
"m" => :minutes,
|
38
|
+
"h" => :hours,
|
39
|
+
"d" => :days,
|
40
|
+
"w" => :weeks,
|
41
|
+
"mo" => :months,
|
42
|
+
"y" => :years,
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
attr_reader :unit
|
46
|
+
|
47
|
+
def initialize(value)
|
48
|
+
raise ArgumentError, "Fractional values are not allowed" if value && value.include?('.')
|
49
|
+
|
50
|
+
if value.is_a?(String) && value.match(/([0-9]+)([a-z]+)/i).try(:length) == 3 && (@unit = MAP[$2])
|
51
|
+
value = $1.to_i
|
52
|
+
super(value * PARTS_IN_SECONDS[@unit], @unit => value)
|
53
|
+
else
|
54
|
+
raise ArgumentError, "#{value} is not a valid duration"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def period
|
59
|
+
@period ||= "#{value / 1.send(@unit)}#{@unit.to_s[0]}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/time_slice.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/duration"
|
5
|
+
require "active_support/core_ext/integer/time"
|
6
|
+
require "rounding"
|
7
|
+
|
8
|
+
##
|
9
|
+
# The TimeSlice class represents a range of time divided into equal intervals.
|
10
|
+
# It provides functionality for creating, manipulating, and querying time ranges
|
11
|
+
# with a specified duration.
|
12
|
+
#
|
13
|
+
# == Usage
|
14
|
+
#
|
15
|
+
# The TimeSlice class can be initialized with a period (interval duration) and
|
16
|
+
# options for specifying the range:
|
17
|
+
#
|
18
|
+
# TimeSlice.new(period, options = {})
|
19
|
+
#
|
20
|
+
# Where:
|
21
|
+
# * +period+: A string representing the duration of each interval (e.g., "1h" for 1 hour, "30m" for 30 minutes)
|
22
|
+
# * +options+: A hash that can include +:from+ (start time), +:to+ (end time), and/or +:length+ (number of intervals)
|
23
|
+
#
|
24
|
+
# == Examples
|
25
|
+
#
|
26
|
+
# # Create a range from 12:00 to 15:00 with 1-hour intervals
|
27
|
+
# range = TimeSlice.new("1h", from: "2019-01-06 12:00", to: "2019-01-06 15:00")
|
28
|
+
#
|
29
|
+
# range.length # => 4 (12-13, 13-14, 14-15, 15-16)
|
30
|
+
# range.index("2019-01-06 14:30:00") # => 2
|
31
|
+
# range.to_a.last # => [2019-01-06 15:00:00, 2019-01-06 16:00:00]
|
32
|
+
#
|
33
|
+
# # Iterate over the intervals
|
34
|
+
# range.each do |start_time, end_time|
|
35
|
+
# puts "#{start_time} - #{end_time}"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # Get previous intervals
|
39
|
+
# prev_range = range.previous("2019-01-06 13:00", 2)
|
40
|
+
#
|
41
|
+
class TimeSlice
|
42
|
+
include Enumerable
|
43
|
+
autoload :Duration, "time_slice/duration"
|
44
|
+
|
45
|
+
attr_reader :duration, :from, :to
|
46
|
+
|
47
|
+
def initialize(period, options = {})
|
48
|
+
@duration = Duration.new(period)
|
49
|
+
parse_time_options(options)
|
50
|
+
set_time_range(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def period
|
54
|
+
duration.period
|
55
|
+
end
|
56
|
+
|
57
|
+
def length
|
58
|
+
((@to - @from) / duration.to_i).to_i + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def range
|
62
|
+
from..to
|
63
|
+
end
|
64
|
+
|
65
|
+
def each
|
66
|
+
return to_enum(:each) unless block_given?
|
67
|
+
length.times { |i| yield(self[i]) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def [](index)
|
71
|
+
return nil if index >= length
|
72
|
+
at = @from + (index * duration.to_i)
|
73
|
+
[at, at + duration.to_i]
|
74
|
+
end
|
75
|
+
|
76
|
+
def last
|
77
|
+
self[length - 1]
|
78
|
+
end
|
79
|
+
|
80
|
+
def slice(index, length)
|
81
|
+
length.times.map { |i| self[index + i] }.compact
|
82
|
+
end
|
83
|
+
|
84
|
+
def index(starts_at)
|
85
|
+
starts_at = parse_time(starts_at)
|
86
|
+
index = ((starts_at - @from) / duration.to_i).to_i
|
87
|
+
index >= 0 && index < length ? index : nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def previous(starts_at, n)
|
91
|
+
starts_at = parse_time(starts_at) - duration.to_i
|
92
|
+
self.class.new(period, to: starts_at, length: n)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def parse_time_options(options)
|
98
|
+
[:from, :to].each do |key|
|
99
|
+
options[key] = parse_time(options[key]) if options[key]
|
100
|
+
end
|
101
|
+
options[:length] = options[:length].to_i if options[:length]
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_time(time)
|
105
|
+
time.is_a?(String) ? Time.zone.parse(time) : time
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_time_range(options)
|
109
|
+
if options[:from] && options[:to]
|
110
|
+
@from = options[:from].floor_to(duration)
|
111
|
+
@to = options[:to].floor_to(duration)
|
112
|
+
elsif options[:from] && options[:length]
|
113
|
+
@from = options[:from].floor_to(duration)
|
114
|
+
@to = @from + (options[:length] * duration.to_i) - duration.to_i
|
115
|
+
elsif options[:to] && options[:length]
|
116
|
+
@to = options[:to].floor_to(duration)
|
117
|
+
@from = @to - (options[:length] * duration.to_i) + duration.to_i
|
118
|
+
elsif options.keys == [:length]
|
119
|
+
@to = Time.current.floor_to(duration)
|
120
|
+
@from = @to - (options[:length] * duration.to_i) + duration.to_i
|
121
|
+
elsif options.keys == [:from]
|
122
|
+
@from = options[:from].floor_to(duration)
|
123
|
+
@to = Time.current.floor_to(duration)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: time_slice
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kien La
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rounding
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: appraisal
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.5.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.5.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.24.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.24.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-focus
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mocha
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.4.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.4.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 13.2.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 13.2.1
|
111
|
+
description: Time slice
|
112
|
+
email:
|
113
|
+
- la.kien@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- lib/time_slice.rb
|
119
|
+
- lib/time_slice/duration.rb
|
120
|
+
- lib/time_slice/version.rb
|
121
|
+
homepage: https://github.com/kla/time_slice
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '3.0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubygems_version: 3.5.14
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: A Ruby library for slicing time.
|
144
|
+
test_files: []
|