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 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: []