when_exe 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +171 -0
  3. data/lib/when_exe.rb +78 -47
  4. data/lib/when_exe/basictypes.rb +752 -747
  5. data/lib/when_exe/calendarnote.rb +805 -801
  6. data/lib/when_exe/calendartypes.rb +1583 -1531
  7. data/lib/when_exe/coordinates.rb +16 -15
  8. data/lib/when_exe/core/duration.rb +114 -110
  9. data/lib/when_exe/core/extension.rb +504 -504
  10. data/lib/when_exe/ephemeris.rb +1917 -1913
  11. data/lib/when_exe/ephemeris/moon.rb +333 -333
  12. data/lib/when_exe/ephemeris/notes.rb +389 -387
  13. data/lib/when_exe/ephemeris/planets.rb +585 -585
  14. data/lib/when_exe/ephemeris/sun.rb +214 -214
  15. data/lib/when_exe/googlecalendar.rb +144 -140
  16. data/lib/when_exe/icalendar.rb +1636 -1636
  17. data/lib/when_exe/inspect.rb +46 -22
  18. data/lib/when_exe/locales/akt.rb +176 -176
  19. data/lib/when_exe/locales/encoding_conversion.rb +134 -126
  20. data/lib/when_exe/locales/iast.rb +90 -90
  21. data/lib/when_exe/locales/locale.rb +750 -746
  22. data/lib/when_exe/locales/transliteration_table.rb +62 -62
  23. data/lib/when_exe/mini_application.rb +307 -305
  24. data/lib/when_exe/parts/enumerator.rb +2 -2
  25. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  26. data/lib/when_exe/parts/method_cash.rb +224 -224
  27. data/lib/when_exe/parts/resource.rb +1069 -1071
  28. data/lib/when_exe/parts/timezone.rb +240 -230
  29. data/lib/when_exe/region/armenian.rb +56 -56
  30. data/lib/when_exe/region/babylonian.rb +405 -0
  31. data/lib/when_exe/region/bahai.rb +146 -146
  32. data/lib/when_exe/region/balinese.rb +622 -622
  33. data/lib/when_exe/region/chinese.rb +95 -25
  34. data/lib/when_exe/region/chinese/calendars.rb +1016 -1016
  35. data/lib/when_exe/region/chinese/epochs.rb +1 -1
  36. data/lib/when_exe/region/chinese/twins.rb +803 -795
  37. data/lib/when_exe/region/christian.rb +824 -824
  38. data/lib/when_exe/region/coptic.rb +106 -87
  39. data/lib/when_exe/region/discordian.rb +225 -225
  40. data/lib/when_exe/region/far_east.rb +188 -188
  41. data/lib/when_exe/region/french.rb +56 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +58 -58
  44. data/lib/when_exe/region/indian.rb +1254 -1251
  45. data/lib/when_exe/region/iranian.rb +8 -8
  46. data/lib/when_exe/region/islamic.rb +3 -3
  47. data/lib/when_exe/region/japanese.rb +93 -99
  48. data/lib/when_exe/region/japanese/calendars.rb +396 -397
  49. data/lib/when_exe/region/japanese/epochs.rb +26 -26
  50. data/lib/when_exe/region/japanese/nihon_shoki.rb +71 -71
  51. data/lib/when_exe/region/japanese/notes.rb +1383 -1386
  52. data/lib/when_exe/region/japanese/residues.rb +1306 -1306
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -0
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +126 -126
  57. data/lib/when_exe/region/korean.rb +378 -378
  58. data/lib/when_exe/region/m17n.rb +114 -113
  59. data/lib/when_exe/region/martian.rb +258 -255
  60. data/lib/when_exe/region/mayan.rb +32 -32
  61. data/lib/when_exe/region/residue.rb +89 -89
  62. data/lib/when_exe/region/roman.rb +36 -24
  63. data/lib/when_exe/region/ryukyu.rb +97 -97
  64. data/lib/when_exe/region/shire.rb +240 -240
  65. data/lib/when_exe/region/soviet.rb +209 -0
  66. data/lib/when_exe/region/symmetry.rb +50 -50
  67. data/lib/when_exe/region/thai.rb +336 -335
  68. data/lib/when_exe/region/tibetan.rb +316 -315
  69. data/lib/when_exe/region/vietnamese.rb +440 -439
  70. data/lib/when_exe/region/weekdate.rb +80 -80
  71. data/lib/when_exe/region/world.rb +175 -175
  72. data/lib/when_exe/region/yerm.rb +14 -14
  73. data/lib/when_exe/region/zoroastrian.rb +203 -203
  74. data/lib/when_exe/timestandard.rb +707 -681
  75. data/lib/when_exe/tmduration.rb +338 -330
  76. data/lib/when_exe/tmobjects.rb +1346 -1325
  77. data/lib/when_exe/tmposition.rb +2115 -2072
  78. data/lib/when_exe/tmreference.rb +1693 -1669
  79. data/lib/when_exe/version.rb +1 -1
  80. data/link_to_online_documents +1 -1
  81. data/test/examples/JapanHolidaysRFC6350.ics +1 -1
  82. data/test/test.rb +67 -61
  83. data/test/test/basictypes.rb +409 -409
  84. data/test/test/calendarnote.rb +86 -69
  85. data/test/test/calendartypes.rb +97 -97
  86. data/test/test/coordinates.rb +396 -396
  87. data/test/test/ephemeris.rb +83 -74
  88. data/test/test/ephemeris/moon.rb +14 -14
  89. data/test/test/ephemeris/planets.rb +14 -14
  90. data/test/test/ephemeris/sun.rb +14 -14
  91. data/test/test/googlecalendar.rb +194 -176
  92. data/test/test/icalendar.rb +867 -858
  93. data/test/test/inspect.rb +117 -117
  94. data/test/test/parts.rb +487 -487
  95. data/test/test/region/balinese.rb +34 -0
  96. data/test/test/region/chinese.rb +218 -206
  97. data/test/test/region/christian.rb +245 -245
  98. data/test/test/region/coptic.rb +27 -27
  99. data/test/test/region/french.rb +33 -33
  100. data/test/test/region/geologicalage.rb +17 -17
  101. data/test/test/region/indian.rb +57 -57
  102. data/test/test/region/iran.rb +54 -54
  103. data/test/test/region/islamic.rb +18 -18
  104. data/test/test/region/japanese.rb +237 -219
  105. data/test/test/region/jewish.rb +61 -61
  106. data/test/test/region/m17n.rb +184 -184
  107. data/test/test/region/mayan.rb +195 -195
  108. data/test/test/region/residue.rb +147 -139
  109. data/test/test/region/thai.rb +116 -116
  110. data/test/test/region/tibetan.rb +30 -30
  111. data/test/test/region/vietnamese.rb +102 -102
  112. data/test/test/region/yerm.rb +146 -146
  113. data/test/test/timestandard.rb +81 -81
  114. data/test/test/tmobjects.rb +328 -328
  115. data/test/test/tmposition.rb +397 -284
  116. data/test/test/tmreference.rb +157 -157
  117. metadata +13 -10
