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,258 @@
|
|
1
|
+
module TimeScales
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
module PartComponents
|
5
|
+
|
6
|
+
module HasYearOfScheme
|
7
|
+
def self.included(other)
|
8
|
+
other.extend HasYearOfScheme::ClassMixin
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :year_of_scheme
|
12
|
+
|
13
|
+
alias year year_of_scheme
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def _initialize(args_array)
|
18
|
+
super
|
19
|
+
@year_of_scheme = ensure_fixnum( args_array.shift )
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepare_time_struct(struct)
|
23
|
+
struct.year = (struct.year || 1) + year_of_scheme - 1
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMixin
|
28
|
+
protected
|
29
|
+
|
30
|
+
def _parts
|
31
|
+
super << Parts::YearOfScheme
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module HasMonthOfYear
|
37
|
+
def self.included(other)
|
38
|
+
other.extend HasMonthOfYear::ClassMixin
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :month_of_year
|
42
|
+
|
43
|
+
alias month month_of_year
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _initialize(args_array)
|
48
|
+
super
|
49
|
+
@month_of_year = ensure_fixnum( args_array.shift )
|
50
|
+
end
|
51
|
+
|
52
|
+
def prepare_time_struct(struct)
|
53
|
+
struct.month = (struct.month || 1) + month_of_year - 1
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
module ClassMixin
|
58
|
+
protected
|
59
|
+
|
60
|
+
def _parts
|
61
|
+
super << Parts::MonthOfYear
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module HasDayOfMonth
|
67
|
+
def self.included(other)
|
68
|
+
other.extend HasDayOfMonth::ClassMixin
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :day_of_month
|
72
|
+
|
73
|
+
alias day day_of_month
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def _initialize(args_array)
|
78
|
+
super
|
79
|
+
@day_of_month = ensure_fixnum( args_array.shift )
|
80
|
+
end
|
81
|
+
|
82
|
+
def prepare_time_struct(struct)
|
83
|
+
struct.day = (struct.day || 1) + day_of_month - 1
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
module ClassMixin
|
88
|
+
protected
|
89
|
+
|
90
|
+
def _parts
|
91
|
+
super << Parts::DayOfMonth
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module HasDayOfYear
|
97
|
+
def self.included(other)
|
98
|
+
other.extend HasDayOfYear::ClassMixin
|
99
|
+
end
|
100
|
+
|
101
|
+
attr_reader :day_of_year
|
102
|
+
|
103
|
+
alias day day_of_year
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def _initialize(args_array)
|
108
|
+
super
|
109
|
+
@day_of_year = ensure_fixnum( args_array.shift )
|
110
|
+
end
|
111
|
+
|
112
|
+
def prepare_time_struct(struct)
|
113
|
+
struct.day = (struct.day || 1) + day_of_year - 1
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
module ClassMixin
|
118
|
+
protected
|
119
|
+
|
120
|
+
def _parts
|
121
|
+
super << Parts::DayOfYear
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
module HasHourOfDay
|
127
|
+
def self.included(other)
|
128
|
+
other.extend HasHourOfDay::ClassMixin
|
129
|
+
end
|
130
|
+
|
131
|
+
attr_reader :hour_of_day
|
132
|
+
|
133
|
+
alias hour hour_of_day
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def _initialize(args_array)
|
138
|
+
super
|
139
|
+
@hour_of_day = ensure_fixnum( args_array.shift )
|
140
|
+
end
|
141
|
+
|
142
|
+
def prepare_time_struct(struct)
|
143
|
+
struct.hour = struct.hour.to_i + hour_of_day
|
144
|
+
super
|
145
|
+
end
|
146
|
+
|
147
|
+
module ClassMixin
|
148
|
+
protected
|
149
|
+
|
150
|
+
def _parts
|
151
|
+
super << Parts::HourOfDay
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
module HasMinuteOfHour
|
157
|
+
def self.included(other)
|
158
|
+
other.extend HasMinuteOfHour::ClassMixin
|
159
|
+
end
|
160
|
+
|
161
|
+
attr_reader :minute_of_hour
|
162
|
+
|
163
|
+
alias minute minute_of_hour
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def _initialize(args_array)
|
168
|
+
super
|
169
|
+
@minute_of_hour = ensure_fixnum( args_array.shift )
|
170
|
+
end
|
171
|
+
|
172
|
+
def prepare_time_struct(struct)
|
173
|
+
struct.minute = struct.minute.to_i + minute_of_hour
|
174
|
+
super
|
175
|
+
end
|
176
|
+
|
177
|
+
module ClassMixin
|
178
|
+
protected
|
179
|
+
|
180
|
+
def _parts
|
181
|
+
super << Parts::MinuteOfHour
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
module HasMonthOfQuarter
|
187
|
+
def self.included(other)
|
188
|
+
other.extend HasMonthOfQuarter::ClassMixin
|
189
|
+
end
|
190
|
+
|
191
|
+
attr_reader :month_of_quarter
|
192
|
+
|
193
|
+
alias month month_of_quarter
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def _initialize(args_array)
|
198
|
+
super
|
199
|
+
@month_of_quarter = ensure_fixnum( args_array.shift )
|
200
|
+
end
|
201
|
+
|
202
|
+
def prepare_time_struct(struct)
|
203
|
+
struct.month = (struct.month || 1) + month_of_quarter - 1
|
204
|
+
super
|
205
|
+
end
|
206
|
+
|
207
|
+
module ClassMixin
|
208
|
+
protected
|
209
|
+
|
210
|
+
def _parts
|
211
|
+
super << Parts::MonthOfQuarter
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
module HasQuarterOfYear
|
217
|
+
def self.included(other)
|
218
|
+
other.extend HasQuarterOfYear::ClassMixin
|
219
|
+
end
|
220
|
+
|
221
|
+
attr_reader :quarter_of_year
|
222
|
+
|
223
|
+
alias quarter quarter_of_year
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def _initialize(args_array)
|
228
|
+
super
|
229
|
+
@quarter_of_year = ensure_fixnum( args_array.shift )
|
230
|
+
end
|
231
|
+
|
232
|
+
def prepare_time_struct(struct)
|
233
|
+
to_month =
|
234
|
+
(struct.month || 1) +
|
235
|
+
3 * (quarter_of_year - 1)
|
236
|
+
if to_month > 12
|
237
|
+
struct.year = (struct.year || 1) + 1
|
238
|
+
to_month -= 12
|
239
|
+
struct.month = to_month
|
240
|
+
else
|
241
|
+
struct.month = to_month
|
242
|
+
end
|
243
|
+
super
|
244
|
+
end
|
245
|
+
|
246
|
+
module ClassMixin
|
247
|
+
protected
|
248
|
+
|
249
|
+
def _parts
|
250
|
+
super << Parts::QuarterOfYear
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module TimeScales
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class PartDef
|
5
|
+
attr_reader :key, :value, :part
|
6
|
+
|
7
|
+
def initialize(key, value=nil)
|
8
|
+
@key = key
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def scale
|
13
|
+
possible_parts.first.scale
|
14
|
+
end
|
15
|
+
|
16
|
+
def outer_scope!
|
17
|
+
@part = possible_parts.length == 1 ?
|
18
|
+
possible_parts.first :
|
19
|
+
possible_parts.detect { |part| part.default_for_unit? }
|
20
|
+
end
|
21
|
+
|
22
|
+
def component_of!(scope)
|
23
|
+
@part = possible_parts.detect { |part|
|
24
|
+
scope.subdivision === part.scope
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def possible_parts
|
31
|
+
@possible_parts ||= begin
|
32
|
+
parts = Parts.all.select { |part| part === key }
|
33
|
+
if parts.empty?
|
34
|
+
parts = Parts.all.select { |part| part.subdivision === key }
|
35
|
+
end
|
36
|
+
if parts.empty?
|
37
|
+
raise NoPartOrUnitForKeyError, "No part or unit matches key #{key.inspect}"
|
38
|
+
end
|
39
|
+
parts
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module TimeScales
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
class PartDefs
|
5
|
+
|
6
|
+
class << self
|
7
|
+
private :new
|
8
|
+
|
9
|
+
def from_key_value_map(kvm)
|
10
|
+
part_defs = kvm.map { |part_key, value|
|
11
|
+
PartDef.new( part_key, value )
|
12
|
+
}
|
13
|
+
new( part_defs )
|
14
|
+
end
|
15
|
+
|
16
|
+
def from_keys(part_keys)
|
17
|
+
part_defs = part_keys.map { |key| PartDef.new(key) }
|
18
|
+
new( part_defs )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(part_defs)
|
23
|
+
@part_defs = part_defs
|
24
|
+
end
|
25
|
+
|
26
|
+
def parts
|
27
|
+
@parts ||= assembly_sequence.map { |pd| pd.part }
|
28
|
+
end
|
29
|
+
|
30
|
+
def part_values
|
31
|
+
assembly_sequence.map { |pd| pd.value }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :part_defs
|
37
|
+
|
38
|
+
def assembly_sequence
|
39
|
+
return [] if part_defs.empty?
|
40
|
+
@assembly_sequence ||= begin
|
41
|
+
seq = part_defs.sort_by { |ps| -ps.scale }
|
42
|
+
seq.first.outer_scope!
|
43
|
+
seq[0..-2].zip( seq[1..-1] ).each do |a,b|
|
44
|
+
b.component_of! a.part
|
45
|
+
end
|
46
|
+
seq
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module TimeScales
|
2
|
+
module Frame
|
3
|
+
|
4
|
+
module Precisions
|
5
|
+
|
6
|
+
module HasYearOfSchemePrecision
|
7
|
+
def succ_begin_time
|
8
|
+
@end_time ||= Time.new( begin_time.year + 1 )
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Has_N_MonthsOfSchemePrecision
|
13
|
+
def succ_begin_time
|
14
|
+
@succ_begin_time ||= begin
|
15
|
+
succ_y = year_of_scheme
|
16
|
+
succ_m = begin_time.month + n_months_precision
|
17
|
+
if succ_m > 12
|
18
|
+
succ_y += 1 ; succ_m = 1
|
19
|
+
end
|
20
|
+
Time.new( succ_y, succ_m )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Precision unit size in months. A year must be divisible
|
27
|
+
# by this value.
|
28
|
+
def n_months_precision
|
29
|
+
raise NotImplementedError, "Subclass responsibility"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module HasMonthOfSchemePrecision
|
34
|
+
include Has_N_MonthsOfSchemePrecision
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def n_months_precision
|
39
|
+
1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module HasQuarterOfSchemePrecision
|
44
|
+
include Has_N_MonthsOfSchemePrecision
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def n_months_precision
|
49
|
+
3
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module HasDayOfSchemePrecision
|
54
|
+
# Gets us to the early part of the next day, regardless
|
55
|
+
# of DST handling, leap seconds, etc.
|
56
|
+
SECONDS_IN_26_HOURS = 60 * 60 * 26
|
57
|
+
|
58
|
+
def succ_begin_time
|
59
|
+
t = begin_time + SECONDS_IN_26_HOURS
|
60
|
+
@succ_begin_time ||= Time.new(t.year, t.month, t.day)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module HasHourOfSchemePrecision
|
65
|
+
SECONDS_PER_HOUR = 60 * 60
|
66
|
+
|
67
|
+
def succ_begin_time
|
68
|
+
@succ_begin_time ||= begin_time + SECONDS_PER_HOUR
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module HasMinuteOfSchemePrecision
|
73
|
+
SECONDS_PER_MINUTE = 60
|
74
|
+
|
75
|
+
def succ_begin_time
|
76
|
+
@succ_begin_time ||= begin_time + SECONDS_PER_MINUTE
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|