time_slice 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TimeSlice
4
+ VERSION = "0.1"
5
+ 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: []