time_scales 0.1.0
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 +15 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/time_scales.rb +28 -0
- data/lib/time_scales/frame.rb +50 -0
- data/lib/time_scales/frame/base.rb +122 -0
- data/lib/time_scales/frame/null_frame.rb +9 -0
- data/lib/time_scales/frame/part_components.rb +258 -0
- data/lib/time_scales/frame/part_def.rb +45 -0
- data/lib/time_scales/frame/part_defs.rb +53 -0
- data/lib/time_scales/frame/precisions.rb +83 -0
- data/lib/time_scales/frame/scheme_relative_frame.rb +21 -0
- data/lib/time_scales/frame/type_builder.rb +73 -0
- data/lib/time_scales/parts.rb +242 -0
- data/lib/time_scales/time_struct.rb +68 -0
- data/lib/time_scales/units.rb +101 -0
- data/lib/time_scales/version.rb +3 -0
- data/time_scales.gemspec +34 -0
- metadata +97 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module TimeScales
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class SchemeRelativeFrame < Frame::Base
|
5
|
+
def to_range
|
6
|
+
@to_range ||= ( begin_time...succ_begin_time )
|
7
|
+
end
|
8
|
+
|
9
|
+
def begin_time
|
10
|
+
@begin_time ||= Time.new( *begin_time_struct.to_a )
|
11
|
+
end
|
12
|
+
|
13
|
+
alias to_time begin_time
|
14
|
+
|
15
|
+
def succ_begin_time
|
16
|
+
raise NotImplementedError, "Subclass responsibility"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module TimeScales
|
2
|
+
|
3
|
+
module Frame
|
4
|
+
|
5
|
+
class TypeBuilder
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def add_type_cache
|
12
|
+
# Closure variable accessible via instance method
|
13
|
+
# from any instance.
|
14
|
+
_type_cache = {}
|
15
|
+
|
16
|
+
define_method(:type_cache){
|
17
|
+
_type_cache
|
18
|
+
}
|
19
|
+
|
20
|
+
private :type_cache
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
add_type_cache
|
26
|
+
|
27
|
+
attr_reader :parts
|
28
|
+
|
29
|
+
def initialize(parts)
|
30
|
+
@parts = parts
|
31
|
+
end
|
32
|
+
|
33
|
+
def call
|
34
|
+
type_cache.fetch( parts ) {
|
35
|
+
type_cache[parts] = build_type
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def build_type
|
42
|
+
_parts = parts ; _is_scheme_scoped = scheme_scoped?
|
43
|
+
klass = Class.new type_base_class do
|
44
|
+
_parts.each do |part| ; include part.component_mixin ; end
|
45
|
+
include _parts.last.scheme_scoped_precision_mixin if _is_scheme_scoped
|
46
|
+
end
|
47
|
+
Frame.const_set type_const_name, klass
|
48
|
+
klass
|
49
|
+
end
|
50
|
+
|
51
|
+
def type_base_class
|
52
|
+
if scheme_scoped?
|
53
|
+
Frame::SchemeRelativeFrame
|
54
|
+
else
|
55
|
+
Frame::Base
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def scheme_scoped?
|
60
|
+
parts.first.scope == Units::Scheme
|
61
|
+
end
|
62
|
+
|
63
|
+
def type_const_name
|
64
|
+
const_name = "#{parts.first.name}"
|
65
|
+
parts[1..-1].each do |part| ; const_name << "_#{part.subdivision_name}" ; end
|
66
|
+
const_name << 'Only' if parts.length == 1
|
67
|
+
const_name << '__Auto'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
module TimeScales
|
2
|
+
|
3
|
+
module Parts
|
4
|
+
|
5
|
+
def self.all
|
6
|
+
@all_parts ||= [
|
7
|
+
YearOfScheme,
|
8
|
+
QuarterOfYear,
|
9
|
+
MonthOfYear,
|
10
|
+
MonthOfQuarter,
|
11
|
+
DayOfMonth,
|
12
|
+
DayOfYear,
|
13
|
+
DayOfQuarter,
|
14
|
+
HourOfDay,
|
15
|
+
MinuteOfHour,
|
16
|
+
].freeze
|
17
|
+
end
|
18
|
+
|
19
|
+
class AbstractPart
|
20
|
+
def to_time_scales_part
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def ===(other)
|
25
|
+
self == other || symbol == other
|
26
|
+
end
|
27
|
+
|
28
|
+
def symbol
|
29
|
+
raise NotImplementedError, "Subclass responsibility"
|
30
|
+
end
|
31
|
+
|
32
|
+
def subdivision
|
33
|
+
raise NotImplementedError, "Subclass responsibility"
|
34
|
+
end
|
35
|
+
|
36
|
+
def scope
|
37
|
+
raise NotImplementedError, "Subclass responsibility"
|
38
|
+
end
|
39
|
+
|
40
|
+
def scale
|
41
|
+
subdivision.scale
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_for_unit?
|
45
|
+
raise NotImplementedError, "Subclass responsibility"
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@to_s ||= self.class.name.sub(/Class$/, '').freeze
|
50
|
+
end
|
51
|
+
|
52
|
+
def component_mixin
|
53
|
+
raise NotImplementedError, "Subclass responsibility"
|
54
|
+
end
|
55
|
+
|
56
|
+
def scheme_scoped_precision_mixin
|
57
|
+
raise NotImplementedError, "Subclass responsibility"
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
@name ||= /::([^:]+)Class$/.match( self.class.name )[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
def subdivision_name
|
65
|
+
subdivision.name
|
66
|
+
end
|
67
|
+
|
68
|
+
def &(time)
|
69
|
+
raise NotImplementedError, "Subclass responsibility"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class YearOfSchemeClass < AbstractPart
|
74
|
+
include Singleton
|
75
|
+
|
76
|
+
def symbol ; :year_of_scheme ; end
|
77
|
+
def subdivision ; Units::Year ; end
|
78
|
+
def scope ; Units::Scheme ; end
|
79
|
+
def default_for_unit? ; true ; end
|
80
|
+
def component_mixin ; Frame::PartComponents::HasYearOfScheme ; end
|
81
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasYearOfSchemePrecision ; end
|
82
|
+
|
83
|
+
def &(time)
|
84
|
+
time.year
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
YearOfScheme = YearOfSchemeClass.instance
|
89
|
+
|
90
|
+
|
91
|
+
class QuarterOfYearClass < AbstractPart
|
92
|
+
include Singleton
|
93
|
+
|
94
|
+
def symbol ; :quarter_of_year ; end
|
95
|
+
def subdivision ; Units::Quarter ; end
|
96
|
+
def scope ; Units::Year ; end
|
97
|
+
def default_for_unit? ; true ; end
|
98
|
+
def component_mixin ; Frame::PartComponents::HasQuarterOfYear ; end
|
99
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasQuarterOfSchemePrecision ; end
|
100
|
+
|
101
|
+
def &(time)
|
102
|
+
month_offs = time.month - 1
|
103
|
+
( month_offs / 3 ) + 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
QuarterOfYear = QuarterOfYearClass.instance
|
108
|
+
|
109
|
+
|
110
|
+
class MonthOfYearClass < AbstractPart
|
111
|
+
include Singleton
|
112
|
+
|
113
|
+
def symbol ; :month_of_year ; end
|
114
|
+
def subdivision ; Units::Month ; end
|
115
|
+
def scope ; Units::Year ; end
|
116
|
+
def default_for_unit? ; true ; end
|
117
|
+
def component_mixin ; Frame::PartComponents::HasMonthOfYear ; end
|
118
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasMonthOfSchemePrecision ; end
|
119
|
+
|
120
|
+
def &(time)
|
121
|
+
time.month
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
MonthOfYear = MonthOfYearClass.instance
|
126
|
+
|
127
|
+
|
128
|
+
class MonthOfQuarterClass < AbstractPart
|
129
|
+
include Singleton
|
130
|
+
|
131
|
+
def symbol ; :month_of_quarter ; end
|
132
|
+
def subdivision ; Units::Month ; end
|
133
|
+
def scope ; Units::Quarter ; end
|
134
|
+
def default_for_unit? ; false ; end
|
135
|
+
def component_mixin ; Frame::PartComponents::HasMonthOfQuarter ; end
|
136
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasMonthOfSchemePrecision ; end
|
137
|
+
|
138
|
+
def &(time)
|
139
|
+
month_offs = time.month - 1
|
140
|
+
( month_offs % 3 ) + 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
MonthOfQuarter = MonthOfQuarterClass.instance
|
145
|
+
|
146
|
+
|
147
|
+
class DayOfMonthClass < AbstractPart
|
148
|
+
include Singleton
|
149
|
+
|
150
|
+
def symbol ; :day_of_month ; end
|
151
|
+
def subdivision ; Units::Day ; end
|
152
|
+
def scope ; Units::Month ; end
|
153
|
+
def default_for_unit? ; true ; end
|
154
|
+
def component_mixin ; Frame::PartComponents::HasDayOfMonth ; end
|
155
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasDayOfSchemePrecision ; end
|
156
|
+
|
157
|
+
def &(time)
|
158
|
+
time.day
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
DayOfMonth = DayOfMonthClass.instance
|
163
|
+
|
164
|
+
|
165
|
+
class DayOfYearClass < AbstractPart
|
166
|
+
include Singleton
|
167
|
+
|
168
|
+
def symbol ; :day_of_year ; end
|
169
|
+
def subdivision ; Units::Day ; end
|
170
|
+
def scope ; Units::Year ; end
|
171
|
+
def default_for_unit? ; false ; end
|
172
|
+
def component_mixin ; Frame::PartComponents::HasDayOfYear ; end
|
173
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasDayOfSchemePrecision ; end
|
174
|
+
|
175
|
+
def &(time)
|
176
|
+
time.yday
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
DayOfYear = DayOfYearClass.instance
|
181
|
+
|
182
|
+
|
183
|
+
class DayOfQuarterClass < AbstractPart
|
184
|
+
include Singleton
|
185
|
+
|
186
|
+
def symbol ; :day_of_quarter ; end
|
187
|
+
def subdivision ; Units::Day ; end
|
188
|
+
def scope ; Units::Quarter ; end
|
189
|
+
def default_for_unit? ; false ; end
|
190
|
+
def component_mixin ; Frame::PartComponents::HasDayOfQuarter ; end
|
191
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasDayOfSchemePrecision ; end
|
192
|
+
|
193
|
+
def &(time)
|
194
|
+
month_offs = time.month - 1
|
195
|
+
qtr_offs = month_offs / 3
|
196
|
+
qtr_start_mo = ( qtr_offs * 3 ) + 1
|
197
|
+
qtr_start_time = Time.new( time.year, qtr_start_mo, 1)
|
198
|
+
time.yday - qtr_start_time.yday + 1
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
DayOfQuarter = DayOfQuarterClass.instance
|
203
|
+
|
204
|
+
|
205
|
+
class HourOfDayClass < AbstractPart
|
206
|
+
include Singleton
|
207
|
+
|
208
|
+
def symbol ; :hour_of_day ; end
|
209
|
+
def subdivision ; Units::Hour ; end
|
210
|
+
def scope ; Units::Day ; end
|
211
|
+
def default_for_unit? ; false ; end
|
212
|
+
def component_mixin ; Frame::PartComponents::HasHourOfDay ; end
|
213
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasHourOfSchemePrecision ; end
|
214
|
+
|
215
|
+
def &(time)
|
216
|
+
time.hour
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
HourOfDay = HourOfDayClass.instance
|
221
|
+
|
222
|
+
|
223
|
+
class MinuteOfHourClass < AbstractPart
|
224
|
+
include Singleton
|
225
|
+
|
226
|
+
def symbol ; :minute_of_hour ; end
|
227
|
+
def subdivision ; Units::Minute ; end
|
228
|
+
def scope ; Units::Hour ; end
|
229
|
+
def default_for_unit? ; true ; end
|
230
|
+
def component_mixin ; Frame::PartComponents::HasMinuteOfHour ; end
|
231
|
+
def scheme_scoped_precision_mixin ; Frame::Precisions::HasMinuteOfSchemePrecision ; end
|
232
|
+
|
233
|
+
def &(time)
|
234
|
+
time.min
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
MinuteOfHour = MinuteOfHourClass.instance
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module TimeScales
|
2
|
+
|
3
|
+
class TimeStruct < Struct.new(
|
4
|
+
:year,
|
5
|
+
:month,
|
6
|
+
:day,
|
7
|
+
:hour,
|
8
|
+
:minute,
|
9
|
+
)
|
10
|
+
|
11
|
+
def normalize
|
12
|
+
return unless year && day
|
13
|
+
self.month ||= 1
|
14
|
+
if month == 1 && day > 31
|
15
|
+
self.month = 2
|
16
|
+
self.day -= 31
|
17
|
+
end
|
18
|
+
feb_days = days_in_feb_of_year
|
19
|
+
if month == 2 && day > feb_days
|
20
|
+
self.month = 3
|
21
|
+
self.day -= feb_days
|
22
|
+
end
|
23
|
+
if month == 3 && day > 31
|
24
|
+
self.month = 4
|
25
|
+
self.day -= 31
|
26
|
+
end
|
27
|
+
if month == 4 && day > 30
|
28
|
+
self.month = 5
|
29
|
+
self.day -= 30
|
30
|
+
end
|
31
|
+
if month == 5 && day > 31
|
32
|
+
self.month = 6
|
33
|
+
self.day -= 31
|
34
|
+
end
|
35
|
+
if month == 6 && day > 30
|
36
|
+
self.month = 7
|
37
|
+
self.day -= 30
|
38
|
+
end
|
39
|
+
if month == 7 && day > 31
|
40
|
+
self.month = 8
|
41
|
+
self.day -= 31
|
42
|
+
end
|
43
|
+
if month == 8 && day > 31
|
44
|
+
self.month = 9
|
45
|
+
self.day -= 31
|
46
|
+
end
|
47
|
+
if month == 9 && day > 30
|
48
|
+
self.month = 10
|
49
|
+
self.day -= 30
|
50
|
+
end
|
51
|
+
if month == 10 && day > 31
|
52
|
+
self.month = 11
|
53
|
+
self.day -= 31
|
54
|
+
end
|
55
|
+
if month == 11 && day > 30
|
56
|
+
self.month = 12
|
57
|
+
self.day -= 30
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def days_in_feb_of_year
|
62
|
+
t = Time.new(year, 3, 1)
|
63
|
+
t.yday - 32
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module TimeScales
|
2
|
+
|
3
|
+
module Units
|
4
|
+
|
5
|
+
class AbstractUnit
|
6
|
+
def to_time_scales_unit
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def ===(other)
|
11
|
+
self == other || symbol == other
|
12
|
+
end
|
13
|
+
|
14
|
+
def symbol
|
15
|
+
raise NotImplementedError, "Subclass responsibility"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Rough order of magnitude of subdivision unit size.
|
19
|
+
# 1.407 times the base-2 logarithm of approximate number of
|
20
|
+
# seconds per unit, then round to nearest integer.
|
21
|
+
def scale
|
22
|
+
raise NotImplementedError, "Subclass responsibility"
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
@name ||= /::([^:]+)Class$/.match( self.class.name )[1]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class SchemeClass < AbstractUnit
|
31
|
+
include Singleton
|
32
|
+
|
33
|
+
def symbol ; :scheme ; end
|
34
|
+
def scale ; 9999 ; end
|
35
|
+
end
|
36
|
+
|
37
|
+
Scheme = SchemeClass.instance
|
38
|
+
|
39
|
+
|
40
|
+
class YearClass < AbstractUnit
|
41
|
+
include Singleton
|
42
|
+
|
43
|
+
def symbol ; :year ; end
|
44
|
+
def scale ; 35 ; end
|
45
|
+
end
|
46
|
+
|
47
|
+
Year = YearClass.instance
|
48
|
+
|
49
|
+
|
50
|
+
class QuarterClass < AbstractUnit
|
51
|
+
include Singleton
|
52
|
+
|
53
|
+
def symbol ; :quarter ; end
|
54
|
+
def scale ; 32 ; end
|
55
|
+
end
|
56
|
+
|
57
|
+
Quarter = QuarterClass.instance
|
58
|
+
|
59
|
+
|
60
|
+
class MonthClass < AbstractUnit
|
61
|
+
include Singleton
|
62
|
+
|
63
|
+
def symbol ; :month ; end
|
64
|
+
def scale ; 30 ; end
|
65
|
+
end
|
66
|
+
|
67
|
+
Month = MonthClass.instance
|
68
|
+
|
69
|
+
|
70
|
+
class DayClass < AbstractUnit
|
71
|
+
include Singleton
|
72
|
+
|
73
|
+
def symbol ; :day ; end
|
74
|
+
def scale ; 23 ; end
|
75
|
+
end
|
76
|
+
|
77
|
+
Day = DayClass.instance
|
78
|
+
|
79
|
+
|
80
|
+
class HourClass < AbstractUnit
|
81
|
+
include Singleton
|
82
|
+
|
83
|
+
def symbol ; :hour ; end
|
84
|
+
def scale ; 17 ; end
|
85
|
+
end
|
86
|
+
|
87
|
+
Hour = HourClass.instance
|
88
|
+
|
89
|
+
|
90
|
+
class MinuteClass < AbstractUnit
|
91
|
+
include Singleton
|
92
|
+
|
93
|
+
def symbol ; :minute ; end
|
94
|
+
def scale ; 8 ; end
|
95
|
+
end
|
96
|
+
|
97
|
+
Minute = MinuteClass.instance
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|