when_exe 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -33
  3. data/bin/locales.rb +1 -1
  4. data/bin/make_ttl.rb.config +1 -1
  5. data/lib/when_exe.rb +27 -16
  6. data/lib/when_exe/basictypes.rb +772 -771
  7. data/lib/when_exe/calendartypes.rb +1485 -1453
  8. data/lib/when_exe/coordinates.rb +5 -0
  9. data/lib/when_exe/core/compatibility.rb +1 -1
  10. data/lib/when_exe/core/duration.rb +147 -116
  11. data/lib/when_exe/core/extension.rb +499 -497
  12. data/lib/when_exe/ephemeris.rb +1952 -1951
  13. data/lib/when_exe/ephemeris/eclipse.rb +5 -4
  14. data/lib/when_exe/ephemeris/notes.rb +457 -421
  15. data/lib/when_exe/ephemeris/planets.rb +585 -585
  16. data/lib/when_exe/ephemeris/sun.rb +214 -214
  17. data/lib/when_exe/google_api.rb +153 -0
  18. data/lib/when_exe/icalendar.rb +1640 -1632
  19. data/lib/when_exe/inspect.rb +42 -20
  20. data/lib/when_exe/linkeddata.rb +28 -7
  21. data/lib/when_exe/locales/autoload.rb +2 -1
  22. data/lib/when_exe/locales/locale.rb +35 -15
  23. data/lib/when_exe/locales/zh.rb +77 -0
  24. data/lib/when_exe/mini_application.rb +3 -1
  25. data/lib/when_exe/{googlecalendar.rb → obsolete/googlecalendar.rb} +144 -144
  26. data/lib/when_exe/parts/enumerator.rb +498 -486
  27. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  28. data/lib/when_exe/parts/timezone.rb +246 -241
  29. data/lib/when_exe/region/armenian.rb +55 -56
  30. data/lib/when_exe/region/babylonian.rb +406 -405
  31. data/lib/when_exe/region/bahai.rb +107 -106
  32. data/lib/when_exe/region/balinese.rb +624 -622
  33. data/lib/when_exe/region/chinese.rb +1071 -1026
  34. data/lib/when_exe/region/chinese/epochs.rb +28 -28
  35. data/lib/when_exe/region/chinese/notes.rb +219 -0
  36. data/lib/when_exe/region/chinese/twins.rb +803 -803
  37. data/lib/when_exe/region/christian.rb +21 -15
  38. data/lib/when_exe/region/coptic.rb +107 -106
  39. data/lib/when_exe/region/discordian.rb +218 -218
  40. data/lib/when_exe/region/east_asian.rb +1 -1
  41. data/lib/when_exe/region/french.rb +126 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +60 -58
  44. data/lib/when_exe/region/hanke_henry.rb +2 -2
  45. data/lib/when_exe/region/indian.rb +1225 -1222
  46. data/lib/when_exe/region/international_fixed.rb +96 -97
  47. data/lib/when_exe/region/iranian.rb +206 -203
  48. data/lib/when_exe/region/islamic.rb +102 -102
  49. data/lib/when_exe/region/japanese.rb +126 -71
  50. data/lib/when_exe/region/japanese/epochs.rb +426 -426
  51. data/lib/when_exe/region/japanese/notes.rb +101 -81
  52. data/lib/when_exe/region/japanese/residues.rb +1345 -1311
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -112
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +130 -131
  57. data/lib/when_exe/region/m17n.rb +114 -114
  58. data/lib/when_exe/region/martian.rb +258 -258
  59. data/lib/when_exe/region/mayan.rb +11 -8
  60. data/lib/when_exe/region/pax.rb +4 -5
  61. data/lib/when_exe/region/pope.rb +1 -1
  62. data/lib/when_exe/region/positivist.rb +100 -100
  63. data/lib/when_exe/region/residue.rb +162 -162
  64. data/lib/when_exe/region/roman.rb +333 -333
  65. data/lib/when_exe/region/{soviet.rb → russian.rb} +221 -209
  66. data/lib/when_exe/region/shire.rb +222 -223
  67. data/lib/when_exe/region/symmetry.rb +50 -50
  68. data/lib/when_exe/region/thai.rb +336 -336
  69. data/lib/when_exe/region/tibetan.rb +315 -316
  70. data/lib/when_exe/region/tranquility.rb +207 -208
  71. data/lib/when_exe/region/vanishing_leprechaun.rb +3 -1
  72. data/lib/when_exe/region/vietnamese.rb +449 -440
  73. data/lib/when_exe/region/weekdate.rb +80 -80
  74. data/lib/when_exe/region/world.rb +170 -171
  75. data/lib/when_exe/region/world_season.rb +89 -89
  76. data/lib/when_exe/region/yerm.rb +3 -3
  77. data/lib/when_exe/region/zoroastrian.rb +205 -205
  78. data/lib/when_exe/timestandard.rb +708 -707
  79. data/lib/when_exe/tmduration.rb +338 -338
  80. data/lib/when_exe/tmobjects.rb +1356 -1356
  81. data/lib/when_exe/tmposition.rb +66 -31
  82. data/lib/when_exe/version.rb +16 -2
  83. data/test/examples/Residue.m17n +83 -83
  84. data/test/examples/Terms.m17n +2 -2
  85. data/test/test.rb +2 -2
  86. data/test/test/google_api.rb +65 -0
  87. data/test/test/linkeddata.rb +1 -1
  88. data/test/test/{googlecalendar.rb → obsolete/googlecalendar.rb} +194 -194
  89. data/test/test/region/indian.rb +90 -85
  90. data/test/test/region/m17n.rb +7 -7
  91. data/test/test/region/mayan.rb +195 -195
  92. data/test/test/region/residue.rb +153 -153
  93. data/test/test/tmposition.rb +11 -1
  94. data/when_exe.gemspec +2 -2
  95. metadata +95 -8
  96. data/test/test.rb.config +0 -1
