time_slice 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|