when_exe 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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