@@ -1,1356 +1,1356 @@
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==:reverse) ? @first - @parent * @count : @first + @parent * @count
573
- if @last
574
- sign = @parent.sign
575
- sign = -sign if @direction==:reverse
576
- @current = nil if (sign * (@current <=> @last)) > 0
577
- end
578
- @count += 1
579
- return value
580
- end
581
- end
582
-
583
- # Enumerator の生成
584
- #
585
- # @overload initialize(range, count_limit=nil))
586
- # @param [Range, When::Parts::GeometricComplex] range
587
- # [ 始点 - range.first ]
588
- # [ 終点 - range.last ]
589
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
590
- #
591
- # @overload initialize(first, direction, count_limit=nil))
592
- # @param [When::TM::TemporalPosition] first 始点
593
- # @param [Symbol] direction
594
- # [ :forward - 昇順 ]
595
- # [ :reverse - 降順 ]
596
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
597
- #
598
- def _enumerator(*args)
599
- return Enumerator.new(*args.unshift(self))
600
- end
601
- alias :to_enum :_enumerator
602
- alias :enum_for :_enumerator
603
- end
604
-
605
- #
606
- # ISO 11404 の時間間隔に基づいて定義された When::TM::Duration の subclass
607
- #
608
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#timeIntervalLengthType gml schema}
609
- #
610
- class IntervalLength < Duration
611
-
612
- Alias = {'Y'=>'year', 'M'=>'month' , 'D'=>'day', 'W'=>'week', 'S'=>'system',
613
- 'h'=>'hour', 'm'=>'minute', 's'=>'second'}
614
-
615
- # Interval Length 形式の表現を分解して配列化する
616
- #
617
- # @param [String] interval
618
- #
619
- # @return [Array<Numeric, String>] ( value, unit_name, factor, radix )
620
- # [ value [Numeric] - 時間間隔 / 単位 ]
621
- # [ unit_name [String] - 単位名 ]
622
- # [ factor [Numeric] - 冪乗 ]
623
- # [ radix [Numeric] - 冪乗の基数 ]
624
- #
625
- # @note value, factor, radix は Numeric
626
- #
627
- def self._to_array(interval)
628
- return nil unless interval =~ /\A([-+]?[\d.]+)(?:(E|X|\((\d+)\))([-+]?\d+))?([A-Za-z]+|\*([\d.]+)S)\z/
629
- value, radix, radix_quantity, factor, unit, unit_quantity = $~[1..6]
630
- value = When::Coordinates::Pair._en_number(value)
631
- radix = case radix
632
- when 'E', nil ; 10
633
- when 'X' ; 60
634
- else ; radix_quantity.to_i
635
- end
636
- factor = factor ? -factor.to_i : 0
637
- return [value, unit_quantity, factor, radix] if unit_quantity
638
- unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]]
639
- return [value, UnitName[unit_quantity], factor, radix] if unit_quantity
640
- return nil
641
- end
642
-
643
- # 時間間隔の長さ (128秒単位)
644
- #
645
- # @return [Numeric]
646
- #
647
- protected :duration=
648
-
649
- # 時間間隔を表現するために使用した測定単位の名称
650
- #
651
- # @return [String] (year|month|week|day|hour|minute|second|system)
652
- #
653
- attr_reader :unit
654
-
655
- # 時間単位となる乗数の基底となる正の整数
656
- #
657
- # @return [Integer]
658
- #
659
- attr_reader :radix
660
-
661
- # 基底のべき乗を行う指数
662
- #
663
- # @return [Integer]
664
- #
665
- attr_reader :factor
666
-
667
- # 時間間隔の長さ / 測定単位
668
- #
669
- # @return [Numeric]
670
- #
671
- attr_accessor :value
672
- protected :value=
673
-
674
- #
675
- # 測定単位の大きさ
676
- #
677
- # @return [Numeric]
678
- #
679
- attr_reader :unit_quantity
680
-
681
- # 時刻配列
682
- #
683
- # @return [Numeric]
684
- # When::TM::PeriodDuration との互換性のために提供
685
- #
686
- # @private
687
- #
688
- def time
689
- [0, 0, @duration / SECOND]
690
- end
691
-
692
- # 日付配列
693
- #
694
- # @return [nil]
695
- # When::TM::PeriodDuration との互換性のために提供
696
- #
697
- # @private
698
- #
699
- def date
700
- nil
701
- end
702
-
703
- # 暦週配列
704
- #
705
- # @return [nil]
706
- # When::TM::PeriodDuration との互換性のために提供
707
- #
708
- # @private
709
- #
710
- def weeks
711
- nil
712
- end
713
-
714
- # 符号反転
715
- #
716
- # @return [When::TM::IntervalLength]
717
- #
718
- def -@
719
- interval = self.dup
720
- interval.value = -@value
721
- interval.duration = -@duration
722
- return interval
723
- end
724
-
725
- #
726
- # 加算
727
- #
728
- # @param [When::TM::Duration] other
729
- # @param [Numeric] other 秒数
730
- # @param [その他] other 日時とみなす
731
- #
732
- # @return [When::TM::IntervalLength] (other が Numeric, When::TM::Duration の場合)
733
- # @return [other と同じクラス] (other がその他の場合)
734
- #
735
- def +(other)
736
- case other
737
- when Duration ; diff = other.duration
738
- when Numeric ; diff = other * SECOND
739
- else ; return other + self
740
- end
741
- interval = self.dup
742
- interval.duration = @duration + diff
743
- interval.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
744
- return interval
745
- end
746
-
747
- #
748
- # 減算
749
- #
750
- # @param [When::TM::IntervalLength] other
751
- # @param [Numeric] other 秒数
752
- #
753
- # @return [When::TM::IntervalLength]
754
- #
755
- def -(other)
756
- interval = self.dup
757
- interval.duration = @duration - (other.kind_of?(Duration) ? other.duration : other * SECOND)
758
- interval.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
759
- return interval
760
- end
761
-
762
- # 乗算
763
- #
764
- # @param [Numeric] times
765
- #
766
- # @return [When::TM::IntervalLength]
767
- #
768
- def *(times)
769
- interval = self.dup
770
- interval.value = times * @value
771
- interval.duration = times * @duration
772
- return interval
773
- end
774
-
775
- #
776
- # 除算
777
- #
778
- # @param [When::TM::IntervalLength] other
779
- # @param [Numeric] other 秒数
780
- #
781
- # @return [When::TM::IntervalLength] (other が Numeric の場合)
782
- # @return [Numeric] (other が When::TM::Duration の場合)
783
- #
784
- def /(other)
785
- other.kind_of?(Duration) ? @duration / other.duration : self * (1.0 / other)
786
- end
787
-
788
- # 文字列化
789
- #
790
- # @return [String]
791
- #
792
- def to_s
793
- expression = @value.to_s
794
- unless @factor == 0
795
- case @radix
796
- when 10 ; expression += 'E'
797
- when 60 ; expression += 'X'
798
- else ; expression += '(%d)' % @radix
799
- end
800
- expression += '%+d' % (-@factor)
801
- end
802
- expression += Alias.invert[@unit] || "*#{When::Coordinates::Pair._en_number(@unit)}S"
803
- return expression
804
- end
805
-
806
- #
807
- # When::TM::IntervalLength への変換
808
- #
809
- # 単なるオブジェクトのコピー
810
- #
811
- # @return [When::TM::IntervalLength]
812
- #
813
- def to_interval_length
814
- self.dup
815
- end
816
-
817
- # オブジェクトの生成
818
- #
819
- # @param [Numeric] value 時間間隔の長さ / 測定単位(省略不可)
820
- # @param [String] unit 時間間隔を表現するために使用した測定単位の名称(デフォルト : system)
821
- # ('year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'system')
822
- # @param [Integer] factor 基底の冪乗を行う指数(デフォルト : 0)
823
- # @param [Integer] radix 時間単位となる乗数の基底となる正の整数(デフォルト : 10)
824
- #
825
- def initialize(value, unit='system', factor=0, radix=10)
826
- @value, @factor, @radix = value, factor, radix
827
- @unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]] if unit.kind_of?(String)
828
- @unit_quantity ||= unit.to_f
829
- raise TypeError, "Wrong Unit Type: #{unit}" unless @unit_quantity.kind_of?(Numeric)
830
- @unit = UnitName[@unit_quantity] ||
831
- When::Coordinates::Pair._en_number(@unit_quantity).to_s
832
- @duration = @value * @radix ** (-@factor) * @unit_quantity
833
- end
834
-
835
- # オブジェクトの生成(終点と始点を指定)
836
- #
837
- # @param [When::TM::TemporalPosition] ended 終点
838
- # @param [When::TM::TemporalPosition] begun 始点
839
- #
840
- # @return [When::TM::IntervalLength]
841
- # [ 日単位の精度 - 終点と始点の分解能のいずれかが“日”またはそれより粗い場合 ]
842
- # [ システム精度 - 終点と始点の分解能がともに“日”より細かい場合 ]
843
- #
844
- def self.difference(ended, begun)
845
- precision = [ended.precision, begun.precision].min
846
- return new(ended-begun) if precision > When::DAY
847
- return new(ended.to_i - begun.to_i, unit='day')
848
- end
849
-
850
- private
851
-
852
- # その他のメソッド
853
- #
854
- # @note
855
- # When::TM::IntervalLength で定義されていないメソッドは
856
- # 処理を @duration (type:Numeric) に委譲する
857
- #
858
- def method_missing(name, *args, &block)
859
- self.class.module_eval %Q{
860
- def #{name}(*args, &block)
861
- @duration.send("#{name}", *args, &block)
862
- end
863
- } unless When::Parts::MethodCash.escape(name)
864
- @duration.send(name, *args, &block)
865
- end
866
- end
867
-
868
- #
869
- # ISO 8601 (JIS X0301) の時間間隔に基づいて定義された When::TM::Duration の subclass
870
- #
871
- class PeriodDuration < Duration
872
-
873
- include When
874
- include Coordinates
875
-
876
- #
877
- # When::TM::PeriodDuration オブジェクトが月や年などの可変長の単位に依存する場合
878
- # に @duration 属性の参照を禁止するために用いる
879
- #
880
- module NoDuration
881
- #
882
- # 属性 @duration が定義できないとをエラーで示す
883
- #
884
- # @return [Numeric]
885
- #
886
- def duration
887
- raise TypeError, "This PeriodDuration object does't have duration value"
888
- end
889
- end
890
-
891
- # ISO 8601 形式の表現を分解して配列化する
892
- #
893
- # @param [String] period
894
- #
895
- # @return [Array<Numeric, Array<Numeric>>] ( sign, date, time, week )
896
- # [ sign [Numeric] - +1 , -1 ]
897
- # [ date [Array<Numeric>] - 年月日 ]
898
- # [ time [Array<Numeric>] - 時分秒 ]
899
- # [ week [Array<Numeric>] - 週日 ]
900
- #
901
- def self._to_array(period)
902
-
903
- return nil unless period.gsub(/_+/, '') =~ /\A([-+])?P(.*?)?(?:T(.+))?\z/
904
-
905
- sign = ($1 == '-') ? -1 : +1
906
- pdate, ptime = $~[2..3]
907
-
908
- # 時分秒形式
909
- if (ptime =~ /\A(?:([.,\d]+)?([:*=])?H)?(?:([.,\d]+)?([:*=])?M)?(?:([.,\d]+)?([:*=])?S)?((?:[.,\d]*[:*=]?X)*)\z/)
910
- trunk = [1,3,5].map {|i|
911
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
912
- }
913
- extra = $7 ? $7.scan(/([.,\d]+)?([:*=])?X/).map {|d|
914
- d[0] ? Pair._en_pair(d[0], d[1]) : 0
915
- } : []
916
- time = [0] + trunk + extra
917
- time = nil if time.uniq == [0]
918
- end
919
-
920
- case pdate
921
- # 年月日形式
922
- when /\A((?:[.,\d]*[-*=]?X)*)(?:([.,\d]+)?([-*=])?Y)?(?:([.,\d]+)?([-+*&%!>=<?])?M)?(?:([.,\d]+)?([-*=?%])?D)?\z/
923
- trunk = [2,4,6].map {|i|
924
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
925
- }
926
- extra = $1 ? $1.scan(/([.,\d]+)?([:*=])?X/).map {|d|
927
- d[0] ? Pair._en_pair(d[0], d[1]) : 0
928
- } : []
929
- date = extra + trunk
930
- date = nil if date.uniq == [0]
931
- return sign, date, time
932
-
933
- # 週日形式
934
- when /\A(?:([.,\d]+)?([:*=])?W)(?:([.,\d]+)?([-*=?%])?D)?\z/
935
- week = [1,3].map {|i|
936
- $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
937
- }
938
- return sign, nil, time if week.uniq == [0]
939
- date = [0, 0, week[1] + 7*week[0]]
940
- return sign, date, time, week
941
-
942
- # 代用形式
943
- else
944
- pdate += 'T' + ptime if ptime
945
- f, d, t, z, e = When::BasicTypes::DateTime._to_array(pdate, {:abbr=>[0]*20})
946
- return nil if e
947
- if d
948
- case f
949
- when :day ; date = [d[0], 0, d[1]]
950
- when :century, nil ; date = (0..2).map {|i| d[i]||0}
951
- when :week ; date, week = [0,0,d[0]*7+(d[1]||0)], [d[0], d[1]||0]
952
- end
953
- end
954
- time = t ? (t.map {|v| v||0}) : nil
955
- return sign, date, time, week
956
- end
957
- end
958
-
959
- # 期間の日数
960
- #
961
- # @return [Integer]
962
- #
963
- # @note 固定の整数で表現できない場合は nil
964
- #
965
- attr_reader :to_day
966
-
967
- # 期間の日付要素
968
- #
969
- # @return [Array<Numeric>]
970
- #
971
- attr_accessor :date
972
- private :date=
973
-
974
- # 期間の週日要素
975
- #
976
- # @return [Array<Numeric>]
977
- #
978
- attr_accessor :week
979
- private :week=
980
-
981
- # 期間の時刻要素
982
- #
983
- # @return [Array<Numeric>]
984
- #
985
- attr_accessor :time
986
- private :time=
987
-
988
- # 要素の参照
989
- #
990
- # @param [Numeric] index When::Coordinates で定義している分解能定数に対応する列挙型( YEAR, ... , SECOND)
991
- #
992
- # @return [Numeric]
993
- #
994
- def [](index)
995
- if index == WEEK
996
- return nil unless @week
997
- return @week[0]
998
- elsif index <= 0
999
- return nil unless @date
1000
- return @date[index-1]
1001
- else
1002
- return nil unless @time
1003
- return @time[index]
1004
- end
1005
- end
1006
-
1007
- # 持続期間であることを文字'P'で示す
1008
- #
1009
- # @return [String]
1010
- #
1011
- def designator
1012
- return 'P'
1013
- end
1014
-
1015
- # 期間に含まれる年数を示す
1016
- #
1017
- # @return [String]
1018
- #
1019
- def years
1020
- return nil unless @date
1021
- return @date[YEAR-1].to_s
1022
- end
1023
-
1024
- # 期間に含まれる月数を示す
1025
- #
1026
- # @return [String
1027
- #
1028
- def months
1029
- return nil unless @date
1030
- return @date[MONTH-1].to_s
1031
- end
1032
-
1033
- # 期間に含まれる週数を示す
1034
- #
1035
- # @return [String]
1036
- #
1037
- def weeks
1038
- return nil unless @week
1039
- return @week[0].to_s
1040
- end
1041
-
1042
- # 期間に含まれる日数を示す
1043
- #
1044
- # @return [String]
1045
- #
1046
- def days
1047
- return nil unless @date
1048
- return @date[DAY-1].to_s
1049
- end
1050
-
1051
- # 期間が1日より短い時間単位を含むとき文字'T'で示す
1052
- #
1053
- # @return [String]
1054
- #
1055
- def time_indicator
1056
- return (@time) ? 'T' : nil
1057
- end
1058
- alias :timeIndicator :time_indicator
1059
-
1060
- # 期間に含まれる時間数を示す
1061
- #
1062
- # @return [String]
1063
- #
1064
- def hours
1065
- return nil unless @time
1066
- return @time[HOUR].to_s
1067
- end
1068
-
1069
- # 期間に含まれる分数を示す
1070
- #
1071
- # @return [String]
1072
- #
1073
- def minutes
1074
- return nil unless @time
1075
- return @time[MINUTE].to_s
1076
- end
1077
-
1078
- # 期間に含まれる秒数を示す
1079
- #
1080
- # @return [String]
1081
- #
1082
- def seconds
1083
- return nil unless @time
1084
- return @time[SECOND].to_s
1085
- end
1086
-
1087
- # 符号反転
1088
- #
1089
- # @return [When::TM::PeriodDuration]
1090
- #
1091
- def -@
1092
- period = self.dup
1093
- period.send(:date=, @date.map {|v| -v}) if @date
1094
- period.send(:week=, @week.map {|v| -v}) if @week
1095
- period.send(:time=, @time.map {|v| -v}) if @time
1096
- period.send(:_duration)
1097
- return period
1098
- end
1099
-
1100
- # 符号
1101
- #
1102
- # @return [Integer] 0 との比較により、負,0,正の値を返す
1103
- #
1104
- def sign
1105
- @sign ||= _sign
1106
- end
1107
-
1108
- def _sign
1109
- ((@week || @date || []) + (@time || [])).each do |v|
1110
- return -1 if +v < 0
1111
- end
1112
- return +1
1113
- end
1114
- private :_sign
1115
-
1116
- # 比較
1117
- #
1118
- # @param [When::TM::PeriodDuration] other
1119
- #
1120
- # @return [Integer] other との比較により、負,0,正の値を返す
1121
- #
1122
- def <=>(other)
1123
- unless (@date && @date.size) == (other.date && other.date.size) &&
1124
- (@week && @week.size) == (other.week && other.week.size) &&
1125
- (@time && @time.size) == (other.time && other.time.size)
1126
- raise ArgumentError, "PeriodDuration structure mismatch"
1127
- end
1128
-
1129
- (0...@week.size).each do |i|
1130
- sgn = +@week[i] <=> +other.week[i]
1131
- return sgn unless sgn == 0
1132
- end if @week
1133
-
1134
- (0...@date.size).each do |i|
1135
- sgn = +@date[i] <=> +other.date[i]
1136
- return sgn unless sgn == 0
1137
- end if @date
1138
-
1139
- (0...@time.size).each do |i|
1140
- sgn = +@time[i] <=> +other.time[i]
1141
- return sgn unless sgn == 0
1142
- end if @time
1143
-
1144
- return 0
1145
- end
1146
-
1147
- # オブジェクトの同値
1148
- #
1149
- # @param [比較先] other
1150
- #
1151
- # @return [Boolean]
1152
- # [ true - 同値 ]
1153
- # [ false - 非同値 ]
1154
- #
1155
- def ==(other)
1156
- (self <=> other) == 0
1157
- rescue
1158
- false
1159
- end
1160
-
1161
- # 加算
1162
- #
1163
- # @param [When::TM::PeriodDuration] other
1164
- # @param [その他] other 日時とみなす
1165
- #
1166
- # @return [WWhen::TM::PeriodDuration] (other が When::TM::PeriodDuration の場合)
1167
- # @return [other と同じクラス] (other がその他の場合)
1168
- #
1169
- def +(other)
1170
- other.kind_of?(When::TM::PeriodDuration) ? _plus(other, +1) : other + self
1171
- end
1172
-
1173
- # 減算
1174
- #
1175
- # @param [When::TM::PeriodDuration] other
1176
- #
1177
- # @return [When::TM::PeriodDuration]
1178
- #
1179
- def -(other)
1180
- _plus(other, -1)
1181
- end
1182
-
1183
- # 乗算
1184
- #
1185
- # @param [Numeric] times
1186
- #
1187
- # @return [When::TM::PeriodDuration]
1188
- #
1189
- def *(times)
1190
- period = self.dup
1191
- period.send(:date=, @date.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @date
1192
- period.send(:week=, @week.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @week
1193
- period.send(:time=, @time.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @time
1194
- period.send(:_duration)
1195
- return period
1196
- end
1197
-
1198
- # 除算
1199
- #
1200
- # @param [Numeric] divisor
1201
- #
1202
- # @return [When::TM::PeriodDuration]
1203
- #
1204
- def /(divisor)
1205
- self * (1.0 / divisor)
1206
- end
1207
-
1208
- # 文字列化
1209
- #
1210
- # @return [String]
1211
- #
1212
- def to_s
1213
- period = 'P'
1214
- if @week
1215
- period += @week[0].abs.to_s + 'W'
1216
- period += @week[1].abs.to_s + 'D' unless @week[1] == 0
1217
- elsif @date
1218
- (-@date.length..-1).each do |i|
1219
- period += @date[i].abs.to_s + PRECISION_NAME[i+1][0..0] unless @date[i] == 0
1220
- end
1221
- end
1222
- if @time
1223
- period += 'T'
1224
- (1..@time.length-1).each do |i|
1225
- period += @time[i].abs.to_s + PRECISION_NAME[i][0..0] unless @time[i] == 0
1226
- end
1227
- end
1228
- period = '-' + period if sign < 0
1229
- period == 'P' ? 'P0D' : period
1230
- end
1231
-
1232
- #
1233
- # When::TM::PeriodDuration への変換
1234
- #
1235
- # 単なるオブジェクトのコピー
1236
- #
1237
- # @return [When::TM::PeriodDuration]
1238
- #
1239
- def to_period_duration
1240
- self.dup
1241
- end
1242
-
1243
- # オブジェクトの生成
1244
- #
1245
- # @overload initialize(date=nil, time=nil, week=nil)
1246
- # @param [Array<Numeric>] date 期間の日付要素
1247
- # @param [Array<Numeric>] time 期間の時刻要素
1248
- # @param [Array<Numeric>] week 期間の週日要素
1249
- #
1250
- # @overload initialize(value, index, range=YEAR..SECOND)
1251
- # @param [Numeric] value ndex に対応する桁での)時間間隔
1252
- # @param [Numeric] index When で定義している分解能定数(YEAR, ... ,SECOND)
1253
- # @param [Range] range 生成する桁の範囲
1254
- #
1255
- def initialize(*args)
1256
- @options = (args[-1].kind_of?(Hash)) ? (args.pop.reject {|key,value| value == nil}) : {}
1257
- if args[0].kind_of?(Numeric)
1258
- _initialize_by_unit(*args)
1259
- else
1260
- @date, @time, @week = args
1261
- end
1262
- _duration
1263
- end
1264
-
1265
- private
1266
-
1267
- #
1268
- # 引数パターン2での初期化
1269
- #
1270
- def _initialize_by_unit(value, index, range=YEAR..SECOND)
1271
- max = range.first
1272
- min = range.last
1273
- min += 1 if (range.exclude_end?)
1274
- if (index < max)
1275
- if (index > MONTH)
1276
- max = index.floor
1277
- else
1278
- value *= 10**(max-index)
1279
- index = max
1280
- end
1281
- elsif (index > min)
1282
- if (index <= DAY)
1283
- min = index.ceil
1284
- else
1285
- value *= 0.1**(index-min)
1286
- index = min
1287
- end
1288
- end
1289
- value = value.to_i unless value.kind_of?(Pair) || value.to_i != value.to_f
1290
- @date = Array.new(1-max, 0) if (max <= DAY) && (index <= DAY)
1291
- @time = Array.new(1+min, 0) if (min > DAY) && (index > DAY)
1292
- if (index == WEEK)
1293
- @week = [value, 0]
1294
- @date[DAY-1] = 7 * value
1295
- elsif (index <= DAY)
1296
- @date[index-1] = value
1297
- else
1298
- @time[index] = value
1299
- end
1300
- end
1301
-
1302
- #
1303
- # 属性 @duration の計算
1304
- #
1305
- def _duration
1306
- @duration = @sign = nil
1307
-
1308
- if @time
1309
- @duration = +@time[HOUR ] * Duration::HOUR
1310
- @duration += +@time[MINUTE] * Duration::MINUTE if @time[MINUTE]
1311
- @duration += +@time[SECOND] * Duration::SECOND if @time[SECOND]
1312
- end
1313
-
1314
- if @week
1315
- @duration = +@week[0] * Duration::WEEK + +@week[1] * Duration::DAY + (@duration||0)
1316
-
1317
- elsif @date
1318
- date = @date.dup
1319
- date.shift while date.first == 0
1320
- case date.size
1321
- when 0 ; @duration ||= 0
1322
- when 1 ; @duration = +@date[DAY-1] * Duration::DAY + (@duration||0)
1323
- else ; @duration = nil
1324
- end
1325
- end
1326
-
1327
- if @duration
1328
- length = @duration / Duration::DAY
1329
- @to_day = length.to_i if length.to_i == length.to_f
1330
- else
1331
- extend NoDuration
1332
- end
1333
- end
1334
-
1335
- # 加減算共通処理
1336
- #
1337
- # @param [When::TM::PeriodDuration] other
1338
- # @param [Numeric] sgn +1, -1
1339
- #
1340
- # @return [When::TM::PeriodDuration]
1341
- #
1342
- def _plus(other, sgn)
1343
- unless (@week && @week.size) == (other.week && other.week.size) &&
1344
- (@date && @date.size) == (other.date && other.date.size) &&
1345
- (@time && @time.size) == (other.time && other.time.size)
1346
- raise ArgumentError, "PeriodDuration structure mismatch"
1347
- end
1348
- period = self.dup
1349
- period.send(:week=, (0...@week.size).to_a.map {|i| @week[i] + sgn * other.week[i]}) if @week
1350
- period.send(:date=, (0...@date.size).to_a.map {|i| @date[i] + sgn * other.date[i]}) if @date
1351
- period.send(:time=, (0...@time.size).to_a.map {|i| @time[i] + sgn * other.time[i]}) if @time
1352
- period.send(:_duration)
1353
- return period
1354
- end
1355
- end
1356
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2015 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==:reverse) ? @first - @parent * @count : @first + @parent * @count
573
+ if @last
574
+ sign = @parent.sign
575
+ sign = -sign if @direction==:reverse
576
+ @current = nil if (sign * (@current <=> @last)) > 0
577
+ end
578
+ @count += 1
579
+ return value
580
+ end
581
+ end
582
+
583
+ # Enumerator の生成
584
+ #
585
+ # @overload initialize(range, count_limit=nil))
586
+ # @param [Range, When::Parts::GeometricComplex] range
587
+ # [ 始点 - range.first ]
588
+ # [ 終点 - range.last ]
589
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
590
+ #
591
+ # @overload initialize(first, direction, count_limit=nil))
592
+ # @param [When::TM::TemporalPosition] first 始点
593
+ # @param [Symbol] direction
594
+ # [ :forward - 昇順 ]
595
+ # [ :reverse - 降順 ]
596
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
597
+ #
598
+ def _enumerator(*args)
599
+ return Enumerator.new(*args.unshift(self))
600
+ end
601
+ alias :to_enum :_enumerator
602
+ alias :enum_for :_enumerator
603
+ end
604
+
605
+ #
606
+ # ISO 11404 の時間間隔に基づいて定義された When::TM::Duration の subclass
607
+ #
608
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#timeIntervalLengthType gml schema}
609
+ #
610
+ class IntervalLength < Duration
611
+
612
+ Alias = {'Y'=>'year', 'M'=>'month' , 'D'=>'day', 'W'=>'week', 'S'=>'system',
613
+ 'h'=>'hour', 'm'=>'minute', 's'=>'second'}
614
+
615
+ # Interval Length 形式の表現を分解して配列化する
616
+ #
617
+ # @param [String] interval
618
+ #
619
+ # @return [Array<Numeric, String>] ( value, unit_name, factor, radix )
620
+ # [ value [Numeric] - 時間間隔 / 単位 ]
621
+ # [ unit_name [String] - 単位名 ]
622
+ # [ factor [Numeric] - 冪乗 ]
623
+ # [ radix [Numeric] - 冪乗の基数 ]
624
+ #
625
+ # @note value, factor, radix は Numeric
626
+ #
627
+ def self._to_array(interval)
628
+ return nil unless interval =~ /\A([-+]?[\d.]+)(?:(E|X|\((\d+)\))([-+]?\d+))?([A-Za-z]+|\*([\d.]+)S)\z/
629
+ value, radix, radix_quantity, factor, unit, unit_quantity = $~[1..6]
630
+ value = When::Coordinates::Pair._en_number(value)
631
+ radix = case radix
632
+ when 'E', nil ; 10
633
+ when 'X' ; 60
634
+ else ; radix_quantity.to_i
635
+ end
636
+ factor = factor ? -factor.to_i : 0
637
+ return [value, unit_quantity, factor, radix] if unit_quantity
638
+ unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]]
639
+ return [value, UnitName[unit_quantity], factor, radix] if unit_quantity
640
+ return nil
641
+ end
642
+
643
+ # 時間間隔の長さ (128秒単位)
644
+ #
645
+ # @return [Numeric]
646
+ #
647
+ protected :duration=
648
+
649
+ # 時間間隔を表現するために使用した測定単位の名称
650
+ #
651
+ # @return [String] (year|month|week|day|hour|minute|second|system)
652
+ #
653
+ attr_reader :unit
654
+
655
+ # 時間単位となる乗数の基底となる正の整数
656
+ #
657
+ # @return [Integer]
658
+ #
659
+ attr_reader :radix
660
+
661
+ # 基底のべき乗を行う指数
662
+ #
663
+ # @return [Integer]
664
+ #
665
+ attr_reader :factor
666
+
667
+ # 時間間隔の長さ / 測定単位
668
+ #
669
+ # @return [Numeric]
670
+ #
671
+ attr_accessor :value
672
+ protected :value=
673
+
674
+ #
675
+ # 測定単位の大きさ
676
+ #
677
+ # @return [Numeric]
678
+ #
679
+ attr_reader :unit_quantity
680
+
681
+ # 時刻配列
682
+ #
683
+ # @return [Numeric]
684
+ # When::TM::PeriodDuration との互換性のために提供
685
+ #
686
+ # @private
687
+ #
688
+ def time
689
+ [0, 0, @duration / SECOND]
690
+ end
691
+
692
+ # 日付配列
693
+ #
694
+ # @return [nil]
695
+ # When::TM::PeriodDuration との互換性のために提供
696
+ #
697
+ # @private
698
+ #
699
+ def date
700
+ nil
701
+ end
702
+
703
+ # 暦週配列
704
+ #
705
+ # @return [nil]
706
+ # When::TM::PeriodDuration との互換性のために提供
707
+ #
708
+ # @private
709
+ #
710
+ def weeks
711
+ nil
712
+ end
713
+
714
+ # 符号反転
715
+ #
716
+ # @return [When::TM::IntervalLength]
717
+ #
718
+ def -@
719
+ interval = self.dup
720
+ interval.value = -@value
721
+ interval.duration = -@duration
722
+ return interval
723
+ end
724
+
725
+ #
726
+ # 加算
727
+ #
728
+ # @param [When::TM::Duration] other
729
+ # @param [Numeric] other 秒数
730
+ # @param [その他] other 日時とみなす
731
+ #
732
+ # @return [When::TM::IntervalLength] (other が Numeric, When::TM::Duration の場合)
733
+ # @return [other と同じクラス] (other がその他の場合)
734
+ #
735
+ def +(other)
736
+ case other
737
+ when Duration ; diff = other.duration
738
+ when Numeric ; diff = other * SECOND
739
+ else ; return other + self
740
+ end
741
+ interval = self.dup
742
+ interval.duration = @duration + diff
743
+ interval.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
744
+ return interval
745
+ end
746
+
747
+ #
748
+ # 減算
749
+ #
750
+ # @param [When::TM::IntervalLength] other
751
+ # @param [Numeric] other 秒数
752
+ #
753
+ # @return [When::TM::IntervalLength]
754
+ #
755
+ def -(other)
756
+ interval = self.dup
757
+ interval.duration = @duration - (other.kind_of?(Duration) ? other.duration : other * SECOND)
758
+ interval.value = interval.duration / (@radix ** (-@factor) * @unit_quantity)
759
+ return interval
760
+ end
761
+
762
+ # 乗算
763
+ #
764
+ # @param [Numeric] times
765
+ #
766
+ # @return [When::TM::IntervalLength]
767
+ #
768
+ def *(times)
769
+ interval = self.dup
770
+ interval.value = times * @value
771
+ interval.duration = times * @duration
772
+ return interval
773
+ end
774
+
775
+ #
776
+ # 除算
777
+ #
778
+ # @param [When::TM::IntervalLength] other
779
+ # @param [Numeric] other 秒数
780
+ #
781
+ # @return [When::TM::IntervalLength] (other が Numeric の場合)
782
+ # @return [Numeric] (other が When::TM::Duration の場合)
783
+ #
784
+ def /(other)
785
+ other.kind_of?(Duration) ? @duration / other.duration : self * (1.0 / other)
786
+ end
787
+
788
+ # 文字列化
789
+ #
790
+ # @return [String]
791
+ #
792
+ def to_s
793
+ expression = @value.to_s
794
+ unless @factor == 0
795
+ case @radix
796
+ when 10 ; expression += 'E'
797
+ when 60 ; expression += 'X'
798
+ else ; expression += '(%d)' % @radix
799
+ end
800
+ expression += '%+d' % (-@factor)
801
+ end
802
+ expression += Alias.invert[@unit] || "*#{When::Coordinates::Pair._en_number(@unit)}S"
803
+ return expression
804
+ end
805
+
806
+ #
807
+ # When::TM::IntervalLength への変換
808
+ #
809
+ # 単なるオブジェクトのコピー
810
+ #
811
+ # @return [When::TM::IntervalLength]
812
+ #
813
+ def to_interval_length
814
+ self.dup
815
+ end
816
+
817
+ # オブジェクトの生成
818
+ #
819
+ # @param [Numeric] value 時間間隔の長さ / 測定単位(省略不可)
820
+ # @param [String] unit 時間間隔を表現するために使用した測定単位の名称(デフォルト : system)
821
+ # ('year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'system')
822
+ # @param [Integer] factor 基底の冪乗を行う指数(デフォルト : 0)
823
+ # @param [Integer] radix 時間単位となる乗数の基底となる正の整数(デフォルト : 10)
824
+ #
825
+ def initialize(value, unit='system', factor=0, radix=10)
826
+ @value, @factor, @radix = value, factor, radix
827
+ @unit_quantity = Unit[unit.downcase] || Unit[Alias[unit[0..0]]] if unit.kind_of?(String)
828
+ @unit_quantity ||= unit.to_f
829
+ raise TypeError, "Wrong Unit Type: #{unit}" unless @unit_quantity.kind_of?(Numeric)
830
+ @unit = UnitName[@unit_quantity] ||
831
+ When::Coordinates::Pair._en_number(@unit_quantity).to_s
832
+ @duration = @value * @radix ** (-@factor) * @unit_quantity
833
+ end
834
+
835
+ # オブジェクトの生成(終点と始点を指定)
836
+ #
837
+ # @param [When::TM::TemporalPosition] ended 終点
838
+ # @param [When::TM::TemporalPosition] begun 始点
839
+ #
840
+ # @return [When::TM::IntervalLength]
841
+ # [ 日単位の精度 - 終点と始点の分解能のいずれかが“日”またはそれより粗い場合 ]
842
+ # [ システム精度 - 終点と始点の分解能がともに“日”より細かい場合 ]
843
+ #
844
+ def self.difference(ended, begun)
845
+ precision = [ended.precision, begun.precision].min
846
+ return new(ended-begun) if precision > When::DAY
847
+ return new(ended.to_i - begun.to_i, unit='day')
848
+ end
849
+
850
+ private
851
+
852
+ # その他のメソッド
853
+ #
854
+ # @note
855
+ # When::TM::IntervalLength で定義されていないメソッドは
856
+ # 処理を @duration (type:Numeric) に委譲する
857
+ #
858
+ def method_missing(name, *args, &block)
859
+ self.class.module_eval %Q{
860
+ def #{name}(*args, &block)
861
+ @duration.send("#{name}", *args, &block)
862
+ end
863
+ } unless When::Parts::MethodCash.escape(name)
864
+ @duration.send(name, *args, &block)
865
+ end
866
+ end
867
+
868
+ #
869
+ # ISO 8601 (JIS X0301) の時間間隔に基づいて定義された When::TM::Duration の subclass
870
+ #
871
+ class PeriodDuration < Duration
872
+
873
+ include When
874
+ include Coordinates
875
+
876
+ #
877
+ # When::TM::PeriodDuration オブジェクトが月や年などの可変長の単位に依存する場合
878
+ # に @duration 属性の参照を禁止するために用いる
879
+ #
880
+ module NoDuration
881
+ #
882
+ # 属性 @duration が定義できないとをエラーで示す
883
+ #
884
+ # @return [Numeric]
885
+ #
886
+ def duration
887
+ raise TypeError, "This PeriodDuration object does't have duration value"
888
+ end
889
+ end
890
+
891
+ # ISO 8601 形式の表現を分解して配列化する
892
+ #
893
+ # @param [String] period
894
+ #
895
+ # @return [Array<Numeric, Array<Numeric>>] ( sign, date, time, week )
896
+ # [ sign [Numeric] - +1 , -1 ]
897
+ # [ date [Array<Numeric>] - 年月日 ]
898
+ # [ time [Array<Numeric>] - 時分秒 ]
899
+ # [ week [Array<Numeric>] - 週日 ]
900
+ #
901
+ def self._to_array(period)
902
+
903
+ return nil unless period.gsub(/_+/, '') =~ /\A([-+])?P(.*?)?(?:T(.+))?\z/
904
+
905
+ sign = ($1 == '-') ? -1 : +1
906
+ pdate, ptime = $~[2..3]
907
+
908
+ # 時分秒形式
909
+ if (ptime =~ /\A(?:([.,\d]+)?([:*=])?H)?(?:([.,\d]+)?([:*=])?M)?(?:([.,\d]+)?([:*=])?S)?((?:[.,\d]*[:*=]?X)*)\z/)
910
+ trunk = [1,3,5].map {|i|
911
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
912
+ }
913
+ extra = $7 ? $7.scan(/([.,\d]+)?([:*=])?X/).map {|d|
914
+ d[0] ? Pair._en_pair(d[0], d[1]) : 0
915
+ } : []
916
+ time = [0] + trunk + extra
917
+ time = nil if time.uniq == [0]
918
+ end
919
+
920
+ case pdate
921
+ # 年月日形式
922
+ when /\A((?:[.,\d]*[-*=]?X)*)(?:([.,\d]+)?([-*=])?Y)?(?:([.,\d]+)?([-+*&%!>=<?])?M)?(?:([.,\d]+)?([-*=?%])?D)?\z/
923
+ trunk = [2,4,6].map {|i|
924
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
925
+ }
926
+ extra = $1 ? $1.scan(/([.,\d]+)?([:*=])?X/).map {|d|
927
+ d[0] ? Pair._en_pair(d[0], d[1]) : 0
928
+ } : []
929
+ date = extra + trunk
930
+ date = nil if date.uniq == [0]
931
+ return sign, date, time
932
+
933
+ # 週日形式
934
+ when /\A(?:([.,\d]+)?([:*=])?W)(?:([.,\d]+)?([-*=?%])?D)?\z/
935
+ week = [1,3].map {|i|
936
+ $~[i] ? Pair._en_pair($~[i], $~[i+1]) : 0
937
+ }
938
+ return sign, nil, time if week.uniq == [0]
939
+ date = [0, 0, week[1] + 7*week[0]]
940
+ return sign, date, time, week
941
+
942
+ # 代用形式
943
+ else
944
+ pdate += 'T' + ptime if ptime
945
+ f, d, t, z, e = When::BasicTypes::DateTime._to_array(pdate, {:abbr=>[0]*20})
946
+ return nil if e
947
+ if d
948
+ case f
949
+ when :day ; date = [d[0], 0, d[1]]
950
+ when :century, nil ; date = (0..2).map {|i| d[i]||0}
951
+ when :week ; date, week = [0,0,d[0]*7+(d[1]||0)], [d[0], d[1]||0]
952
+ end
953
+ end
954
+ time = t ? (t.map {|v| v||0}) : nil
955
+ return sign, date, time, week
956
+ end
957
+ end
958
+
959
+ # 期間の日数
960
+ #
961
+ # @return [Integer]
962
+ #
963
+ # @note 固定の整数で表現できない場合は nil
964
+ #
965
+ attr_reader :to_day
966
+
967
+ # 期間の日付要素
968
+ #
969
+ # @return [Array<Numeric>]
970
+ #
971
+ attr_accessor :date
972
+ private :date=
973
+
974
+ # 期間の週日要素
975
+ #
976
+ # @return [Array<Numeric>]
977
+ #
978
+ attr_accessor :week
979
+ private :week=
980
+
981
+ # 期間の時刻要素
982
+ #
983
+ # @return [Array<Numeric>]
984
+ #
985
+ attr_accessor :time
986
+ private :time=
987
+
988
+ # 要素の参照
989
+ #
990
+ # @param [Numeric] index When::Coordinates で定義している分解能定数に対応する列挙型( YEAR, ... , SECOND)
991
+ #
992
+ # @return [Numeric]
993
+ #
994
+ def [](index)
995
+ if index == WEEK
996
+ return nil unless @week
997
+ return @week[0]
998
+ elsif index <= 0
999
+ return nil unless @date
1000
+ return @date[index-1]
1001
+ else
1002
+ return nil unless @time
1003
+ return @time[index]
1004
+ end
1005
+ end
1006
+
1007
+ # 持続期間であることを文字'P'で示す
1008
+ #
1009
+ # @return [String]
1010
+ #
1011
+ def designator
1012
+ return 'P'
1013
+ end
1014
+
1015
+ # 期間に含まれる年数を示す
1016
+ #
1017
+ # @return [String]
1018
+ #
1019
+ def years
1020
+ return nil unless @date
1021
+ return @date[YEAR-1].to_s
1022
+ end
1023
+
1024
+ # 期間に含まれる月数を示す
1025
+ #
1026
+ # @return [String
1027
+ #
1028
+ def months
1029
+ return nil unless @date
1030
+ return @date[MONTH-1].to_s
1031
+ end
1032
+
1033
+ # 期間に含まれる週数を示す
1034
+ #
1035
+ # @return [String]
1036
+ #
1037
+ def weeks
1038
+ return nil unless @week
1039
+ return @week[0].to_s
1040
+ end
1041
+
1042
+ # 期間に含まれる日数を示す
1043
+ #
1044
+ # @return [String]
1045
+ #
1046
+ def days
1047
+ return nil unless @date
1048
+ return @date[DAY-1].to_s
1049
+ end
1050
+
1051
+ # 期間が1日より短い時間単位を含むとき文字'T'で示す
1052
+ #
1053
+ # @return [String]
1054
+ #
1055
+ def time_indicator
1056
+ return (@time) ? 'T' : nil
1057
+ end
1058
+ alias :timeIndicator :time_indicator
1059
+
1060
+ # 期間に含まれる時間数を示す
1061
+ #
1062
+ # @return [String]
1063
+ #
1064
+ def hours
1065
+ return nil unless @time
1066
+ return @time[HOUR].to_s
1067
+ end
1068
+
1069
+ # 期間に含まれる分数を示す
1070
+ #
1071
+ # @return [String]
1072
+ #
1073
+ def minutes
1074
+ return nil unless @time
1075
+ return @time[MINUTE].to_s
1076
+ end
1077
+
1078
+ # 期間に含まれる秒数を示す
1079
+ #
1080
+ # @return [String]
1081
+ #
1082
+ def seconds
1083
+ return nil unless @time
1084
+ return @time[SECOND].to_s
1085
+ end
1086
+
1087
+ # 符号反転
1088
+ #
1089
+ # @return [When::TM::PeriodDuration]
1090
+ #
1091
+ def -@
1092
+ period = self.dup
1093
+ period.send(:date=, @date.map {|v| -v}) if @date
1094
+ period.send(:week=, @week.map {|v| -v}) if @week
1095
+ period.send(:time=, @time.map {|v| -v}) if @time
1096
+ period.send(:_duration)
1097
+ return period
1098
+ end
1099
+
1100
+ # 符号
1101
+ #
1102
+ # @return [Integer] 0 との比較により、負,0,正の値を返す
1103
+ #
1104
+ def sign
1105
+ @sign ||= _sign
1106
+ end
1107
+
1108
+ def _sign
1109
+ ((@week || @date || []) + (@time || [])).each do |v|
1110
+ return -1 if +v < 0
1111
+ end
1112
+ return +1
1113
+ end
1114
+ private :_sign
1115
+
1116
+ # 比較
1117
+ #
1118
+ # @param [When::TM::PeriodDuration] other
1119
+ #
1120
+ # @return [Integer] other との比較により、負,0,正の値を返す
1121
+ #
1122
+ def <=>(other)
1123
+ unless (@date && @date.size) == (other.date && other.date.size) &&
1124
+ (@week && @week.size) == (other.week && other.week.size) &&
1125
+ (@time && @time.size) == (other.time && other.time.size)
1126
+ raise ArgumentError, "PeriodDuration structure mismatch"
1127
+ end
1128
+
1129
+ (0...@week.size).each do |i|
1130
+ sgn = +@week[i] <=> +other.week[i]
1131
+ return sgn unless sgn == 0
1132
+ end if @week
1133
+
1134
+ (0...@date.size).each do |i|
1135
+ sgn = +@date[i] <=> +other.date[i]
1136
+ return sgn unless sgn == 0
1137
+ end if @date
1138
+
1139
+ (0...@time.size).each do |i|
1140
+ sgn = +@time[i] <=> +other.time[i]
1141
+ return sgn unless sgn == 0
1142
+ end if @time
1143
+
1144
+ return 0
1145
+ end
1146
+
1147
+ # オブジェクトの同値
1148
+ #
1149
+ # @param [比較先] other
1150
+ #
1151
+ # @return [Boolean]
1152
+ # [ true - 同値 ]
1153
+ # [ false - 非同値 ]
1154
+ #
1155
+ def ==(other)
1156
+ (self <=> other) == 0
1157
+ rescue
1158
+ false
1159
+ end
1160
+
1161
+ # 加算
1162
+ #
1163
+ # @param [When::TM::PeriodDuration] other
1164
+ # @param [その他] other 日時とみなす
1165
+ #
1166
+ # @return [WWhen::TM::PeriodDuration] (other が When::TM::PeriodDuration の場合)
1167
+ # @return [other と同じクラス] (other がその他の場合)
1168
+ #
1169
+ def +(other)
1170
+ other.kind_of?(When::TM::PeriodDuration) ? _plus(other, +1) : other + self
1171
+ end
1172
+
1173
+ # 減算
1174
+ #
1175
+ # @param [When::TM::PeriodDuration] other
1176
+ #
1177
+ # @return [When::TM::PeriodDuration]
1178
+ #
1179
+ def -(other)
1180
+ _plus(other, -1)
1181
+ end
1182
+
1183
+ # 乗算
1184
+ #
1185
+ # @param [Numeric] times
1186
+ #
1187
+ # @return [When::TM::PeriodDuration]
1188
+ #
1189
+ def *(times)
1190
+ period = self.dup
1191
+ period.send(:date=, @date.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @date
1192
+ period.send(:week=, @week.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @week
1193
+ period.send(:time=, @time.map {|v| v *= times; v.to_i==v.to_f ? v.to_i : v}) if @time
1194
+ period.send(:_duration)
1195
+ return period
1196
+ end
1197
+
1198
+ # 除算
1199
+ #
1200
+ # @param [Numeric] divisor
1201
+ #
1202
+ # @return [When::TM::PeriodDuration]
1203
+ #
1204
+ def /(divisor)
1205
+ self * (1.0 / divisor)
1206
+ end
1207
+
1208
+ # 文字列化
1209
+ #
1210
+ # @return [String]
1211
+ #
1212
+ def to_s
1213
+ period = 'P'
1214
+ if @week
1215
+ period += @week[0].abs.to_s + 'W'
1216
+ period += @week[1].abs.to_s + 'D' unless @week[1] == 0
1217
+ elsif @date
1218
+ (-@date.length..-1).each do |i|
1219
+ period += @date[i].abs.to_s + PRECISION_NAME[i+1][0..0] unless @date[i] == 0
1220
+ end
1221
+ end
1222
+ if @time
1223
+ period += 'T'
1224
+ (1..@time.length-1).each do |i|
1225
+ period += @time[i].abs.to_s + PRECISION_NAME[i][0..0] unless @time[i] == 0
1226
+ end
1227
+ end
1228
+ period = '-' + period if sign < 0
1229
+ period == 'P' ? 'P0D' : period
1230
+ end
1231
+
1232
+ #
1233
+ # When::TM::PeriodDuration への変換
1234
+ #
1235
+ # 単なるオブジェクトのコピー
1236
+ #
1237
+ # @return [When::TM::PeriodDuration]
1238
+ #
1239
+ def to_period_duration
1240
+ self.dup
1241
+ end
1242
+
1243
+ # オブジェクトの生成
1244
+ #
1245
+ # @overload initialize(date=nil, time=nil, week=nil)
1246
+ # @param [Array<Numeric>] date 期間の日付要素
1247
+ # @param [Array<Numeric>] time 期間の時刻要素
1248
+ # @param [Array<Numeric>] week 期間の週日要素
1249
+ #
1250
+ # @overload initialize(value, index, range=YEAR..SECOND)
1251
+ # @param [Numeric] value ndex に対応する桁での)時間間隔
1252
+ # @param [Numeric] index When で定義している分解能定数(YEAR, ... ,SECOND)
1253
+ # @param [Range] range 生成する桁の範囲
1254
+ #
1255
+ def initialize(*args)
1256
+ @options = (args[-1].kind_of?(Hash)) ? (args.pop.reject {|key,value| value == nil}) : {}
1257
+ if args[0].kind_of?(Numeric)
1258
+ _initialize_by_unit(*args)
1259
+ else
1260
+ @date, @time, @week = args
1261
+ end
1262
+ _duration
1263
+ end
1264
+
1265
+ private
1266
+
1267
+ #
1268
+ # 引数パターン2での初期化
1269
+ #
1270
+ def _initialize_by_unit(value, index, range=YEAR..SECOND)
1271
+ max = range.first
1272
+ min = range.last
1273
+ min += 1 if (range.exclude_end?)
1274
+ if (index < max)
1275
+ if (index > MONTH)
1276
+ max = index.floor
1277
+ else
1278
+ value *= 10**(max-index)
1279
+ index = max
1280
+ end
1281
+ elsif (index > min)
1282
+ if (index <= DAY)
1283
+ min = index.ceil
1284
+ else
1285
+ value *= 0.1**(index-min)
1286
+ index = min
1287
+ end
1288
+ end
1289
+ value = value.to_i unless value.kind_of?(Pair) || value.to_i != value.to_f
1290
+ @date = Array.new(1-max, 0) if (max <= DAY) && (index <= DAY)
1291
+ @time = Array.new(1+min, 0) if (min > DAY) && (index > DAY)
1292
+ if (index == WEEK)
1293
+ @week = [value, 0]
1294
+ @date[DAY-1] = 7 * value
1295
+ elsif (index <= DAY)
1296
+ @date[index-1] = value
1297
+ else
1298
+ @time[index] = value
1299
+ end
1300
+ end
1301
+
1302
+ #
1303
+ # 属性 @duration の計算
1304
+ #
1305
+ def _duration
1306
+ @duration = @sign = nil
1307
+
1308
+ if @time
1309
+ @duration = +@time[HOUR ] * Duration::HOUR
1310
+ @duration += +@time[MINUTE] * Duration::MINUTE if @time[MINUTE]
1311
+ @duration += +@time[SECOND] * Duration::SECOND if @time[SECOND]
1312
+ end
1313
+
1314
+ if @week
1315
+ @duration = +@week[0] * Duration::WEEK + +@week[1] * Duration::DAY + (@duration||0)
1316
+
1317
+ elsif @date
1318
+ date = @date.dup
1319
+ date.shift while date.first == 0
1320
+ case date.size
1321
+ when 0 ; @duration ||= 0
1322
+ when 1 ; @duration = +@date[DAY-1] * Duration::DAY + (@duration||0)
1323
+ else ; @duration = nil
1324
+ end
1325
+ end
1326
+
1327
+ if @duration
1328
+ length = @duration / Duration::DAY
1329
+ @to_day = length.to_i if length.to_i == length.to_f
1330
+ else
1331
+ extend NoDuration
1332
+ end
1333
+ end
1334
+
1335
+ # 加減算共通処理
1336
+ #
1337
+ # @param [When::TM::PeriodDuration] other
1338
+ # @param [Numeric] sgn +1, -1
1339
+ #
1340
+ # @return [When::TM::PeriodDuration]
1341
+ #
1342
+ def _plus(other, sgn)
1343
+ unless (@week && @week.size) == (other.week && other.week.size) &&
1344
+ (@date && @date.size) == (other.date && other.date.size) &&
1345
+ (@time && @time.size) == (other.time && other.time.size)
1346
+ raise ArgumentError, "PeriodDuration structure mismatch"
1347
+ end
1348
+ period = self.dup
1349
+ period.send(:week=, (0...@week.size).to_a.map {|i| @week[i] + sgn * other.week[i]}) if @week
1350
+ period.send(:date=, (0...@date.size).to_a.map {|i| @date[i] + sgn * other.date[i]}) if @date
1351
+ period.send(:time=, (0...@time.size).to_a.map {|i| @time[i] + sgn * other.time[i]}) if @time
1352
+ period.send(:_duration)
1353
+ return period
1354
+ end
1355
+ end
1356
+ end