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.
@@ -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