@@ -1,1325 +1,1346 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2013 Takashi SUGA
4
-
5
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
- =end
7
-
8
- module When::TM
9
- #
10
- # (5.2) Temporal Objects Package
11
- #
12
- #
13
-
14
- #
15
- # 長さと距離を計算するための操作を規定する
16
- #
17
- module Separation
18
-
19
- # When::TM::GeometricPrimitive 自身の持続時間
20
- #
21
- # @return [When::TM::Duration]
22
- #
23
- # @abstract
24
- #
25
- def length
26
- raise TypeError, "Absolute Class #{self.class}"
27
- end
28
-
29
- # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
30
- #
31
- # @param [When::TM::GeometricPrimitive] other
32
- #
33
- # @return [When::TM::Duration]
34
- #
35
- # @abstract
36
- #
37
- def distance(other)
38
- raise TypeError, "Absolute Class #{self.class}"
39
- end
40
-
41
- end
42
-
43
- #
44
- # 互いの相対位置を判定する操作を規定する
45
- #
46
- module Order
47
-
48
- # 他のWhen::TM::Primitiveとの相対的な時間位置
49
- #
50
- # @param [When::TM::Primitive] other
51
- #
52
- # @return [When::TM::RelativePosition]
53
- #
54
- # @abstract
55
- #
56
- def relative_position(other)
57
- raise TypeError, "Absolute Class #{self.class}"
58
- end
59
- alias :relativePosition :relative_position
60
-
61
- end
62
-
63
- # 相対的な時間位置
64
- #
65
- # Allen(1983) が分類した13種類の相対的な時間位置を定義している
66
- #
67
- # ISO19108では値は Code クラスだが、本実装では値は Symbol である
68
- #
69
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#RelatedTimeType gml schema}
70
- #
71
- # see Allen,J.F., Maintaining Knowledge about Temporal Intervals, Communications of the ACM, 1983,vol.26 pp.832-843
72
- #
73
- module RelativePosition
74
-
75
- Before = :Before
76
- After = :After
77
- Begins = :Begins
78
- Ends = :Ends
79
- During = :During
80
- Equals = :Equals
81
- Contains = :Contains
82
- Overlaps = :Overlaps
83
- Meets = :Meets
84
- OverlappedBy = :OverlappedBy
85
- MetBy = :MetBy
86
- BegunBy = :BegunBy
87
- EndedBy = :EndedBy
88
-
89
- end
90
-
91
- # 時間のオブジェクト
92
- #
93
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeObjectType gml schema}
94
- #
95
- class Object < When::BasicTypes::Object
96
-
97
- end
98
-
99
- # 複数の時間プリミティブの集成
100
- #
101
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeComplexType gml schema}
102
- #
103
- class Complex < Object
104
-
105
- end
106
-
107
- # 位相複体 - 連結した位相プリミティブの集合
108
- #
109
- # A temporal topology complex.
110
- #
111
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeTopologyComplexType gml schema}
112
- #
113
- class TopologicalComplex < Complex
114
-
115
- # 対応するプリミティブ (relation - Complex)
116
- #
117
- # @return [Array<When::TM::TopologicalPrimitive>]
118
- #
119
- attr_reader :primitive
120
-
121
- # オブジェクトの生成
122
- #
123
- # @param [When::TM::TopologicalPrimitive] primitive 対応するプリミティブの Array
124
- #
125
- def initialize(primitive)
126
- @primitive = primitive
127
- @primitive.each do |p|
128
- p.complex << self
129
- end
130
- end
131
- end
132
-
133
- # 時間プリミティブ
134
- #
135
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimePrimitiveType gml schema}
136
- #
137
- class Primitive < Object
138
-
139
- include Order
140
-
141
- end
142
-
143
- # 時間幾何プリミティブ
144
- #
145
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeGeometricPrimitiveType gml schema}
146
- #
147
- class GeometricPrimitive < Primitive
148
-
149
- include Separation
150
-
151
- end
152
-
153
- # 瞬間 - 零次元幾何プリミティブ
154
- #
155
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimeInstantType gml schema}
156
- #
157
- class Instant < GeometricPrimitive
158
-
159
- # この瞬間の時間位置
160
- #
161
- # @return [When::TM::Position]
162
- #
163
- attr_reader :position
164
-
165
- # この瞬間を始まりとする期間 (relation - Beginning)
166
- #
167
- # @return [Array<When::TM::Period>]
168
- #
169
- attr_reader :begun_by
170
- alias :begunBy :begun_by
171
-
172
- # この瞬間を終わりとする期間 (relation - Ending)
173
- #
174
- # @return [Array<When::TM::Period>]
175
- #
176
- attr_reader :ended_by
177
- alias :endedBy :ended_by
178
-
179
- # 対応するノード (relation - Realization)
180
- #
181
- # @return [When::TM::Node]
182
- #
183
- attr_reader :topology
184
-
185
- # When::TM::GeometricPrimitive 自身の持続時間
186
- #
187
- # @return [When::TM::Duration]
188
- #
189
- def length()
190
- return When.Duration(0)
191
- end
192
-
193
- # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
194
- #
195
- # @param [When::TM::GeometricPrimitive] other
196
- #
197
- # @return [When::TM::Duration]
198
- #
199
- def distance(other)
200
- case other
201
- when Instant
202
- return (self.position - other.position).abs
203
- when Period
204
- verify = other.begin.position - self.position
205
- return verify if verify.sign >= 0
206
- return [self.position - other.end.position, When::TM::PeriodDuration.new(0,When::DAY)].max
207
- else
208
- raise TypeError, "The right operand should be When::TM::Instant or When::TM::Period"
209
- end
210
- end
211
-
212
- # 他のWhen::TM::Primitiveとの相対的な時間位置
213
- #
214
- # @param [When::TM::Primitive] other
215
- #
216
- # @return [When::TM::RelativePosition]
217
- #
218
- def relative_position(other)
219
- case other
220
- when Instant
221
- verify = self.position <=> other.position
222
- return RelativePosition::Before if verify < 0
223
- return RelativePosition::Equals if verify == 0
224
- return RelativePosition::After
225
- when Period
226
- verify = self.position <=> other.begin.position
227
- return RelativePosition::Before if verify < 0
228
- return RelativePosition::Begins if verify == 0
229
- verify = self.position <=> other.end.position
230
- return RelativePosition::During if verify < 0
231
- return RelativePosition::Ends if verify == 0
232
- return RelativePosition::After
233
- else
234
- raise TypeError, "The right operand should be When::TM::Instant or When::TM::Period"
235
- end
236
- end
237
- alias :relativePosition :relative_position
238
-
239
- # オブジェクトの生成
240
- #
241
- # @param [When::TM::Position] position 対応する時間位置
242
- #
243
- def initialize(position)
244
- @position = position
245
- @begun_by = []
246
- @ended_by = []
247
- end
248
-
249
- private
250
-
251
- # その他のメソッド
252
- #
253
- # @note
254
- # When::TM::Instant で定義されていないメソッドは
255
- # 処理を @position (type: When::TM::Position) に委譲する
256
- #
257
- def method_missing(name, *args, &block)
258
- self.class.module_eval %Q{
259
- def #{name}(*args, &block)
260
- list = args.map {|arg| arg.kind_of?(self.class) ? arg.position : arg}
261
- @position.send("#{name}", *list, &block)
262
- end
263
- } unless When::Parts::MethodCash.escape(name)
264
- list = args.map {|arg| arg.kind_of?(self.class) ? arg.position : arg}
265
- @position.send(name, *list, &block)
266
- end
267
- end
268
-
269
- # 期間 - 一次元幾何プリミティブ
270
- #
271
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimePeriodType gml schema}
272
- #
273
- class Period < GeometricPrimitive
274
-
275
- RELATIVE_POSITION = {
276
- RelativePosition::Contains => {
277
- RelativePosition::Before => RelativePosition::Overlaps,
278
- RelativePosition::EndedBy => RelativePosition::EndedBy,
279
- RelativePosition::Contains => RelativePosition::Contains
280
- },
281
- RelativePosition::BegunBy => {
282
- RelativePosition::Before => RelativePosition::Begins,
283
- RelativePosition::EndedBy => RelativePosition::Equals,
284
- RelativePosition::Contains => RelativePosition::BegunBy
285
- },
286
- RelativePosition::After => {
287
- RelativePosition::Before => RelativePosition::During,
288
- RelativePosition::EndedBy => RelativePosition::Ends,
289
- RelativePosition::Contains => RelativePosition::OverlappedBy
290
- }
291
- }
292
-
293
- # この期間が始まる瞬間 (relation - Beginning)
294
- #
295
- # @return [When::TM::Instant]
296
- #
297
- attr_reader :begin
298
-
299
- # この期間が終わる瞬間 (relation - Ending)
300
- #
301
- # @return [When::TM::Instant]
302
- #
303
- attr_reader :end
304
-
305
- # 対応するエッジ (relation - Realization)
306
- #
307
- # @return [When::TM::Edge]
308
- #
309
- attr_reader :topology
310
-
311
- # When::TM::GeometricPrimitive 自身の持続時間
312
- #
313
- # @return [When::TM::Duration]
314
- #
315
- def length()
316
- @length ||= @end - @begin
317
- end
318
-
319
- # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
320
- #
321
- # @param [When::TM::GeometricPrimitive] other
322
- #
323
- # @return [When::TM::Duration]
324
- #
325
- def distance(other)
326
- case other
327
- when Instant
328
- return other.distance(self)
329
- when Period
330
- verify = other.begin.position - self.end.position
331
- return verify if verify.sign >= 0
332
- return [self.begin.position - other.end.position, When::TM::PeriodDuration.new(0,When::DAY)].max
333
- else
334
- raise TypeError, "The right operand should be Instant or Period"
335
- end
336
- end
337
-
338
- # 他のPrimitiveとの相対的な時間位置
339
- #
340
- # @param [When::TM::Primitive] other
341
- #
342
- # @return [When::TM::RelativePosition]
343
- #
344
- def relative_position(other)
345
- case other
346
- when Instant
347
- verify = self.end.position <=> other.position
348
- return RelativePosition::Before if verify < 0
349
- return RelativePosition::EndedBy if verify == 0
350
- verify = self.begin.position <=> other.position
351
- return RelativePosition::Contains if verify < 0
352
- return RelativePosition::BegunBy if verify == 0
353
- return RelativePosition::After
354
- when Period
355
- verify_b = relative_position(other.begin)
356
- case verify_b
357
- when RelativePosition::Before ; return RelativePosition::Before
358
- when RelativePosition::EndedBy ; return RelativePosition::Meets
359
- end
360
- verify_e = relative_position(other.end)
361
- case verify_e
362
- when RelativePosition::BegunBy ; return RelativePosition::MetBy
363
- when RelativePosition::After ; return RelativePosition::After
364
- else ; return RELATIVE_POSITION[verify_b][verify_e]
365
- end
366
- else
367
- raise TypeError, "The right operand should be Instant or Period"
368
- end
369
- end
370
- alias :relativePosition :relative_position
371
-
372
- # オブジェクトの生成
373
- #
374
- # @param [When::TM::Instant] begun 始点
375
- #
376
- # @param [When::TM::Instant] ended 終点
377
- #
378
- def initialize(begun, ended)
379
- raise ArgumentError, 'Order mismatch: begun > ended' if begun > ended
380
- @begin = begun
381
- @end = ended
382
- @begin.begun_by << self
383
- @end.ended_by << self
384
- end
385
-
386
- private
387
-
388
- # その他のメソッド
389
- #
390
- # @note
391
- # When::TM::Period で定義されていないメソッドは
392
- # 処理を @begin (type: When::TM::Instant) に委譲する
393
- #
394
- def method_missing(name, *args, &block)
395
- self.class.module_eval %Q{
396
- def #{name}(*args, &block)
397
- @begin.send("#{name}", *args, &block)
398
- end
399
- } unless When::Parts::MethodCash.escape(name)
400
- @begin.send(name, *args, &block)
401
- end
402
- end
403
-
404
- # 時間位相プリミティブ - 単独で不可分な位相要素
405
- #
406
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#AbstractTimeTopologyPrimitiveType gml schema}
407
- #
408
- class TopologicalPrimitive < Primitive
409
-
410
- # 対応するコンプレックス (relation - Complex)
411
- #
412
- # @return [Array<When::TM::TopologicalComplex>]
413
- #
414
- attr_accessor :complex
415
-
416
- #
417
- # オブジェクトの生成
418
- #
419
- def initialize
420
- @complex = []
421
- end
422
-
423
- private
424
-
425
- alias :_method_missing :method_missing
426
-
427
- # その他のメソッド
428
- #
429
- # @note
430
- # When::TM::TopologicalPrimitive で定義されていないメソッドは
431
- # 処理を @geometry (type: When::TM::Instant) に委譲する
432
- #
433
- def method_missing(name, *args, &block)
434
- return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name) ||
435
- !respond_to?(:geometry)
436
- self.class.module_eval %Q{
437
- def #{name}(*args, &block)
438
- list = args.map {|arg| arg.respond_to?(:geometry) ? arg.geometry : arg}
439
- geometry.send("#{name}", *list, &block)
440
- end
441
- } unless When::Parts::MethodCash.escape(name)
442
- list = args.map {|arg| arg.respond_to?(:geometry) ? arg.geometry : arg}
443
- geometry.send(name, *list, &block)
444
- end
445
- end
446
-
447
- # 零次元位相プリミティブ - 幾何的実現は When::TM::Instant と対応する
448
- #
449
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeNodeType gml schema}
450
- #
451
- class Node < TopologicalPrimitive
452
-
453
- # 対応するエッジ (relation - Termination)
454
- #
455
- # @return [Array<When::TM::Edge>]
456
- #
457
- attr_reader :previous_edge
458
- alias :previousEdge :previous_edge
459
-
460
- # 対応するエッジ (relation - Initiation)
461
- #
462
- # @return [Array<When::TM::Edge>]
463
- #
464
- attr_reader :next_edge
465
- alias :nextEdge :next_edge
466
-
467
- # 対応する瞬間 (relation - Realization)
468
- #
469
- # @return [When::TM::Instant]
470
- #
471
- attr_reader :geometry
472
-
473
- # オブジェクトの生成
474
- #
475
- # @param [When::TM::Instant] geometry 対応する瞬間
476
- #
477
- def initialize(geometry)
478
- super()
479
- @previous_edge = []
480
- @next_edge = []
481
- @geometry = geometry
482
- @geometry.topology = self
483
- end
484
- end
485
-
486
- # 一次元位相プリミティブ - 幾何的実現は When::TM::Period と対応する
487
- #
488
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeEdgeType gml schema}
489
- #
490
- class Edge < TopologicalPrimitive
491
-
492
- # 対応するノード (relation - Termination)
493
- #
494
- # @return [When::TM::Node]
495
- #
496
- attr_reader :end
497
-
498
- # 対応するノード (relation - Initiation)
499
- #
500
- # @return [When::TM::Node]
501
- #
502
- attr_reader :start
503
-
504
- # 対応する期間 (relation - Realization)
505
- #
506
- # @return [When::TM::Period]
507
- #
508
- attr_reader :geometry
509
-
510
- # オブジェクトの生成
511
- #
512
- # @param [When::TM::Node] start 始点
513
- #
514
- # @param [When::TM::Node] ended 終点
515
- #
516
- def initialize(start, ended)
517
- super()
518
- @start = start
519
- @end = ended
520
- @geometry = Period.new(@start.geometry, @end.geometry)
521
- @geometry.topology = self
522
- @start.next_edge << self
523
- @end.previous_edge << self
524
- end
525
- end
526
-
527
- # 時間次元における長さ又は距離を記述するために使うデータ型
528
- #
529
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#duration gml schema}
530
- #
531
- class Duration
532
-
533
- #
534
- # When::TM::IntervalLength への変換
535
- #
536
- # @return [When::TM::IntervalLength]
537
- #
538
- def to_interval_length
539
- [['week', WEEK], ['day', DAY], ['hour', HOUR], ['minute', MINUTE], ['second', SECOND]].each do |unit|
540
- div, mod = duration.divmod(unit[1])
541
- return When::TM::IntervalLength.new(div, unit[0]) if mod == 0
542
- end
543
- When::TM::IntervalLength.new(duration / SECOND, 'second')
544
- end
545
-
546
- #
547
- # When::TM::PeriodDuration への変換
548
- #
549
- # @return [When::TM::PeriodDuration]
550
- #
551
- def to_period_duration
552
- [[When::WEEK, WEEK], [When::DAY, DAY], [When::HOUR, HOUR], [When::MINUTE, MINUTE], [When::SECOND, SECOND]].each do |unit|
553
- div, mod = duration.divmod(unit[1])
554
- return When::TM::PeriodDuration.new(div, unit[0]) if mod == 0
555
- end
556
- When::TM::PeriodDuration.new(duration / SECOND, When::SECOND)
557
- end
558
-
559
- #
560
- # Duration 用の Enumerator
561
- #
562
- class Enumerator < When::Parts::Enumerator
563
-
564
- # 次の時間位置を取得する
565
- #
566
- # @return [When::TM::TemporalPosition] 次の時間位置
567
- #
568
- def succ
569
- value = @current
570
- @current = (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ? nil :
571
- (@current==:first) ? @first :
572
- (@direction==:forward) ? @first + @parent * @count : @first - @parent * @count
573
- @count += 1
574
- return value
575
- end
576
- end
577
-
578
- # Enumerator の生成
579
- #
580
- # @overload initialize(range, count_limit=nil))
581
- # @param [Range, When::Parts::GeometricComplex] range
582
- # [ 始点 - range.first ]
583
- # [ 終点 - range.last ]
584
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
585
- #
586
- # @overload initialize(first, direction, count_limit=nil))
587
- # @param [When::TM::TemporalPosition] first 始点
588
- # @param [Symbol] direction
589
- # [ :forward - 昇順 ]
590
- # [ :reverse - 降順 ]
591
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
592
- #
593
- def _enumerator(*args)
594
- return Enumerator.new(*args.unshift(self))
595
- end
596
- alias :to_enum :_enumerator
597
- alias :enum_for :_enumerator
598
- end
599
-
600
- #
601
- # ISO 11404 の時間間隔に基づいて定義された When::TM::Duration の subclass
602
- #
603
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#timeIntervalLengthType gml schema}
604
- #
605
- class IntervalLength < Duration
606
-
607
- Alias = {'Y'=>'year', 'M'=>'month' , 'D'=>'day', 'W'=>'week', 'S'=>'system',
608
- 'h'=>'hour', 'm'=>'minute', 's'=>'second'}
609
-
610
- # Interval Length 形式の表現を分解して配列化する
611
- #
612
- # @param [String] interval
613
- #
614
- # @return [Array<Numeric, String>] ( value, unit_name, factor, radix )
615
- # [ value [Numeric] - 時間間隔 / 単位 ]
616
- # [ unit_name [String] - 単位名 ]
617
- # [ factor [Numeric] - 冪乗 ]
618
- # [ radix [Numeric] - 冪乗の基数 ]
619
- #
620
- # @note value, factor, radix は Numeric
621
- #
622
- def self._to_array(interval)
623
- return nil unless interval =~ /^([-+]?[\d.]+)(?:(E|X|\((\d+)\))([-+]?\d+))?([A-Za-z]+|\*([\d.]+)S)$/
624
- value, radix, radix_quantity, factor, unit, unit_quantity = $~[1..6]
625
- value = When::Coordinates::Pair._en_number(value)
626
- radix = case radix
627
- when 'E', nil ; 10
628
- when 'X' ; 60
629
- else ; radix_quantity.to_i
630
- end
631
- factor = factor ? -factor.to_i : 0
632
- return [value, unit_quantity, factor, radix] if unit_quantity
633
- unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]]
634
- return [value, UnitName[unit_quantity], factor, radix] if unit_quantity
635
- return nil
636
- end
637
-
638
- # 時間間隔の長さ (128秒単位)
639
- #
640
- # @return [Numeric]
641
- #
642
- protected :duration=
643
-
644
- # 時間間隔を表現するために使用した測定単位の名称
645
- #
646
- # @return [String] (year|month|week|day|hour|minute|second|system)
647
- #
648
- attr_reader :unit
649
-
650
- # 時間単位となる乗数の基底となる正の整数
651
- #
652
- # @return [Integer]
653
- #
654
- attr_reader :radix
655
-
656
- # 基底のべき乗を行う指数
657
- #
658
- # @return [Integer]
659
- #
660
- attr_reader :factor
661
-
662
- # 時間間隔の長さ / 測定単位
663
- #
664
- # @return [Numeric]
665
- #
666
- attr_accessor :value
667
- protected :value=
668
-
669
- #
670
- # 測定単位の大きさ
671
- #
672
- # @return [Numeric]
673
- #
674
- attr_reader :unit_quantity
675
-
676
- # 時刻配列
677
- #
678
- # @return [Numeric]
679
- # When::TM::PeriodDuration との互換性のために提供
680
- #
681
- # @private
682
- #
683
- def time
684
- [0, 0, @duration / SECOND]
685
- end
686
-
687
- # 日付配列
688
- #
689
- # @return [nil]
690
- # When::TM::PeriodDuration との互換性のために提供
691
- #
692
- # @private
693
- #
694
- def date
695
- nil
696
- end
697
-
698
- # 暦週配列
699
- #
700
- # @return [nil]
701
- # When::TM::PeriodDuration との互換性のために提供
702
- #
703
- # @private
704
- #
705
- def weeks
706
- nil
707
- end
708
-
709
- # 符号反転
710
- #
711
- # @return [When::TM::IntervalLength]
712
- #
713
- def -@
714
- interval = self.dup
715
- interval.value = -@value
716
- interval.duration = -@duration
717
- return interval
718
- end
719
-
720
- #
721
- # 加算
722
- #
723
- # @param [When::TM::Duration] other
724
- # @param [Numeric] other 秒数
725
- # @param [その他] other 日時とみなす
726
- #
727
- # @return [When::TM::IntervalLength] (other が Numeric, When::TM::Duration の場合)
728
- # @return [other と同じクラス] (other がその他の場合)
729
- #
730
- def +(other)
731
- case other
732
- when Duration ; diff = other.duration
733
- when Numeric ; diff = other * SECOND
734
- else ; return other + self
735
- end
736
- interval = self.dup
737
- interval.duration = @duration + diff
738
- internal.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
739
- return interval
740
- end
741
-
742
- #
743
- # 減算
744
- #
745
- # @param [When::TM::IntervalLength] other
746
- # @param [Numeric] other 秒数
747
- #
748
- # @return [When::TM::IntervalLength]
749
- #
750
- def -(other)
751
- interval = self.dup
752
- interval.duration = @duration - (other.kind_of?(Duration) ? other.duration : other * SECOND)
753
- internal.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
754
- return interval
755
- end
756
-
757
- # 乗算
758
- #
759
- # @param [Numeric] times
760
- #
761
- # @return [When::TM::IntervalLength]
762
- #
763
- def *(times)
764
- interval = self.dup
765
- interval.value = times * @value
766
- interval.duration = times * @duration
767
- return interval
768
- end
769
-
770
- #
771
- # 除算
772
- #
773
- # @param [When::TM::IntervalLength] other
774
- # @param [Numeric] other 秒数
775
- #
776
- # @return [When::TM::IntervalLength] (other が Numeric の場合)
777
- # @return [Numeric] (other が When::TM::Duration の場合)
778
- #
779
- def /(other)
780
- other.kind_of?(Duration) ? @duration / other.duration : self * (1.0 / other)
781
- end
782
-
783
- # 文字列化
784
- #
785
- # @return [String]
786
- #
787
- def to_s
788
- expression = @value.to_s
789
- unless @factor == 0
790
- case @radix
791
- when 10 ; expression += 'E'
792
- when 60 ; expression += 'X'
793
- else ; expression += '(%d)' % @radix
794
- end
795
- expression += '%+d' % (-@factor)
796
- end
797
- expression += Alias.invert[@unit] || "*#{When::Coordinates::Pair._en_number(@unit)}S"
798
- return expression
799
- end
800
-
801
- #
802
- # When::TM::IntervalLength への変換
803
- #
804
- # 単なるオブジェクトのコピー
805
- #
806
- # @return [When::TM::IntervalLength]
807
- #
808
- def to_interval_length
809
- self.dup
810
- end
811
-
812
- # オブジェクトの生成
813
- #
814
- # @param [Numeric] value 時間間隔の長さ / 測定単位(省略不可)
815
- # @param [String] unit 時間間隔を表現するために使用した測定単位の名称(デフォルト : system)
816
- # ('year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'system')
817
- # @param [Integer] factor 基底の冪乗を行う指数(デフォルト : 0)
818
- # @param [Integer] radix 時間単位となる乗数の基底となる正の整数(デフォルト : 10)
819
- #
820
- def initialize(value, unit='system', factor=0, radix=10)
821
- @value, @factor, @radix = value, factor, radix
822
- @unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]] if unit.kind_of?(String)
823
- @unit_quantity ||= unit.to_f
824
- raise TypeError, "Wrong Unit Type: #{unit}" unless @unit_quantity.kind_of?(Numeric)
825
- @unit = UnitName[@unit_quantity] ||
826
- When::Coordinates::Pair._en_number(@unit_quantity).to_s
827
- @duration = @value * @radix ** (-@factor) * @unit_quantity
828
- end
829
-
830
- # オブジェクトの生成(終点と始点を指定)
831
- #
832
- # @param [When::TM::TemporalPosition] ended 終点
833
- # @param [When::TM::TemporalPosition] begun 始点
834
- #
835
- # @return [When::TM::IntervalLength]
836
- # [ 日単位の精度 - 終点と始点の分解能のいずれかが“日”またはそれより粗い場合 ]
837
- # [ システム精度 - 終点と始点の分解能がともに“日”より細かい場合 ]
838
- #
839
- def self.difference(ended, begun)
840
- precision = [ended.precision, begun.precision].min
841
- return new(ended-begun) if precision > When::DAY
842
- return new(ended.to_i - begun.to_i, unit='day')
843
- end
844
-
845
- private
846
-
847
- # その他のメソッド
848
- #
849
- # @note
850
- # When::TM::IntervalLength で定義されていないメソッドは
851
- # 処理を @duration (type:Numeric) に委譲する
852
- #
853
- def method_missing(name, *args, &block)
854
- self.class.module_eval %Q{
855
- def #{name}(*args, &block)
856
- @duration.send("#{name}", *args, &block)
857
- end
858
- } unless When::Parts::MethodCash.escape(name)
859
- @duration.send(name, *args, &block)
860
- end
861
- end
862
-
863
- #
864
- # ISO 8601 (JIS X0301) の時間間隔に基づいて定義された When::TM::Duration の subclass
865
- #
866
- class PeriodDuration < Duration
867
-
868
- include When
869
- include Coordinates
870
-
871
- #
872
- # When::TM::PeriodDuration オブジェクトが月や年などの可変長の単位に依存する場合
873
- # に @duration 属性の参照を禁止するために用いる
874
- #
875
- module NoDuration
876
- #
877
- # 属性 @duration が定義できないとをエラーで示す
878
- #
879
- # @return [Numeric]
880
- #
881
- def duration
882
- raise TypeError, "This PeriodDuration object does't have duration value"
883
- end
884
- end
885
-
886
- # ISO 8601 形式の表現を分解して配列化する
887
- #
888
- # @param [String] period
889
- #
890
- # @return [Array<Numeric, Array<Numeric>>] ( sign, date, time, week )
891
- # [ sign [Numeric] - +1 , -1 ]
892
- # [ date [Array<Numeric>] - 年月日 ]
893
- # [ time [Array<Numeric>] - 時分秒 ]
894
- # [ week [Array<Numeric>] - 週日 ]
895
- #
896
- def self._to_array(period)
897
-
898
- return nil unless period.gsub(/_+/, '') =~ /^([-+])?P(.*?)?(?:T(.+))?$/
899
-
900
- sign = ($1 == '-') ? -1 : +1
901
- pdate, ptime = $~[2..3]
902
-
903
- # 時分秒形式
904
- if (ptime =~ /^(?:([.,\d]+)?([:*=])?H)?(?:([.,\d]+)?([:*=])?M)?(?:([.,\d]+)?([:*=])?S)?((?:[.,\d]*[:*=]?X)*)$/)
905
- trunk = [1,3,5].map {|i|
906
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
907
- }
908
- extra = $7 ? $7.scan(/([.,\d]+)?([:*=])?X/).map {|d|
909
- d[0] ? Pair._en_pair(d[0], d[1]) : 0
910
- } : []
911
- time = [0] + trunk + extra
912
- time = nil if time.uniq == [0]
913
- end
914
-
915
- case pdate
916
- # 年月日形式
917
- when /^((?:[.,\d]*[-*=]?X)*)(?:([.,\d]+)?([-*=])?Y)?(?:([.,\d]+)?([-+*&%!>=<?])?M)?(?:([.,\d]+)?([-*=?%])?D)?$/
918
- trunk = [2,4,6].map {|i|
919
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
920
- }
921
- extra = $1 ? $1.scan(/([.,\d]+)?([:*=])?X/).map {|d|
922
- d[0] ? Pair._en_pair(d[0], d[1]) : 0
923
- } : []
924
- date = extra + trunk
925
- date = nil if date.uniq == [0]
926
- return sign, date, time
927
-
928
- # 週日形式
929
- when /^(?:([.,\d]+)?([:*=])?W)(?:([.,\d]+)?([-*=?%])?D)?$/
930
- week = [1,3].map {|i|
931
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
932
- }
933
- return sign, nil, time if week.uniq == [0]
934
- date = [0, 0, week[1] + 7*week[0]]
935
- return sign, date, time, week
936
-
937
- # 代用形式
938
- else
939
- pdate += 'T' + ptime if ptime
940
- f, d, t, z, e = When::BasicTypes::DateTime._to_array(pdate, {:abbr=>[0]*20})
941
- return nil if e
942
- if d
943
- case f
944
- when :day ; date = [d[0], 0, d[1]]
945
- when :century, nil ; date = (0..2).map {|i| d[i]||0}
946
- when :week ; date, week = [0,0,d[0]*7+(d[1]||0)], [d[0], d[1]||0]
947
- end
948
- end
949
- time = t ? (t.map {|v| v||0}) : nil
950
- return sign, date, time, week
951
- end
952
- end
953
-
954
- # 期間の日付要素
955
- #
956
- # @return [Array<Numeric>]
957
- #
958
- attr_accessor :date
959
- protected :date=
960
-
961
- # 期間の週日要素
962
- #
963
- # @return [Array<Numeric>]
964
- #
965
- attr_accessor :week
966
- protected :week=
967
-
968
- # 期間の時刻要素
969
- #
970
- # @return [Array<Numeric>]
971
- #
972
- attr_accessor :time
973
- protected :time=
974
-
975
- # 要素の参照
976
- #
977
- # @param [Numeric] index When::Coordinates で定義している分解能定数に対応する列挙型( YEAR, ... , SECOND)
978
- #
979
- # @return [Numeric]
980
- #
981
- def [](index)
982
- if (index == WEEK)
983
- return nil unless (@week)
984
- return @week[0]
985
- elsif (index <= 0)
986
- return nil unless (@date)
987
- return @date[index-1]
988
- else
989
- return nil unless (@time)
990
- return @time[index]
991
- end
992
- end
993
-
994
- # 持続期間であることを文字'P'で示す
995
- #
996
- # @return [String]
997
- #
998
- def designator
999
- return 'P'
1000
- end
1001
-
1002
- # 期間に含まれる年数を示す
1003
- #
1004
- # @return [Numeric]
1005
- #
1006
- def years
1007
- return nil unless (@date)
1008
- return @date[YEAR-1].to_s
1009
- end
1010
-
1011
- # 期間に含まれる月数を示す
1012
- #
1013
- # @return [Numeric]
1014
- #
1015
- def months
1016
- return nil unless (@date)
1017
- return @date[MONTH-1].to_s
1018
- end
1019
-
1020
- # 期間に含まれる週数を示す
1021
- #
1022
- # @return [Numeric]
1023
- #
1024
- def weeks
1025
- return nil unless (@week)
1026
- return @week[0].to_s
1027
- end
1028
-
1029
- # 期間に含まれる日数を示す
1030
- #
1031
- # @return [Numeric]
1032
- #
1033
- def days
1034
- return nil unless (@date)
1035
- return @date[DAY-1].to_s
1036
- end
1037
-
1038
- # 期間が1日より短い時間単位を含むとき文字'T'で示す
1039
- #
1040
- # @return [String]
1041
- #
1042
- def time_indicator
1043
- return (@time) ? 'T' : nil
1044
- end
1045
- alias :timeIndicator :time_indicator
1046
-
1047
- # 期間に含まれる時間数を示す
1048
- #
1049
- # @return [Numeric]
1050
- #
1051
- def hours
1052
- return nil unless (@time)
1053
- return @time[HOUR].to_s
1054
- end
1055
-
1056
- # 期間に含まれる分数を示す
1057
- #
1058
- # @return [Numeric]
1059
- #
1060
- def minutes
1061
- return nil unless (@time)
1062
- return @time[MINUTE].to_s
1063
- end
1064
-
1065
- # 期間に含まれる秒数を示す
1066
- #
1067
- # @return [Numeric]
1068
- #
1069
- def seconds
1070
- return nil unless (@time)
1071
- return @time[SECOND].to_s
1072
- end
1073
-
1074
- # 符号反転
1075
- #
1076
- # @return [When::TM::PeriodDuration]
1077
- #
1078
- def -@
1079
- period = self.dup
1080
- period.date = @date.map {|v| -v} if @date
1081
- period.week = @week.map {|v| -v} if @week
1082
- period.time = @time.map {|v| -v} if @time
1083
- return period
1084
- end
1085
-
1086
- # 符号
1087
- #
1088
- # @return [Integer] 0 との比較により、負,0,正の値を返す
1089
- #
1090
- def sign
1091
- ((@week || @date || []) + (@time || [])).each do |v|
1092
- return -1 if +v < 0
1093
- end
1094
- return +1
1095
- end
1096
-
1097
- # 比較
1098
- #
1099
- # @param [When::TM::PeriodDuration] other
1100
- #
1101
- # @return [Integer] other との比較により、負,0,正の値を返す
1102
- #
1103
- def <=>(other)
1104
- unless (@date && @date.size) == (other.date && other.date.size) &&
1105
- (@week && @week.size) == (other.week && other.week.size) &&
1106
- (@time && @time.size) == (other.time && other.time.size)
1107
- raise ArgumentError, "PeriodDuration structure mismatch"
1108
- end
1109
-
1110
- (0...@week.size).each do |i|
1111
- sgn = +@week[i] <=> +other.week[i]
1112
- return sgn unless sgn == 0
1113
- end if @week
1114
-
1115
- (0...@date.size).each do |i|
1116
- sgn = +@date[i] <=> +other.date[i]
1117
- return sgn unless sgn == 0
1118
- end if @date
1119
-
1120
- (0...@time.size).each do |i|
1121
- sgn = +@time[i] <=> +other.time[i]
1122
- return sgn unless sgn == 0
1123
- end if @time
1124
-
1125
- return 0
1126
- end
1127
-
1128
- # オブジェクトの同値
1129
- #
1130
- # @param [比較先] other
1131
- #
1132
- # @return [Boolean]
1133
- # [ true - 同値 ]
1134
- # [ false - 非同値 ]
1135
- #
1136
- def ==(other)
1137
- (self <=> other) == 0
1138
- rescue
1139
- false
1140
- end
1141
-
1142
- # 加算
1143
- #
1144
- # @param [When::TM::PeriodDuration] other
1145
- # @param [その他] other 日時とみなす
1146
- #
1147
- # @return [WWhen::TM::PeriodDuration] (other が When::TM::PeriodDuration の場合)
1148
- # @return [other と同じクラス] (other がその他の場合)
1149
- #
1150
- def +(other)
1151
- other.kind_of?(When::TM::PeriodDuration) ? _plus(other, +1) : other + self
1152
- end
1153
-
1154
- # 減算
1155
- #
1156
- # @param [When::TM::PeriodDuration] other
1157
- #
1158
- # @return [When::TM::PeriodDuration]
1159
- #
1160
- def -(other)
1161
- _plus(other, -1)
1162
- end
1163
-
1164
- # 乗算
1165
- #
1166
- # @param [Numeric] times
1167
- #
1168
- # @return [When::TM::PeriodDuration]
1169
- #
1170
- def *(times)
1171
- period = self.dup
1172
- period.date = @date.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v} if @date
1173
- period.week = @week.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v} if @week
1174
- period.time = @time.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v} if @time
1175
- return period
1176
- end
1177
-
1178
- # 除算
1179
- #
1180
- # @param [Numeric] divisor
1181
- #
1182
- # @return [When::TM::PeriodDuration]
1183
- #
1184
- def /(divisor)
1185
- self * (1.0 / divisor)
1186
- end
1187
-
1188
- # 文字列化
1189
- #
1190
- # @return [String]
1191
- #
1192
- def to_s
1193
- period = 'P'
1194
- if @week
1195
- period += @week[0].abs.to_s + 'W'
1196
- period += @week[1].abs.to_s + 'D' unless @week[1] == 0
1197
- elsif @date
1198
- (-@date.length..-1).each do |i|
1199
- period += @date[i].abs.to_s + PRECISION_NAME[i+1][0..0] unless @date[i] == 0
1200
- end
1201
- end
1202
- if @time
1203
- period += 'T'
1204
- (1..@time.length-1).each do |i|
1205
- period += @time[i].abs.to_s + PRECISION_NAME[i][0..0] unless @time[i] == 0
1206
- end
1207
- end
1208
- period = '-' + period if sign < 0
1209
- period == 'P' ? 'P0D' : period
1210
- end
1211
-
1212
- #
1213
- # When::TM::PeriodDuration への変換
1214
- #
1215
- # 単なるオブジェクトのコピー
1216
- #
1217
- # @return [When::TM::PeriodDuration]
1218
- #
1219
- def to_period_duration
1220
- self.dup
1221
- end
1222
-
1223
- # オブジェクトの生成
1224
- #
1225
- # @overload initialize(date=nil, time=nil, week=nil)
1226
- # @param [Array<Numeric>] date 期間の日付要素
1227
- # @param [Array<Numeric>] time 期間の時刻要素
1228
- # @param [Array<Numeric>] week 期間の週日要素
1229
- #
1230
- # @overload initialize(value, index, range=YEAR..SECOND)
1231
- # @param [Numeric] value ndex に対応する桁での)時間間隔
1232
- # @param [Numeric] index When で定義している分解能定数(YEAR, ... ,SECOND)
1233
- # @param [Range] range 生成する桁の範囲
1234
- #
1235
- def initialize(*args)
1236
- @options = (args[-1].kind_of?(Hash)) ? (args.pop.reject {|key,value| value == nil}) : {}
1237
- if args[0].kind_of?(Numeric)
1238
- _initialize_by_unit(*args)
1239
- else
1240
- @date, @time, @week = args
1241
- end
1242
- _duration
1243
- end
1244
-
1245
- private
1246
-
1247
- #
1248
- # 引数パターン2での初期化
1249
- #
1250
- def _initialize_by_unit(value, index, range=YEAR..SECOND)
1251
- max = range.first
1252
- min = range.last
1253
- min += 1 if (range.exclude_end?)
1254
- if (index < max)
1255
- if (index > MONTH)
1256
- max = index.floor
1257
- else
1258
- value *= 10**(max-index)
1259
- index = max
1260
- end
1261
- elsif (index > min)
1262
- if (index <= DAY)
1263
- min = index.ceil
1264
- else
1265
- value *= 0.1**(index-min)
1266
- index = min
1267
- end
1268
- end
1269
- value = value.to_i unless value.kind_of?(Pair) || value.to_i != value.to_f
1270
- @date = Array.new(1-max, 0) if (max <= DAY) && (index <= DAY)
1271
- @time = Array.new(1+min, 0) if (min > DAY) && (index > DAY)
1272
- if (index == WEEK)
1273
- @week = [value, 0]
1274
- @date[DAY-1] = 7 * value
1275
- elsif (index <= DAY)
1276
- @date[index-1] = value
1277
- else
1278
- @time[index] = value
1279
- end
1280
- end
1281
-
1282
- #
1283
- # 属性 @duration の計算
1284
- #
1285
- def _duration
1286
- if @time
1287
- @duration = +@time[HOUR ] * Duration::HOUR
1288
- @duration += +@time[MINUTE] * Duration::MINUTE if @time[MINUTE]
1289
- @duration += +@time[SECOND] * Duration::SECOND if @time[SECOND]
1290
- end
1291
- if @week
1292
- @duration = +@week[0] * Duration::WEEK + +@week[1] * Duration::DAY + (@duration||0)
1293
- elsif @date
1294
- date = @date.dup
1295
- date.shift while date[0] == 0
1296
- case date.size
1297
- when 0 ; @duration ||= 0
1298
- when 1 ; @duration = +@date[DAY-1] * Duration::DAY + (@duration||0)
1299
- else ; @duration = nil
1300
- end
1301
- end
1302
- extend NoDuration unless @duration
1303
- end
1304
-
1305
- # 加減算共通処理
1306
- #
1307
- # @param [When::TM::PeriodDuration] other
1308
- # @param [Numeric] sgn +1, -1
1309
- #
1310
- # @return [When::TM::PeriodDuration]
1311
- #
1312
- def _plus(other, sgn)
1313
- unless (@week && @week.size) == (other.week && other.week.size) &&
1314
- (@date && @date.size) == (other.date && other.date.size) &&
1315
- (@time && @time.size) == (other.time && other.time.size)
1316
- raise ArgumentError, "PeriodDuration structure mismatch"
1317
- end
1318
- period = self.dup
1319
- period.week = (0...@week.size).to_a.map {|i| @week[i] + sgn * other.week[i]} if @week
1320
- period.date = (0...@date.size).to_a.map {|i| @date[i] + sgn * other.date[i]} if @date
1321
- period.time = (0...@time.size).to_a.map {|i| @time[i] + sgn * other.time[i]} if @time
1322
- return period
1323
- end
1324
- end
1325
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2013 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ module When::TM
9
+ #
10
+ # (5.2) Temporal Objects Package
11
+ #
12
+ #
13
+
14
+ #
15
+ # 長さと距離を計算するための操作を規定する
16
+ #
17
+ module Separation
18
+
19
+ # When::TM::GeometricPrimitive 自身の持続時間
20
+ #
21
+ # @return [When::TM::Duration]
22
+ #
23
+ # @abstract
24
+ #
25
+ def length
26
+ raise TypeError, "Absolute Class #{self.class}"
27
+ end
28
+
29
+ # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
30
+ #
31
+ # @param [When::TM::GeometricPrimitive] other
32
+ #
33
+ # @return [When::TM::Duration]
34
+ #
35
+ # @abstract
36
+ #
37
+ def distance(other)
38
+ raise TypeError, "Absolute Class #{self.class}"
39
+ end
40
+
41
+ end
42
+
43
+ #
44
+ # 互いの相対位置を判定する操作を規定する
45
+ #
46
+ module Order
47
+
48
+ # 他のWhen::TM::Primitiveとの相対的な時間位置
49
+ #
50
+ # @param [When::TM::Primitive] other
51
+ #
52
+ # @return [When::TM::RelativePosition]
53
+ #
54
+ # @abstract
55
+ #
56
+ def relative_position(other)
57
+ raise TypeError, "Absolute Class #{self.class}"
58
+ end
59
+ alias :relativePosition :relative_position
60
+
61
+ end
62
+
63
+ # 相対的な時間位置
64
+ #
65
+ # Allen(1983) が分類した13種類の相対的な時間位置を定義している
66
+ #
67
+ # ISO19108では値は Code クラスだが、本実装では値は Symbol である
68
+ #
69
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#RelatedTimeType gml schema}
70
+ #
71
+ # see Allen,J.F., Maintaining Knowledge about Temporal Intervals, Communications of the ACM, 1983,vol.26 pp.832-843
72
+ #
73
+ module RelativePosition
74
+
75
+ Before = :Before
76
+ After = :After
77
+ Begins = :Begins
78
+ Ends = :Ends
79
+ During = :During
80
+ Equals = :Equals
81
+ Contains = :Contains
82
+ Overlaps = :Overlaps
83
+ Meets = :Meets
84
+ OverlappedBy = :OverlappedBy
85
+ MetBy = :MetBy
86
+ BegunBy = :BegunBy
87
+ EndedBy = :EndedBy
88
+
89
+ end
90
+
91
+ # 時間のオブジェクト
92
+ #
93
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeObjectType gml schema}
94
+ #
95
+ class Object < When::BasicTypes::Object
96
+
97
+ end
98
+
99
+ # 複数の時間プリミティブの集成
100
+ #
101
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeComplexType gml schema}
102
+ #
103
+ class Complex < Object
104
+
105
+ end
106
+
107
+ # 位相複体 - 連結した位相プリミティブの集合
108
+ #
109
+ # A temporal topology complex.
110
+ #
111
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeTopologyComplexType gml schema}
112
+ #
113
+ class TopologicalComplex < Complex
114
+
115
+ # 対応するプリミティブ (relation - Complex)
116
+ #
117
+ # @return [Array<When::TM::TopologicalPrimitive>]
118
+ #
119
+ attr_reader :primitive
120
+
121
+ # オブジェクトの生成
122
+ #
123
+ # @param [When::TM::TopologicalPrimitive] primitive 対応するプリミティブの Array
124
+ #
125
+ def initialize(primitive)
126
+ @primitive = primitive
127
+ @primitive.each do |p|
128
+ p.complex << self
129
+ end
130
+ end
131
+ end
132
+
133
+ # 時間プリミティブ
134
+ #
135
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimePrimitiveType gml schema}
136
+ #
137
+ class Primitive < Object
138
+
139
+ include Order
140
+
141
+ end
142
+
143
+ # 時間幾何プリミティブ
144
+ #
145
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#AbstractTimeGeometricPrimitiveType gml schema}
146
+ #
147
+ class GeometricPrimitive < Primitive
148
+
149
+ include Separation
150
+
151
+ end
152
+
153
+ # 瞬間 - 零次元幾何プリミティブ
154
+ #
155
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimeInstantType gml schema}
156
+ #
157
+ class Instant < GeometricPrimitive
158
+
159
+ # この瞬間の時間位置
160
+ #
161
+ # @return [When::TM::Position]
162
+ #
163
+ attr_reader :position
164
+
165
+ # この瞬間を始まりとする期間 (relation - Beginning)
166
+ #
167
+ # @return [Array<When::TM::Period>]
168
+ #
169
+ attr_reader :begun_by
170
+ alias :begunBy :begun_by
171
+
172
+ # この瞬間を終わりとする期間 (relation - Ending)
173
+ #
174
+ # @return [Array<When::TM::Period>]
175
+ #
176
+ attr_reader :ended_by
177
+ alias :endedBy :ended_by
178
+
179
+ # 対応するノード (relation - Realization)
180
+ #
181
+ # @return [When::TM::Node]
182
+ #
183
+ attr_reader :topology
184
+
185
+ # When::TM::GeometricPrimitive 自身の持続時間
186
+ #
187
+ # @return [When::TM::Duration]
188
+ #
189
+ def length()
190
+ return When.Duration(0)
191
+ end
192
+
193
+ # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
194
+ #
195
+ # @param [When::TM::GeometricPrimitive] other
196
+ #
197
+ # @return [When::TM::Duration]
198
+ #
199
+ def distance(other)
200
+ case other
201
+ when Instant
202
+ return (self.position - other.position).abs
203
+ when Period
204
+ verify = other.begin.position - self.position
205
+ return verify if verify.sign >= 0
206
+ return [self.position - other.end.position, When::TM::PeriodDuration.new(0,When::DAY)].max
207
+ else
208
+ raise TypeError, "The right operand should be When::TM::Instant or When::TM::Period"
209
+ end
210
+ end
211
+
212
+ # 他のWhen::TM::Primitiveとの相対的な時間位置
213
+ #
214
+ # @param [When::TM::Primitive] other
215
+ #
216
+ # @return [When::TM::RelativePosition]
217
+ #
218
+ def relative_position(other)
219
+ case other
220
+ when Instant
221
+ verify = self.position <=> other.position
222
+ return RelativePosition::Before if verify < 0
223
+ return RelativePosition::Equals if verify == 0
224
+ return RelativePosition::After
225
+ when Period
226
+ verify = self.position <=> other.begin.position
227
+ return RelativePosition::Before if verify < 0
228
+ return RelativePosition::Begins if verify == 0
229
+ verify = self.position <=> other.end.position
230
+ return RelativePosition::During if verify < 0
231
+ return RelativePosition::Ends if verify == 0
232
+ return RelativePosition::After
233
+ else
234
+ raise TypeError, "The right operand should be When::TM::Instant or When::TM::Period"
235
+ end
236
+ end
237
+ alias :relativePosition :relative_position
238
+
239
+ # オブジェクトの生成
240
+ #
241
+ # @param [When::TM::Position] position 対応する時間位置
242
+ #
243
+ def initialize(position)
244
+ @position = position
245
+ @begun_by = []
246
+ @ended_by = []
247
+ end
248
+
249
+ private
250
+
251
+ # その他のメソッド
252
+ #
253
+ # @note
254
+ # When::TM::Instant で定義されていないメソッドは
255
+ # 処理を @position (type: When::TM::Position) に委譲する
256
+ #
257
+ def method_missing(name, *args, &block)
258
+ self.class.module_eval %Q{
259
+ def #{name}(*args, &block)
260
+ list = args.map {|arg| arg.kind_of?(self.class) ? arg.position : arg}
261
+ @position.send("#{name}", *list, &block)
262
+ end
263
+ } unless When::Parts::MethodCash.escape(name)
264
+ list = args.map {|arg| arg.kind_of?(self.class) ? arg.position : arg}
265
+ @position.send(name, *list, &block)
266
+ end
267
+ end
268
+
269
+ # 期間 - 一次元幾何プリミティブ
270
+ #
271
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimePeriodType gml schema}
272
+ #
273
+ class Period < GeometricPrimitive
274
+
275
+ RELATIVE_POSITION = {
276
+ RelativePosition::Contains => {
277
+ RelativePosition::Before => RelativePosition::Overlaps,
278
+ RelativePosition::EndedBy => RelativePosition::EndedBy,
279
+ RelativePosition::Contains => RelativePosition::Contains
280
+ },
281
+ RelativePosition::BegunBy => {
282
+ RelativePosition::Before => RelativePosition::Begins,
283
+ RelativePosition::EndedBy => RelativePosition::Equals,
284
+ RelativePosition::Contains => RelativePosition::BegunBy
285
+ },
286
+ RelativePosition::After => {
287
+ RelativePosition::Before => RelativePosition::During,
288
+ RelativePosition::EndedBy => RelativePosition::Ends,
289
+ RelativePosition::Contains => RelativePosition::OverlappedBy
290
+ }
291
+ }
292
+
293
+ # この期間が始まる瞬間 (relation - Beginning)
294
+ #
295
+ # @return [When::TM::Instant]
296
+ #
297
+ attr_reader :begin
298
+
299
+ # この期間が終わる瞬間 (relation - Ending)
300
+ #
301
+ # @return [When::TM::Instant]
302
+ #
303
+ attr_reader :end
304
+
305
+ # 対応するエッジ (relation - Realization)
306
+ #
307
+ # @return [When::TM::Edge]
308
+ #
309
+ attr_reader :topology
310
+
311
+ # When::TM::GeometricPrimitive 自身の持続時間
312
+ #
313
+ # @return [When::TM::Duration]
314
+ #
315
+ def length()
316
+ @length ||= @end - @begin
317
+ end
318
+
319
+ # 他のWhen::TM::GeometricPrimitiveとの時間位置の差の絶対値
320
+ #
321
+ # @param [When::TM::GeometricPrimitive] other
322
+ #
323
+ # @return [When::TM::Duration]
324
+ #
325
+ def distance(other)
326
+ case other
327
+ when Instant
328
+ return other.distance(self)
329
+ when Period
330
+ verify = other.begin.position - self.end.position
331
+ return verify if verify.sign >= 0
332
+ return [self.begin.position - other.end.position, When::TM::PeriodDuration.new(0,When::DAY)].max
333
+ else
334
+ raise TypeError, "The right operand should be Instant or Period"
335
+ end
336
+ end
337
+
338
+ # 他のPrimitiveとの相対的な時間位置
339
+ #
340
+ # @param [When::TM::Primitive] other
341
+ #
342
+ # @return [When::TM::RelativePosition]
343
+ #
344
+ def relative_position(other)
345
+ case other
346
+ when Instant
347
+ verify = self.end.position <=> other.position
348
+ return RelativePosition::Before if verify < 0
349
+ return RelativePosition::EndedBy if verify == 0
350
+ verify = self.begin.position <=> other.position
351
+ return RelativePosition::Contains if verify < 0
352
+ return RelativePosition::BegunBy if verify == 0
353
+ return RelativePosition::After
354
+ when Period
355
+ verify_b = relative_position(other.begin)
356
+ case verify_b
357
+ when RelativePosition::Before ; return RelativePosition::Before
358
+ when RelativePosition::EndedBy ; return RelativePosition::Meets
359
+ end
360
+ verify_e = relative_position(other.end)
361
+ case verify_e
362
+ when RelativePosition::BegunBy ; return RelativePosition::MetBy
363
+ when RelativePosition::After ; return RelativePosition::After
364
+ else ; return RELATIVE_POSITION[verify_b][verify_e]
365
+ end
366
+ else
367
+ raise TypeError, "The right operand should be Instant or Period"
368
+ end
369
+ end
370
+ alias :relativePosition :relative_position
371
+
372
+ # オブジェクトの生成
373
+ #
374
+ # @param [When::TM::Instant] begun 始点
375
+ #
376
+ # @param [When::TM::Instant] ended 終点
377
+ #
378
+ def initialize(begun, ended)
379
+ raise ArgumentError, 'Order mismatch: begun > ended' if begun > ended
380
+ @begin = begun
381
+ @end = ended
382
+ @begin.begun_by << self
383
+ @end.ended_by << self
384
+ end
385
+
386
+ private
387
+
388
+ # その他のメソッド
389
+ #
390
+ # @note
391
+ # When::TM::Period で定義されていないメソッドは
392
+ # 処理を @begin (type: When::TM::Instant) に委譲する
393
+ #
394
+ def method_missing(name, *args, &block)
395
+ self.class.module_eval %Q{
396
+ def #{name}(*args, &block)
397
+ @begin.send("#{name}", *args, &block)
398
+ end
399
+ } unless When::Parts::MethodCash.escape(name)
400
+ @begin.send(name, *args, &block)
401
+ end
402
+ end
403
+
404
+ # 時間位相プリミティブ - 単独で不可分な位相要素
405
+ #
406
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#AbstractTimeTopologyPrimitiveType gml schema}
407
+ #
408
+ class TopologicalPrimitive < Primitive
409
+
410
+ # 対応するコンプレックス (relation - Complex)
411
+ #
412
+ # @return [Array<When::TM::TopologicalComplex>]
413
+ #
414
+ attr_accessor :complex
415
+
416
+ #
417
+ # オブジェクトの生成
418
+ #
419
+ def initialize
420
+ @complex = []
421
+ end
422
+
423
+ private
424
+
425
+ alias :_method_missing :method_missing
426
+
427
+ # その他のメソッド
428
+ #
429
+ # @note
430
+ # When::TM::TopologicalPrimitive で定義されていないメソッドは
431
+ # 処理を @geometry (type: When::TM::Instant) に委譲する
432
+ #
433
+ def method_missing(name, *args, &block)
434
+ return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name) ||
435
+ !respond_to?(:geometry)
436
+ self.class.module_eval %Q{
437
+ def #{name}(*args, &block)
438
+ list = args.map {|arg| arg.respond_to?(:geometry) ? arg.geometry : arg}
439
+ geometry.send("#{name}", *list, &block)
440
+ end
441
+ } unless When::Parts::MethodCash.escape(name)
442
+ list = args.map {|arg| arg.respond_to?(:geometry) ? arg.geometry : arg}
443
+ geometry.send(name, *list, &block)
444
+ end
445
+ end
446
+
447
+ # 零次元位相プリミティブ - 幾何的実現は When::TM::Instant と対応する
448
+ #
449
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeNodeType gml schema}
450
+ #
451
+ class Node < TopologicalPrimitive
452
+
453
+ # 対応するエッジ (relation - Termination)
454
+ #
455
+ # @return [Array<When::TM::Edge>]
456
+ #
457
+ attr_reader :previous_edge
458
+ alias :previousEdge :previous_edge
459
+
460
+ # 対応するエッジ (relation - Initiation)
461
+ #
462
+ # @return [Array<When::TM::Edge>]
463
+ #
464
+ attr_reader :next_edge
465
+ alias :nextEdge :next_edge
466
+
467
+ # 対応する瞬間 (relation - Realization)
468
+ #
469
+ # @return [When::TM::Instant]
470
+ #
471
+ attr_reader :geometry
472
+
473
+ # オブジェクトの生成
474
+ #
475
+ # @param [When::TM::Instant] geometry 対応する瞬間
476
+ #
477
+ def initialize(geometry)
478
+ super()
479
+ @previous_edge = []
480
+ @next_edge = []
481
+ @geometry = geometry
482
+ @geometry.topology = self
483
+ end
484
+ end
485
+
486
+ # 一次元位相プリミティブ - 幾何的実現は When::TM::Period と対応する
487
+ #
488
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalTopology.xsd#TimeEdgeType gml schema}
489
+ #
490
+ class Edge < TopologicalPrimitive
491
+
492
+ # 対応するノード (relation - Termination)
493
+ #
494
+ # @return [When::TM::Node]
495
+ #
496
+ attr_reader :end
497
+
498
+ # 対応するノード (relation - Initiation)
499
+ #
500
+ # @return [When::TM::Node]
501
+ #
502
+ attr_reader :start
503
+
504
+ # 対応する期間 (relation - Realization)
505
+ #
506
+ # @return [When::TM::Period]
507
+ #
508
+ attr_reader :geometry
509
+
510
+ # オブジェクトの生成
511
+ #
512
+ # @param [When::TM::Node] start 始点
513
+ #
514
+ # @param [When::TM::Node] ended 終点
515
+ #
516
+ def initialize(start, ended)
517
+ super()
518
+ @start = start
519
+ @end = ended
520
+ @geometry = Period.new(@start.geometry, @end.geometry)
521
+ @geometry.topology = self
522
+ @start.next_edge << self
523
+ @end.previous_edge << self
524
+ end
525
+ end
526
+
527
+ # 時間次元における長さ又は距離を記述するために使うデータ型
528
+ #
529
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#duration gml schema}
530
+ #
531
+ class Duration
532
+
533
+ #
534
+ # When::TM::IntervalLength への変換
535
+ #
536
+ # @return [When::TM::IntervalLength]
537
+ #
538
+ def to_interval_length
539
+ [['week', WEEK], ['day', DAY], ['hour', HOUR], ['minute', MINUTE], ['second', SECOND]].each do |unit|
540
+ div, mod = duration.divmod(unit[1])
541
+ return When::TM::IntervalLength.new(div, unit[0]) if mod == 0
542
+ end
543
+ When::TM::IntervalLength.new(duration / SECOND, 'second')
544
+ end
545
+
546
+ #
547
+ # When::TM::PeriodDuration への変換
548
+ #
549
+ # @return [When::TM::PeriodDuration]
550
+ #
551
+ def to_period_duration
552
+ [[When::WEEK, WEEK], [When::DAY, DAY], [When::HOUR, HOUR], [When::MINUTE, MINUTE], [When::SECOND, SECOND]].each do |unit|
553
+ div, mod = duration.divmod(unit[1])
554
+ return When::TM::PeriodDuration.new(div, unit[0]) if mod == 0
555
+ end
556
+ When::TM::PeriodDuration.new(duration / SECOND, When::SECOND)
557
+ end
558
+
559
+ #
560
+ # Duration 用の Enumerator
561
+ #
562
+ class Enumerator < When::Parts::Enumerator
563
+
564
+ # 次の時間位置を取得する
565
+ #
566
+ # @return [When::TM::TemporalPosition] 次の時間位置
567
+ #
568
+ def succ
569
+ value = @current
570
+ @current = (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ? nil :
571
+ (@current==:first) ? @first :
572
+ (@direction==:forward) ? @first + @parent * @count : @first - @parent * @count
573
+ @count += 1
574
+ return value
575
+ end
576
+ end
577
+
578
+ # Enumerator の生成
579
+ #
580
+ # @overload initialize(range, count_limit=nil))
581
+ # @param [Range, When::Parts::GeometricComplex] range
582
+ # [ 始点 - range.first ]
583
+ # [ 終点 - range.last ]
584
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
585
+ #
586
+ # @overload initialize(first, direction, count_limit=nil))
587
+ # @param [When::TM::TemporalPosition] first 始点
588
+ # @param [Symbol] direction
589
+ # [ :forward - 昇順 ]
590
+ # [ :reverse - 降順 ]
591
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
592
+ #
593
+ def _enumerator(*args)
594
+ return Enumerator.new(*args.unshift(self))
595
+ end
596
+ alias :to_enum :_enumerator
597
+ alias :enum_for :_enumerator
598
+ end
599
+
600
+ #
601
+ # ISO 11404 の時間間隔に基づいて定義された When::TM::Duration の subclass
602
+ #
603
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#timeIntervalLengthType gml schema}
604
+ #
605
+ class IntervalLength < Duration
606
+
607
+ Alias = {'Y'=>'year', 'M'=>'month' , 'D'=>'day', 'W'=>'week', 'S'=>'system',
608
+ 'h'=>'hour', 'm'=>'minute', 's'=>'second'}
609
+
610
+ # Interval Length 形式の表現を分解して配列化する
611
+ #
612
+ # @param [String] interval
613
+ #
614
+ # @return [Array<Numeric, String>] ( value, unit_name, factor, radix )
615
+ # [ value [Numeric] - 時間間隔 / 単位 ]
616
+ # [ unit_name [String] - 単位名 ]
617
+ # [ factor [Numeric] - 冪乗 ]
618
+ # [ radix [Numeric] - 冪乗の基数 ]
619
+ #
620
+ # @note value, factor, radix は Numeric
621
+ #
622
+ def self._to_array(interval)
623
+ return nil unless interval =~ /\A([-+]?[\d.]+)(?:(E|X|\((\d+)\))([-+]?\d+))?([A-Za-z]+|\*([\d.]+)S)\z/
624
+ value, radix, radix_quantity, factor, unit, unit_quantity = $~[1..6]
625
+ value = When::Coordinates::Pair._en_number(value)
626
+ radix = case radix
627
+ when 'E', nil ; 10
628
+ when 'X' ; 60
629
+ else ; radix_quantity.to_i
630
+ end
631
+ factor = factor ? -factor.to_i : 0
632
+ return [value, unit_quantity, factor, radix] if unit_quantity
633
+ unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]]
634
+ return [value, UnitName[unit_quantity], factor, radix] if unit_quantity
635
+ return nil
636
+ end
637
+
638
+ # 時間間隔の長さ (128秒単位)
639
+ #
640
+ # @return [Numeric]
641
+ #
642
+ protected :duration=
643
+
644
+ # 時間間隔を表現するために使用した測定単位の名称
645
+ #
646
+ # @return [String] (year|month|week|day|hour|minute|second|system)
647
+ #
648
+ attr_reader :unit
649
+
650
+ # 時間単位となる乗数の基底となる正の整数
651
+ #
652
+ # @return [Integer]
653
+ #
654
+ attr_reader :radix
655
+
656
+ # 基底のべき乗を行う指数
657
+ #
658
+ # @return [Integer]
659
+ #
660
+ attr_reader :factor
661
+
662
+ # 時間間隔の長さ / 測定単位
663
+ #
664
+ # @return [Numeric]
665
+ #
666
+ attr_accessor :value
667
+ protected :value=
668
+
669
+ #
670
+ # 測定単位の大きさ
671
+ #
672
+ # @return [Numeric]
673
+ #
674
+ attr_reader :unit_quantity
675
+
676
+ # 時刻配列
677
+ #
678
+ # @return [Numeric]
679
+ # When::TM::PeriodDuration との互換性のために提供
680
+ #
681
+ # @private
682
+ #
683
+ def time
684
+ [0, 0, @duration / SECOND]
685
+ end
686
+
687
+ # 日付配列
688
+ #
689
+ # @return [nil]
690
+ # When::TM::PeriodDuration との互換性のために提供
691
+ #
692
+ # @private
693
+ #
694
+ def date
695
+ nil
696
+ end
697
+
698
+ # 暦週配列
699
+ #
700
+ # @return [nil]
701
+ # When::TM::PeriodDuration との互換性のために提供
702
+ #
703
+ # @private
704
+ #
705
+ def weeks
706
+ nil
707
+ end
708
+
709
+ # 符号反転
710
+ #
711
+ # @return [When::TM::IntervalLength]
712
+ #
713
+ def -@
714
+ interval = self.dup
715
+ interval.value = -@value
716
+ interval.duration = -@duration
717
+ return interval
718
+ end
719
+
720
+ #
721
+ # 加算
722
+ #
723
+ # @param [When::TM::Duration] other
724
+ # @param [Numeric] other 秒数
725
+ # @param [その他] other 日時とみなす
726
+ #
727
+ # @return [When::TM::IntervalLength] (other が Numeric, When::TM::Duration の場合)
728
+ # @return [other と同じクラス] (other がその他の場合)
729
+ #
730
+ def +(other)
731
+ case other
732
+ when Duration ; diff = other.duration
733
+ when Numeric ; diff = other * SECOND
734
+ else ; return other + self
735
+ end
736
+ interval = self.dup
737
+ interval.duration = @duration + diff
738
+ internal.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
739
+ return interval
740
+ end
741
+
742
+ #
743
+ # 減算
744
+ #
745
+ # @param [When::TM::IntervalLength] other
746
+ # @param [Numeric] other 秒数
747
+ #
748
+ # @return [When::TM::IntervalLength]
749
+ #
750
+ def -(other)
751
+ interval = self.dup
752
+ interval.duration = @duration - (other.kind_of?(Duration) ? other.duration : other * SECOND)
753
+ internal.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
754
+ return interval
755
+ end
756
+
757
+ # 乗算
758
+ #
759
+ # @param [Numeric] times
760
+ #
761
+ # @return [When::TM::IntervalLength]
762
+ #
763
+ def *(times)
764
+ interval = self.dup
765
+ interval.value = times * @value
766
+ interval.duration = times * @duration
767
+ return interval
768
+ end
769
+
770
+ #
771
+ # 除算
772
+ #
773
+ # @param [When::TM::IntervalLength] other
774
+ # @param [Numeric] other 秒数
775
+ #
776
+ # @return [When::TM::IntervalLength] (other が Numeric の場合)
777
+ # @return [Numeric] (other が When::TM::Duration の場合)
778
+ #
779
+ def /(other)
780
+ other.kind_of?(Duration) ? @duration / other.duration : self * (1.0 / other)
781
+ end
782
+
783
+ # 文字列化
784
+ #
785
+ # @return [String]
786
+ #
787
+ def to_s
788
+ expression = @value.to_s
789
+ unless @factor == 0
790
+ case @radix
791
+ when 10 ; expression += 'E'
792
+ when 60 ; expression += 'X'
793
+ else ; expression += '(%d)' % @radix
794
+ end
795
+ expression += '%+d' % (-@factor)
796
+ end
797
+ expression += Alias.invert[@unit] || "*#{When::Coordinates::Pair._en_number(@unit)}S"
798
+ return expression
799
+ end
800
+
801
+ #
802
+ # When::TM::IntervalLength への変換
803
+ #
804
+ # 単なるオブジェクトのコピー
805
+ #
806
+ # @return [When::TM::IntervalLength]
807
+ #
808
+ def to_interval_length
809
+ self.dup
810
+ end
811
+
812
+ # オブジェクトの生成
813
+ #
814
+ # @param [Numeric] value 時間間隔の長さ / 測定単位(省略不可)
815
+ # @param [String] unit 時間間隔を表現するために使用した測定単位の名称(デフォルト : system)
816
+ # ('year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'system')
817
+ # @param [Integer] factor 基底の冪乗を行う指数(デフォルト : 0)
818
+ # @param [Integer] radix 時間単位となる乗数の基底となる正の整数(デフォルト : 10)
819
+ #
820
+ def initialize(value, unit='system', factor=0, radix=10)
821
+ @value, @factor, @radix = value, factor, radix
822
+ @unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]] if unit.kind_of?(String)
823
+ @unit_quantity ||= unit.to_f
824
+ raise TypeError, "Wrong Unit Type: #{unit}" unless @unit_quantity.kind_of?(Numeric)
825
+ @unit = UnitName[@unit_quantity] ||
826
+ When::Coordinates::Pair._en_number(@unit_quantity).to_s
827
+ @duration = @value * @radix ** (-@factor) * @unit_quantity
828
+ end
829
+
830
+ # オブジェクトの生成(終点と始点を指定)
831
+ #
832
+ # @param [When::TM::TemporalPosition] ended 終点
833
+ # @param [When::TM::TemporalPosition] begun 始点
834
+ #
835
+ # @return [When::TM::IntervalLength]
836
+ # [ 日単位の精度 - 終点と始点の分解能のいずれかが“日”またはそれより粗い場合 ]
837
+ # [ システム精度 - 終点と始点の分解能がともに“日”より細かい場合 ]
838
+ #
839
+ def self.difference(ended, begun)
840
+ precision = [ended.precision, begun.precision].min
841
+ return new(ended-begun) if precision > When::DAY
842
+ return new(ended.to_i - begun.to_i, unit='day')
843
+ end
844
+
845
+ private
846
+
847
+ # その他のメソッド
848
+ #
849
+ # @note
850
+ # When::TM::IntervalLength で定義されていないメソッドは
851
+ # 処理を @duration (type:Numeric) に委譲する
852
+ #
853
+ def method_missing(name, *args, &block)
854
+ self.class.module_eval %Q{
855
+ def #{name}(*args, &block)
856
+ @duration.send("#{name}", *args, &block)
857
+ end
858
+ } unless When::Parts::MethodCash.escape(name)
859
+ @duration.send(name, *args, &block)
860
+ end
861
+ end
862
+
863
+ #
864
+ # ISO 8601 (JIS X0301) の時間間隔に基づいて定義された When::TM::Duration の subclass
865
+ #
866
+ class PeriodDuration < Duration
867
+
868
+ include When
869
+ include Coordinates
870
+
871
+ #
872
+ # When::TM::PeriodDuration オブジェクトが月や年などの可変長の単位に依存する場合
873
+ # に @duration 属性の参照を禁止するために用いる
874
+ #
875
+ module NoDuration
876
+ #
877
+ # 属性 @duration が定義できないとをエラーで示す
878
+ #
879
+ # @return [Numeric]
880
+ #
881
+ def duration
882
+ raise TypeError, "This PeriodDuration object does't have duration value"
883
+ end
884
+ end
885
+
886
+ # ISO 8601 形式の表現を分解して配列化する
887
+ #
888
+ # @param [String] period
889
+ #
890
+ # @return [Array<Numeric, Array<Numeric>>] ( sign, date, time, week )
891
+ # [ sign [Numeric] - +1 , -1 ]
892
+ # [ date [Array<Numeric>] - 年月日 ]
893
+ # [ time [Array<Numeric>] - 時分秒 ]
894
+ # [ week [Array<Numeric>] - 週日 ]
895
+ #
896
+ def self._to_array(period)
897
+
898
+ return nil unless period.gsub(/_+/, '') =~ /\A([-+])?P(.*?)?(?:T(.+))?\z/
899
+
900
+ sign = ($1 == '-') ? -1 : +1
901
+ pdate, ptime = $~[2..3]
902
+
903
+ # 時分秒形式
904
+ if (ptime =~ /\A(?:([.,\d]+)?([:*=])?H)?(?:([.,\d]+)?([:*=])?M)?(?:([.,\d]+)?([:*=])?S)?((?:[.,\d]*[:*=]?X)*)\z/)
905
+ trunk = [1,3,5].map {|i|
906
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
907
+ }
908
+ extra = $7 ? $7.scan(/([.,\d]+)?([:*=])?X/).map {|d|
909
+ d[0] ? Pair._en_pair(d[0], d[1]) : 0
910
+ } : []
911
+ time = [0] + trunk + extra
912
+ time = nil if time.uniq == [0]
913
+ end
914
+
915
+ case pdate
916
+ # 年月日形式
917
+ when /\A((?:[.,\d]*[-*=]?X)*)(?:([.,\d]+)?([-*=])?Y)?(?:([.,\d]+)?([-+*&%!>=<?])?M)?(?:([.,\d]+)?([-*=?%])?D)?\z/
918
+ trunk = [2,4,6].map {|i|
919
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
920
+ }
921
+ extra = $1 ? $1.scan(/([.,\d]+)?([:*=])?X/).map {|d|
922
+ d[0] ? Pair._en_pair(d[0], d[1]) : 0
923
+ } : []
924
+ date = extra + trunk
925
+ date = nil if date.uniq == [0]
926
+ return sign, date, time
927
+
928
+ # 週日形式
929
+ when /\A(?:([.,\d]+)?([:*=])?W)(?:([.,\d]+)?([-*=?%])?D)?\z/
930
+ week = [1,3].map {|i|
931
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
932
+ }
933
+ return sign, nil, time if week.uniq == [0]
934
+ date = [0, 0, week[1] + 7*week[0]]
935
+ return sign, date, time, week
936
+
937
+ # 代用形式
938
+ else
939
+ pdate += 'T' + ptime if ptime
940
+ f, d, t, z, e = When::BasicTypes::DateTime._to_array(pdate, {:abbr=>[0]*20})
941
+ return nil if e
942
+ if d
943
+ case f
944
+ when :day ; date = [d[0], 0, d[1]]
945
+ when :century, nil ; date = (0..2).map {|i| d[i]||0}
946
+ when :week ; date, week = [0,0,d[0]*7+(d[1]||0)], [d[0], d[1]||0]
947
+ end
948
+ end
949
+ time = t ? (t.map {|v| v||0}) : nil
950
+ return sign, date, time, week
951
+ end
952
+ end
953
+
954
+ # 期間の日数
955
+ #
956
+ # @return [Integer]
957
+ #
958
+ # @note 固定の整数で表現できない場合は nil
959
+ #
960
+ attr_reader :to_day
961
+
962
+ # 期間の日付要素
963
+ #
964
+ # @return [Array<Numeric>]
965
+ #
966
+ attr_accessor :date
967
+ private :date=
968
+
969
+ # 期間の週日要素
970
+ #
971
+ # @return [Array<Numeric>]
972
+ #
973
+ attr_accessor :week
974
+ private :week=
975
+
976
+ # 期間の時刻要素
977
+ #
978
+ # @return [Array<Numeric>]
979
+ #
980
+ attr_accessor :time
981
+ private :time=
982
+
983
+ # 要素の参照
984
+ #
985
+ # @param [Numeric] index When::Coordinates で定義している分解能定数に対応する列挙型( YEAR, ... , SECOND)
986
+ #
987
+ # @return [Numeric]
988
+ #
989
+ def [](index)
990
+ if index == WEEK
991
+ return nil unless @week
992
+ return @week[0]
993
+ elsif index <= 0
994
+ return nil unless @date
995
+ return @date[index-1]
996
+ else
997
+ return nil unless @time
998
+ return @time[index]
999
+ end
1000
+ end
1001
+
1002
+ # 持続期間であることを文字'P'で示す
1003
+ #
1004
+ # @return [String]
1005
+ #
1006
+ def designator
1007
+ return 'P'
1008
+ end
1009
+
1010
+ # 期間に含まれる年数を示す
1011
+ #
1012
+ # @return [String]
1013
+ #
1014
+ def years
1015
+ return nil unless @date
1016
+ return @date[YEAR-1].to_s
1017
+ end
1018
+
1019
+ # 期間に含まれる月数を示す
1020
+ #
1021
+ # @return [String
1022
+ #
1023
+ def months
1024
+ return nil unless @date
1025
+ return @date[MONTH-1].to_s
1026
+ end
1027
+
1028
+ # 期間に含まれる週数を示す
1029
+ #
1030
+ # @return [String]
1031
+ #
1032
+ def weeks
1033
+ return nil unless @week
1034
+ return @week[0].to_s
1035
+ end
1036
+
1037
+ # 期間に含まれる日数を示す
1038
+ #
1039
+ # @return [String]
1040
+ #
1041
+ def days
1042
+ return nil unless @date
1043
+ return @date[DAY-1].to_s
1044
+ end
1045
+
1046
+ # 期間が1日より短い時間単位を含むとき文字'T'で示す
1047
+ #
1048
+ # @return [String]
1049
+ #
1050
+ def time_indicator
1051
+ return (@time) ? 'T' : nil
1052
+ end
1053
+ alias :timeIndicator :time_indicator
1054
+
1055
+ # 期間に含まれる時間数を示す
1056
+ #
1057
+ # @return [String]
1058
+ #
1059
+ def hours
1060
+ return nil unless @time
1061
+ return @time[HOUR].to_s
1062
+ end
1063
+
1064
+ # 期間に含まれる分数を示す
1065
+ #
1066
+ # @return [String]
1067
+ #
1068
+ def minutes
1069
+ return nil unless @time
1070
+ return @time[MINUTE].to_s
1071
+ end
1072
+
1073
+ # 期間に含まれる秒数を示す
1074
+ #
1075
+ # @return [String]
1076
+ #
1077
+ def seconds
1078
+ return nil unless @time
1079
+ return @time[SECOND].to_s
1080
+ end
1081
+
1082
+ # 符号反転
1083
+ #
1084
+ # @return [When::TM::PeriodDuration]
1085
+ #
1086
+ def -@
1087
+ period = self.dup
1088
+ period.send(:date=, @date.map {|v| -v}) if @date
1089
+ period.send(:week=, @week.map {|v| -v}) if @week
1090
+ period.send(:time=, @time.map {|v| -v}) if @time
1091
+ period.send(:_duration)
1092
+ return period
1093
+ end
1094
+
1095
+ # 符号
1096
+ #
1097
+ # @return [Integer] 0 との比較により、負,0,正の値を返す
1098
+ #
1099
+ def sign
1100
+ ((@week || @date || []) + (@time || [])).each do |v|
1101
+ return -1 if +v < 0
1102
+ end
1103
+ return +1
1104
+ end
1105
+
1106
+ # 比較
1107
+ #
1108
+ # @param [When::TM::PeriodDuration] other
1109
+ #
1110
+ # @return [Integer] other との比較により、負,0,正の値を返す
1111
+ #
1112
+ def <=>(other)
1113
+ unless (@date && @date.size) == (other.date && other.date.size) &&
1114
+ (@week && @week.size) == (other.week && other.week.size) &&
1115
+ (@time && @time.size) == (other.time && other.time.size)
1116
+ raise ArgumentError, "PeriodDuration structure mismatch"
1117
+ end
1118
+
1119
+ (0...@week.size).each do |i|
1120
+ sgn = +@week[i] <=> +other.week[i]
1121
+ return sgn unless sgn == 0
1122
+ end if @week
1123
+
1124
+ (0...@date.size).each do |i|
1125
+ sgn = +@date[i] <=> +other.date[i]
1126
+ return sgn unless sgn == 0
1127
+ end if @date
1128
+
1129
+ (0...@time.size).each do |i|
1130
+ sgn = +@time[i] <=> +other.time[i]
1131
+ return sgn unless sgn == 0
1132
+ end if @time
1133
+
1134
+ return 0
1135
+ end
1136
+
1137
+ # オブジェクトの同値
1138
+ #
1139
+ # @param [比較先] other
1140
+ #
1141
+ # @return [Boolean]
1142
+ # [ true - 同値 ]
1143
+ # [ false - 非同値 ]
1144
+ #
1145
+ def ==(other)
1146
+ (self <=> other) == 0
1147
+ rescue
1148
+ false
1149
+ end
1150
+
1151
+ # 加算
1152
+ #
1153
+ # @param [When::TM::PeriodDuration] other
1154
+ # @param [その他] other 日時とみなす
1155
+ #
1156
+ # @return [WWhen::TM::PeriodDuration] (other が When::TM::PeriodDuration の場合)
1157
+ # @return [other と同じクラス] (other がその他の場合)
1158
+ #
1159
+ def +(other)
1160
+ other.kind_of?(When::TM::PeriodDuration) ? _plus(other, +1) : other + self
1161
+ end
1162
+
1163
+ # 減算
1164
+ #
1165
+ # @param [When::TM::PeriodDuration] other
1166
+ #
1167
+ # @return [When::TM::PeriodDuration]
1168
+ #
1169
+ def -(other)
1170
+ _plus(other, -1)
1171
+ end
1172
+
1173
+ # 乗算
1174
+ #
1175
+ # @param [Numeric] times
1176
+ #
1177
+ # @return [When::TM::PeriodDuration]
1178
+ #
1179
+ def *(times)
1180
+ period = self.dup
1181
+ period.send(:date=, @date.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @date
1182
+ period.send(:week=, @week.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @week
1183
+ period.send(:time=, @time.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @time
1184
+ period.send(:_duration)
1185
+ return period
1186
+ end
1187
+
1188
+ # 除算
1189
+ #
1190
+ # @param [Numeric] divisor
1191
+ #
1192
+ # @return [When::TM::PeriodDuration]
1193
+ #
1194
+ def /(divisor)
1195
+ self * (1.0 / divisor)
1196
+ end
1197
+
1198
+ # 文字列化
1199
+ #
1200
+ # @return [String]
1201
+ #
1202
+ def to_s
1203
+ period = 'P'
1204
+ if @week
1205
+ period += @week[0].abs.to_s + 'W'
1206
+ period += @week[1].abs.to_s + 'D' unless @week[1] == 0
1207
+ elsif @date
1208
+ (-@date.length..-1).each do |i|
1209
+ period += @date[i].abs.to_s + PRECISION_NAME[i+1][0..0] unless @date[i] == 0
1210
+ end
1211
+ end
1212
+ if @time
1213
+ period += 'T'
1214
+ (1..@time.length-1).each do |i|
1215
+ period += @time[i].abs.to_s + PRECISION_NAME[i][0..0] unless @time[i] == 0
1216
+ end
1217
+ end
1218
+ period = '-' + period if sign < 0
1219
+ period == 'P' ? 'P0D' : period
1220
+ end
1221
+
1222
+ #
1223
+ # When::TM::PeriodDuration への変換
1224
+ #
1225
+ # 単なるオブジェクトのコピー
1226
+ #
1227
+ # @return [When::TM::PeriodDuration]
1228
+ #
1229
+ def to_period_duration
1230
+ self.dup
1231
+ end
1232
+
1233
+ # オブジェクトの生成
1234
+ #
1235
+ # @overload initialize(date=nil, time=nil, week=nil)
1236
+ # @param [Array<Numeric>] date 期間の日付要素
1237
+ # @param [Array<Numeric>] time 期間の時刻要素
1238
+ # @param [Array<Numeric>] week 期間の週日要素
1239
+ #
1240
+ # @overload initialize(value, index, range=YEAR..SECOND)
1241
+ # @param [Numeric] value ndex に対応する桁での)時間間隔
1242
+ # @param [Numeric] index When で定義している分解能定数(YEAR, ... ,SECOND)
1243
+ # @param [Range] range 生成する桁の範囲
1244
+ #
1245
+ def initialize(*args)
1246
+ @options = (args[-1].kind_of?(Hash)) ? (args.pop.reject {|key,value| value == nil}) : {}
1247
+ if args[0].kind_of?(Numeric)
1248
+ _initialize_by_unit(*args)
1249
+ else
1250
+ @date, @time, @week = args
1251
+ end
1252
+ _duration
1253
+ end
1254
+
1255
+ private
1256
+
1257
+ #
1258
+ # 引数パターン2での初期化
1259
+ #
1260
+ def _initialize_by_unit(value, index, range=YEAR..SECOND)
1261
+ max = range.first
1262
+ min = range.last
1263
+ min += 1 if (range.exclude_end?)
1264
+ if (index < max)
1265
+ if (index > MONTH)
1266
+ max = index.floor
1267
+ else
1268
+ value *= 10**(max-index)
1269
+ index = max
1270
+ end
1271
+ elsif (index > min)
1272
+ if (index <= DAY)
1273
+ min = index.ceil
1274
+ else
1275
+ value *= 0.1**(index-min)
1276
+ index = min
1277
+ end
1278
+ end
1279
+ value = value.to_i unless value.kind_of?(Pair) || value.to_i != value.to_f
1280
+ @date = Array.new(1-max, 0) if (max <= DAY) && (index <= DAY)
1281
+ @time = Array.new(1+min, 0) if (min > DAY) && (index > DAY)
1282
+ if (index == WEEK)
1283
+ @week = [value, 0]
1284
+ @date[DAY-1] = 7 * value
1285
+ elsif (index <= DAY)
1286
+ @date[index-1] = value
1287
+ else
1288
+ @time[index] = value
1289
+ end
1290
+ end
1291
+
1292
+ #
1293
+ # 属性 @duration の計算
1294
+ #
1295
+ def _duration
1296
+ @duration = nil
1297
+
1298
+ if @time
1299
+ @duration = +@time[HOUR ] * Duration::HOUR
1300
+ @duration += +@time[MINUTE] * Duration::MINUTE if @time[MINUTE]
1301
+ @duration += +@time[SECOND] * Duration::SECOND if @time[SECOND]
1302
+ end
1303
+
1304
+ if @week
1305
+ @duration = +@week[0] * Duration::WEEK + +@week[1] * Duration::DAY + (@duration||0)
1306
+
1307
+ elsif @date
1308
+ date = @date.dup
1309
+ date.shift while date.first == 0
1310
+ case date.size
1311
+ when 0 ; @duration ||= 0
1312
+ when 1 ; @duration = +@date[DAY-1] * Duration::DAY + (@duration||0)
1313
+ else ; @duration = nil
1314
+ end
1315
+ end
1316
+
1317
+ if @duration
1318
+ length = @duration / Duration::DAY
1319
+ @to_day = length.to_i if length.to_i == length.to_f
1320
+ else
1321
+ extend NoDuration
1322
+ end
1323
+ end
1324
+
1325
+ # 加減算共通処理
1326
+ #
1327
+ # @param [When::TM::PeriodDuration] other
1328
+ # @param [Numeric] sgn +1, -1
1329
+ #
1330
+ # @return [When::TM::PeriodDuration]
1331
+ #
1332
+ def _plus(other, sgn)
1333
+ unless (@week && @week.size) == (other.week && other.week.size) &&
1334
+ (@date && @date.size) == (other.date && other.date.size) &&
1335
+ (@time && @time.size) == (other.time && other.time.size)
1336
+ raise ArgumentError, "PeriodDuration structure mismatch"
1337
+ end
1338
+ period = self.dup
1339
+ period.send(:week=, (0...@week.size).to_a.map {|i| @week[i] + sgn * other.week[i]}) if @week
1340
+ period.send(:date=, (0...@date.size).to_a.map {|i| @date[i] + sgn * other.date[i]}) if @date
1341
+ period.send(:time=, (0...@time.size).to_a.map {|i| @time[i] + sgn * other.time[i]}) if @time
1342
+ period.send(:_duration)
1343
+ return period
1344
+ end
1345
+ end
1346
+ end