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,2072 +1,2115 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2014 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.4) Temporal Position Package
11
- #
12
- #
13
-
14
- # 列挙データ型である When::TM::IndeterminateValue は,不確定な位置のために
15
- # 6つの値を規定する.このうち Min と Max は 本ライブラリでの拡張である.
16
- #
17
- # These values are interpreted as follows:
18
- #- “Unknown” 時間位置を示す値が与えられていないことを示す(本ライブラリでは“Unknown”は使用しない)
19
- #- “Now” 常に現時点の時間位置を示す(オブジェクト生成時の時間位置でないことに注意)
20
- #- “Before” 実際の時間位置は未知だが、指定した値よりも前であることを示す(本ライブラリでは“Before”は無視される)
21
- #- “After” 実際の時間位置は未知だが、指定した値よりも後であることを示す(本ライブラリでは“After”は無視される)
22
- #- “Min” 無限の過去を示す
23
- #- “Max” 無限の未来を示す
24
- #
25
- # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimeIndeterminateValueType]
26
- #
27
- module IndeterminateValue
28
-
29
- After = :After
30
- Before = :Before
31
- Now = :Now
32
- Unknown = :Unknown
33
- # additional value for this library
34
- Min = :Min
35
- # additional value for this library
36
- Max = :Max
37
-
38
- S = {'After'=>After, 'Before'=>Before, 'Now'=>Now, 'Unknown'=>Unknown, '-Infinity'=>Min, '+Infinity'=>Max}
39
- I = S.invert
40
- end
41
-
42
- # 時間位置共用体
43
- # union of
44
- # When::TM::TemporalPosition
45
- # When::BasicTypes::Date
46
- # When::BasicTypes::Time
47
- # When::BasicTypes::DateTime
48
- #
49
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimePositionType gml schema}
50
- #
51
- class Position
52
-
53
- include IndeterminateValue
54
- include When::Parts::Resource
55
-
56
- # @private
57
- HashProperty =
58
- [:indeterminated_position, :frame,
59
- [:precision, When::SYSTEM], :events, :options, :trans, :query,
60
- :location, [:time_standard, When::TimeStandard::UniversalTime], [:rate_of_clock, 1.0]]
61
-
62
- # 代表文字列
63
- #
64
- # @return [When::BasicTypes::DateTime]
65
- #
66
- attr_reader :date_time8601
67
- alias :dateTime8601 :date_time8601
68
-
69
- # 時間位置
70
- #
71
- # @return [When::TM::TemporalPosition]
72
- #
73
- attr_reader :any_other
74
- alias :anyOther :any_other
75
-
76
- # 諸々のオブジェクトから When::TM::TemporalPosition を取り出す
77
- #
78
- # @param [Object] position 変換元の時間位置
79
- # @param [Hash] options
80
- # see {When::TM::TemporalPosition._instance}
81
- #
82
- # @return [When::TM::TemporalPosition] position の型により下記を返す
83
- # - {When::BasicTypes::Date}, {When::BasicTypes::Time}, {When::BasicTypes::DateTime} - When::TM::Calendar#jul_trans による変換結果
84
- # - {When::TM::TemporalPosition} - そのまま position ( optionは無視 )
85
- # - {When::TM::Position} - position.any_other ( optionは無視 )
86
- # - {When::Parts::GeometricComplex} - position.first ( optionは無視 )
87
- #
88
- def self.any_other(position, options={})
89
- case position
90
- when TemporalPosition ; position
91
- when Position ; position.any_other
92
- when When::Parts::GeometricComplex ; position.first
93
- else ; When.Calendar(options[:frame] || 'Gregorian').jul_trans(position, options) || position
94
- end
95
- end
96
-
97
- # オブジェクトの生成
98
- #
99
- # @param [String] specification When.exe Standard Representation として解釈して生成する
100
- # @param [Hash] options 暦法や時法などの指定
101
- # see {When::TM::TemporalPosition._instance}
102
- #
103
- # @note
104
- # specification が String 以外の場合、そのオブジェクトの代表的な日時
105
- # (When::TM::CalendarEra#reference_dateなど)により解釈する
106
- #
107
- def initialize(specification, options={})
108
-
109
- case specification
110
- when String
111
- @date_time8601 = specification
112
- @any_other = TemporalPosition._instance(specification, options)
113
- when Position
114
- @date_time8601 = specification.date_time8601
115
- @any_other = specification.any_other
116
- return
117
- else
118
- @any_other = specification
119
- end
120
-
121
- klass = specification.class
122
- message = "Irregal specification type: #{klass}"
123
-
124
- case specification
125
- when CalDate ; @date_time8601 = When::BasicTypes::Date.new(specification.to_s)
126
- when ClockTime ; @date_time8601 = When::BasicTypes::Time.new(specification.to_s)
127
- when TemporalPosition ; @date_time8601 = When::BasicTypes::DateTime.new(specification.to_s)
128
- when CalendarEra ; @date_time8601 = When::BasicTypes::Date.new(specification.reference_date.to_s)
129
- when OrdinalEra ; @date_time8601 = When::BasicTypes::Date.new(specification.begin.to_s)
130
- when When::BasicTypes::Date ; raise TypeError, message unless klass == CalDate
131
- when When::BasicTypes::Time ; raise TypeError, message unless klass == ClockTime
132
- when String ;
133
- else ; raise TypeError, message
134
- end
135
- end
136
-
137
- private
138
-
139
- alias :_method_missing :method_missing
140
-
141
- # その他のメソッド
142
- #
143
- # @note
144
- # When::TM::Position で定義されていないメソッドは
145
- # 処理を @date_time8601 (type: When::BasicTypes::DateTime)
146
- # または @any_other (type: When::TM::TemporalPosition) に委譲する
147
- # (両方ともに有効なメソッドは@any_otherを優先する)
148
- #
149
- def method_missing(name, *args, &block)
150
- return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name)
151
- self.class.module_eval %Q{
152
- def #{name}(*args, &block)
153
- union = @any_other.respond_to?("#{name}") ? @any_other : @date_time8601
154
- union.send("#{name}", *args, &block)
155
- end
156
- } unless When::Parts::MethodCash.escape(name)
157
- union = @any_other.respond_to?(name) ? @any_other : @date_time8601
158
- union.send(name, *args, &block)
159
- end
160
- end
161
-
162
- # 「時間位置」の基底クラス
163
- #
164
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TemporalPositionType gml schema}
165
- #
166
- class TemporalPosition < ::Object
167
-
168
- #
169
- # When::TM::JulianDate, When::TM::CalDate or DateAndTime への変換メソッドを提供
170
- #
171
- module Conversion
172
- #
173
- # 対応する When::TM::JulianDate を生成
174
- #
175
- # @param [Hash] options 以下の通り
176
- # @option options [When::TM::Clock] :clock
177
- # @option options [When::Parts::Timezone] :tz
178
- #
179
- # @return [When::TM::JulianDate]
180
- #
181
- def julian_date(options={})
182
- When::TM::JulianDate.new(self, options)
183
- end
184
- alias :to_julian_date :julian_date
185
-
186
- #
187
- # 対応する When::TM::CalDate or DateAndTime を生成
188
- #
189
- # @param [Hash] options 暦法や時法などの指定
190
- # see {When::TM::TemporalPosition._instance}
191
- #
192
- # @return [When::TM::CalDate, When::TM::DateAndTime]
193
- #
194
- def tm_pos(options={})
195
- When.Calendar(options[:frame] || 'Gregorian').jul_trans(self, options)
196
- end
197
- alias :to_tm_pos :tm_pos
198
- end
199
-
200
- include When
201
- include Comparable
202
- include IndeterminateValue
203
- include Coordinates
204
- include Parts::Resource
205
- include Conversion
206
-
207
- # @private
208
- HashProperty = Position::HashProperty
209
-
210
- # この時間位置の意味づけ
211
- #
212
- # @return [When::TM::IndeterminateValue]
213
- #
214
- attr_reader :indeterminated_position
215
- alias :indeterminatedPosition :indeterminated_position
216
-
217
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
218
- #
219
- # The time reference system associated with the temporal position being described
220
- #
221
- # @return [When::TM::ReferenceSystem]
222
- #
223
- attr_accessor :frame
224
-
225
- # この時間位置と関連付けられたイベント - additional attribute
226
- #
227
- # @return [Array<When::Parts::Enumerator>]
228
- #
229
- attr_accessor :events
230
- #protected :events=
231
-
232
- # この時間位置の分解能 - additional attribute
233
- #
234
- # @return [Numeric]
235
- #
236
- # @note precision より resolution の方が分解能の意味にふさわしいが ISO19108 で別の意味に用いられているため resolution とした。
237
- #
238
- attr_accessor :precision
239
-
240
- # その他の属性 - additional attribute
241
- #
242
- # @return [Hash] { String=>Object }
243
- #
244
- attr_reader :options
245
-
246
- # その他の属性 - additional attribute
247
- #
248
- # @return [Hash] { String=>Object }
249
- #
250
- attr_accessor :trans
251
-
252
- # その他の属性 - additional attribute
253
- #
254
- # @return [Hash] { String=>When::BasicTypes::M17n }
255
- #
256
- attr_accessor :query
257
-
258
- # その他の属性 - additional attribute
259
- #
260
- # @return [When::Coordinates::Spatial]
261
- #
262
- attr_accessor :location
263
-
264
- class << self
265
-
266
- include When
267
- include Coordinates
268
-
269
- # Temporal Objetct の生成
270
- #
271
- # @param [String] specification When.exe Standard Representation
272
- # @param [Hash] options 下記の通り
273
- # @option options [When::TM::ReferenceSystem] :frame 暦法の指定
274
- # @option options [When::Parts::Timezone::Base, String] :clock 時法の指定
275
- # @option options [String] :tz 時法の指定(時間帯を指定する場合 :clock の替わりに用いることができる)
276
- # @option options [Array<Numeric>] :abbr ISO8601上位省略形式のためのデフォルト日付(省略時 指定なし)
277
- # @option options [Integer] :extra_year_digits ISO8601拡大表記のための年の構成要素拡大桁数(省略時 1桁)
278
- # @option options [Integer] :ordinal_date_digits ISO8601拡大表記の年内通日の桁数(省略時 3桁)
279
- # @option options [String] :wkst ISO8601週日形式のための暦週開始曜日(省略時 'MO')
280
- # @option options [Integer] :precision 生成するオブジェクトの分解能
281
- # @option options [When::TimeStandard::TimeStandard] :time_standard 時刻系の指定(省略時 When::TimeStandard::UnversalTime)
282
- # @option options [When::Ephemeris::Spatial] :location 観測地の指定(省略時 指定なし)
283
- # @option options [String] :era_name 暦年代
284
- # @option options [Hash] :trans 暦年代の上下限
285
- # - :count => 条件に合致する暦年代のうち何番目を採用するか
286
- # - :lower => 暦年代適用の下限
287
- # true - epoch_of_use の始め(省略時)
288
- # :reference_date - 参照事象の日付
289
- # - :upper => 暦年代適用の上限
290
- # true - epoch_of_use の終わり(省略時)
291
- # :reference_date - 参照事象の日付
292
- # @option options [Hash] :query 暦年代の絞込み条件
293
- # - :area => area による暦年代絞込み
294
- # - :period => period による暦年代絞込み
295
- # - :name => name による暦年代絞込み(epoch の attribute使用可)
296
- #
297
- # @note options の中身は本メソッドによって更新されることがある。
298
- #
299
- # @note :tz は 'Asia/Tokyo'など時間帯を表す文字列をキーにして、登録済みのWhen::V::Timezone, When::Parts::Timezoneを検索して使用する。
300
- # :clock はWhen::Parts::Timezone::Baseオブジェクトをそのまま使用するか '+09:00'などの文字列をWhen::TM::Clock化して使用する。
301
- # :tz の方が :clock よりも優先される。
302
- #
303
- # @return [When::TM::TemporalPosition] ISO8601 time point
304
- # @return [When::TM::Duration] ISO8601 duration
305
- # @return [When::Parts::GeometricComplex] ISO8601 repeating interval
306
- #
307
- def _instance(specification, options={})
308
-
309
- # Suffix - Frame specification
310
- rfc5545form, frame, *rest = specification.split(/\^{1,2}/)
311
- return rest.inject(_instance(rfc5545form + '^' + frame, options)) {|p,c| When.Resource(c, '_c:').jul_trans(p)} if rest.size > 0
312
-
313
- options[:frame] = When.Resource(frame, '_c:') if (frame)
314
-
315
- # Prefix - RFC 5545 Options
316
- if (rfc5545form =~ /^([^:]+[^-:\d]):([^:].+)$/)
317
- rfc5545option, iso8601form = $~[1..2]
318
- rfc5545option.split(/;/).each do |eq|
319
- key, value = eq.split(/=/, 2)
320
- case key
321
- when 'VALUE' ; options[:precision] = value
322
- when 'TZID' ; options[:clock] =
323
- case When::V::Timezone[value]
324
- when Array ; When::V::Timezone[value][-1]
325
- when nil ; When::Parts::Timezone.new(value)
326
- else ; When::V::Timezone[value]
327
- end
328
- else ; options[key] = value
329
- end
330
- end
331
- else
332
- iso8601form = rfc5545form
333
- end
334
- options = options.dup
335
-
336
- # IndeterminateValue
337
- if (iso8601form.sub!(/\/After$|^Before\/|^Now$|^Unknown$|^[-+]Infinity$/i, ''))
338
- options[:indeterminated_position] = When::TimeValue::S[$&.sub(/\//,'')]
339
- case options[:indeterminated_position]
340
- when When::TimeValue::Now,
341
- When::TimeValue::Unknown,
342
- When::TimeValue::Max,
343
- When::TimeValue::Min
344
- return self.new(self._options(options))
345
- end
346
- end
347
-
348
- # each specification
349
- splitted = iso8601form.split(/\//)
350
- if (splitted[0] =~ /^R(\d+)?$/)
351
- repeat = $1 ? $1.to_i : true
352
- splitted.shift
353
- end
354
- case splitted.length
355
- when 1
356
- when 2
357
- if (splitted[0] !~ /^[-+]?P/ && splitted[1] =~ /^\d/ && splitted[1].length < splitted[0].length)
358
- splitted[1] = splitted[0][0..(splitted[0].length-splitted[1].length-1)] + splitted[1]
359
- end
360
- else
361
- raise ArgumentError, "Irregal ISO8601 Format: #{iso8601form}"
362
- end
363
- options = self._options(options)
364
- element = splitted.map { |v| _date_time_or_duration(v, options.dup) }
365
-
366
- # total result
367
- case repeat
368
- when nil
369
- case element[1]
370
- when nil
371
- return element[0]
372
- when Duration
373
- case element[0]
374
- when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
375
- when self ; return When::Parts::GeometricComplex.new(*element)
376
- else ; return When::Parts::GeometricComplex.new(element[0].first, element[1])
377
- end
378
- when self
379
- case element[0]
380
- when Duration ; return When::Parts::GeometricComplex.new([[element[1]-element[0],false], [element[1],true ]])
381
- when self ; return When::Parts::GeometricComplex.new(element[0]..element[1])
382
- else ; return When::Parts::GeometricComplex.new(element[0].first..element[1])
383
- end
384
- else
385
- case element[0]
386
- when Duration ; return When::Parts::GeometricComplex.new([[element[1].first-element[0],false],
387
- [element[1].last-element[0]-1,true ]])
388
- when self ; return When::Parts::GeometricComplex.new(element[0]...element[1].last)
389
- else ; return When::Parts::GeometricComplex.new(element[0].first...element[1].last)
390
- end
391
- end
392
- when 0 ; return []
393
- when Integer ; return [element[0]] * repeat unless element[1]
394
- end
395
-
396
- case element[1]
397
- when Duration
398
- case element[0]
399
- when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
400
- else ; duration = element[1]
401
- end
402
- when self
403
- case element[0]
404
- when Duration ; duration = -element[0]
405
- when self ; duration = element[1] - element[0]
406
- else ; duration = element[1] - element[0].first
407
- end
408
- else
409
- case element[0]
410
- when Duration ; duration = -element[0]
411
- when self ; duration = element[1].first - element[0]
412
- else ; duration = element[1].first - element[0].first
413
- end
414
- end
415
- base = element[0].kind_of?(Duration) ? element[1] : element[0]
416
-
417
- if repeat.kind_of?(Integer)
418
- result = case base
419
- when self ; (1..repeat-1).inject([base]) {|a,i| a << (a[-1] + duration) }
420
- else ; (1..repeat-1).inject([base]) {|a,i| a << When::Parts::GeometricComplex.new(
421
- a[-1].first+duration...a[-1].last+duration)}
422
- end
423
- result.reverse! if duration.sign < 0
424
- return result
425
-
426
- else
427
- duration = -duration if duration.sign < 0
428
- return case base
429
- when self ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base})
430
- else ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base.first,
431
- 'dtend' =>base.last})
432
- end
433
- end
434
- end
435
-
436
- # option の正規化
437
- # @private
438
- def _options(options)
439
- query = options.dup
440
- main = {}
441
- clock = Clock.get_clock_option(query)
442
- main[:clock] = clock if clock
443
- [:indeterminated_position, :frame, :events, :precision,
444
- :era_name, :era, :abbr, :extra_year_digits, :ordinal_date_digits, :wkst, :time_standard, :location].each do |key|
445
- main[key] = query.delete(key) if (query.key?(key))
446
- end
447
- long = query.delete(:long)
448
- lat = query.delete(:lat)
449
- alt = query.delete(:alt)
450
- main[:location] ||= "_l:long=#{long||0}&lat=#{lat||0}&alt=#{alt||0}" if long && lat
451
- trans = query.delete(:trans) || {}
452
- [:lower, :upper, :count].each do |key|
453
- trans[key] = query.delete(key) if (query.key?(key))
454
- end
455
- query = query.merge(query.delete(:query)) if (query.key?(:query))
456
- main[:query] = query if (query.size > 0)
457
- main[:trans] = trans if (trans.size > 0)
458
- return main
459
- end
460
-
461
- # 比較
462
- # @private
463
- def _verify(source, target)
464
- return source.universal_time <=> target.universal_time if source.time_standard.equal?(target.time_standard)
465
- return source.dynamical_time <=> target.dynamical_time
466
- end
467
-
468
- private
469
-
470
- # date_time_or_duration
471
- def _date_time_or_duration(specification, options)
472
- # IntervalLength
473
- args = IntervalLength._to_array(specification)
474
- return IntervalLength.new(*args) if args
475
-
476
- # PeriodDuration
477
- sign, *args = PeriodDuration._to_array(specification)
478
- if (sign)
479
- args << options
480
- duration = PeriodDuration.new(*args)
481
- return (sign >= 0) ? duration : -duration
482
- end
483
-
484
- # TemporalPosition
485
- specification =~ /(.+?)(?:\[([-+]?\d+)\])?$/
486
- options[:sdn] = $2.to_i if $2
487
- f, d, t, z, e = When::BasicTypes::DateTime._to_array($1, options)
488
- raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:clock])
489
- options.delete(:abbr)
490
- z ||= options[:clock]
491
- z = When.Clock(z) if (z =~ /^[A-Z]+$/)
492
-
493
- unless (d)
494
- # ClockTime
495
- raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:frame])
496
- options[:frame] ||= z
497
- options.delete(:clock)
498
- return ClockTime.new(t, options)
499
- end
500
-
501
- options[:era_name] = e if e
502
- options[:_format ] = f if f
503
- d, w = d[0..0], d[1..-1] if (f == :week || f == :day)
504
- position = z ? DateAndTime.new(d, t||[0], options.update({:clock => z})) :
505
- t ? DateAndTime.new(d, t, options) :
506
- CalDate.new(d, options)
507
- case f
508
- when :day
509
- position += PeriodDuration.new(w[0]-1, DAY)
510
- when :week
511
- position = ((position + PeriodDuration.new(4, DAY)) & (Residue.day_of_week(options[:wkst]) << 1)) +
512
- PeriodDuration.new((w[0]-1)*7 + (w[1]||1)-1, DAY)
513
- position = When::Parts::GeometricComplex.new(position...(position+P1W)) unless w[1]
514
- end
515
- return position
516
- end
517
- end
518
-
519
- # 時刻系
520
- #
521
- # @return [When::TimeStandard]
522
- #
523
- def time_standard
524
- return @time_standard if @time_standard.kind_of?(When::TimeStandard)
525
- @time_standard ||= clock.time_standard if respond_to?(:clock) && clock
526
- @time_standard ||= frame.time_standard if frame
527
- @time_standard ||= 'UniversalTime'
528
- @time_standard = When.Resource(@time_standard, '_t:')
529
- end
530
-
531
- # 時間の歩度
532
- #
533
- # @return [Numeric]
534
- #
535
- def rate_of_clock
536
- time_standard.rate_of_clock
537
- end
538
-
539
- # 内部時間
540
- #
541
- # @return [Numeric]
542
- #
543
- # 1970-01-01T00:00:00Z からの Universal Time, Coordinated の経過時間 / 128秒
544
- #
545
- # 暦法によっては、異なる意味を持つことがある
546
- #
547
- def universal_time
548
- case @indeterminated_position
549
- when Now ; time_standard.from_time_object(Time.now)
550
- when Max ; +Float::MAX/4
551
- when Min ; -Float::MAX/4
552
- else ; raise NameError, "Temporal Reference System is not defined"
553
- end
554
- end
555
-
556
- # 外部時間
557
- #
558
- # @return [Numeric]
559
- #
560
- # 1970-01-01T00:00:00TT からの terrestrial time の経過時間 / 128秒
561
- #
562
- def dynamical_time
563
- return @dynamical_time if @dynamical_time && @indeterminated_position != Now
564
- @dynamical_time =
565
- case @indeterminated_position
566
- when Max ; +Float::MAX/4
567
- when Min ; -Float::MAX/4
568
- else ; time_standard.to_dynamical_time(universal_time)
569
- end
570
- end
571
-
572
- # ユリウス日時(実数)
573
- #
574
- # @return [Float]
575
- #
576
- # universal time での経過日数を, ユリウス日と1970-01-01T00:00:00Zで時計あわせしたもの
577
- #
578
- def to_f
579
- JulianDate._t_to_d(universal_time)
580
- end
581
-
582
- # ユリウス日(整数)
583
- #
584
- # @return [Integer]
585
- #
586
- # -4712-01-01T12:00:00Z からの経過日数に対応する通番(当該時間帯での午前0時に1進める)
587
- #
588
- def to_i
589
- sd = universal_time
590
- sd -= @frame.universal_time if @frame.kind_of?(Clock)
591
- div, mod = sd.divmod(Duration::DAY)
592
- div + JulianDate::JD19700101
593
- end
594
-
595
- # 剰余類化
596
- #
597
- # @param [Numeric] remainder 剰余
598
- # @param [Integer] divisor 法(>0)
599
- #
600
- # @return [When::Coordinates::Residue]
601
- #
602
- # 当日を基準とする剰余類
603
- #
604
- def to_residue(remainder, divisor)
605
- When::Coordinates::Residue.new(remainder, divisor, {'day'=>to_i})
606
- end
607
-
608
- # ユリウス日時(実数)
609
- #
610
- # @return [Float]
611
- #
612
- # dynamical time での経過日数を, ユリウス日と1970-01-01T00:00:00TTで時計あわせしたもの
613
- #
614
- def +@
615
- JulianDate._t_to_d(dynamical_time)
616
- end
617
-
618
- # When::TM::ClockTime オブジェクトへの変換
619
- #
620
- # @return [When::TM::ClokTime]
621
- #
622
- def to_clock_time
623
- raise TypeError, "Clock not assigned" unless clock
624
- clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY)
625
- clk_time.clk_time[0] += to_i
626
- return clk_time
627
- end
628
-
629
- # 標準ライブラリの DateTime オブジェクトへの変換
630
- #
631
- # @param [Hash] option 時間の歩度が1.0でない場合のための option
632
- # see {When::TM::TemporalPosition._instance}
633
- #
634
- # @param [Integer] start ::DateTime オブジェクトのグレゴリオ改暦日(ユリウス通日)
635
- #
636
- # @return [::DateTime]
637
- #
638
- def to_date_time(option={:frame=>When::UTC}, start=_default_start)
639
- return JulianDate.dynamical_time(dynamical_time, option).to_date_time unless time_standard.rate_of_clock == 1.0
640
- raise TypeError, "Clock not assigned" unless clock
641
- Rational
642
- offset = Rational(-(clock.universal_time/Duration::SECOND).to_i, (Duration::DAY/Duration::SECOND).to_i)
643
- clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY).clk_time
644
- ::DateTime.jd(to_i, clk_time[1], clk_time[2], clk_time[3].to_i, offset, start)
645
- end
646
-
647
- # 標準ライブラリの Date オブジェクトへの変換
648
- #
649
- # @param [Hash] option 時間の歩度が1.0でない場合のための option
650
- # see {When::TM::TemporalPosition._instance}
651
- #
652
- # @param [Integer] start ::DateTime オブジェクトのグレゴリオ改暦日(ユリウス通日)
653
- #
654
- # @return [::Date]
655
- #
656
- def to_date(option={}, start=_default_start)
657
- return JulianDate.dynamical_time(dynamical_time, option).to_date unless time_standard.rate_of_clock == 1.0
658
- ::Date.jd(to_i, start)
659
- end
660
-
661
- # 組み込みライブラリの Time オブジェクトへの変換
662
- #
663
- # @return [::Time]
664
- #
665
- def to_time
666
- time_standard.to_time_object(universal_time)
667
- end
668
-
669
- # 要素の参照
670
- #
671
- # @param [Integer, String] index 参照する要素の指定
672
- #
673
- # @return [Numeric]
674
- #
675
- def [](index)
676
- return value(index) if index.kind_of?(String) || !index.respond_to?(:inject)
677
- index.inject([]) {|list, i| list << value(i) }
678
- end
679
-
680
- # 加算
681
- #
682
- # @param [Numeric, When::TM::Duration] other
683
- #
684
- # @return [When::TM::TemporalPosition]
685
- #
686
- def +(other)
687
- case other
688
- when Integer ; self + PeriodDuration.new(other, When::DAY)
689
- when Numeric ; self + IntervalLength.new(other, 'day')
690
- when PeriodDuration ; _plus(other)
691
- when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time + other.duration), self._attr) :
692
- JulianDate.dynamical_time(dynamical_time + other.duration, self._attr)
693
- else ; raise TypeError, "The right operand should be Numeric or Duration"
694
- end
695
- rescue RangeError
696
- (@frame ^ self) + other
697
- end
698
-
699
- # 減算
700
- #
701
- # @param [Numeric, When::TM::Duration, When::TM::TemporalPosition] other
702
- #
703
- # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::Duration
704
- # @return [When::TM::Duration] if other is a When::TM::TemporalPosition
705
- #
706
- def -(other)
707
- case other
708
- when TimeValue ; self.time_standard.rate_of_clock == other.time_standard.rate_of_clock && [@precision, other.precision].min <= When::DAY ?
709
- PeriodDuration.new(self.to_i - other.to_i, When::DAY) :
710
- IntervalLength.new((self.dynamical_time - other.dynamical_time) / Duration::SECOND, 'second')
711
- when Integer ; self - PeriodDuration.new(other, When::DAY)
712
- when Numeric ; self - IntervalLength.new(other, 'day')
713
- when PeriodDuration ; _plus(-other)
714
- when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time - other.duration), self._attr) :
715
- JulianDate.dynamical_time(dynamical_time - other.duration, self._attr)
716
- else ; raise TypeError, "The right operand should be Numeric, Duration or TemporalPosition"
717
- end
718
- rescue RangeError
719
- (@frame ^ self) - other
720
- end
721
-
722
- # 分解能に対応する Duration
723
- #
724
- # @return [When::TM::PeriodDuration]
725
- #
726
- def period
727
- return @period if @period
728
- period_name = When::Coordinates::PERIOD_NAME[@precision]
729
- raise ArgumentError, "Presicion not defined" unless period_name
730
- @period = When.Duration(period_name)
731
- end
732
-
733
- # 前の日時
734
- #
735
- # @return [When::TM::TemporalPosition]
736
- #
737
- # 分解能に対応する Duration だけ,日時を戻す
738
- #
739
- def prev
740
- @precision==When::DAY ? _force_euqal_day(-1) : self-period
741
- rescue RangeError
742
- (When::Gregorian ^ self) - period
743
- end
744
-
745
- # 次の日時
746
- #
747
- # @return [When::TM::TemporalPosition]
748
- #
749
- # 分解能に対応する Duration だけ,日時を進める
750
- #
751
- def succ
752
- @precision==When::DAY ? _force_euqal_day(+1) : self+period
753
- rescue RangeError
754
- (When::Gregorian ^ self) + period
755
- end
756
- alias :next :succ
757
-
758
- #
759
- # 前後の日時を取得可能か?
760
- #
761
- # @return [Boolean]
762
- # [ true - 可能 ]
763
- # [ false - 不可 ]
764
- #
765
- def has_next?
766
- When::Coordinates::PERIOD_NAME[@precision] != nil
767
- end
768
-
769
- # 下位桁の切り捨て
770
- #
771
- # @param [Integer] digit これより下の桁を切り捨てる(省略すると When::DAY)
772
- #
773
- # @param [Integer] precision 切り捨て結果の分解能
774
- #
775
- # @return [When::TM::TemporalPosition] (本 Class では、実際には切り捨てない)
776
- #
777
- def floor(digit=DAY, precision=digit)
778
- self
779
- end
780
-
781
- # 分解能が時刻を持つか
782
- #
783
- # @return [Boolean]
784
- #
785
- def has_time?
786
- (@precision > 0)
787
- end
788
-
789
- # 指定の日時を含むか?
790
- #
791
- # @param [When::TM::TemporalPosition] date チェックされる日時
792
- #
793
- # @return [Boolean]
794
- # [ true - 含む ]
795
- # [ false - 含まない ]
796
- #
797
- def include?(date)
798
- return false if self.precision > date.precision
799
- return self == date
800
- end
801
-
802
- # オブジェクトの同値
803
- #
804
- # @param [比較先] other
805
- #
806
- # @return [Boolean]
807
- # [ true - 同値 ]
808
- # [ false - 非同値 ]
809
- #
810
- def ==(other)
811
- (self <=> other) == 0
812
- rescue
813
- false
814
- end
815
-
816
- # 大小比較
817
- #
818
- # @param [When::TM::TemporalPosition] other チェックされる日時
819
- # @param [Numeric] other チェックされる日時の universal time(self と同じtime_standardとみなす)
820
- #
821
- # @return [Integer] (-1, 0, 1)
822
- #
823
- # 分解能の低い方にあわせて比較を行う
824
- #
825
- # Ex. when?('2011-03') <=> when?('2011-03-10') -> 0
826
- #
827
- def <=>(other)
828
- other = other.first if other.kind_of?(Range)
829
- return universal_time <=> other if other.kind_of?(Numeric)
830
-
831
- [self.indeterminated_position, other.indeterminated_position].each do |position|
832
- prec = SYSTEM if [TimeValue::Min, TimeValue::Max].include?(position)
833
- end
834
- prec = [self.precision, other.precision].min unless prec
835
-
836
- case prec
837
- when DAY ; return self.to_i <=> other.to_i
838
- when SYSTEM ; return TemporalPosition._verify(self, other)
839
- end
840
-
841
- if prec < DAY && respond_to?(:most_significant_coordinate) &&
842
- other.respond_to?(:most_significant_coordinate) && @frame.equal?(other.frame)
843
- result = most_significant_coordinate <=> other.most_significant_coordinate
844
- return result unless result == 0 && @cal_date.length + prec > 1
845
- (@cal_date.length + prec - 2).times do |i|
846
- result = @cal_date[i+1] <=> other.cal_date[i+1]
847
- return result unless result == 0
848
- end
849
- @cal_date[prec - 1] <=> other.cal_date[prec - 1]
850
- else
851
- source = (prec >= self.precision ) ? self : self.floor(prec)
852
- target = (prec >= other.precision) ? other : other.floor(prec)
853
- return source.to_i <=> target.to_i if prec <= DAY
854
- TemporalPosition._verify(source, target)
855
- end
856
- end
857
-
858
- # 条件を満たすオブジェクトの抽出
859
- #
860
- # @param [Module, Array<Moduel>, When::Coordinates::Residue, When::TM::Duration, When::TM::Calendar, When::TM::CalendarEra] other
861
- # @param [Boolean] leaf extract only leaf Objects.
862
- # @param [Block] block If block is given, the specified block is yield.
863
- #
864
- # @return [Array of (element^self) for all When::Parts::Resource registered elements] (If other is Module)
865
- # @return [Array of (self^element) for elemnt of other which belong to the specified module or class] (If other is [Array])
866
- # @return [Enumerator which generates temporal position sequence begins from self with the specified duration] (If other is [When::TM::Duration])
867
- # @return [Array of temporal position using the specified calendar as a frame] (If other is [When::TM::Calendar])
868
- # @return [Array of temporal position using the specified calendar era as a calendarEraName] (If other is [When::TM::CalendarEra])
869
- #
870
- def scan(other, leaf=false, &block)
871
- list = []
872
- case other
873
- when Numeric, TemporalPosition, Position
874
- raise TypeError, "Operand should not be Numeric or (Temporal)Position"
875
-
876
- when Module
877
- objects = []
878
- ObjectSpace.each_object(other) do |object|
879
- objects << object if object.registered?
880
- end
881
- objects.each do |object|
882
- element = (object ^ self)
883
- if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
884
- list << element
885
- yield(element) if block_given?
886
- end
887
- end
888
- return list
889
-
890
- when Array
891
- return other.map {|v| scan(v, leaf, &block)}
892
-
893
- else
894
- if other.respond_to?(:_enumerator)
895
- enumerator = other._enumerator(self)
896
- return enumerator unless block_given?
897
- return enumerator.each(&block)
898
- end
899
-
900
- element = (other ^ self)
901
- if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
902
- list << element
903
- yield(element) if block_given?
904
- end
905
- if (other.respond_to?(:child) && other.child)
906
- other.child.each do |object|
907
- list += scan(object, leaf, &block)
908
- end
909
- end
910
- return list
911
- end
912
- end
913
-
914
- # 条件を満たすオブジェクトの抽出
915
- #
916
- # @param (see #scan)
917
- # @return (see #scan)
918
- #
919
- def ^(other, leaf=true, &block)
920
- scan(other, leaf, &block)
921
- end
922
-
923
- # 属性を変更したコピーを作る
924
- #
925
- # @param [Hash] options { 属性=>属性値 }
926
- #
927
- # @return [When::TM::TemporalPosition]
928
- #
929
- def copy(options={})
930
- position = self.dup
931
- position._copy(options)
932
- position
933
- end
934
-
935
- # 属性の Hash
936
- # @private
937
- def _attr
938
- attributes = {}
939
- [:frame, :events, :precision, :options, :trans, :query].each do |key|
940
- attributes[key] = instance_variable_get("@#{key}")
941
- end
942
- attributes[:location] = location
943
- attributes[:time_standard] = time_standard
944
- return attributes.delete_if {|k,v| !v}
945
- end
946
-
947
- protected
948
-
949
- # 属性のコピー
950
- # @private
951
- def _copy(options={})
952
- @frame = options[:frame] if (options.key?(:frame))
953
- @events = options[:events] if (options.key?(:events))
954
- @precision = options[:precision] if (options.key?(:precision))
955
- @query = options[:query] if (options.key?(:query))
956
- @location = options[:location] if (options.key?(:location))
957
- @time_standard = options[:time_standard] if (options.key?(:time_standard))
958
- @sdn = @universal_time = @dynamical_time = @period = nil
959
- _normalize(options)
960
- return self
961
- end
962
-
963
- private
964
-
965
- # オブジェクトの生成
966
- #
967
- # @param [Hash] options see {When::TM::TemporalPosition._instance}
968
- #
969
- def initialize(options={})
970
- options.reject! {|key,value| value == nil}
971
- options.each_pair do |key,value|
972
- self.instance_variable_set("@#{key}", value)
973
- end
974
- @keys = []
975
- @location = When.Resource(@location, '_l:') if @location.kind_of?(String)
976
- # @sdn = @universal_time = @dynamical_time = nil
977
- _normalize(options)
978
- end
979
-
980
- # オブジェクトの正規化
981
- def _normalize(options={})
982
- @precision ||= SYSTEM
983
- @period = nil
984
- @keys |= @frame.keys if @frame
985
- end
986
-
987
- # 指定桁のチェック
988
- def _digit(index)
989
- digit = index.kind_of?(String) ? PRECISION[index.upcase] : index
990
- raise RangeError, " wrong digit: #{index}" unless digit.kind_of?(Integer) && (!block_given? || yield(digit))
991
- digit
992
- end
993
-
994
- # 指定の日を探す
995
- def _force_euqal_day(diff)
996
- jdn = self.to_i + diff
997
- date = self
998
- done = {}
999
- loop do
1000
- case
1001
- when date.to_i == jdn
1002
- return date
1003
- when date.to_i > jdn
1004
- next_date = date - When::P1D
1005
- date = (date.to_i == next_date.to_i) ? date - When::P1D * 2 : next_date
1006
- when date.to_i < jdn
1007
- next_date = date + When::P1D
1008
- date = (date.to_i == next_date.to_i) ? date + When::P1D * 2 : next_date
1009
- end
1010
- raise RangeError, "can't find target date: #{self} -> #{jdn}" if done.key?(date.to_i)
1011
- done[date.to_i] = true
1012
- end
1013
- end
1014
-
1015
- #
1016
- # 対応する ::Date start 属性
1017
- def _default_start
1018
- frame ? frame._default_start : ::Date::GREGORIAN
1019
- end
1020
-
1021
- alias :_method_missing :method_missing
1022
-
1023
- # その他のメソッド
1024
- #
1025
- # @note
1026
- # When::TM::TemporalPosition で定義されていないメソッドは
1027
- # 処理を @frame (type: When::TM::Calendar or When::TM::Clock) に委譲する
1028
- #
1029
- def method_missing(name, *args, &block)
1030
-
1031
- return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name)
1032
- self.class.module_eval %Q{
1033
- def #{name}(*args, &block)
1034
- @frame.send("#{name}", self, *args, &block)
1035
- end
1036
- } unless When::Parts::MethodCash.escape(name)
1037
- @frame.send(name, self, *args, &block)
1038
- end
1039
- end
1040
-
1041
- #
1042
- # 時間座標 - 単一の時間間隔によって定義する連続な間隔尺度を基礎とする
1043
- #
1044
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeCoordinateType gml schema}
1045
- #
1046
- class Coordinate < TemporalPosition
1047
-
1048
- class << self
1049
- # 内部時間によるオブジェクトの生成
1050
- alias :universal_time :new
1051
-
1052
- # 外部時間によるオブジェクトの生成
1053
- #
1054
- # @param [Numeric] dynamical_time 外部時間による時間座標値
1055
- #
1056
- # @param [Hash] options 下記の通り
1057
- # @option options [When::TimeStandard] :time_standard
1058
- #
1059
- # @return [When::TM::Coordinate]
1060
- #
1061
- def dynamical_time(dynamical_time, options={})
1062
- universal_time(When.Resource(options[:time_standard] || 'UniversalTime', '_t:').from_dynamical_time(dynamical_time), options)
1063
- end
1064
-
1065
- # 他種の時間位置によるオブジェクトの生成
1066
- #
1067
- # @param [Numeric, When::TM::ClockTime, ::Time, ::Date, ::DateTime] time 他種の時間位置によるオブジェクト
1068
- #
1069
- # @param [Hash] options 下記の通り
1070
- # @option options [When::TM::Clock] :clock
1071
- # @option options [When::Parts::Timezone] :tz
1072
- #
1073
- # @return [When::TM::Coordinate]
1074
- #
1075
- def new(time, options={})
1076
- options = options.dup
1077
- options[:frame] = Clock.get_clock_option(options)
1078
- case time
1079
- when Numeric
1080
- options[:frame] ||= When::UTC unless time.kind_of?(Integer)
1081
- universal_time = (2*time - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1082
- when ClockTime
1083
- options[:frame] ||= time.clock
1084
- universal_time = time.clk_time[0] + time.universal_time
1085
- when ::Time
1086
- options[:frame] ||= When.Clock(time.gmtoff)
1087
- universal_time = When.Resource('_t:UniversalTime').from_time_object(time)
1088
- when TimeValue
1089
- options[:frame] ||= time.clock
1090
- universal_time = time.universal_time
1091
- else
1092
- if ::Object.const_defined?(:Date) && time.respond_to?(:ajd)
1093
- case time
1094
- when ::DateTime
1095
- options[:frame] ||= When.Clock((time.offset * 86400).to_i)
1096
- universal_time = (2*time.ajd - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1097
- when ::Date
1098
- universal_time = JulianDate._d_to_t(time.jd)
1099
- end
1100
- end
1101
- end
1102
- raise TypeError, "Can't create #{self} from #{time.class}" unless universal_time
1103
- universal_time(universal_time, options)
1104
- end
1105
- end
1106
-
1107
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1108
- #
1109
- # The time reference system associated with the temporal position being described
1110
- #
1111
- # @return [When::TM::ReferenceSystem]
1112
- #
1113
- alias :clock :frame
1114
-
1115
- # 内部時間による時間座標値
1116
- #
1117
- # @return [Numeric]
1118
- #
1119
- attr_accessor :universal_time
1120
- alias :coordinateValue :universal_time
1121
- protected :universal_time=
1122
-
1123
- # CoordinateSystem による時間座標値
1124
- #
1125
- # @return [Numeric]
1126
- #
1127
- def coordinateValue
1128
- (universal_time - frame.origin.universal_time) / frame.interval.to_f
1129
- end
1130
-
1131
- # 加算
1132
- #
1133
- # @param [Numeric, When::TM::IntervalLength] other
1134
- #
1135
- # @return [When::TM::TemporalPosition]
1136
- #
1137
- def +(other)
1138
- other = other.to_interval_length if other.kind_of?(PeriodDuration)
1139
- super(other)
1140
- end
1141
-
1142
- # 減算
1143
- #
1144
- # @param [When::TM::TemporalPosition, Numeric, When::TM::IntervalLength] other
1145
- #
1146
- # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::IntervalLength
1147
- # @return [When::TM::IntervalLength] if other is a When::TM::TemporalPosition
1148
- #
1149
- def -(other)
1150
- other = other.to_interval_length if other.kind_of?(PeriodDuration)
1151
- super(other)
1152
- end
1153
-
1154
- # オブジェクトの生成
1155
- #
1156
- # @param [Numeric] universal_time 内部時間による時間座標値
1157
- #
1158
- # @param [Hash] options 下記の通り
1159
- # @option options [When::TM::CoordinateSystem] :frame
1160
- #
1161
- def initialize(universal_time, options={})
1162
- super(options)
1163
- @universal_time = universal_time
1164
- end
1165
- end
1166
-
1167
- #
1168
- # ユリウス日
1169
- #
1170
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#JulianDateType gml schema}
1171
- #
1172
- class JulianDate < Coordinate
1173
-
1174
- # Julian Day Number
1175
- # 19700101T120000Z
1176
- JD19700101 = 2440588
1177
-
1178
- class << self
1179
-
1180
- JD19700100_5 = JD19700101 - 0.5
1181
-
1182
- #
1183
- # 日時の内部表現をユリウス日に変換
1184
- #
1185
- # @param [Numeric] t
1186
- #
1187
- # @return [Numeric]
1188
- #
1189
- def _t_to_d(t)
1190
- t / Duration::DAY + JD19700100_5
1191
- end
1192
-
1193
- #
1194
- # ユリウス日をに日時の内部表現変換
1195
- #
1196
- # @param [Numeric] d
1197
- #
1198
- # @return [Numeric]
1199
- #
1200
- def _d_to_t(d)
1201
- (d - JD19700100_5) * Duration::DAY
1202
- end
1203
- end
1204
-
1205
- # 加算
1206
- #
1207
- # @param [Numeric, When::TM::IntervalLength] other
1208
- #
1209
- # @return [When::TM::TemporalPosition]
1210
- #
1211
- def +(other)
1212
- new_date = super
1213
- new_date.frame = new_date.frame._daylight(new_date.universal_time) if new_date.frame && new_date.frame._need_validate
1214
- return new_date
1215
- end
1216
-
1217
- # ユリウス日が指定の剰余となる日
1218
- #
1219
- # @param [When::Coordinates::Residue] other
1220
- #
1221
- # @return [When::TM::TemporalPosition]
1222
- #
1223
- def &(other)
1224
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1225
- raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1226
- jdn = to_i
1227
- new_date = self.dup
1228
- new_date.universal_time += ((other & jdn) - jdn) * Duration::DAY
1229
- return new_date
1230
- end
1231
-
1232
- # ユリウス日の剰余
1233
- #
1234
- # @param [When::Coordinates::Residue] other
1235
- #
1236
- # @return [Numeric]
1237
- #
1238
- def %(other)
1239
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1240
- raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1241
- other % to_i
1242
- end
1243
-
1244
- private
1245
-
1246
- # オブジェクトの生成
1247
- #
1248
- # @param [Numeric] universal_time 内部時間による時間座標値
1249
- #
1250
- # @param [Hash] options 以下の通り
1251
- # @option options [When::TM::Clock] :frame
1252
- # @option options [Integer] :precision
1253
- #
1254
- def initialize(universal_time, options={})
1255
- @frame = options.delete(:frame)
1256
- @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1257
- @frame = @frame._daylight(universal_time) if @frame && @frame._need_validate
1258
- precision = options.delete(:precision)
1259
- precision ||= DAY unless @frame.kind_of?(Clock)
1260
- @precision = Index.precision(precision)
1261
- super
1262
- end
1263
- end
1264
-
1265
- #
1266
- # 順序時間参照系で参照する位置
1267
- #
1268
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeOrdinalPositionType gml schema}
1269
- #
1270
- class OrdinalPosition < TemporalPosition
1271
-
1272
- # この順序位置と関連付けられた順序年代 (relation - Reference)
1273
- #
1274
- # The ordinal era associated with the ordinal position being described
1275
- #
1276
- # @return [When::TM::OrdinalEra]
1277
- #
1278
- attr_reader :ordinal_position
1279
- alias :ordinalPosition :ordinal_position
1280
-
1281
- # オブジェクトの生成
1282
- #
1283
- # @param [When::TM::OrdinalEra] ordinal_position この順序位置と関連付けられた順序年代
1284
- # @param [Hash] options see {When::TM::TemporalPosition._instance}
1285
- #
1286
- def initialize(ordinal_position, options={})
1287
- super(options)
1288
- @ordinal_position = ordinal_position
1289
- end
1290
- end
1291
-
1292
- #
1293
- # 時刻
1294
- #
1295
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#ClockTimeType gml schema}
1296
- #
1297
- class ClockTime < TemporalPosition
1298
-
1299
- # 時刻要素
1300
- #
1301
- # @return [Array<Numeric>]
1302
- #
1303
- # @note ISO19108 では sequence<Integer> だが、閏時・閏秒などが表現可能なよう Numeric としている。
1304
- #
1305
- attr_reader :clk_time
1306
- alias :clkTime :clk_time
1307
-
1308
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1309
- #
1310
- # The time reference system associated with the temporal position being described
1311
- #
1312
- # @return [When::TM::ReferenceSystem]
1313
- #
1314
- alias :clock :frame
1315
-
1316
- # 内部時間
1317
- #
1318
- # @return [Numeric]
1319
- #
1320
- # T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1321
- #
1322
- # 時法によっては、異なる意味を持つことがある
1323
- #
1324
- def universal_time
1325
- raise NameError, "Temporal Reference System is not defined" unless @frame
1326
- @universal_time ||= @frame.to_universal_time(@clk_time)
1327
- end
1328
-
1329
- # 繰り上がり
1330
- #
1331
- # @return [Numeric]
1332
- #
1333
- # 日付の境界が午前0時でない場合、clk_time の最上位桁に 0 以外が入ることがある
1334
- #
1335
- def carry
1336
- return @clk_time[0]
1337
- end
1338
-
1339
- # 要素の参照
1340
- #
1341
- # @param [Integer, String] index 参照する要素の指定
1342
- #
1343
- # @return [Numeric]
1344
- #
1345
- def value(index)
1346
- @clk_time[_digit(index) {|digit| digit >= DAY}]
1347
- end
1348
-
1349
- #protected
1350
- # 属性のコピー
1351
- # @private
1352
- def _copy(options={})
1353
- @clk_time = options[:time] if (options.key?(:time))
1354
- @frame = options[:clock] if (options.key?(:clock))
1355
- if (options.key?(:tz_prop))
1356
- @frame = @frame.dup
1357
- @frame.tz_prop = options[:tz_prop]
1358
- end
1359
- return super
1360
- end
1361
-
1362
- # オブジェクトの生成
1363
- #
1364
- # @param [String] time ISO8601形式の時刻表現
1365
- # @param [Array<Numeric>] time (日, 時, 分, 秒)
1366
- #
1367
- #
1368
- # @param [Hash] options 以下の通り
1369
- # @option options [When::TM::Clock] :frame
1370
- # @option options [Integer] :precision
1371
- #
1372
- def initialize(time, options={})
1373
- # 参照系の取得
1374
- @frame = options[:frame] || Clock.local_time
1375
- @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1376
- options.delete(:frame)
1377
-
1378
- # 時刻表現の解読 ( Time Zone の解釈 )
1379
- if (time.kind_of?(String))
1380
- case time
1381
- when /^([-+])?(\d{2,}?):?(\d{2})?:?(\d{2}(\.\d+)?)?$/
1382
- sign, hh, mm, ss = $~[1..4]
1383
- time = @frame._validate([0,0,0,0],
1384
- [0,
1385
- -(sign.to_s + "0" + hh.to_s).to_i,
1386
- -(sign.to_s + "0" + mm.to_s).to_i,
1387
- Pair._en_number(-(sign.to_s + "0" + ss.to_s).to_f)])
1388
- time[0] = Pair.new(0, time[0].to_i) if (time[0] != 0)
1389
- when /^Z$/
1390
- time = [0,0,0,0]
1391
- else
1392
- raise ArgumentError, "Invalid Time Format"
1393
- end
1394
- end
1395
- @clk_time = time
1396
-
1397
- # 分解能
1398
- @precision = @frame._precision(time, options.delete(:precision))
1399
-
1400
- super(options)
1401
- end
1402
-
1403
- private
1404
-
1405
- # オブジェクトの正規化
1406
- def _normalize(options={})
1407
- # strftime で使用する locale
1408
- @keys |= @frame.keys
1409
-
1410
- # 時刻の正規化
1411
- @clk_time = @frame._validate(@clk_time) unless options[:validate]
1412
- end
1413
-
1414
- # 加減算共通処理
1415
- def _plus(period)
1416
- self.dup._copy({:time=>@frame._validate(@clk_time, @frame._arrange_length(period.time)),
1417
- :events=>nil, :query=>nil, :validate=>:done})
1418
- end
1419
- end
1420
-
1421
- #
1422
- # 暦日
1423
- #
1424
- # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#CalDateType]
1425
- #
1426
- class CalDate < TemporalPosition
1427
-
1428
- # 検索オプション
1429
- # @private
1430
- SearchOption = {After=>[0, -2, Before], Before=>[-2, 0, After]}
1431
-
1432
- # 日付要素
1433
- #
1434
- # @return [Array<Numeric>]
1435
- #
1436
- # @note ISO19108 では sequence<Integer> だが、閏月などが表現可能なよう Numeric としている。
1437
- #
1438
- attr_reader :cal_date
1439
- alias :calDate :cal_date
1440
-
1441
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1442
- #
1443
- # The time reference system associated with the temporal position being described
1444
- #
1445
- # @return [When::TM::ReferenceSystem]
1446
- #
1447
- alias :calendar :frame
1448
-
1449
- # 暦年代名
1450
- #
1451
- # @return [Array] ( name, epoch, reverse, go back )
1452
- # - name [String] 暦年代名
1453
- # - epoch [Integer] 使用する When::TM::Calendar で暦元に対応する年
1454
- # - reverse [Boolean] 年数が昇順(false,nil)か降順(true)か
1455
- # - go back [Boolean] 参照イベントより前の暦日か(true)、否か(false,nil)
1456
- #
1457
- attr_accessor :calendar_era_name
1458
- alias :calendarEraName :calendar_era_name
1459
-
1460
- # 暦年代
1461
- #
1462
- # @return [When::TM::CalendarEra]
1463
- #
1464
- attr_accessor :calendar_era
1465
- alias :calendarEra :calendar_era
1466
-
1467
- # 時法の取得 - ダミー
1468
- def clock
1469
- nil
1470
- end
1471
-
1472
- # 内部時間
1473
- #
1474
- # @return [Numeric]
1475
- #
1476
- # 当日正午の 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1477
- #
1478
- def universal_time
1479
- return super if [Now, Max, Min].include?(@indeterminated_position)
1480
- @universal_time ||= JulianDate._d_to_t(to_i)
1481
- end
1482
-
1483
- # ユリウス日
1484
- #
1485
- # @return [Integer]
1486
- #
1487
- # -4712-01-01T12:00:00Z からの経過日数に対応する通番(日の境界で1進める)
1488
- #
1489
- def to_i
1490
- return @sdn if @sdn
1491
- name, base = @calendar_era_name
1492
- if base
1493
- date = @cal_date.dup
1494
- date[0] += base
1495
- else
1496
- date = @cal_date
1497
- end
1498
- @sdn = @frame.to_julian_date(date)
1499
- end
1500
-
1501
- # 剰余類化
1502
- #
1503
- # @param [Numeric] remainder 剰余
1504
- # @param [Integer] divisor 法(>0)
1505
- #
1506
- # @return [When::Coordinates::Residue] 当日、当年を基準とする剰余類
1507
- #
1508
- def to_residue(remainder, divisor)
1509
- When::Coordinates::Residue.new(remainder, divisor, {'day' => least_significant_coordinate,
1510
- 'year' => most_significant_coordinate})
1511
- end
1512
-
1513
- # 要素の参照
1514
- #
1515
- # @param [Integer, String] index 参照する要素の指定
1516
- #
1517
- # @return [Numeric]
1518
- #
1519
- def value(index)
1520
- @cal_date[(_digit(index) {|digit| digit <= DAY})-1]
1521
- end
1522
-
1523
- #
1524
- # 最上位の要素
1525
- #
1526
- # @return [Numeric] 暦年代の epoch に関わらず暦法に従った年の通し番号を返す
1527
- #
1528
- def most_significant_coordinate
1529
- coordinate = @cal_date[0]
1530
- coordinate += @calendar_era_name[1] if @calendar_era_name
1531
- @frame.index_of_MSC.times do |i|
1532
- coordinate = +coordinate * @frame.indices[i].unit + @cal_date[i+1] - @frame.indices[i].base
1533
- end
1534
- coordinate
1535
- end
1536
-
1537
- #
1538
- # 最下位の要素
1539
- #
1540
- # @return [Numeric] 剰余類の演算に用いる日の通し番号を返す
1541
- #
1542
- def least_significant_coordinate
1543
- return to_i + @frame.indices[-1].shift
1544
- end
1545
-
1546
- # ユリウス日または通年が指定の剰余となる日
1547
- #
1548
- # @param [When::Coordinates::Residue] other
1549
- #
1550
- # @return [When::TM::CalDate]
1551
- #
1552
- def &(other)
1553
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1554
- case other.event
1555
- when 'day'
1556
- # 指定の剰余となる日
1557
- other -= @frame.indices[-1].shift unless @frame.indices[-1].shift == 0
1558
- date = @frame.to_cal_date(other & to_i)
1559
- date[0] -= @calendar_era_name[1] if @calendar_era_name
1560
- return self.dup._copy({:events=>nil, :query=>@query, :validate=>:done, :date=>date})
1561
-
1562
- when 'year'
1563
- # 指定の剰余となる年
1564
- date = @cal_date.dup
1565
- date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1566
- date[0] -= @calendar_era_name[1] if @calendar_era_name
1567
- return self.dup._copy({:date=>date, :events=>nil, :query=>@query})
1568
-
1569
- else
1570
- raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1571
- end
1572
- end
1573
-
1574
- # ユリウス日または通年の剰余
1575
- #
1576
- # @param [When::Coordinates::Residue] other
1577
- #
1578
- # @return [Numeric]
1579
- #
1580
- def %(other)
1581
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1582
- if precision <= When::YEAR && other.units['year'] && other.event != 'year'
1583
- other.to('year') % (most_significant_coordinate + @frame._diff_to_CE)
1584
- else
1585
- case other.event
1586
- when 'day' ; other % least_significant_coordinate
1587
- when 'year' ; other % (most_significant_coordinate + @frame._diff_to_CE)
1588
- else ; raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1589
- end
1590
- end
1591
- end
1592
-
1593
- # 下位桁の切り捨て
1594
- #
1595
- # @param [Integer] digit 切り捨てずに残す、最下位の桁
1596
- #
1597
- # @param [Integer] precision 切り捨て結果の分解能
1598
- #
1599
- # @return [When::TM::CalDate]
1600
- #
1601
- def floor(digit=DAY, precision=digit)
1602
- options = {:date=>@cal_date[0..(digit-1)], :events=>nil, :query=>nil}
1603
- options[:precision] = precision if precision
1604
- self.dup._copy(options)
1605
- end
1606
-
1607
- # 下位桁の切り上げ
1608
- #
1609
- # @param [Integer] digit 切り上げずに残す、最下位の桁
1610
- #
1611
- # @param [Integer] precision 切り上げ結果の分解能
1612
- #
1613
- # @return [When::TM::CalDate]
1614
- #
1615
- def ceil(digit=DAY, precision=digit)
1616
- (self + PeriodDuration.new(1, digit, (-@frame.indices.length)..0)).floor(digit, precision)
1617
- end
1618
-
1619
- # 要素数 ― 上位要素に含まれる下位要素の数
1620
- #
1621
- # @param [Integer] upper 上位要素のインデックス
1622
- # @param [Integer] lower 下位要素のインデックス(DAY または MONTH)
1623
- #
1624
- # @return [Integer]
1625
- #
1626
- def length(upper, lower=DAY)
1627
- range = [floor(upper).to_i, ceil(upper).to_i]
1628
- range = range.map {|d| (Residue.mod(d) {|m| frame._new_month(m)})[0]} if lower == MONTH
1629
- range[1] - range[0]
1630
- end
1631
-
1632
- # 時刻情報のない When::TM::CalDate を返す
1633
- #
1634
- # @return [When::TM::CalDate]
1635
- #
1636
- alias :to_cal_date :dup
1637
- alias :to_CalDate :to_cal_date
1638
-
1639
- # 暦年代が末端の参照であるか?
1640
- #
1641
- # @return [Boolean]
1642
- #
1643
- def leaf?
1644
- name, = @calendar_era_name
1645
- return true unless name.respond_to?(:_pool)
1646
- era = name._pool['..']
1647
- return true unless era.respond_to?(:leaf?)
1648
- return era.leaf?
1649
- end
1650
-
1651
- # 属性の Hash
1652
- # @private
1653
- def _attr
1654
- super.merge({:era_name=>@calendar_era_name, :era=>@calendar_era})
1655
- end
1656
- protected
1657
-
1658
- # 属性のコピー
1659
- # @private
1660
- def _copy(options={})
1661
- @cal_date = options[:date] if (options.key?(:date))
1662
- return super
1663
- end
1664
-
1665
- # オブジェクトの生成
1666
- #
1667
- # @param [Array<Numeric>] date 日付表現
1668
- #
1669
- # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1670
- # @option options [When::TM::Calendar] :frame
1671
- # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1672
- # (Integer は 当該年号の 0 年に相当する通年)
1673
- # @option options [Integer] :precision
1674
- #
1675
- def initialize(date, options={})
1676
- # 年号 & 日付
1677
- @calendar_era_name = options[:era_name]
1678
- @calendar_era = options[:era]
1679
- @cal_date = date
1680
-
1681
- super(options)
1682
- end
1683
-
1684
- private
1685
-
1686
- # オブジェクトの正規化
1687
- def _normalize(options={})
1688
-
1689
- # 日付配列の長さ
1690
- cal_date_index = @cal_date.index(nil) || @cal_date.length
1691
-
1692
- # 日付の正規化
1693
- if @calendar_era_name
1694
- # Calendar Era がある場合
1695
- trans_options = @trans || {} # TODO? 消す
1696
- count = trans_options[:count] || 1
1697
- query_options = (@query || {}).dup
1698
- query_options[:label] = @calendar_era_name
1699
- query_options[:count] = count
1700
- era = date = nil
1701
- if @calendar_era
1702
- era, date = _search_era(@calendar_era, trans_options)
1703
- else
1704
- eras = CalendarEra._instance(query_options)
1705
- raise ArgumentError, "CalendarEraName doesn't exist: #{query_options[:label]}" if (eras.size==0)
1706
- eras.each do |e|
1707
- era, date = _search_era(e, trans_options)
1708
- next unless era
1709
- count -= 1
1710
- break unless count > 0
1711
- end
1712
- end
1713
- raise RangeError, "Out of CalendarEra Range" unless era
1714
- @calendar_era_name = date.calendar_era_name
1715
- @calendar_era = era
1716
- @cal_date = date.cal_date
1717
- @frame = date.frame
1718
- @query = (@query||{}).merge(date.query)
1719
- @trans = (@trans||{}).merge(date.trans)
1720
- @keys |= @calendar_era_name[0].keys | @frame.keys
1721
- else
1722
- # Calendar Era がない場合
1723
- @frame = When.Resource(options[:frame] || @frame || 'Gregorian', '_c:')
1724
- @cal_date = @frame._validate(@cal_date) unless options[:validate] == :done
1725
- @keys |= @frame.keys
1726
- end
1727
-
1728
- # 分解能
1729
- precision = options.delete(:precision) || @precision
1730
- cal_date_index =
1731
- case options.delete(:_format)
1732
- when :century ; CENTURY
1733
- when :week, :day ; DAY
1734
- else ; cal_date_index - (@frame.indices.length + 1)
1735
- end
1736
- precision ||= cal_date_index if cal_date_index < DAY
1737
- precision ||= @clk_time.precision if @clk_time
1738
- @precision = Index.precision(precision || DAY)
1739
- end
1740
-
1741
- # 暦年代を探す
1742
- def _search_era(era, trans_options)
1743
- cal_date = @cal_date.dup
1744
- cal_date[0] = -cal_date[0] if era.reverse?
1745
- cal_date[0] += era.epoch_year
1746
- date = era.trans(cal_date, trans_options)
1747
- loop do
1748
- case date
1749
- when Before, After
1750
- i0, i1, reverse = SearchOption[date]
1751
- new_era = (date == Before) ? era.prev : era.succ
1752
- break unless new_era
1753
- date = new_era.trans(cal_date, trans_options)
1754
- if date == reverse
1755
- cal_date = new_era.epoch[i0].frame.to_cal_date(era.epoch[i1].frame.to_julian_date(cal_date))
1756
- date = new_era.trans(cal_date, trans_options)
1757
- break if date == reverse
1758
- end
1759
- era = new_era
1760
- when TimeValue
1761
- return era, date
1762
- else
1763
- break
1764
- end
1765
- end
1766
- return nil
1767
- end
1768
-
1769
- # 加減算共通処理
1770
- def _plus(period)
1771
- self.dup._copy({:date=>_date_with_era(@frame._validate(_date_without_era,
1772
- @frame._arrange_length(period.date))),
1773
- :events=>nil, :query=>nil, :validate=>:done})
1774
- end
1775
-
1776
- # 年号を除外した @frame の暦法に対応する日付
1777
- def _date_without_era
1778
- date = @cal_date.dup
1779
- date[0] += @calendar_era_name[1] if @calendar_era_name
1780
- date
1781
- end
1782
-
1783
- # 年号に続く日付
1784
- def _date_with_era(date)
1785
- date[0] -= @calendar_era_name[1] if @calendar_era_name
1786
- date
1787
- end
1788
- end
1789
-
1790
- #
1791
- # 時刻を伴った日付
1792
- #
1793
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#DateAndTimeType gml schema}
1794
- #
1795
- class DateAndTime < CalDate
1796
-
1797
- # 時刻要素
1798
- #
1799
- # @return [When::TM::ClockTime]
1800
- #
1801
- attr_reader :clk_time
1802
- alias :clkTime :clk_time
1803
-
1804
- # 時法の取得
1805
- #
1806
- # @return [When::TM::Clock]
1807
- #
1808
- def clock
1809
- @clk_time.frame
1810
- end
1811
-
1812
- # 内部時間
1813
- #
1814
- # @return [Numeric]
1815
- #
1816
- # 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1817
- #
1818
- # 暦法によっては、異なる意味を持つことがある
1819
- #
1820
- def universal_time
1821
- return super if [Now, Max, Min].include?(@indeterminated_position)
1822
- raise NameError, "Temporal Reference System is not defined" unless (@frame && clock)
1823
- @universal_time ||= (to_i - +@clk_time.clk_time[0] - JulianDate::JD19700101) * Duration::DAY +
1824
- @clk_time.universal_time
1825
- end
1826
-
1827
- # 要素の参照
1828
- #
1829
- # @param [Integer] index 参照する要素の指定
1830
- #
1831
- # @return [Numeric]
1832
- #
1833
- def value(index)
1834
- digit = _digit(index)
1835
- return (digit <= DAY) ? @cal_date[digit-1] : @clk_time.clk_time[digit]
1836
- end
1837
-
1838
- # ユリウス日または通年が指定の剰余となる日
1839
- #
1840
- # @param [When::Coordinates::Residue] other
1841
- #
1842
- # @return [When::TM::DateAndTime]
1843
- #
1844
- def &(other)
1845
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1846
- case other.event
1847
- when 'day'
1848
- # 指定の剰余となる日
1849
- other -= @frame.indices[-1].shift unless @frame.indices[-1].shift == 0
1850
- return self.dup._copy({:events=>nil, :query=>@query, :validate=>:done,
1851
- :date=>_date_with_era(@frame.to_cal_date(other & to_i)),
1852
- :time=>@clk_time.clk_time.dup})
1853
-
1854
- when 'year'
1855
- # 指定の剰余となる年
1856
- date = @cal_date.dup
1857
- date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1858
- return self.dup._copy({:events=>nil, :query=>@query,
1859
- :date=>_date_with_era(date),
1860
- :time=>@clk_time.clk_time.dup})
1861
-
1862
- else
1863
- raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1864
- end
1865
- end
1866
-
1867
- # 下位桁の切り捨て
1868
- #
1869
- # @param [Integer] digit 切り捨てずに残す、最下位の桁
1870
- #
1871
- # @param [Integer] precision 切り捨て結果の分解能
1872
- #
1873
- # @return [When::TM::DateAndTime]
1874
- #
1875
- def floor(digit=DAY, precision=digit)
1876
- count = digit - clock.indices.length
1877
- date = (digit>=DAY) ? @cal_date.dup : @frame._validate(@cal_date[0..(digit-1)])
1878
- time = clock._validate(@clk_time.clk_time[0..((digit<=DAY) ? 0 : ((count>=0) ? -1 : digit))])
1879
-
1880
- if (count >= 0)
1881
- factor = 10**count
1882
- time[-1] = (time[-1] * factor).floor.to_f / factor
1883
- end
1884
-
1885
- # オブジェクトの生成
1886
- options = {:date=>date, :validate=>:done, :events=>nil, :query=>nil,
1887
- :time=>(digit<=DAY) ? time : @clk_time.dup._copy({:time=>time})}
1888
- options[:precision] = precision if precision
1889
- return self.dup._copy(options)
1890
- end
1891
-
1892
- # 下位桁の切り上げ
1893
- #
1894
- # @param [Integer] digit 切り上げずに残す、最下位の桁
1895
- #
1896
- # @param [Integer] precision 切り上げ結果の分解能
1897
- #
1898
- # @return [When::TM::DateAndTime]
1899
- #
1900
- def ceil(digit=DAY, precision=digit)
1901
- length = clock.indices.length
1902
- count = digit - length
1903
- period = PeriodDuration.new((count<=0) ? 1 : 0.1**count, digit, (-@frame.indices.length)..length)
1904
- result = floor(digit, precision) + period
1905
- result += clock.tz_difference if (result.universal_time <= self.universal_time)
1906
- return result
1907
- end
1908
-
1909
- # 位置情報
1910
- #
1911
- # @return [When::Coordinates::Spatial]
1912
- #
1913
- def location
1914
- return @location if @location
1915
- timezone = @clk_time.frame.tz_prop
1916
- return nil unless timezone.kind_of?(When::Parts::Timezone)
1917
- @location = timezone.location
1918
- end
1919
-
1920
- # 時刻情報のない When::TM::CalDate を返す
1921
- #
1922
- # @return [When::TM::CalDate]
1923
- #
1924
- def to_cal_date
1925
- options = _attr
1926
- options.delete(:clock)
1927
- options[:precision] = [When::DAY, options[:precision]].min
1928
- CalDate.new(@cal_date, options)
1929
- end
1930
- alias :to_CalDate :to_cal_date
1931
-
1932
- #protected
1933
-
1934
- # 属性の Hash
1935
- # @private
1936
- def _attr
1937
- super.merge({:clock=>clock})
1938
- end
1939
-
1940
- # 属性のコピー
1941
- # @private
1942
- def _copy(options={})
1943
- # 夏時間の調整
1944
- case options[:time]
1945
- when Array
1946
- if clock._need_validate
1947
- new_clock = clock._daylight([@frame, options[:date], options[:time]])
1948
- options[:time] = options[:time].map {|t| t * 1}
1949
- else
1950
- new_clock = clock
1951
- end
1952
- options[:time] = @clk_time.dup._copy(options.merge({:clock=>new_clock}))
1953
- when nil
1954
- options[:time] = @clk_time.dup._copy(options)
1955
- end
1956
-
1957
- return super(options)
1958
- end
1959
-
1960
- # オブジェクトの生成
1961
- #
1962
- # @param [Array<Numeric>] date 日付表現
1963
- # @param [Array<Numeric>] time 時刻表現
1964
- # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1965
- # @option options [When::TM::Calendar] :frame
1966
- # @option options [When::TM::Clock] :clock
1967
- # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1968
- # (Integer 当該年号の 0 年に相当する通年)
1969
- # @option options [Integer] :precision
1970
- #
1971
- def initialize(date, time, options={})
1972
- options[:time] = time
1973
- super(date, options)
1974
- end
1975
-
1976
- private
1977
-
1978
- # オブジェクトの正規化
1979
- def _normalize(options={})
1980
-
1981
- # Clock
1982
- unless options[:validate]
1983
- clock = Clock.get_clock_option(options)
1984
- clock ||= options[:time].frame if options[:time].kind_of?(ClockTime)
1985
- clock ||= Clock.local_time
1986
- end
1987
- clock = When.Clock(clock) if clock.kind_of?(String)
1988
- clock_is_timezone = clock && !clock.kind_of?(When::TM::Clock)
1989
- clock = clock.daylight if clock_is_timezone
1990
-
1991
- # ClockTime
1992
- @clk_time =
1993
- case options[:time]
1994
- when ClockTime ; options[:time]
1995
- when Array ; ClockTime.new(options[:time], {:frame=>clock, :precision=>options[:precision], :validate=>:done})
1996
- else ; clock.to_clk_time(options[:time], {:precision=>options[:precision]})
1997
- end
1998
-
1999
- super
2000
-
2001
- # 日付と時刻の正規化
2002
- unless options[:validate]
2003
- time = @clk_time.clk_time
2004
- precision = @clk_time.precision
2005
- second = time[clock.base.length-1]
2006
- second -= clock.base[-1] if second
2007
- if second && second != 0 && time_standard.has_leap?
2008
- zero = DateAndTime.new(@cal_date, time[0..-2],
2009
- {:frame=>@frame, :clock=>clock, :precision=>precision,
2010
- :era_name=>@calendar_era_name, :era=>options[:era],
2011
- :time_standard=>time_standard, :location=>@location})
2012
- end
2013
-
2014
- # 日付と時刻の関係の調整
2015
- @cal_date = _date_with_era(@frame._validate(_date_without_era) {|jdn|
2016
- time[0] += jdn
2017
- time[0..-1] = clock._validate(time)
2018
- jdn = time[0] * 1
2019
- time[0] -= jdn
2020
- jdn
2021
- })
2022
-
2023
- # 夏時間の調整
2024
- if clock._need_validate
2025
- if @calendar_era_name
2026
- cal_date = @cal_date.dup
2027
- cal_date[0] += @calendar_era_name[1]
2028
- else
2029
- cal_date = @cal_date
2030
- end
2031
- clock = clock._daylight([@frame, cal_date, time])
2032
- end
2033
- time = time.map {|t| t * 1}
2034
- @clk_time = ClockTime.new(time, {:frame=>clock, :precision=>precision, :validate=>:done}) if clock_is_timezone
2035
-
2036
- # 閏秒
2037
- if zero
2038
- leap = ((dynamical_time - zero.dynamical_time) * clock.second - second).to_i
2039
- if leap != 0 && leap.abs < clock.second
2040
- @cal_date = zero.cal_date
2041
- @clk_time = zero.clk_time
2042
- @clk_time.clk_time[-1] += second
2043
- leap /= clock.second
2044
- @universal_time = When::Coordinates::LeapSeconds.new(@universal_time-leap, leap, 1/clock.second)
2045
- @dynamical_time -= leap
2046
- end
2047
- end
2048
- end
2049
-
2050
- # 後処理
2051
- @keys |= @clk_time.keys
2052
- end
2053
-
2054
- # 加減算共通処理
2055
- def _plus(period)
2056
- # 日時の加算
2057
- time = @clk_time.clk_time.dup
2058
- pdate = @frame._arrange_length(period.date)
2059
- ptime = clock._arrange_length(period.time)
2060
- date = _date_with_era(@frame._validate(_date_without_era, pdate) {|jdn|
2061
- time[0] += jdn
2062
- time = clock._validate(time, ptime)
2063
- jdn = time[0] * 1
2064
- time[0] -= jdn
2065
- jdn
2066
- })
2067
-
2068
- # オブジェクトの生成
2069
- self.dup._copy({:date=>date, :time=>time, :validate=>:done, :events=>nil, :query=>nil})
2070
- end
2071
- end
2072
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2014 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.4) Temporal Position Package
11
+ #
12
+ #
13
+
14
+ # 列挙データ型である When::TM::IndeterminateValue は,不確定な位置のために
15
+ # 6つの値を規定する.このうち Min と Max は 本ライブラリでの拡張である.
16
+ #
17
+ # These values are interpreted as follows:
18
+ #- “Unknown” 時間位置を示す値が与えられていないことを示す(本ライブラリでは“Unknown”は使用しない)
19
+ #- “Now” 常に現時点の時間位置を示す(オブジェクト生成時の時間位置でないことに注意)
20
+ #- “Before” 実際の時間位置は未知だが、指定した値よりも前であることを示す(本ライブラリでは“Before”は無視される)
21
+ #- “After” 実際の時間位置は未知だが、指定した値よりも後であることを示す(本ライブラリでは“After”は無視される)
22
+ #- “Min” 無限の過去を示す
23
+ #- “Max” 無限の未来を示す
24
+ #
25
+ # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimeIndeterminateValueType]
26
+ #
27
+ module IndeterminateValue
28
+
29
+ After = :After
30
+ Before = :Before
31
+ Now = :Now
32
+ Unknown = :Unknown
33
+ # additional value for this library
34
+ Min = :Min
35
+ # additional value for this library
36
+ Max = :Max
37
+
38
+ S = {'After'=>After, 'Before'=>Before, 'Now'=>Now, 'Unknown'=>Unknown, '-Infinity'=>Min, '+Infinity'=>Max}
39
+ I = S.invert
40
+ end
41
+
42
+ # 時間位置共用体
43
+ # union of
44
+ # When::TM::TemporalPosition
45
+ # When::BasicTypes::Date
46
+ # When::BasicTypes::Time
47
+ # When::BasicTypes::DateTime
48
+ #
49
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimePositionType gml schema}
50
+ #
51
+ class Position
52
+
53
+ include IndeterminateValue
54
+ include When::Parts::Resource
55
+
56
+ # @private
57
+ HashProperty =
58
+ [:indeterminated_position, :frame,
59
+ [:precision, When::SYSTEM], :events, :options, :trans, :query,
60
+ :location, [:time_standard, When::TimeStandard::UniversalTime], [:rate_of_clock, 1.0]]
61
+
62
+ # 代表文字列
63
+ #
64
+ # @return [When::BasicTypes::DateTime]
65
+ #
66
+ attr_reader :date_time8601
67
+ alias :dateTime8601 :date_time8601
68
+
69
+ # 時間位置
70
+ #
71
+ # @return [When::TM::TemporalPosition]
72
+ #
73
+ attr_reader :any_other
74
+ alias :anyOther :any_other
75
+
76
+ # 諸々のオブジェクトから When::TM::TemporalPosition を取り出す
77
+ #
78
+ # @param [Object] position 変換元の時間位置
79
+ # @param [Hash] options
80
+ # see {When::TM::TemporalPosition._instance}
81
+ #
82
+ # @return [When::TM::TemporalPosition] position の型により下記を返す
83
+ # - {When::BasicTypes::Date}, {When::BasicTypes::Time}, {When::BasicTypes::DateTime} - When::TM::Calendar#jul_trans による変換結果
84
+ # - {When::TM::TemporalPosition} - そのまま position ( optionは無視 )
85
+ # - {When::TM::Position} - position.any_other ( optionは無視 )
86
+ # - {When::Parts::GeometricComplex} - position.first ( optionは無視 )
87
+ #
88
+ def self.any_other(position, options={})
89
+ case position
90
+ when TemporalPosition ; position
91
+ when Position ; position.any_other
92
+ when When::Parts::GeometricComplex ; position.first
93
+ else ; When.Calendar(options[:frame] || 'Gregorian').jul_trans(position, options) || position
94
+ end
95
+ end
96
+
97
+ # オブジェクトの生成
98
+ #
99
+ # @param [String] specification When.exe Standard Representation として解釈して生成する
100
+ # @param [Hash] options 暦法や時法などの指定
101
+ # see {When::TM::TemporalPosition._instance}
102
+ #
103
+ # @note
104
+ # specification が String 以外の場合、そのオブジェクトの代表的な日時
105
+ # (When::TM::CalendarEra#reference_dateなど)により解釈する
106
+ #
107
+ def initialize(specification, options={})
108
+
109
+ case specification
110
+ when String
111
+ @date_time8601 = specification
112
+ @any_other = TemporalPosition._instance(specification, options)
113
+ when Position
114
+ @date_time8601 = specification.date_time8601
115
+ @any_other = specification.any_other
116
+ return
117
+ else
118
+ @any_other = specification
119
+ end
120
+
121
+ klass = specification.class
122
+ message = "Irregal specification type: #{klass}"
123
+
124
+ case specification
125
+ when CalDate ; @date_time8601 = When::BasicTypes::Date.new(specification.to_s)
126
+ when ClockTime ; @date_time8601 = When::BasicTypes::Time.new(specification.to_s)
127
+ when TemporalPosition ; @date_time8601 = When::BasicTypes::DateTime.new(specification.to_s)
128
+ when CalendarEra ; @date_time8601 = When::BasicTypes::Date.new(specification.reference_date.to_s)
129
+ when OrdinalEra ; @date_time8601 = When::BasicTypes::Date.new(specification.begin.to_s)
130
+ when When::BasicTypes::Date ; raise TypeError, message unless klass == CalDate
131
+ when When::BasicTypes::Time ; raise TypeError, message unless klass == ClockTime
132
+ when String ;
133
+ else ; raise TypeError, message
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ alias :_method_missing :method_missing
140
+
141
+ # その他のメソッド
142
+ #
143
+ # @note
144
+ # When::TM::Position で定義されていないメソッドは
145
+ # 処理を @date_time8601 (type: When::BasicTypes::DateTime)
146
+ # または @any_other (type: When::TM::TemporalPosition) に委譲する
147
+ # (両方ともに有効なメソッドは@any_otherを優先する)
148
+ #
149
+ def method_missing(name, *args, &block)
150
+ return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name)
151
+ self.class.module_eval %Q{
152
+ def #{name}(*args, &block)
153
+ union = @any_other.respond_to?("#{name}") ? @any_other : @date_time8601
154
+ union.send("#{name}", *args, &block)
155
+ end
156
+ } unless When::Parts::MethodCash.escape(name)
157
+ union = @any_other.respond_to?(name) ? @any_other : @date_time8601
158
+ union.send(name, *args, &block)
159
+ end
160
+ end
161
+
162
+ # 「時間位置」の基底クラス
163
+ #
164
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TemporalPositionType gml schema}
165
+ #
166
+ class TemporalPosition < ::Object
167
+
168
+ #
169
+ # When::TM::JulianDate, When::TM::CalDate or DateAndTime への変換メソッドを提供
170
+ #
171
+ module Conversion
172
+ #
173
+ # 対応する When::TM::JulianDate を生成
174
+ #
175
+ # @param [Hash] options 以下の通り
176
+ # @option options [When::TM::Clock] :clock
177
+ # @option options [When::Parts::Timezone] :tz
178
+ #
179
+ # @return [When::TM::JulianDate]
180
+ #
181
+ def julian_date(options={})
182
+ When::TM::JulianDate.new(self, options)
183
+ end
184
+ alias :to_julian_date :julian_date
185
+
186
+ #
187
+ # 対応する When::TM::CalDate or DateAndTime を生成
188
+ #
189
+ # @param [Hash] options 暦法や時法などの指定
190
+ # see {When::TM::TemporalPosition._instance}
191
+ #
192
+ # @return [When::TM::CalDate, When::TM::DateAndTime]
193
+ #
194
+ def tm_pos(options={})
195
+ When.Calendar(options[:frame] || 'Gregorian').jul_trans(self, options)
196
+ end
197
+ alias :to_tm_pos :tm_pos
198
+ end
199
+
200
+ include When
201
+ include Comparable
202
+ include IndeterminateValue
203
+ include Coordinates
204
+ include Parts::Resource
205
+ include Conversion
206
+
207
+ # @private
208
+ HashProperty = Position::HashProperty
209
+
210
+ # この時間位置の意味づけ
211
+ #
212
+ # @return [When::TM::IndeterminateValue]
213
+ #
214
+ attr_reader :indeterminated_position
215
+ alias :indeterminatedPosition :indeterminated_position
216
+
217
+ # この時間位置と関連付けられた時間参照系 (relation - Reference)
218
+ #
219
+ # The time reference system associated with the temporal position being described
220
+ #
221
+ # @return [When::TM::ReferenceSystem]
222
+ #
223
+ attr_accessor :frame
224
+
225
+ # この時間位置と関連付けられたイベント - additional attribute
226
+ #
227
+ # @return [Array<When::Parts::Enumerator>]
228
+ #
229
+ attr_accessor :events
230
+ #protected :events=
231
+
232
+ # この時間位置の分解能 - additional attribute
233
+ #
234
+ # @return [Numeric]
235
+ #
236
+ # @note precision より resolution の方が分解能の意味にふさわしいが ISO19108 で別の意味に用いられているため resolution とした。
237
+ #
238
+ attr_accessor :precision
239
+
240
+ # その他の属性 - additional attribute
241
+ #
242
+ # @return [Hash] { String=>Object }
243
+ #
244
+ attr_reader :options
245
+
246
+ # その他の属性 - additional attribute
247
+ #
248
+ # @return [Hash] { String=>Object }
249
+ #
250
+ attr_accessor :trans
251
+
252
+ # その他の属性 - additional attribute
253
+ #
254
+ # @return [Hash] { String=>When::BasicTypes::M17n }
255
+ #
256
+ attr_accessor :query
257
+
258
+ # その他の属性 - additional attribute
259
+ #
260
+ # @return [When::Coordinates::Spatial]
261
+ #
262
+ attr_accessor :location
263
+
264
+ class << self
265
+
266
+ include When
267
+ include Coordinates
268
+
269
+ # Temporal Objetct の生成
270
+ #
271
+ # @param [String] specification When.exe Standard Representation
272
+ # @param [Hash] options 下記の通り
273
+ # @option options [When::TM::ReferenceSystem] :frame 暦法の指定
274
+ # @option options [When::Parts::Timezone::Base, String] :clock 時法の指定
275
+ # @option options [String] :tz 時法の指定(時間帯を指定する場合 :clock の替わりに用いることができる)
276
+ # @option options [Array<Numeric>] :abbr ISO8601上位省略形式のためのデフォルト日付(省略時 指定なし)
277
+ # @option options [Integer] :extra_year_digits ISO8601拡大表記のための年の構成要素拡大桁数(省略時 1桁)
278
+ # @option options [Integer] :ordinal_date_digits ISO8601拡大表記の年内通日の桁数(省略時 3桁)
279
+ # @option options [String] :wkst ISO8601週日形式のための暦週開始曜日(省略時 'MO')
280
+ # @option options [Integer] :precision 生成するオブジェクトの分解能
281
+ # @option options [When::TimeStandard::TimeStandard] :time_standard 時刻系の指定(省略時 When::TimeStandard::UnversalTime)
282
+ # @option options [When::Ephemeris::Spatial] :location 観測地の指定(省略時 指定なし)
283
+ # @option options [String] :era_name 暦年代
284
+ # @option options [Hash] :trans 暦年代の上下限
285
+ # - :count => 条件に合致する暦年代のうち何番目を採用するか
286
+ # - :lower => 暦年代適用の下限
287
+ # true - epoch_of_use の始め(省略時)
288
+ # :reference_date - 参照事象の日付
289
+ # - :upper => 暦年代適用の上限
290
+ # true - epoch_of_use の終わり(省略時)
291
+ # :reference_date - 参照事象の日付
292
+ # @option options [Hash] :query 暦年代の絞込み条件
293
+ # - :area => area による暦年代絞込み
294
+ # - :period => period による暦年代絞込み
295
+ # - :name => name による暦年代絞込み(epoch の attribute使用可)
296
+ #
297
+ # @note options の中身は本メソッドによって更新されることがある。
298
+ #
299
+ # @note :tz は 'Asia/Tokyo'など時間帯を表す文字列をキーにして、登録済みのWhen::V::Timezone, When::Parts::Timezoneを検索して使用する。
300
+ # :clock はWhen::Parts::Timezone::Baseオブジェクトをそのまま使用するか '+09:00'などの文字列をWhen::TM::Clock化して使用する。
301
+ # :tz の方が :clock よりも優先される。
302
+ #
303
+ # @return [When::TM::TemporalPosition] ISO8601 time point
304
+ # @return [When::TM::Duration] ISO8601 duration
305
+ # @return [When::Parts::GeometricComplex] ISO8601 repeating interval
306
+ #
307
+ def _instance(specification, options={})
308
+
309
+ # Suffix - Frame specification
310
+ rfc5545form, frame, *rest = specification.split(/\^{1,2}/)
311
+ return rest.inject(_instance(rfc5545form + '^' + frame, options)) {|p,c| When.Resource(c, '_c:').jul_trans(p)} if rest.size > 0
312
+
313
+ options[:frame] = When.Resource(frame, '_c:') if (frame)
314
+
315
+ # Prefix - RFC 5545 Options
316
+ if (rfc5545form =~ /\A([^:]+[^-:\d]{2}):([^:].+)\z/)
317
+ rfc5545option, iso8601form = $~[1..2]
318
+ rfc5545option.split(/;/).each do |eq|
319
+ key, value = eq.split(/=/, 2)
320
+ case key
321
+ when 'VALUE' ; options[:precision] = value
322
+ when 'TZID' ; options[:clock] =
323
+ case When::V::Timezone[value]
324
+ when Array ; When::V::Timezone[value][-1]
325
+ when nil ; When::Parts::Timezone.new(value)
326
+ else ; When::V::Timezone[value]
327
+ end
328
+ else ; options[key] = value
329
+ end
330
+ end
331
+ else
332
+ iso8601form = rfc5545form
333
+ end
334
+ options = options.dup
335
+
336
+ # IndeterminateValue
337
+ if (iso8601form.sub!(/\/After$|^Before\/|^Now$|^Unknown$|^[-+]Infinity\z/i, ''))
338
+ options[:indeterminated_position] = When::TimeValue::S[$&.sub(/\//,'')]
339
+ case options[:indeterminated_position]
340
+ when When::TimeValue::Now,
341
+ When::TimeValue::Unknown,
342
+ When::TimeValue::Max,
343
+ When::TimeValue::Min
344
+ return self.new(self._options(options))
345
+ end
346
+ end
347
+
348
+ # each specification
349
+ splitted = iso8601form.split(/\//)
350
+ if (splitted[0] =~ /\AR(\d+)?\z/)
351
+ repeat = $1 ? $1.to_i : true
352
+ splitted.shift
353
+ end
354
+ case splitted.length
355
+ when 1
356
+ when 2
357
+ if (splitted[0] !~ /\A[-+]?P/ && splitted[1] =~ /\A\d/ && splitted[1].length < splitted[0].length)
358
+ splitted[1] = splitted[0][0..(splitted[0].length-splitted[1].length-1)] + splitted[1]
359
+ end
360
+ else
361
+ raise ArgumentError, "Irregal ISO8601 Format: #{iso8601form}"
362
+ end
363
+ options = self._options(options)
364
+ element = splitted.map { |v| _date_time_or_duration(v, options.dup) }
365
+
366
+ # total result
367
+ case repeat
368
+ when nil
369
+ case element[1]
370
+ when nil
371
+ return element[0]
372
+ when Duration
373
+ case element[0]
374
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
375
+ when self ; return When::Parts::GeometricComplex.new(*element)
376
+ else ; return When::Parts::GeometricComplex.new(element[0].first, element[1])
377
+ end
378
+ when self
379
+ case element[0]
380
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1]-element[0],false], [element[1],true ]])
381
+ when self ; return When::Parts::GeometricComplex.new(element[0]..element[1])
382
+ else ; return When::Parts::GeometricComplex.new(element[0].first..element[1])
383
+ end
384
+ else
385
+ case element[0]
386
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1].first-element[0],false],
387
+ [element[1].last-element[0]-1,true ]])
388
+ when self ; return When::Parts::GeometricComplex.new(element[0]...element[1].last)
389
+ else ; return When::Parts::GeometricComplex.new(element[0].first...element[1].last)
390
+ end
391
+ end
392
+ when 0 ; return []
393
+ when Integer ; return [element[0]] * repeat unless element[1]
394
+ end
395
+
396
+ case element[1]
397
+ when Duration
398
+ case element[0]
399
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
400
+ else ; duration = element[1]
401
+ end
402
+ when self
403
+ case element[0]
404
+ when Duration ; duration = -element[0]
405
+ when self ; duration = element[1] - element[0]
406
+ else ; duration = element[1] - element[0].first
407
+ end
408
+ else
409
+ case element[0]
410
+ when Duration ; duration = -element[0]
411
+ when self ; duration = element[1].first - element[0]
412
+ else ; duration = element[1].first - element[0].first
413
+ end
414
+ end
415
+ base = element[0].kind_of?(Duration) ? element[1] : element[0]
416
+
417
+ if repeat.kind_of?(Integer)
418
+ result = case base
419
+ when self ; (1..repeat-1).inject([base]) {|a,i| a << (a[-1] + duration) }
420
+ else ; (1..repeat-1).inject([base]) {|a,i| a << When::Parts::GeometricComplex.new(
421
+ a[-1].first+duration...a[-1].last+duration)}
422
+ end
423
+ result.reverse! if duration.sign < 0
424
+ return result
425
+
426
+ else
427
+ duration = -duration if duration.sign < 0
428
+ return case base
429
+ when self ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base})
430
+ else ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base.first,
431
+ 'dtend' =>base.last})
432
+ end
433
+ end
434
+ end
435
+
436
+ # option の正規化
437
+ # @private
438
+ def _options(options)
439
+ query = options.dup
440
+ main = {}
441
+ clock = Clock.get_clock_option(query)
442
+ main[:clock] = clock if clock
443
+ [:indeterminated_position, :frame, :events, :precision,
444
+ :era_name, :era, :abbr, :extra_year_digits, :ordinal_date_digits, :wkst, :time_standard, :location].each do |key|
445
+ main[key] = query.delete(key) if (query.key?(key))
446
+ end
447
+ long = query.delete(:long)
448
+ lat = query.delete(:lat)
449
+ alt = query.delete(:alt)
450
+ main[:location] ||= "_l:long=#{long||0}&lat=#{lat||0}&alt=#{alt||0}" if long && lat
451
+ trans = query.delete(:trans) || {}
452
+ [:lower, :upper, :count].each do |key|
453
+ trans[key] = query.delete(key) if (query.key?(key))
454
+ end
455
+ query = query.merge(query.delete(:query)) if (query.key?(:query))
456
+ main[:query] = query if (query.size > 0)
457
+ main[:trans] = trans if (trans.size > 0)
458
+ return main
459
+ end
460
+
461
+ # 比較
462
+ # @private
463
+ def _verify(source, target)
464
+ return source.universal_time <=> target.universal_time if source.time_standard.equal?(target.time_standard)
465
+ return source.dynamical_time <=> target.dynamical_time
466
+ end
467
+
468
+ private
469
+
470
+ # date_time_or_duration
471
+ def _date_time_or_duration(specification, options)
472
+ # IntervalLength
473
+ args = IntervalLength._to_array(specification)
474
+ return IntervalLength.new(*args) if args
475
+
476
+ # PeriodDuration
477
+ sign, *args = PeriodDuration._to_array(specification)
478
+ if (sign)
479
+ args << options
480
+ duration = PeriodDuration.new(*args)
481
+ return (sign >= 0) ? duration : -duration
482
+ end
483
+
484
+ # TemporalPosition
485
+ specification =~ /(.+?)(?:\[([-+]?\d+)\])?\z/
486
+ options[:sdn] = $2.to_i if $2
487
+ f, d, t, z, e = When::BasicTypes::DateTime._to_array($1, options)
488
+ raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:clock])
489
+ options.delete(:abbr)
490
+ z ||= options[:clock]
491
+ z = When.Clock(z) if (z =~ /\A[A-Z]+\z/)
492
+
493
+ unless (d)
494
+ # ClockTime
495
+ raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:frame])
496
+ options[:frame] ||= z
497
+ options.delete(:clock)
498
+ return ClockTime.new(t, options)
499
+ end
500
+
501
+ options[:era_name] = e if e
502
+ options[:_format ] = f if f
503
+ d, w = d[0..0], d[1..-1] if (f == :week || f == :day)
504
+ position = z ? DateAndTime.new(d, t||[0], options.update({:clock => z})) :
505
+ t ? DateAndTime.new(d, t, options) :
506
+ CalDate.new(d, options)
507
+ case f
508
+ when :day
509
+ position += PeriodDuration.new(w[0]-1, DAY)
510
+ when :week
511
+ position = ((position + PeriodDuration.new(4, DAY)) & (Residue.day_of_week(options[:wkst]) << 1)) +
512
+ PeriodDuration.new((w[0]-1)*7 + (w[1]||1)-1, DAY)
513
+ position = When::Parts::GeometricComplex.new(position...(position+P1W)) unless w[1]
514
+ end
515
+ return position
516
+ end
517
+ end
518
+
519
+ # 時刻系
520
+ #
521
+ # @return [When::TimeStandard]
522
+ #
523
+ def time_standard
524
+ return @time_standard if @time_standard.kind_of?(When::TimeStandard)
525
+ @time_standard ||= clock.time_standard if respond_to?(:clock) && clock
526
+ @time_standard ||= frame.time_standard if frame
527
+ @time_standard ||= 'UniversalTime'
528
+ @time_standard = When.Resource(@time_standard, '_t:')
529
+ end
530
+
531
+ # 時間の歩度
532
+ #
533
+ # @return [Numeric]
534
+ #
535
+ def rate_of_clock
536
+ time_standard.rate_of_clock
537
+ end
538
+
539
+ # 内部時間
540
+ #
541
+ # @return [Numeric]
542
+ #
543
+ # 1970-01-01T00:00:00Z からの Universal Time, Coordinated の経過時間 / 128秒
544
+ #
545
+ # 暦法によっては、異なる意味を持つことがある
546
+ #
547
+ def universal_time
548
+ case @indeterminated_position
549
+ when Now ; time_standard.from_time_object(Time.now)
550
+ when Max ; +Float::MAX/4
551
+ when Min ; -Float::MAX/4
552
+ else ; raise NameError, "Temporal Reference System is not defined"
553
+ end
554
+ end
555
+
556
+ # 外部時間
557
+ #
558
+ # @return [Numeric]
559
+ #
560
+ # 1970-01-01T00:00:00TT からの terrestrial time の経過時間 / 128秒
561
+ #
562
+ def dynamical_time
563
+ return @dynamical_time if @dynamical_time && @indeterminated_position != Now
564
+ @dynamical_time =
565
+ case @indeterminated_position
566
+ when Max ; +Float::MAX/4
567
+ when Min ; -Float::MAX/4
568
+ else ; time_standard.to_dynamical_time(universal_time)
569
+ end
570
+ end
571
+
572
+ # ユリウス日時(実数)
573
+ #
574
+ # @return [Float]
575
+ #
576
+ # universal time での経過日数を, ユリウス日と1970-01-01T00:00:00Zで時計あわせしたもの
577
+ #
578
+ def to_f
579
+ JulianDate._t_to_d(universal_time)
580
+ end
581
+
582
+ # ユリウス日(整数)
583
+ #
584
+ # @return [Integer]
585
+ #
586
+ # -4712-01-01T12:00:00Z からの経過日数に対応する通番(当該時間帯での午前0時に1進める)
587
+ #
588
+ def to_i
589
+ sd = universal_time
590
+ sd -= @frame.universal_time if @frame.kind_of?(Clock)
591
+ div, mod = sd.divmod(Duration::DAY)
592
+ div + JulianDate::JD19700101
593
+ end
594
+
595
+ # 剰余類化
596
+ #
597
+ # @param [Numeric] remainder 剰余
598
+ # @param [Integer] divisor 法(>0)
599
+ #
600
+ # @return [When::Coordinates::Residue]
601
+ #
602
+ # 当日を基準とする剰余類
603
+ #
604
+ def to_residue(remainder, divisor)
605
+ When::Coordinates::Residue.new(remainder, divisor, {'day'=>to_i})
606
+ end
607
+
608
+ # ユリウス日時(実数)
609
+ #
610
+ # @return [Float]
611
+ #
612
+ # dynamical time での経過日数を, ユリウス日と1970-01-01T00:00:00TTで時計あわせしたもの
613
+ #
614
+ def +@
615
+ JulianDate._t_to_d(dynamical_time)
616
+ end
617
+
618
+ # When::TM::ClockTime オブジェクトへの変換
619
+ #
620
+ # @return [When::TM::ClokTime]
621
+ #
622
+ def to_clock_time
623
+ raise TypeError, "Clock not assigned" unless clock
624
+ clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY)
625
+ clk_time.clk_time[0] += to_i
626
+ return clk_time
627
+ end
628
+
629
+ # 標準ライブラリの DateTime オブジェクトへの変換
630
+ #
631
+ # @param [Hash] option 時間の歩度が1.0でない場合のための option
632
+ # see {When::TM::TemporalPosition._instance}
633
+ #
634
+ # @param [Integer] start ::DateTime オブジェクトのグレゴリオ改暦日(ユリウス通日)
635
+ #
636
+ # @return [::DateTime]
637
+ #
638
+ def to_date_time(option={:frame=>When::UTC}, start=_default_start)
639
+ return JulianDate.dynamical_time(dynamical_time, option).to_date_time unless time_standard.rate_of_clock == 1.0
640
+ raise TypeError, "Clock not assigned" unless clock
641
+ Rational
642
+ offset = Rational(-(clock.universal_time/Duration::SECOND).to_i, (Duration::DAY/Duration::SECOND).to_i)
643
+ clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY).clk_time
644
+ ::DateTime.jd(to_i, clk_time[1], clk_time[2], clk_time[3].to_i, offset, start)
645
+ end
646
+
647
+ # 標準ライブラリの Date オブジェクトへの変換
648
+ #
649
+ # @param [Hash] option 時間の歩度が1.0でない場合のための option
650
+ # see {When::TM::TemporalPosition._instance}
651
+ #
652
+ # @param [Integer] start ::DateTime オブジェクトのグレゴリオ改暦日(ユリウス通日)
653
+ #
654
+ # @return [::Date]
655
+ #
656
+ def to_date(option={}, start=_default_start)
657
+ return JulianDate.dynamical_time(dynamical_time, option).to_date unless time_standard.rate_of_clock == 1.0
658
+ ::Date.jd(to_i, start)
659
+ end
660
+
661
+ # 組み込みライブラリの Time オブジェクトへの変換
662
+ #
663
+ # @return [::Time]
664
+ #
665
+ def to_time
666
+ time_standard.to_time_object(universal_time)
667
+ end
668
+
669
+ # 要素の参照
670
+ #
671
+ # @param [Integer, String] index 参照する要素の指定
672
+ #
673
+ # @return [Numeric]
674
+ #
675
+ def [](index)
676
+ return value(index) if index.kind_of?(String) || !index.respond_to?(:inject)
677
+ index.inject([]) {|list, i| list << value(i) }
678
+ end
679
+
680
+ # 加算
681
+ #
682
+ # @param [Numeric, When::TM::Duration] other
683
+ #
684
+ # @return [When::TM::TemporalPosition]
685
+ #
686
+ def +(other)
687
+ case other
688
+ when Integer ; self + PeriodDuration.new(other, When::DAY)
689
+ when Numeric ; self + IntervalLength.new(other, 'day')
690
+ when PeriodDuration ; _plus(other)
691
+ when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time + other.duration), self._attr) :
692
+ JulianDate.dynamical_time(dynamical_time + other.duration, self._attr)
693
+ else ; raise TypeError, "The right operand should be Numeric or Duration"
694
+ end
695
+ rescue RangeError
696
+ (@frame ^ self) + other
697
+ end
698
+
699
+ # 減算
700
+ #
701
+ # @param [Numeric, When::TM::Duration, When::TM::TemporalPosition] other
702
+ #
703
+ # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::Duration
704
+ # @return [When::TM::Duration] if other is a When::TM::TemporalPosition
705
+ #
706
+ def -(other)
707
+ case other
708
+ when TimeValue ; self.time_standard.rate_of_clock == other.time_standard.rate_of_clock && [@precision, other.precision].min <= When::DAY ?
709
+ PeriodDuration.new(self.to_i - other.to_i, When::DAY) :
710
+ IntervalLength.new((self.dynamical_time - other.dynamical_time) / Duration::SECOND, 'second')
711
+ when Integer ; self - PeriodDuration.new(other, When::DAY)
712
+ when Numeric ; self - IntervalLength.new(other, 'day')
713
+ when PeriodDuration ; _plus(-other)
714
+ when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time - other.duration), self._attr) :
715
+ JulianDate.dynamical_time(dynamical_time - other.duration, self._attr)
716
+ else ; raise TypeError, "The right operand should be Numeric, Duration or TemporalPosition"
717
+ end
718
+ rescue RangeError
719
+ (@frame ^ self) - other
720
+ end
721
+
722
+ # 分解能に対応する Duration
723
+ #
724
+ # @return [When::TM::PeriodDuration]
725
+ #
726
+ def period
727
+ return @period if @period
728
+ period_name = When::Coordinates::PERIOD_NAME[@precision]
729
+ raise ArgumentError, "Presicion not defined" unless period_name
730
+ @period = When.Duration(period_name)
731
+ end
732
+
733
+ # 前の日時
734
+ #
735
+ # @return [When::TM::TemporalPosition]
736
+ #
737
+ # 分解能に対応する Duration だけ,日時を戻す
738
+ #
739
+ def prev
740
+ @precision==When::DAY ? _force_euqal_day(-1) : self-period
741
+ rescue RangeError
742
+ (When::Gregorian ^ self) - period
743
+ end
744
+
745
+ # 次の日時
746
+ #
747
+ # @return [When::TM::TemporalPosition]
748
+ #
749
+ # 分解能に対応する Duration だけ,日時を進める
750
+ #
751
+ def succ
752
+ @precision==When::DAY ? _force_euqal_day(+1) : self+period
753
+ rescue RangeError
754
+ (When::Gregorian ^ self) + period
755
+ end
756
+ alias :next :succ
757
+
758
+ #
759
+ # 前後の日時を取得可能か?
760
+ #
761
+ # @return [Boolean]
762
+ # [ true - 可能 ]
763
+ # [ false - 不可 ]
764
+ #
765
+ def has_next?
766
+ When::Coordinates::PERIOD_NAME[@precision] != nil
767
+ end
768
+
769
+ # 下位桁の切り捨て
770
+ #
771
+ # @param [Integer] digit これより下の桁を切り捨てる(省略すると When::DAY)
772
+ #
773
+ # @param [Integer] precision 切り捨て結果の分解能
774
+ #
775
+ # @return [When::TM::TemporalPosition] (本 Class では、実際には切り捨てない)
776
+ #
777
+ def floor(digit=DAY, precision=digit)
778
+ self
779
+ end
780
+
781
+ # 分解能が時刻を持つか
782
+ #
783
+ # @return [Boolean]
784
+ #
785
+ def has_time?
786
+ (@precision > 0)
787
+ end
788
+
789
+ # 指定の日時を含むか?
790
+ #
791
+ # @param [When::TM::TemporalPosition] date チェックされる日時
792
+ #
793
+ # @return [Boolean]
794
+ # [ true - 含む ]
795
+ # [ false - 含まない ]
796
+ #
797
+ def include?(date)
798
+ return false if self.precision > date.precision
799
+ return self == date
800
+ end
801
+
802
+ # オブジェクトの同値
803
+ #
804
+ # @param [比較先] other
805
+ #
806
+ # @return [Boolean]
807
+ # [ true - 同値 ]
808
+ # [ false - 非同値 ]
809
+ #
810
+ def ==(other)
811
+ (self <=> other) == 0
812
+ rescue
813
+ false
814
+ end
815
+
816
+ # 大小比較
817
+ #
818
+ # @param [When::TM::TemporalPosition] other チェックされる日時
819
+ # @param [Numeric] other チェックされる日時の universal time(self と同じtime_standardとみなす)
820
+ #
821
+ # @return [Integer] (-1, 0, 1)
822
+ #
823
+ # 分解能の低い方にあわせて比較を行う
824
+ #
825
+ # Ex. when?('2011-03') <=> when?('2011-03-10') -> 0
826
+ #
827
+ def <=>(other)
828
+ other = other.first if other.kind_of?(Range)
829
+ return universal_time <=> other if other.kind_of?(Numeric)
830
+
831
+ [self.indeterminated_position, other.indeterminated_position].each do |position|
832
+ prec = SYSTEM if [TimeValue::Min, TimeValue::Max].include?(position)
833
+ end
834
+ prec = [self.precision, other.precision].min unless prec
835
+
836
+ case prec
837
+ when DAY ; return self.to_i <=> other.to_i
838
+ when SYSTEM ; return TemporalPosition._verify(self, other)
839
+ end
840
+
841
+ if prec < DAY && respond_to?(:most_significant_coordinate) &&
842
+ other.respond_to?(:most_significant_coordinate) && @frame.equal?(other.frame)
843
+ self_year = most_significant_coordinate
844
+ other_year = other.most_significant_coordinate
845
+ if @cal_date.length + prec == 1
846
+ self_year *= 1
847
+ other_year *= 1
848
+ end
849
+ result = self_year <=> other_year
850
+ return result unless result == 0 && @cal_date.length + prec > 1
851
+ (@cal_date.length + prec - 2).times do |i|
852
+ result = @cal_date[i+1] <=> other.cal_date[i+1]
853
+ return result unless result == 0
854
+ end
855
+ @cal_date[prec - 1] <=> other.cal_date[prec - 1]
856
+ else
857
+ source = (prec >= self.precision ) ? self : self.floor(prec)
858
+ target = (prec >= other.precision) ? other : other.floor(prec)
859
+ return source.to_i <=> target.to_i if prec <= DAY
860
+ TemporalPosition._verify(source, target)
861
+ end
862
+ end
863
+
864
+ # 条件を満たすオブジェクトの抽出
865
+ #
866
+ # @param [Module, Array<Moduel>, When::Coordinates::Residue, When::TM::Duration, When::TM::Calendar, When::TM::CalendarEra] other
867
+ # @param [Boolean] leaf extract only leaf Objects.
868
+ # @param [Block] block If block is given, the specified block is yield.
869
+ #
870
+ # @return [Array of (element^self) for all When::Parts::Resource registered elements] (If other is Module)
871
+ # @return [Array of (self^element) for elemnt of other which belong to the specified module or class] (If other is [Array])
872
+ # @return [Enumerator which generates temporal position sequence begins from self with the specified duration] (If other is [When::TM::Duration])
873
+ # @return [Array of temporal position using the specified calendar as a frame] (If other is [When::TM::Calendar])
874
+ # @return [Array of temporal position using the specified calendar era as a calendarEraName] (If other is [When::TM::CalendarEra])
875
+ #
876
+ def scan(other, leaf=false, &block)
877
+ list = []
878
+ case other
879
+ when Numeric, TemporalPosition, Position
880
+ raise TypeError, "Operand should not be Numeric or (Temporal)Position"
881
+
882
+ when Module
883
+ objects = []
884
+ ObjectSpace.each_object(other) do |object|
885
+ objects << object if object.registered?
886
+ end
887
+ objects.each do |object|
888
+ element = (object ^ self)
889
+ if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
890
+ list << element
891
+ yield(element) if block_given?
892
+ end
893
+ end
894
+ return list
895
+
896
+ when Array
897
+ return other.map {|v| scan(v, leaf, &block)}
898
+
899
+ else
900
+ if other.respond_to?(:_enumerator)
901
+ enumerator = other._enumerator(self)
902
+ return enumerator unless block_given?
903
+ return enumerator.each(&block)
904
+ end
905
+
906
+ element = (other ^ self)
907
+ if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
908
+ list << element
909
+ yield(element) if block_given?
910
+ end
911
+ if (other.respond_to?(:child) && other.child)
912
+ other.child.each do |object|
913
+ list += scan(object, leaf, &block)
914
+ end
915
+ end
916
+ return list
917
+ end
918
+ end
919
+
920
+ # 条件を満たすオブジェクトの抽出
921
+ #
922
+ # @param (see #scan)
923
+ # @return (see #scan)
924
+ #
925
+ def ^(other, leaf=true, &block)
926
+ scan(other, leaf, &block)
927
+ end
928
+
929
+ # 属性を変更したコピーを作る
930
+ #
931
+ # @param [Hash] options { 属性=>属性値 }
932
+ #
933
+ # @return [When::TM::TemporalPosition]
934
+ #
935
+ def copy(options={})
936
+ position = self.dup
937
+ position._copy(options)
938
+ position
939
+ end
940
+
941
+ # 属性の Hash
942
+ # @private
943
+ def _attr
944
+ attributes = {}
945
+ [:frame, :events, :precision, :options, :trans, :query].each do |key|
946
+ attributes[key] = instance_variable_get("@#{key}")
947
+ end
948
+ attributes[:location] = location
949
+ attributes[:time_standard] = time_standard
950
+ return attributes.delete_if {|k,v| !v}
951
+ end
952
+
953
+ protected
954
+
955
+ # 属性のコピー
956
+ # @private
957
+ def _copy(options={})
958
+ @frame = options[:frame] if (options.key?(:frame))
959
+ @events = options[:events] if (options.key?(:events))
960
+ @precision = options[:precision] if (options.key?(:precision))
961
+ @query = options[:query] if (options.key?(:query))
962
+ @location = options[:location] if (options.key?(:location))
963
+ @time_standard = options[:time_standard] if (options.key?(:time_standard))
964
+ @sdn = @universal_time = @dynamical_time = @period = nil
965
+ _normalize(options)
966
+ return self
967
+ end
968
+
969
+ private
970
+
971
+ # オブジェクトの生成
972
+ #
973
+ # @param [Hash] options see {When::TM::TemporalPosition._instance}
974
+ #
975
+ def initialize(options={})
976
+ options.reject! {|key,value| value == nil}
977
+ options.each_pair do |key,value|
978
+ self.instance_variable_set("@#{key}", value)
979
+ end
980
+ @keys = []
981
+ @location = When.Resource(@location, '_l:') if @location.kind_of?(String)
982
+ # @sdn = @universal_time = @dynamical_time = nil
983
+ _normalize(options)
984
+ end
985
+
986
+ # オブジェクトの正規化
987
+ def _normalize(options={})
988
+ @precision ||= SYSTEM
989
+ @period = nil
990
+ @keys |= @frame.keys if @frame
991
+ end
992
+
993
+ # 指定桁のチェック
994
+ def _digit(index)
995
+ digit = index.kind_of?(String) ? PRECISION[index.upcase] : index
996
+ raise RangeError, " wrong digit: #{index}" unless digit.kind_of?(Integer) && (!block_given? || yield(digit))
997
+ digit
998
+ end
999
+
1000
+ # 指定の日を探す
1001
+ def _force_euqal_day(diff)
1002
+ jdn = self.to_i + diff
1003
+ date = self
1004
+ done = {}
1005
+ loop do
1006
+ case
1007
+ when date.to_i == jdn
1008
+ return date
1009
+ when date.to_i > jdn
1010
+ next_date = date - When::P1D
1011
+ date = (date.to_i == next_date.to_i) ? date - When::P2D : next_date
1012
+ when date.to_i < jdn
1013
+ next_date = date + When::P1D
1014
+ date = (date.to_i == next_date.to_i) ? date + When::P2D : next_date
1015
+ end
1016
+ raise RangeError, "can't find target date: #{self} -> #{jdn}" if done.key?(date.to_i)
1017
+ done[date.to_i] = true
1018
+ end
1019
+ end
1020
+
1021
+ #
1022
+ # 対応する ::Date の start 属性
1023
+ def _default_start
1024
+ frame ? frame._default_start : ::Date::GREGORIAN
1025
+ end
1026
+
1027
+ alias :_method_missing :method_missing
1028
+
1029
+ # その他のメソッド
1030
+ #
1031
+ # @note
1032
+ # When::TM::TemporalPosition で定義されていないメソッドは
1033
+ # 処理を @frame (type: When::TM::Calendar or When::TM::Clock) に委譲する
1034
+ #
1035
+ def method_missing(name, *args, &block)
1036
+
1037
+ return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name)
1038
+ self.class.module_eval %Q{
1039
+ def #{name}(*args, &block)
1040
+ @frame.send("#{name}", self, *args, &block)
1041
+ end
1042
+ } unless When::Parts::MethodCash.escape(name)
1043
+ @frame.send(name, self, *args, &block)
1044
+ end
1045
+ end
1046
+
1047
+ #
1048
+ # 時間座標 - 単一の時間間隔によって定義する連続な間隔尺度を基礎とする
1049
+ #
1050
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeCoordinateType gml schema}
1051
+ #
1052
+ class Coordinate < TemporalPosition
1053
+
1054
+ class << self
1055
+ # 内部時間によるオブジェクトの生成
1056
+ alias :universal_time :new
1057
+
1058
+ # 外部時間によるオブジェクトの生成
1059
+ #
1060
+ # @param [Numeric] dynamical_time 外部時間による時間座標値
1061
+ #
1062
+ # @param [Hash] options 下記の通り
1063
+ # @option options [When::TimeStandard] :time_standard
1064
+ #
1065
+ # @return [When::TM::Coordinate]
1066
+ #
1067
+ def dynamical_time(dynamical_time, options={})
1068
+ universal_time(When.Resource(options[:time_standard] || 'UniversalTime', '_t:').from_dynamical_time(dynamical_time), options)
1069
+ end
1070
+
1071
+ # 他種の時間位置によるオブジェクトの生成
1072
+ #
1073
+ # @param [Numeric, When::TM::ClockTime, ::Time, ::Date, ::DateTime] time 他種の時間位置によるオブジェクト
1074
+ #
1075
+ # @param [Hash] options 下記の通り
1076
+ # @option options [When::TM::Clock] :clock
1077
+ # @option options [When::Parts::Timezone] :tz
1078
+ #
1079
+ # @return [When::TM::Coordinate]
1080
+ #
1081
+ def new(time, options={})
1082
+ options = options.dup
1083
+ options[:frame] = Clock.get_clock_option(options)
1084
+ case time
1085
+ when Numeric
1086
+ options[:frame] ||= When::UTC unless time.kind_of?(Integer)
1087
+ universal_time = (2*time - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1088
+ when ClockTime
1089
+ options[:frame] ||= time.clock
1090
+ universal_time = time.clk_time[0] + time.universal_time
1091
+ when ::Time
1092
+ options[:frame] ||= When.Clock(time.gmtoff)
1093
+ universal_time = options[:frame].time_standard.from_time_object(time)
1094
+ when TimeValue
1095
+ options[:frame] ||= time.clock
1096
+ universal_time = time.universal_time
1097
+ else
1098
+ if ::Object.const_defined?(:Date) && time.respond_to?(:ajd)
1099
+ case time
1100
+ when ::DateTime
1101
+ options[:frame] ||= When.Clock((time.offset * 86400).to_i)
1102
+ universal_time = (2*time.ajd - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1103
+ when ::Date
1104
+ universal_time = JulianDate._d_to_t(time.jd)
1105
+ if options[:frame] && options[:frame].rate_of_clock != 1.0
1106
+ universal_time = options[:frame].time_standard.from_dynamical_time(
1107
+ When::TimeStandard.to_dynamical_time(universal_time))
1108
+ end
1109
+ end
1110
+ end
1111
+ end
1112
+ raise TypeError, "Can't create #{self} from #{time.class}" unless universal_time
1113
+ universal_time(universal_time, options)
1114
+ end
1115
+ end
1116
+
1117
+ # この時間位置と関連付けられた時間参照系 (relation - Reference)
1118
+ #
1119
+ # The time reference system associated with the temporal position being described
1120
+ #
1121
+ # @return [When::TM::ReferenceSystem]
1122
+ #
1123
+ alias :clock :frame
1124
+
1125
+ # 内部時間による時間座標値
1126
+ #
1127
+ # @return [Numeric]
1128
+ #
1129
+ attr_accessor :universal_time
1130
+ alias :coordinateValue :universal_time
1131
+ protected :universal_time=
1132
+
1133
+ # CoordinateSystem による時間座標値
1134
+ #
1135
+ # @return [Numeric]
1136
+ #
1137
+ def coordinateValue
1138
+ (universal_time - frame.origin.universal_time) / frame.interval.to_f
1139
+ end
1140
+
1141
+ # 加算
1142
+ #
1143
+ # @param [Numeric, When::TM::IntervalLength] other
1144
+ #
1145
+ # @return [When::TM::TemporalPosition]
1146
+ #
1147
+ def +(other)
1148
+ other = other.to_interval_length if other.kind_of?(PeriodDuration)
1149
+ super(other)
1150
+ end
1151
+
1152
+ # 減算
1153
+ #
1154
+ # @param [When::TM::TemporalPosition, Numeric, When::TM::IntervalLength] other
1155
+ #
1156
+ # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::IntervalLength
1157
+ # @return [When::TM::IntervalLength] if other is a When::TM::TemporalPosition
1158
+ #
1159
+ def -(other)
1160
+ other = other.to_interval_length if other.kind_of?(PeriodDuration)
1161
+ super(other)
1162
+ end
1163
+
1164
+ # オブジェクトの生成
1165
+ #
1166
+ # @param [Numeric] universal_time 内部時間による時間座標値
1167
+ #
1168
+ # @param [Hash] options 下記の通り
1169
+ # @option options [When::TM::CoordinateSystem] :frame
1170
+ #
1171
+ def initialize(universal_time, options={})
1172
+ super(options)
1173
+ @universal_time = universal_time
1174
+ end
1175
+ end
1176
+
1177
+ #
1178
+ # ユリウス日
1179
+ #
1180
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#JulianDateType gml schema}
1181
+ #
1182
+ class JulianDate < Coordinate
1183
+
1184
+ # Julian Day Number
1185
+ # 19700101T120000Z
1186
+ JD19700101 = 2440588
1187
+
1188
+ class << self
1189
+
1190
+ JD19700100_5 = JD19700101 - 0.5
1191
+
1192
+ #
1193
+ # 日時の内部表現をユリウス日に変換
1194
+ #
1195
+ # @param [Numeric] t
1196
+ #
1197
+ # @return [Numeric]
1198
+ #
1199
+ def _t_to_d(t)
1200
+ t / Duration::DAY + JD19700100_5
1201
+ end
1202
+
1203
+ #
1204
+ # ユリウス日をに日時の内部表現変換
1205
+ #
1206
+ # @param [Numeric] d
1207
+ #
1208
+ # @return [Numeric]
1209
+ #
1210
+ def _d_to_t(d)
1211
+ (d - JD19700100_5) * Duration::DAY
1212
+ end
1213
+
1214
+ # Generation of Temporal Objetct
1215
+ #
1216
+ # @param [String] specification ユリウス通日を表す文字列
1217
+ # @param [Hash] options 暦法や時法などの指定 (see {When::TM::TemporalPosition._instance})
1218
+ #
1219
+ # @return [When::TM::TemporalPosition, When::TM::Duration, When::Parts::GeometricComplex or Array<them>]
1220
+ #
1221
+ def parse(specification, options={})
1222
+ jdn, *calendars = specification.split(/\^{1,2}/)
1223
+ jdn = jdn.sub!(/[.@]/, '.') ? jdn.to_f : jdn.to_i
1224
+ frame = calendars.shift || options[:frame]
1225
+ return self.new(jdn, options) unless frame
1226
+ calendars.unshift(frame).inject(jdn) {|date, calendar| When.Calendar(calendar).jul_trans(date, options)}
1227
+ end
1228
+ end
1229
+
1230
+ # 加算
1231
+ #
1232
+ # @param [Numeric, When::TM::IntervalLength] other
1233
+ #
1234
+ # @return [When::TM::TemporalPosition]
1235
+ #
1236
+ def +(other)
1237
+ new_date = super
1238
+ new_date.frame = new_date.frame._daylight(new_date.universal_time) if new_date.frame && new_date.frame._need_validate
1239
+ return new_date
1240
+ end
1241
+
1242
+ # ユリウス日が指定の剰余となる日
1243
+ #
1244
+ # @param [When::Coordinates::Residue] other
1245
+ #
1246
+ # @return [When::TM::TemporalPosition]
1247
+ #
1248
+ def &(other)
1249
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1250
+ raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1251
+ jdn = to_i
1252
+ new_date = self.dup
1253
+ new_date.universal_time += ((other & jdn) - jdn) * Duration::DAY
1254
+ return new_date
1255
+ end
1256
+
1257
+ # ユリウス日の剰余
1258
+ #
1259
+ # @param [When::Coordinates::Residue] other
1260
+ #
1261
+ # @return [Numeric]
1262
+ #
1263
+ def %(other)
1264
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1265
+ raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1266
+ other % to_i
1267
+ end
1268
+
1269
+ private
1270
+
1271
+ # オブジェクトの生成
1272
+ #
1273
+ # @param [Numeric] universal_time 内部時間による時間座標値
1274
+ #
1275
+ # @param [Hash] options 以下の通り
1276
+ # @option options [When::TM::Clock] :frame
1277
+ # @option options [Integer] :precision
1278
+ #
1279
+ def initialize(universal_time, options={})
1280
+ @frame = options.delete(:frame)
1281
+ @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1282
+ @frame = @frame._daylight(universal_time) if @frame && @frame._need_validate
1283
+ precision = options.delete(:precision)
1284
+ precision ||= DAY unless @frame.kind_of?(Clock)
1285
+ @precision = Index.precision(precision)
1286
+ super
1287
+ end
1288
+ end
1289
+
1290
+ #
1291
+ # 順序時間参照系で参照する位置
1292
+ #
1293
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeOrdinalPositionType gml schema}
1294
+ #
1295
+ class OrdinalPosition < TemporalPosition
1296
+
1297
+ # この順序位置と関連付けられた順序年代 (relation - Reference)
1298
+ #
1299
+ # The ordinal era associated with the ordinal position being described
1300
+ #
1301
+ # @return [When::TM::OrdinalEra]
1302
+ #
1303
+ attr_reader :ordinal_position
1304
+ alias :ordinalPosition :ordinal_position
1305
+
1306
+ # オブジェクトの生成
1307
+ #
1308
+ # @param [When::TM::OrdinalEra] ordinal_position この順序位置と関連付けられた順序年代
1309
+ # @param [Hash] options see {When::TM::TemporalPosition._instance}
1310
+ #
1311
+ def initialize(ordinal_position, options={})
1312
+ super(options)
1313
+ @ordinal_position = ordinal_position
1314
+ end
1315
+ end
1316
+
1317
+ #
1318
+ # 時刻
1319
+ #
1320
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#ClockTimeType gml schema}
1321
+ #
1322
+ class ClockTime < TemporalPosition
1323
+
1324
+ # 時刻要素
1325
+ #
1326
+ # @return [Array<Numeric>]
1327
+ #
1328
+ # @note ISO19108 では sequence<Integer> だが、閏時・閏秒などが表現可能なよう Numeric としている。
1329
+ #
1330
+ attr_reader :clk_time
1331
+ alias :clkTime :clk_time
1332
+
1333
+ # この時間位置と関連付けられた時間参照系 (relation - Reference)
1334
+ #
1335
+ # The time reference system associated with the temporal position being described
1336
+ #
1337
+ # @return [When::TM::ReferenceSystem]
1338
+ #
1339
+ alias :clock :frame
1340
+
1341
+ # 内部時間
1342
+ #
1343
+ # @return [Numeric]
1344
+ #
1345
+ # T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1346
+ #
1347
+ # 時法によっては、異なる意味を持つことがある
1348
+ #
1349
+ def universal_time
1350
+ raise NameError, "Temporal Reference System is not defined" unless @frame
1351
+ @universal_time ||= @frame.to_universal_time(@clk_time)
1352
+ end
1353
+
1354
+ # 繰り上がり
1355
+ #
1356
+ # @return [Numeric]
1357
+ #
1358
+ # 日付の境界が午前0時でない場合、clk_time の最上位桁に 0 以外が入ることがある
1359
+ #
1360
+ def carry
1361
+ return @clk_time[0]
1362
+ end
1363
+
1364
+ # 要素の参照
1365
+ #
1366
+ # @param [Integer, String] index 参照する要素の指定
1367
+ #
1368
+ # @return [Numeric]
1369
+ #
1370
+ def value(index)
1371
+ @clk_time[_digit(index) {|digit| digit >= DAY}]
1372
+ end
1373
+
1374
+ #protected
1375
+ # 属性のコピー
1376
+ # @private
1377
+ def _copy(options={})
1378
+ @clk_time = options[:time] if (options.key?(:time))
1379
+ @frame = options[:clock] if (options.key?(:clock))
1380
+ if (options.key?(:tz_prop))
1381
+ @frame = @frame.dup
1382
+ @frame.tz_prop = options[:tz_prop]
1383
+ end
1384
+ return super
1385
+ end
1386
+
1387
+ # オブジェクトの生成
1388
+ #
1389
+ # @param [String] time ISO8601形式の時刻表現
1390
+ # @param [Array<Numeric>] time (日, 時, 分, 秒)
1391
+ #
1392
+ #
1393
+ # @param [Hash] options 以下の通り
1394
+ # @option options [When::TM::Clock] :frame
1395
+ # @option options [Integer] :precision
1396
+ #
1397
+ def initialize(time, options={})
1398
+ # 参照系の取得
1399
+ @frame = options[:frame] || Clock.local_time
1400
+ @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1401
+ options.delete(:frame)
1402
+
1403
+ # 時刻表現の解読 ( Time Zone の解釈 )
1404
+ if (time.kind_of?(String))
1405
+ case time
1406
+ when /\A([-+])?(\d{2,}?):?(\d{2})?:?(\d{2}(\.\d+)?)?\z/
1407
+ sign, hh, mm, ss = $~[1..4]
1408
+ time = @frame._validate([0,0,0,0],
1409
+ [0,
1410
+ -(sign.to_s + "0" + hh.to_s).to_i,
1411
+ -(sign.to_s + "0" + mm.to_s).to_i,
1412
+ Pair._en_number(-(sign.to_s + "0" + ss.to_s).to_f)])
1413
+ time[0] = Pair.new(0, time[0].to_i) if (time[0] != 0)
1414
+ when /\AZ\z/
1415
+ time = [0,0,0,0]
1416
+ else
1417
+ raise ArgumentError, "Invalid Time Format"
1418
+ end
1419
+ end
1420
+ @clk_time = time
1421
+
1422
+ # 分解能
1423
+ @precision = @frame._precision(time, options.delete(:precision))
1424
+
1425
+ super(options)
1426
+ end
1427
+
1428
+ private
1429
+
1430
+ # オブジェクトの正規化
1431
+ def _normalize(options={})
1432
+ # strftime で使用する locale
1433
+ @keys |= @frame.keys
1434
+
1435
+ # 時刻の正規化
1436
+ @clk_time = @frame._validate(@clk_time) unless options[:validate]
1437
+ end
1438
+
1439
+ # 加減算共通処理
1440
+ def _plus(period)
1441
+ self.dup._copy({:time=>@frame._validate(@clk_time, @frame._arrange_length(period.time)),
1442
+ :events=>nil, :query=>nil, :validate=>:done})
1443
+ end
1444
+ end
1445
+
1446
+ #
1447
+ # 暦日
1448
+ #
1449
+ # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#CalDateType]
1450
+ #
1451
+ class CalDate < TemporalPosition
1452
+
1453
+ # 検索オプション
1454
+ # @private
1455
+ SearchOption = {After=>[0, -2, Before], Before=>[-2, 0, After]}
1456
+
1457
+ # 日付要素
1458
+ #
1459
+ # @return [Array<Numeric>]
1460
+ #
1461
+ # @note ISO19108 では sequence<Integer> だが、閏月などが表現可能なよう Numeric としている。
1462
+ #
1463
+ attr_reader :cal_date
1464
+ alias :calDate :cal_date
1465
+
1466
+ # この時間位置と関連付けられた時間参照系 (relation - Reference)
1467
+ #
1468
+ # The time reference system associated with the temporal position being described
1469
+ #
1470
+ # @return [When::TM::ReferenceSystem]
1471
+ #
1472
+ alias :calendar :frame
1473
+
1474
+ # 暦年代名
1475
+ #
1476
+ # @return [Array] ( name, epoch, reverse, go back )
1477
+ # - name [String] 暦年代名
1478
+ # - epoch [Integer] 使用する When::TM::Calendar で暦元に対応する年
1479
+ # - reverse [Boolean] 年数が昇順(false,nil)か降順(true)
1480
+ # - go back [Boolean] 参照イベントより前の暦日か(true)、否か(false,nil)
1481
+ #
1482
+ attr_accessor :calendar_era_name
1483
+ private :calendar_era_name=
1484
+ alias :calendarEraName :calendar_era_name
1485
+
1486
+ # 暦年代
1487
+ #
1488
+ # @return [When::TM::CalendarEra]
1489
+ #
1490
+ attr_accessor :calendar_era
1491
+ private :calendar_era=
1492
+ alias :calendarEra :calendar_era
1493
+
1494
+ # 時法の取得 - ダミー
1495
+ def clock
1496
+ nil
1497
+ end
1498
+
1499
+ # 内部時間
1500
+ #
1501
+ # @return [Numeric]
1502
+ #
1503
+ # 当日正午の 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1504
+ #
1505
+ def universal_time
1506
+ return super if [Now, Max, Min].include?(@indeterminated_position)
1507
+ @universal_time ||= JulianDate._d_to_t(to_i)
1508
+ end
1509
+
1510
+ # ユリウス日
1511
+ #
1512
+ # @return [Integer]
1513
+ #
1514
+ # -4712-01-01からの経過日数に対応する通番
1515
+ #
1516
+ def to_i
1517
+ @sdn ||= _to_i
1518
+ end
1519
+
1520
+ #
1521
+ # 暦法上の通日
1522
+ #
1523
+ def _to_i
1524
+ name, base = @calendar_era_name
1525
+ if base
1526
+ date = @cal_date.dup
1527
+ date[0] += base
1528
+ else
1529
+ date = @cal_date
1530
+ end
1531
+ @frame.to_julian_date(date)
1532
+ end
1533
+ private :_to_i
1534
+
1535
+ # 剰余類化
1536
+ #
1537
+ # @param [Numeric] remainder 剰余
1538
+ # @param [Integer] divisor 法(>0)
1539
+ #
1540
+ # @return [When::Coordinates::Residue] 当日、当年を基準とする剰余類
1541
+ #
1542
+ def to_residue(remainder, divisor)
1543
+ When::Coordinates::Residue.new(remainder, divisor, {'day' => least_significant_coordinate,
1544
+ 'year' => most_significant_coordinate})
1545
+ end
1546
+
1547
+ # 要素の参照
1548
+ #
1549
+ # @param [Integer, String] index 参照する要素の指定
1550
+ #
1551
+ # @return [Numeric]
1552
+ #
1553
+ def value(index)
1554
+ @cal_date[(_digit(index) {|digit| digit <= DAY})-1]
1555
+ end
1556
+
1557
+ #
1558
+ # 最上位の要素
1559
+ #
1560
+ # @return [Numeric] 暦年代の epoch に関わらず暦法に従った年の通し番号を返す
1561
+ #
1562
+ def most_significant_coordinate
1563
+ coordinate = @cal_date[0]
1564
+ coordinate += @calendar_era_name[1] if @calendar_era_name
1565
+ @frame.index_of_MSC.times do |i|
1566
+ coordinate = +coordinate * @frame.indices[i].unit + @cal_date[i+1] - @frame.indices[i].base
1567
+ end
1568
+ coordinate
1569
+ end
1570
+
1571
+ #
1572
+ # 最下位の要素
1573
+ #
1574
+ # @return [Numeric] 剰余類の演算に用いる日の通し番号を返す
1575
+ #
1576
+ def least_significant_coordinate
1577
+ return to_i + @frame.indices[-1].shift
1578
+ end
1579
+
1580
+ # ユリウス日または通年が指定の剰余となる日
1581
+ #
1582
+ # @param [When::Coordinates::Residue] other
1583
+ #
1584
+ # @return [When::TM::CalDate]
1585
+ #
1586
+ def &(other)
1587
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1588
+ case other.event
1589
+ when 'day'
1590
+ # 指定の剰余となる日
1591
+ sdn = other & to_i
1592
+ date = @frame.to_cal_date(sdn)
1593
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1594
+ result = self.dup._copy({:events=>nil, :query=>@query, :validate=>:done, :date=>date})
1595
+ result.send(:_force_euqal_day, sdn-result.to_i)
1596
+
1597
+ when 'year'
1598
+ # 指定の剰余となる年
1599
+ date = @cal_date.dup
1600
+ date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1601
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1602
+ return self.dup._copy({:date=>date, :events=>nil, :query=>@query})
1603
+
1604
+ else
1605
+ raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1606
+ end
1607
+ end
1608
+
1609
+ # ユリウス日または通年の剰余
1610
+ #
1611
+ # @param [When::Coordinates::Residue] other
1612
+ #
1613
+ # @return [Numeric]
1614
+ #
1615
+ def %(other)
1616
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1617
+ if precision <= When::YEAR && other.units['year'] && other.event != 'year'
1618
+ other.to('year') % (most_significant_coordinate + @frame._diff_to_CE)
1619
+ else
1620
+ case other.event
1621
+ when 'day' ; other % least_significant_coordinate
1622
+ when 'year' ; other % (most_significant_coordinate + @frame._diff_to_CE)
1623
+ else ; raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1624
+ end
1625
+ end
1626
+ end
1627
+
1628
+ # 下位桁の切り捨て
1629
+ #
1630
+ # @param [Integer] digit 切り捨てずに残す、最下位の桁
1631
+ #
1632
+ # @param [Integer] precision 切り捨て結果の分解能
1633
+ #
1634
+ # @return [When::TM::CalDate]
1635
+ #
1636
+ def floor(digit=DAY, precision=digit)
1637
+ options = {:date=>@cal_date[0..(digit-1)], :events=>nil, :query=>nil}
1638
+ options[:precision] = precision if precision
1639
+ self.dup._copy(options)
1640
+ end
1641
+
1642
+ # 下位桁の切り上げ
1643
+ #
1644
+ # @param [Integer] digit 切り上げずに残す、最下位の桁
1645
+ #
1646
+ # @param [Integer] precision 切り上げ結果の分解能
1647
+ #
1648
+ # @return [When::TM::CalDate]
1649
+ #
1650
+ def ceil(digit=DAY, precision=digit)
1651
+ (self + PeriodDuration.new(1, digit, (-@frame.indices.length)..0)).floor(digit, precision)
1652
+ end
1653
+
1654
+ # 要素数 ― 上位要素に含まれる下位要素の数
1655
+ #
1656
+ # @param [Integer] upper 上位要素のインデックス
1657
+ # @param [Integer] lower 下位要素のインデックス(DAY または MONTH)
1658
+ #
1659
+ # @return [Integer]
1660
+ #
1661
+ def length(upper, lower=DAY)
1662
+ range = [floor(upper).to_i, ceil(upper).to_i]
1663
+ range = range.map {|d| (Residue.mod(d) {|m| frame._new_month(m)})[0]} if lower == MONTH
1664
+ range[1] - range[0]
1665
+ end
1666
+
1667
+ # 時刻情報のない When::TM::CalDate を返す
1668
+ #
1669
+ # @return [When::TM::CalDate]
1670
+ #
1671
+ alias :to_cal_date :dup
1672
+ alias :to_CalDate :to_cal_date
1673
+
1674
+ # 暦年代が末端の参照であるか?
1675
+ #
1676
+ # @return [Boolean]
1677
+ #
1678
+ def leaf?
1679
+ name, = @calendar_era_name
1680
+ return true unless name.respond_to?(:_pool)
1681
+ era = name._pool['..']
1682
+ return true unless era.respond_to?(:leaf?)
1683
+ return era.leaf?
1684
+ end
1685
+
1686
+ # 属性の Hash
1687
+ # @private
1688
+ def _attr
1689
+ super.merge({:era_name=>@calendar_era_name, :era=>@calendar_era})
1690
+ end
1691
+ protected
1692
+
1693
+ # 属性のコピー
1694
+ # @private
1695
+ def _copy(options={})
1696
+ @cal_date = options[:date] if (options.key?(:date))
1697
+ return super
1698
+ end
1699
+
1700
+ # オブジェクトの生成
1701
+ #
1702
+ # @param [Array<Numeric>] date 日付表現
1703
+ #
1704
+ # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1705
+ # @option options [When::TM::Calendar] :frame
1706
+ # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1707
+ # (Integer 当該年号の 0 年に相当する通年)
1708
+ # @option options [Integer] :precision
1709
+ #
1710
+ def initialize(date, options={})
1711
+ # 年号 & 日付
1712
+ @calendar_era_name = options[:era_name]
1713
+ @calendar_era = options[:era]
1714
+ @cal_date = date
1715
+
1716
+ super(options)
1717
+ end
1718
+
1719
+ private
1720
+
1721
+ # オブジェクトの正規化
1722
+ def _normalize(options={})
1723
+
1724
+ # 日付配列の長さ
1725
+ cal_date_index = @cal_date.index(nil) || @cal_date.length
1726
+
1727
+ # 日付の正規化
1728
+ if @calendar_era_name
1729
+ # Calendar Era がある場合
1730
+ trans_options = @trans || {} # TODO? 消す
1731
+ count = trans_options[:count] || 1
1732
+ query_options = (@query || {}).dup
1733
+ query_options[:label] = @calendar_era_name
1734
+ query_options[:count] = count
1735
+ era = date = nil
1736
+ if @calendar_era
1737
+ era, date = _search_era(@calendar_era, trans_options)
1738
+ else
1739
+ eras = CalendarEra._instance(query_options)
1740
+ raise ArgumentError, "CalendarEraName doesn't exist: #{query_options[:label]}" if (eras.size==0)
1741
+ eras.each do |e|
1742
+ era, date = _search_era(e, trans_options)
1743
+ next unless era
1744
+ count -= 1
1745
+ break unless count > 0
1746
+ end
1747
+ end
1748
+ raise RangeError, "Out of CalendarEra Range" unless era
1749
+ @calendar_era_name = date.calendar_era_name
1750
+ @calendar_era = era
1751
+ @cal_date = date.cal_date
1752
+ @frame = date.frame
1753
+ @query = (@query||{}).merge(date.query)
1754
+ @trans = (@trans||{}).merge(date.trans)
1755
+ @keys |= @calendar_era_name[0].keys | @frame.keys
1756
+ else
1757
+ # Calendar Era がない場合
1758
+ @frame = When.Resource(options[:frame] || @frame || 'Gregorian', '_c:')
1759
+ @cal_date = @frame._validate(@cal_date) unless options[:validate] == :done
1760
+ @keys |= @frame.keys
1761
+ end
1762
+
1763
+ # 分解能
1764
+ precision = options.delete(:precision) || @precision
1765
+ cal_date_index =
1766
+ case options.delete(:_format)
1767
+ when :century ; CENTURY
1768
+ when :week, :day ; DAY
1769
+ else ; cal_date_index - (@frame.indices.length + 1)
1770
+ end
1771
+ precision ||= cal_date_index if cal_date_index < DAY
1772
+ precision ||= @clk_time.precision if @clk_time
1773
+ @precision = Index.precision(precision || DAY)
1774
+ end
1775
+
1776
+ # 暦年代を探す
1777
+ def _search_era(era, trans_options)
1778
+ cal_date = @cal_date.dup
1779
+ cal_date[0] = -cal_date[0] if era.reverse?
1780
+ cal_date[0] += era.epoch_year
1781
+ date = era.trans(cal_date, trans_options)
1782
+ loop do
1783
+ case date
1784
+ when Before, After
1785
+ i0, i1, reverse = SearchOption[date]
1786
+ new_era = (date == Before) ? era.prev : era.succ
1787
+ break unless new_era
1788
+ date = new_era.trans(cal_date, trans_options)
1789
+ if date == reverse
1790
+ cal_date = new_era.epoch[i0].frame.to_cal_date(era.epoch[i1].frame.to_julian_date(cal_date))
1791
+ date = new_era.trans(cal_date, trans_options)
1792
+ break if date == reverse
1793
+ end
1794
+ era = new_era
1795
+ when TimeValue
1796
+ return era, date
1797
+ else
1798
+ break
1799
+ end
1800
+ end
1801
+ return nil
1802
+ end
1803
+
1804
+ # 加減算共通処理
1805
+ def _plus(period)
1806
+ _frame_adjust(period, self.dup._copy({:date=>_date_with_era(@frame._validate(_date_without_era,
1807
+ @frame._arrange_length(period.date))),
1808
+ :events=>nil, :query=>nil, :validate=>:done}))
1809
+ end
1810
+
1811
+ # 年号を除外した @frame の暦法に対応する日付
1812
+ def _date_without_era
1813
+ date = @cal_date.dup
1814
+ date[0] += @calendar_era_name[1] if @calendar_era_name
1815
+ date
1816
+ end
1817
+
1818
+ # 年号に続く日付
1819
+ def _date_with_era(date)
1820
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1821
+ date
1822
+ end
1823
+
1824
+ # 暦法が変わった場合の補正
1825
+ def _frame_adjust(period, date)
1826
+ return date if @frame.equal?(date.frame) || (period.to_day||0) == 0
1827
+ diff = period.to_day - (date.to_i - to_i)
1828
+ return date if diff == 0
1829
+ date.send(:_force_euqal_day, diff)
1830
+ end
1831
+ end
1832
+
1833
+ #
1834
+ # 時刻を伴った日付
1835
+ #
1836
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#DateAndTimeType gml schema}
1837
+ #
1838
+ class DateAndTime < CalDate
1839
+
1840
+ # 時刻要素
1841
+ #
1842
+ # @return [When::TM::ClockTime]
1843
+ #
1844
+ attr_reader :clk_time
1845
+ alias :clkTime :clk_time
1846
+
1847
+ # 時法の取得
1848
+ #
1849
+ # @return [When::TM::Clock]
1850
+ #
1851
+ def clock
1852
+ @clk_time.frame
1853
+ end
1854
+
1855
+ # 内部時間
1856
+ #
1857
+ # @return [Numeric]
1858
+ #
1859
+ # 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1860
+ #
1861
+ # 暦法によっては、異なる意味を持つことがある
1862
+ #
1863
+ def universal_time
1864
+ return super if [Now, Max, Min].include?(@indeterminated_position)
1865
+ raise NameError, "Temporal Reference System is not defined" unless (@frame && clock)
1866
+ @universal_time ||= (to_i - JulianDate::JD19700101) * Duration::DAY + @clk_time.universal_time
1867
+ end
1868
+
1869
+ # 要素の参照
1870
+ #
1871
+ # @param [Integer] index 参照する要素の指定
1872
+ #
1873
+ # @return [Numeric]
1874
+ #
1875
+ def value(index)
1876
+ digit = _digit(index)
1877
+ return (digit <= DAY) ? @cal_date[digit-1] : @clk_time.clk_time[digit]
1878
+ end
1879
+
1880
+ # ユリウス日または通年が指定の剰余となる日
1881
+ #
1882
+ # @param [When::Coordinates::Residue] other
1883
+ #
1884
+ # @return [When::TM::DateAndTime]
1885
+ #
1886
+ def &(other)
1887
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1888
+ case other.event
1889
+ when 'day'
1890
+ # 指定の剰余となる日
1891
+ sdn = other & to_i
1892
+ result = self.dup._copy({:events=>nil, :query=>@query, :validate=>:done,
1893
+ :date=>_date_with_era(@frame.to_cal_date(sdn)),
1894
+ :time=>@clk_time.clk_time.dup})
1895
+ result.send(:_force_euqal_day, sdn-result.to_i)
1896
+
1897
+ when 'year'
1898
+ # 指定の剰余となる年
1899
+ date = @cal_date.dup
1900
+ date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1901
+ return self.dup._copy({:events=>nil, :query=>@query,
1902
+ :date=>_date_with_era(date),
1903
+ :time=>@clk_time.clk_time.dup})
1904
+
1905
+ else
1906
+ raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1907
+ end
1908
+ end
1909
+
1910
+ # 下位桁の切り捨て
1911
+ #
1912
+ # @param [Integer] digit 切り捨てずに残す、最下位の桁
1913
+ #
1914
+ # @param [Integer] precision 切り捨て結果の分解能
1915
+ #
1916
+ # @return [When::TM::DateAndTime]
1917
+ #
1918
+ def floor(digit=DAY, precision=digit)
1919
+ count = digit - clock.indices.length
1920
+ date = (digit>=DAY) ? @cal_date.dup : @frame._validate(@cal_date[0..(digit-1)])
1921
+ time = @clk_time.clk_time[0..((digit<=DAY) ? 0 : ((count>=0) ? -1 : digit))]
1922
+ time[0] += to_i
1923
+ time = clock._validate(time)
1924
+ time[0] -= to_i
1925
+
1926
+ if (count >= 0)
1927
+ factor = 10**count
1928
+ time[-1] = (time[-1] * factor).floor.to_f / factor
1929
+ end
1930
+
1931
+ # オブジェクトの生成
1932
+ options = {:date=>date, :validate=>:done, :events=>nil, :query=>nil,
1933
+ :time=>(digit<=DAY) ? time : @clk_time.dup._copy({:time=>time})}
1934
+ options[:precision] = precision if precision
1935
+ return self.dup._copy(options)
1936
+ end
1937
+
1938
+ # 下位桁の切り上げ
1939
+ #
1940
+ # @param [Integer] digit 切り上げずに残す、最下位の桁
1941
+ #
1942
+ # @param [Integer] precision 切り上げ結果の分解能
1943
+ #
1944
+ # @return [When::TM::DateAndTime]
1945
+ #
1946
+ def ceil(digit=DAY, precision=digit)
1947
+ length = clock.indices.length
1948
+ count = digit - length
1949
+ period = PeriodDuration.new((count<=0) ? 1 : 0.1**count, digit, (-@frame.indices.length)..length)
1950
+ result = floor(digit, precision) + period
1951
+ result += clock.tz_difference if (result.universal_time <= self.universal_time)
1952
+ return result
1953
+ end
1954
+
1955
+ # 位置情報
1956
+ #
1957
+ # @return [When::Coordinates::Spatial]
1958
+ #
1959
+ def location
1960
+ @location ||= @clk_time.frame.location
1961
+ end
1962
+
1963
+ # 時刻情報のない When::TM::CalDate を返す
1964
+ #
1965
+ # @return [When::TM::CalDate]
1966
+ #
1967
+ def to_cal_date
1968
+ options = _attr
1969
+ options.delete(:clock)
1970
+ options[:precision] = [When::DAY, options[:precision]].min
1971
+ CalDate.new(@cal_date, options)
1972
+ end
1973
+ alias :to_CalDate :to_cal_date
1974
+
1975
+ #protected
1976
+
1977
+ # 属性の Hash
1978
+ # @private
1979
+ def _attr
1980
+ super.merge({:clock=>clock})
1981
+ end
1982
+
1983
+ # 属性のコピー
1984
+ # @private
1985
+ def _copy(options={})
1986
+ # 夏時間の調整
1987
+ case options[:time]
1988
+ when Array
1989
+ if clock._need_validate
1990
+ new_clock = clock._daylight([@frame, options[:date], options[:time]])
1991
+ options[:time] = options[:time].map {|t| t * 1}
1992
+ else
1993
+ new_clock = clock
1994
+ end
1995
+ options[:time] = @clk_time.dup._copy(options.merge({:clock=>new_clock}))
1996
+ when nil
1997
+ options[:time] = @clk_time.dup._copy(options)
1998
+ end
1999
+
2000
+ return super(options)
2001
+ end
2002
+
2003
+ # オブジェクトの生成
2004
+ #
2005
+ # @param [Array<Numeric>] date 日付表現
2006
+ # @param [Array<Numeric>] time 時刻表現
2007
+ # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
2008
+ # @option options [When::TM::Calendar] :frame
2009
+ # @option options [When::TM::Clock] :clock
2010
+ # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
2011
+ # (Integer は 当該年号の 0 年に相当する通年)
2012
+ # @option options [Integer] :precision
2013
+ #
2014
+ def initialize(date, time, options={})
2015
+ options[:time] = time
2016
+ super(date, options)
2017
+ end
2018
+
2019
+ private
2020
+
2021
+ # オブジェクトの正規化
2022
+ def _normalize(options={})
2023
+
2024
+ # Clock
2025
+ unless options[:validate]
2026
+ clock = Clock.get_clock_option(options)
2027
+ clock ||= options[:time].frame if options[:time].kind_of?(ClockTime)
2028
+ clock ||= Clock.local_time
2029
+ end
2030
+ clock = When.Clock(clock) if clock.kind_of?(String)
2031
+ clock_is_timezone = clock && !clock.kind_of?(When::TM::Clock)
2032
+ clock = clock.daylight if clock_is_timezone
2033
+
2034
+ # ClockTime
2035
+ @clk_time =
2036
+ case options[:time]
2037
+ when ClockTime ; options[:time]
2038
+ when Array ; ClockTime.new(options[:time], {:frame=>clock, :precision=>options[:precision], :validate=>:done})
2039
+ else ; clock.to_clk_time(options[:time], {:precision=>options[:precision]})
2040
+ end
2041
+
2042
+ super
2043
+
2044
+ # 日付と時刻の正規化
2045
+ unless options[:validate]
2046
+ time = @clk_time.clk_time
2047
+ precision = @clk_time.precision
2048
+ second = time[clock.base.length-1]
2049
+ second -= clock.base[-1] if second
2050
+ if second && second != 0 && time_standard.has_leap?
2051
+ zero = DateAndTime.new(@cal_date, time[0..-2],
2052
+ {:frame=>@frame, :clock=>clock, :precision=>precision,
2053
+ :era_name=>@calendar_era_name, :era=>options[:era],
2054
+ :time_standard=>time_standard, :location=>@location})
2055
+ end
2056
+
2057
+ # 日付と時刻の関係の調整
2058
+ @cal_date = _date_with_era(@frame._validate(_date_without_era) {|jdn|
2059
+ time[0] += jdn
2060
+ time[0..-1] = clock._validate(time)
2061
+ jdn = time[0] * 1
2062
+ time[0] -= jdn
2063
+ jdn
2064
+ })
2065
+
2066
+ # 夏時間の調整
2067
+ if clock._need_validate
2068
+ if @calendar_era_name
2069
+ cal_date = @cal_date.dup
2070
+ cal_date[0] += @calendar_era_name[1]
2071
+ else
2072
+ cal_date = @cal_date
2073
+ end
2074
+ clock = clock._daylight([@frame, cal_date, time])
2075
+ end
2076
+ time = [time[0]] + time[1..-1].map {|t| t * 1}
2077
+ @clk_time = ClockTime.new(time, {:frame=>clock, :precision=>precision, :validate=>:done}) if clock_is_timezone
2078
+
2079
+ # 閏秒
2080
+ if zero
2081
+ leap = ((dynamical_time - zero.dynamical_time) * clock.second - second).to_i
2082
+ if leap != 0 && leap.abs < clock.second
2083
+ @cal_date = zero.cal_date
2084
+ @clk_time = zero.clk_time
2085
+ @clk_time.clk_time[-1] += second
2086
+ leap /= clock.second
2087
+ @universal_time = When::Coordinates::LeapSeconds.new(@universal_time-leap, leap, 1/clock.second)
2088
+ @dynamical_time -= leap
2089
+ end
2090
+ end
2091
+ end
2092
+
2093
+ # 後処理
2094
+ @keys |= @clk_time.keys
2095
+ end
2096
+
2097
+ # 加減算共通処理
2098
+ def _plus(period)
2099
+ # 日時の加算
2100
+ time = @clk_time.clk_time.dup
2101
+ pdate = @frame._arrange_length(period.date)
2102
+ ptime = clock._arrange_length(period.time)
2103
+ date = _date_with_era(@frame._validate(_date_without_era, pdate) {|jdn|
2104
+ time[0] += jdn
2105
+ time = clock._validate(time, ptime)
2106
+ jdn = time[0] * 1
2107
+ time[0] -= jdn
2108
+ jdn
2109
+ })
2110
+
2111
+ # オブジェクトの生成
2112
+ _frame_adjust(period, self.dup._copy({:date=>date, :time=>time, :validate=>:done, :events=>nil, :query=>nil}))
2113
+ end
2114
+ end
2115
+ end