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,1669 +1,1693 @@
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
- #
9
- # ISO 19108 Geographic information - Temporal schema
10
- #
11
- module When::TM
12
- #
13
- # (5.3) Temporal Reference System Package
14
- #
15
-
16
- # 時間参照系
17
- #
18
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#AbstractTimeReferenceSystemType gml schema}
19
- #
20
- class ReferenceSystem < When::TM::Object
21
-
22
- # この時間参照系が使用する空間と時間の制限(実装は時間のみ)
23
- #
24
- # Limits of space and time within which the temporal reference system is use
25
- #
26
- # @return [When::EX::Extent]
27
- #
28
- # @note
29
- # domain の最小・最大から範囲を決定して domain_of_validity としている
30
- # domain_of_validity は ISO19108との互換性を確保するため提供しているが、
31
- # 有効期間が複数に断片化していることがあるので、より正確な情報を含む
32
- # domain の使用を推奨する
33
- #
34
- # マルチスレッド動作時 CalendarEra の生成で Calendar の本属性が更新される
35
- # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
36
- #
37
- attr_accessor :domain_of_validity
38
- alias :domainOfValidity :domain_of_validity
39
-
40
- # この時間参照系と関連付けられた時間位置 (relation - Reference)
41
- #
42
- # The temporal position associated with the time reference system being described
43
- #
44
- # @return [Array<When::TM::(Temporal)Position>]
45
- #
46
- attr_reader :position
47
-
48
- # この時間参照系が使用する時間の範囲
49
- #
50
- # Range of time within which the temporal reference system is use
51
- #
52
- # @return [Hash<String=>When::Parts::GeomerticComplex>]
53
- #
54
- # @note
55
- # マルチスレッド動作時 CalendarEra の生成で Calendar の本属性が更新される
56
- # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
57
- #
58
- def domain
59
- @domain ||= Hash.new {|hash, key| hash[key] = When::Parts::GeometricComplex.new([])}
60
- end
61
-
62
- # 時間参照系を識別する名称
63
- #
64
- # Name by which the temporal reference system is known
65
- #
66
- # @return [When::RS::Identifier]
67
- #
68
- def name
69
- @name ||= When::RS::Identifier.new(label, @version, @remark)
70
- end
71
- end
72
-
73
- # 順序時間参照系
74
- #
75
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeOrdinalReferenceSystemType gml schema}
76
- #
77
- class OrdinalReferenceSystem < ReferenceSystem
78
-
79
- # この順序時間参照系の最上位レベルを構成する順序年代 (relation - Structure)
80
- #
81
- # Ordinal eras that make up the highest level of this ordinal reference system
82
- #
83
- # @return [Array<When::TM::OrdinalEra>]
84
- #
85
- alias :component :child
86
-
87
- private
88
-
89
- # オブジェクトの正規化
90
- def _normalize(args=[], options={})
91
- end
92
- end
93
-
94
- # 暦
95
- #
96
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCalendarType gml schema}
97
- #
98
- class Calendar < ReferenceSystem
99
-
100
- include When::Coordinates
101
- include Spatial::Normalize
102
- include Temporal
103
- include When::TimeStandard::TimeBasis
104
-
105
- # 初期化
106
- #
107
- # @return [void]
108
- #
109
- # @note
110
- # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
111
- # このため、本メソッド自体はスレッドセーフでない。
112
- #
113
- def self._setup_
114
- @_lock_ = Mutex.new if When.multi_thread
115
- @_pool = {}
116
- end
117
-
118
- # この暦と関連付けられた暦年代 (relation - Basis)
119
- #
120
- # The calendar eras associated with the calendar being described
121
- #
122
- # @return [Array<When::TM::CalendarEra>]
123
- #
124
- # @note
125
- # マルチスレッド動作時 CalendarEra の生成で 本属性が更新される
126
- # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
127
- #
128
- attr_reader :reference_frame
129
- alias :referenceFrame :reference_frame
130
-
131
- # 一暦日の中の時間位置を定めるために、この暦とともに使用する時計 (relation - Resolution)
132
- #
133
- # The clock that is used with this calendar to define
134
- # temporal position within a calendar day
135
- #
136
- # @return [Array<When::TM::Clock>]
137
- #
138
- attr_reader :time_basis
139
- alias :timeBasis :time_basis
140
-
141
- # 時刻制 - additional attribute
142
- #
143
- # @return [When::TimeStandard, nil]
144
- #
145
- def time_standard
146
- @time_basis ? @time_basis.time_standard : nil
147
- end
148
-
149
- # 時間の歩度
150
- #
151
- # @return [Numeric]
152
- #
153
- def rate_of_clock
154
- @time_basis ? @time_basis.time_standard.rate_of_clock : 1.0
155
- end
156
-
157
- # 日付と時刻をユリウス日(When::TM::JulianDate)に変換する
158
- #
159
- # @param [When::TM::CalDate] cal_date
160
- # @param [When::TM::ClockTime] time
161
- #
162
- # @return [When::TM::JulianDate]
163
- #
164
- def date_trans(cal_date, time=nil, options={})
165
- time = cal_date.clk_time if ((time == nil) && cal_date.kind_of?(DateAndTime))
166
- frac = (time) ? time.universal_time : 0.0
167
- jdn = to_julian_date(cal_date.cal_date)
168
- return JulianDate.universal_time((jdn - JulianDate::JD19700101) * Duration::DAY + frac, options)
169
- end
170
- alias :dateTrans :date_trans
171
-
172
- # ユリウス日(When::TM::JulianDate)を日付に変換する
173
- #
174
- # @param [When::TM::JulianDate] jdt
175
- # @param [Hash] options see {When::TM::TemporalPosition._instance}
176
- #
177
- # @return [When::TM::CalDate] if (options[:clock or :tz] == nil)
178
- # @return [When::TM::CalDateAndTime] if (options[:clock or :tz] != nil)
179
- #
180
- def jul_trans(jdt, options={})
181
- options = TemporalPosition._options(options)
182
- unless jdt.kind_of?(When::TimeValue)
183
- options[:clock] ||= @time_basis unless rate_of_clock == 1.0
184
- jdt = JulianDate.new(jdt, options)
185
- end
186
- cal_options = jdt._attr
187
- cal_options.delete(:era_name)
188
- cal_options.delete(:era)
189
- unless rate_of_clock == jdt.time_standard.rate_of_clock
190
- cal_options.delete(:time_standard)
191
- cal_options[:clock] = @time_basis || When::UTC
192
- jdt = JulianDate.dynamical_time(jdt.dynamical_time, {:time_standard=>time_standard})
193
- end
194
-
195
- cal_options.update(options)
196
- cal_options[:frame] = self
197
- clock = Clock.get_clock_option(cal_options) || jdt.clock
198
-
199
- if clock
200
- clock = When.Clock(clock) if clock.kind_of?(String)
201
- clock = clock._daylight(jdt.universal_time) if clock._need_validate
202
- frac = clock.universal_time
203
- sdn, time = (jdt.universal_time - frac).divmod(Duration::DAY)
204
- cal_options[:clock] = clock
205
- cal_date = to_cal_date(sdn.to_i + JulianDate::JD19700101)
206
- cal_date[0] -= cal_options[:era_name][1] if cal_options[:era_name]
207
- DateAndTime.new(cal_date, time+frac, cal_options)
208
- else
209
- cal_date = to_cal_date(jdt.to_i)
210
- cal_date[0] -= cal_options[:era_name][1] if cal_options[:era_name]
211
- CalDate.new(cal_date, cal_options)
212
- end
213
- end
214
- alias :julTrans :jul_trans
215
- alias :^ :jul_trans
216
-
217
- # ユリウス日(Numeric)を日付に変換する
218
- #
219
- # @param [Integer] jdn
220
- #
221
- # @return [Array<Numeric>]
222
- #
223
- def to_cal_date(jdn)
224
- _encode(_number_to_coordinates(jdn))
225
- end
226
-
227
- # 日付をユリウス日(Numeric)に変換する
228
- #
229
- # @param [Array<Numeric>] cal_date
230
- #
231
- # @return [Integer] JulianDate
232
- #
233
- def to_julian_date(cal_date)
234
- date = _decode(cal_date)
235
- date[0] = +date[0]
236
- _coordinates_to_number(*date)
237
- end
238
-
239
- # 日付・時刻をUniversal Time(Numeric)に変換する
240
- #
241
- # @param [Array<Numeric>] cal_date 日付
242
- # @param [Array<Numeric>] clk_time 時刻
243
- # @param [When::TM::Clock] clock 時法
244
- #
245
- # @return [Numeric] Universal Time
246
- #
247
- def to_universal_time(cal_date, clk_time, clock=When::UTC)
248
- time = clk_time.dup
249
- time[0] += _coordinates_to_number(*_decode(cal_date))
250
- clock.to_universal_time(time) - When::TM::JulianDate::JD19700101 * When::TM::Duration::DAY
251
- end
252
-
253
- # 月初の通日
254
- #
255
- # @param [Integer] m 通月
256
- #
257
- # @return [Numeric] 月初の通日
258
- #
259
- def _new_month_(m)
260
- date = @base.map {|d| d||0}
261
- if @indices[-2].unit
262
- date[-2] += m
263
- _coordinates_to_number(*_decode(date))
264
- else
265
- d0 = _coordinates_to_number(*_decode(date))
266
- date[-3] += (m * @mean_month / @mean_year).floor - 1
267
- d1 = _coordinates_to_number(*_decode(date))
268
- date[-2] += m - ((d1 - d0) / @mean_month + 0.5).floor
269
- _coordinates_to_number(*_decode(date))
270
- end
271
- end
272
-
273
- # 通日 - > [通月,月内日数,月の日数]
274
- #
275
- # @param [Integer] sdn 通日
276
- #
277
- # @return [Array<Numeric>] ( m, d, n )
278
- # [ m - 通月]
279
- # [ d - 月内の日数 (0 始まり)]
280
- # [ n - 月の日数]
281
- #
282
- def _to_month_number_(sdn)
283
- Residue.mod(sdn) {|m| _new_month(m)}
284
- end
285
-
286
- private
287
-
288
- # オブジェクトの正規化
289
- def _normalize(args=[], options={})
290
- @indices ||= DefaultDateIndices
291
- _normalize_spatial
292
- _normalize_time_basis
293
- _default_index_of_MSC
294
- _normalize_temporal
295
- @reference_frame ||= []
296
- end
297
- end
298
-
299
- # 時計
300
- #
301
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeClockType gml schema}
302
- #
303
- class Clock < ReferenceSystem
304
-
305
- include When::Coordinates
306
- include When::Parts::Timezone::Base
307
- include Spatial::Normalize
308
- include Temporal
309
-
310
- class << self
311
- include When::Parts::Resource::Pool
312
-
313
- # When::TM::Clock Class のグローバルな設定を行う
314
- #
315
- # @param [When::Parts::Timezone::Base, String] local 地方時を使用する場合、指定する
316
- #
317
- # @return [void]
318
- #
319
- # @note
320
- # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
321
- # このため、本メソッド自体はスレッドセーフでない。
322
- #
323
- def _setup_(local=nil)
324
- @_lock_ = Mutex.new if When.multi_thread
325
- @_pool = {}
326
- @local_time = local
327
- end
328
-
329
- # 設定情報を取得する
330
- #
331
- # @return [Hash] 設定情報
332
- #
333
- def _setup_info
334
- {:local => _local_time}
335
- end
336
-
337
- # 地方時
338
- #
339
- # @param [When::Parts::Timezone::Base, String] local 地方時
340
- #
341
- # @return [When::Parts::Timezone::Base, String]
342
- #
343
- # @note
344
- # @local_timeは、原則、ライブラリ立ち上げ時に _setup_ で初期化する。
345
- # 以降、@local_timeに代入を行っても、すでに生成した When::TM::TemporalPosition 等には反映されない。
346
- #
347
- def local_time=(local)
348
- if @_pool
349
- @local_time = local
350
- else
351
- _setup_(local)
352
- end
353
- end
354
-
355
- # When::TM::Clock のローカルタイムを読みだす
356
- #
357
- # @return [When::Parts::Timezone::Base]
358
- #
359
- def local_time
360
- _local_time[1]
361
- end
362
-
363
- # When::TM::Clock のローカルタイムが設定されているか?
364
- #
365
- # @return [true, false]
366
- #
367
- def is_local_time_set?
368
- _local_time[0]
369
- end
370
-
371
- # 共通処理
372
- def _local_time
373
- case @local_time
374
- when Array ; @local_time
375
- when nil ; @local_time = [false, When::UTC]
376
- when String ; @local_time = [true, When::Parts::Timezone[@local_time] ||
377
- When::V::Timezone[@local_time] ||
378
- When.Clock(@local_time)]
379
- else ; @local_time = [true, @local_time]
380
- end
381
- end
382
- private :_local_time
383
-
384
- # @private
385
- def get_clock(options)
386
- get_clock_option(options) || local_time
387
- end
388
-
389
- # @private
390
- def get_clock_option(options)
391
- clock = options.delete(:clock)
392
- tz = options.delete(:tz)
393
- tz ? (When::V::Timezone[tz] || When::Parts::Timezone[tz]) : clock
394
- end
395
-
396
- # @private
397
- def to_hms(hms, extended=true)
398
- case hms
399
- when Numeric
400
- sgn = (hms >= 0) ? '+' : '-'
401
- hh, mm = hms.abs.divmod(3600)
402
- mm, ss = mm.divmod(60)
403
- ss, ff = ss.divmod(1)
404
- ff = (ff == 0 || When::STRING <= When::SECOND) ? '' :
405
- ("%.#{When::STRING - When::SECOND}f" % ff)[1..-1]
406
- ss = (ss == 0 && ff == '') ? '' : ("%02d" % ss)
407
- mm = "%02d" % mm
408
- hh = "%02d" % hh
409
- when /^([-+])(\d{2})([:=*])?(\d{2})?([:=*])?(\d{2})?(\.\d+)?$/
410
- sgn, hh, d1, mm, d2, ss, ff = $~[1..7]
411
- ff ||= ''
412
- ss ||= ''
413
- mm ||= ''
414
- else
415
- return nil
416
- end
417
-
418
- if (extended)
419
- d1 ||= (mm=='') ? '' : ':'
420
- d2 ||= (ss=='') ? '' : ':'
421
- else
422
- d1 = ''
423
- d2 = ''
424
- end
425
- sgn + hh + d1 + mm + d2 + ss + ff
426
- end
427
- end
428
-
429
- # この時法の基点となる事象
430
- #
431
- # Event used as the datum for this clock
432
- #
433
- # @return [String]
434
- #
435
- # @note
436
- # new の options 引数に :reference_event があれば設定される。
437
- # ライブラリとしては本変数を参照していない。下記の振る舞いを String で説明するため用いてもよい。
438
- #
439
- # @note
440
- # 日付の境界が午前0時でない場合、When::Coordinates::Temporal.border により、境界が指定される。
441
- # border, behavior メソッドをオーバーライドすることで、日の出、日の入りなど event 時刻が一定
442
- # しない場合にも対応する。
443
- #
444
- attr_reader :reference_event
445
- alias :referenceEvent :reference_event
446
-
447
- # この時法による参照事象の時刻
448
- #
449
- # Time of the reference event for this clock
450
- #
451
- # @return [When::TM::ClockTime]
452
- #
453
- attr_reader :reference_time
454
- alias :referenceTime :reference_time
455
-
456
- # UTCによる参照事象の時刻
457
- #
458
- # UTC time of the reference event
459
- #
460
- # @return [When::TM::ClockTime]
461
- #
462
- attr_reader :utc_reference
463
- alias :utcReference :utc_reference
464
-
465
- # 一暦日の中の時間位置を定めるために、この時計とともに使用する暦 (relation - Resolution)
466
- #
467
- # The calendar that is used with this clock to define
468
- # temporal position within a calendar day
469
- #
470
- # @return [Array<When::TM::Calendar>]
471
- #
472
- attr_reader :date_basis
473
- alias :dateBasis :date_basis
474
-
475
- # 時刻制 - additional attribute
476
- #
477
- # @return [When::TimeStandard]
478
- #
479
- attr_reader :time_standard
480
-
481
- # この時法を生成した時間帯プロパティ - additional attribute
482
- #
483
- # @return [When::V::TimezoneProperty]
484
- #
485
- # @note
486
- # When::TM::TemporalPosition に対して加減算を行うと、時間帯が変わる可能性がある。
487
- # 本変数により、時間帯決定ルール(When::V::TimezoneProperty#rrule)を参照する。
488
- #
489
- attr_accessor :tz_prop
490
- alias :tzProp :tz_prop
491
-
492
- # universal_timeとこの時法の最小単位との比 - additional attribute
493
- #
494
- # @return [Numeric]
495
- #
496
- attr_reader :second
497
-
498
- # この時法のUTCとの差(ISO 8601 extended format) - additional attribute
499
- #
500
- # @return [String] (±hh:mm)
501
- #
502
- attr_reader :zone
503
- alias :to_extended :zone
504
-
505
- # 時間の歩度
506
- #
507
- # @return [Numeric]
508
- #
509
- def rate_of_clock
510
- @time_standard.rate_of_clock
511
- end
512
-
513
- # 128秒単位の実数による参照事象の時刻
514
- #
515
- # Fraction time of the reference event
516
- #
517
- # @param [Integer] sdn 参照事象の通し番号(ダミー)
518
- #
519
- # @return [Numeric]
520
- #
521
- # T00:00:00Z からの参照事象の経過時間 / 128秒
522
- #
523
- def universal_time(sdn=nil)
524
- return @utc_reference.universal_time
525
- end
526
-
527
- # この時法の時刻をUTC時刻に変換する
528
- #
529
- # @param [When::TM::ClockTime] u_time
530
- #
531
- # @return [When::TM::ClockTime]
532
- #
533
- def utc_trans(u_time)
534
- return When::UTC.to_clk_time(self.to_universal_time(u_time.clk_time))
535
- end
536
- alias :utcTrans :utc_trans
537
-
538
- # UTC時刻をこの時法の時刻に変換する
539
- #
540
- # @param [When::TM::ClockTime] clk_time
541
- #
542
- # @return [When::TM::ClockTime]
543
- #
544
- def clk_trans(clk_time)
545
- return self.to_clk_time(When::UTC.to_universal_time(u_time.clk_time))
546
- end
547
- alias :clkTrans :clk_trans
548
-
549
- # この時法の時刻を128秒単位の実数に変換する
550
- #
551
- # @param [Array<Numeric>] clk_time
552
- #
553
- # @return [Numeric]
554
- #
555
- def to_universal_time(clk_time)
556
- return _coordinates_to_number(_decode(clk_time)) / @second
557
- end
558
-
559
- # 128秒単位の実数をこの時法の時刻に変換する
560
- #
561
- # @param [Numeric] fod
562
- #
563
- # @return [When::TM::ClockTime]
564
- #
565
- def to_clk_time(fod, options={})
566
- options[:frame] = self
567
- fod, second = fod.trunk, fod.branch / fod.second if fod.kind_of?(When::Coordinates::LeapSeconds)
568
- clk_time = ClockTime.new(_encode(_number_to_coordinates(fod * @second)), options)
569
- return clk_time if (second||0) == 0
570
- clk_time.clk_time[-1] += second
571
- return clk_time
572
- end
573
-
574
- # 時刻をNumeric(serial time)に変換する
575
- #
576
- # @param [Numeric] clk_time
577
- #
578
- # @return [Numeric] serial time
579
- #
580
- def _coordinates_to_number(clk_time)
581
- u = 1
582
- s = 0
583
- (@base.length-1).downto(1) do |i|
584
- s += u * (+clk_time[i] - @base[i]) if (clk_time[i])
585
- u *= @unit[i]
586
- end
587
- return s + u * (+clk_time[0]) + @origin_of_LSC
588
- end
589
-
590
- # Numeric(serial time)を時刻に変換する
591
- #
592
- # @param [Numeric] serial_time
593
- #
594
- # @return [Numeric]
595
- #
596
- def _number_to_coordinates(serial_time)
597
- time = [serial_time-@origin_of_LSC]
598
- (@base.length-1).downto(1) do |i|
599
- carry, time[0] = (+time[0]).divmod(@unit[i])
600
- time[0] += @base[i]
601
- time.unshift(carry)
602
- end
603
- return time
604
- end
605
-
606
- #
607
- # _m17n_form のための要素生成
608
- #
609
- # @param [Hash] options 下記のとおり
610
- # @option options [Symbol] :method :to_m17n なら時間帯名を返す、その他は When::Parts::Resource#to_h 参照
611
- #
612
- # @private
613
- def _to_hash_value(options={})
614
- options[:method] == :to_m17n ? tzname(:hash)[0] : super
615
- end
616
-
617
- # この時法のUTCとの差(ISO 8601 basic format)
618
- #
619
- # @return [String] (±hhmm)
620
- #
621
- def to_basic
622
- return '' unless @zone
623
- @zone.gsub(/:/, '')
624
- end
625
-
626
- # 期間オブジェクトの桁数合わせ
627
- # @private
628
- def _arrange_length(period)
629
- return period unless period.kind_of?(Array)
630
- diff = @indices.length - period.length + 1
631
- return period if (diff == 0)
632
- return (diff > 0) ? period + Array.new(diff, 0) : period[0...diff]
633
- end
634
-
635
- # 時刻配列の分解能
636
- # @private
637
- def _precision(time, default=nil)
638
- nil_index = time.index(nil) || time.length
639
- precision = nil_index - 1 if (nil_index < @base.length || time[-1].kind_of?(Integer))
640
- When::Coordinates::Index.precision(default || precision)
641
- end
642
-
643
- # 丸め量 / When::TM::Duration::SYSTEM
644
- # @private
645
- def _round_value(precision)
646
- offset = When::TM::Duration::DAY / 2
647
- precision.times do |i|
648
- offset /= @unit[i+1] ? @unit[i+1] : 10
649
- end
650
- offset
651
- end
652
-
653
- # この時法の時間帯名
654
- #
655
- # @param [Symbol] format
656
- # - :extended ISO 8601 extended format (default)
657
- # - :basic ISO 8601 basic format
658
- # - :hash 時間帯名の後ろにISO 8601 extended format を付加する
659
- #
660
- # @return [Array<String>]
661
- #
662
- # @note
663
- # :extended または :basicが指定され、上記は時間帯名が定義されていない場合は、ISO 8601形式で返す
664
- #
665
- def tzname(format=:extended)
666
- name = @tz_prop.tzname if @tz_prop.kind_of?(When::V::TimezoneProperty) && format != :hash
667
- name ||= format == :basic ? to_basic : @zone
668
- name = Array(name)
669
- return name unless format == :hash
670
- tzid = case @tz_prop
671
- when When::V::TimezoneProperty ; @tz_prop['..'].property['tzid'].object
672
- when When::Parts::Timezone ; @tz_prop.timezone.name
673
- else ; ''
674
- end
675
- name[0] = tzid + name[0]
676
- name
677
- end
678
-
679
- # 標準時間帯の時計
680
- # @return [When::TM::Clock]
681
- def standard
682
- _tz_prop ? _tz_prop.standard : self
683
- end
684
-
685
- # 夏時間帯の時計
686
- # @return [When::TM::Clock]
687
- def daylight
688
- _tz_prop ? _tz_prop.daylight : self
689
- end
690
-
691
- # 夏時間帯と標準時間帯の時間差
692
- # @return [When::TM:IntervalLength]
693
- def tz_difference
694
- _tz_prop ? _tz_prop.tz_difference : 0
695
- end
696
-
697
- # 夏時間の有無
698
- # @private
699
- def _need_validate
700
- _tz_prop ? _tz_prop._need_validate : false
701
- end
702
-
703
- # 夏時間
704
- # @private
705
- def _daylight(time)
706
- _tz_prop ? _tz_prop._daylight(time) : self
707
- end
708
-
709
- private
710
-
711
- # Timezone オブジェクト
712
- def _tz_prop
713
- @tz_prop.kind_of?(When::V::TimezoneProperty) ? @tz_prop._pool['..'] : @tz_prop
714
- end
715
-
716
- # オブジェクトの正規化
717
- def _normalize(args=[], options={})
718
- @indices ||= DefaultTimeIndices
719
-
720
- # note
721
- @note ||= 'JulianDay'
722
-
723
- # normalize spatial module
724
- _normalize_spatial
725
-
726
- # normalize temporal module
727
- _normalize_temporal
728
-
729
- # second
730
- @second = (@second||1/When::TM::Duration::SECOND).to_f
731
-
732
- # zone
733
- @zone = Clock.to_hms(@zone || @label || @reference_event)
734
-
735
- # time_standard
736
- @time_standard = When.Resource(@time_standard||'UniversalTime', '_t:') unless @time_standard.kind_of?(When::TimeStandard)
737
-
738
- # utc_reference
739
- @utc_reference ||= @zone ? ClockTime.new(@zone.to_s, {:frame=>When::UTC}) : Clock.local_time
740
-
741
- # reference_time & origin_of_LSC
742
- case @reference_time
743
- when Array ; clk_time = @reference_time
744
- when String ; clk_time = Pair._en_pair_date_time(@reference_time)
745
- when ClockTime ; clk_time = @reference_time.clk_time
746
- when nil ; clk_time = [0, 0]
747
- else ; raise TypeError, "reference_time type mismatch"
748
- end
749
- @origin_of_LSC -= (to_universal_time(clk_time) - @utc_reference.universal_time) * @second
750
- @reference_time = ClockTime.new(clk_time, {:frame=>self})
751
- end
752
- end
753
-
754
- # 時間座標系
755
- #
756
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCoordinateSystemType gml schema}
757
- #
758
- class CoordinateSystem < ReferenceSystem
759
-
760
- # グレゴリオ暦の日付及びその日のUTCの時刻で表現する、座標参照系の尺度の原点位置
761
- #
762
- # Position of the origin of the scale on which the temporal coordinate
763
- # system is based expressed as a date in the Gregorian calendar and
764
- # time of day in UTC
765
- #
766
- # @return [When::BasicTypes::DateTime]
767
- #
768
- attr_reader :origin
769
-
770
- # この参照系の軸で時間の参照単位とする間隔
771
- #
772
- # Standard unit of time used to measure duration
773
- # on the axis of the coordinate system
774
- #
775
- # @return [When::TM::Duration] (changed from String)
776
- #
777
- attr_reader :interval
778
-
779
- # この時間参照系の座標値をグレゴリオ暦及びUTC時刻に変換する
780
- #
781
- # @param [When::TM::Coordinate] c_value
782
- #
783
- # @return [When::TM::DateAndTime] (When::TM::BasicTypes::DateTimeはまちがい)
784
- #
785
- def transform_coord(c_value)
786
- When::Gregorian.jul_trans(JulianDate.universal_time(c_value.universal_time, {:frame=>When::UTC}))
787
- end
788
- alias :transformCoord :transform_coord
789
-
790
- # グレゴリオ暦及びUTC時刻をこの時間参照系の座標値に変換する
791
- #
792
- # @param [When::TM::DateAndTime (BasicTypes::DateTimeはまちがい)] date_time
793
- #
794
- # @return [When::TM::Coordinate]
795
- #
796
- def transform_date_time(date_time)
797
- Coordinate.universal_time(date_time.universal_time, {:frame=>self})
798
- end
799
- alias :transformDateTime :transform_date_time
800
-
801
- private
802
-
803
- # オブジェクトの正規化
804
- def _normalize(args=[], options={})
805
- @origin = When.when?(@origin, options)
806
- @interval = When.Duration(@interval)
807
- raise TypeError, "Interval should be IntervalLength" unless @interval.kind_of?(IntervalLength)
808
- end
809
- end
810
-
811
- # 順序時間参照系
812
- #
813
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeOrdinalEraType gml schema}
814
- #
815
- class OrdinalEra < When::TM::Object
816
-
817
- include Separation
818
-
819
- # この順序年代を識別する名称
820
- #
821
- # Name that identifies a specific ordinal era
822
- #
823
- # @return [String]
824
- #
825
- alias :name :label
826
-
827
- # この順序年代をさらに分割する順序年代
828
- #
829
- # Ordinal eras that subdivide this ordinal era
830
- #
831
- # @return [Array<When::TM::OrdinalEra>]
832
- #
833
- alias :member :child
834
-
835
- # この順序年代が属する順序時間参照系 (relation - Structure)
836
- #
837
- # Ordinal reference system that contains this ordinal era
838
- #
839
- # @return [When::TM::OrdinalReferenceSystem, nil]
840
- #
841
- def system
842
- _pool['..'].kind_of?(String) ? _pool['..'] : nil
843
- end
844
-
845
- # この順序年代が属する上位順序年代 (relation - Composition)
846
- #
847
- # Ordinal era that contains this ordinal era
848
- #
849
- # @return [When::TM::OrdinalEra, nil]
850
- #
851
- def group
852
- _pool['..'].kind_of?(String)? nil : _pool['..']
853
- end
854
-
855
- # この順序年代が始まる時点
856
- #
857
- # Date at which the ordinal era began
858
- #
859
- # @return [When::BasicTypes::DateTime]
860
- #
861
- attr_reader :begin
862
-
863
- # この順序年代が終わる時点
864
- #
865
- # Date at which the ordinal era ended
866
- #
867
- # @return [When::BasicTypes::DateTime]
868
- #
869
- def end
870
- @end || (@_pool['.->'] ? @_pool['.->'].begin : nil)
871
- end
872
-
873
- # 日時が属するか?
874
- #
875
- # @param [When::TM::TemporalPosition] other
876
- #
877
- # @return [Boolean]
878
- # [ true - 属する ]
879
- # [ false - 属さない ]
880
- #
881
- def include?(other)
882
- self.begin <= other && other < self.end
883
- end
884
-
885
- # When::TM::TemporalPosition ^ When::TM::OrdinalEra で呼ばれる
886
- # @private
887
- def ^(other)
888
- include?(other) ? self : nil
889
- end
890
-
891
- private
892
-
893
- # オブジェクトの正規化
894
- def _normalize(args=[], options={})
895
- # options
896
- @options ||= {}
897
- term_options = @options.merge({'namespace'=>@namespace, 'locale'=>@locale})
898
-
899
- label, begun, ended = args
900
-
901
- # label
902
- @label = label if label
903
- @label = m17n(@label, nil, nil, term_options) if (@label.instance_of?(String))
904
- @label._pool['..'] ||= self
905
- @_pool[@label.to_s] = @label
906
-
907
- # begin
908
- @begin = begun if begun
909
- @begin = When.when?(@begin, @options) if @begin
910
- @begin = @child[0].begin if !@begin && @child[0]
911
-
912
- # end
913
- @end = ended if ended
914
- @end = When.when?(@end, @options) if @end
915
- @end = @child[-1].end if !@end && @child[-1]
916
- end
917
-
918
- # その他のメソッド
919
- #
920
- # @note
921
- # When::TM::OrdinalEra で定義されていないメソッドは
922
- # 処理を @begin (type: When::TM::TemporalPosition) に委譲する
923
- #
924
- def method_missing(name, *args, &block)
925
- self.class.module_eval %Q{
926
- def #{name}(*args, &block)
927
- @begin.send("#{name}", *args, &block)
928
- end
929
- } unless When::Parts::MethodCash.escape(name)
930
- @begin.send(name, *args, &block)
931
- end
932
- end
933
-
934
- # 暦年代
935
- #
936
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCalendarEraType gml schema}
937
- #
938
- class CalendarEra < When::TM::Object
939
-
940
- include When
941
-
942
- class << self
943
-
944
- include When::Parts::Resource::Pool
945
-
946
- # When::TM::CalendarEra Class のグローバルな設定を行う
947
- #
948
- # @param [Array<String>] order When::TM::CalendarEra の検索順序を指定するIRI文字列のArray
949
- #
950
- # @return [void]
951
- #
952
- # @note
953
- # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
954
- # このため、本メソッド自体はスレッドセーフでない。
955
- #
956
- def _setup_(order=nil)
957
- @_lock_ = Mutex.new if When.multi_thread
958
- @_pool = {}
959
- @order = order || DefaultEpochs
960
- end
961
-
962
- # 設定情報を取得する
963
- #
964
- # @return [Hash] 設定情報
965
- #
966
- def _setup_info
967
- {:order => @order || _setup_}
968
- end
969
-
970
- # When::TM::CalendarEra オブジェクトを検索し取得する
971
- #
972
- # @overload _instance(key, epoch=nil, reverse=nil, options={})
973
- # @param [String, Regexp] key 検索する暦年代または、暦年代にマッチする正規表現
974
- # @param [Integer] epoch 年数を昇順にカウントする方式での暦元(0年)の通年(デフォルトは nil - 指定なし)
975
- # @param [Integer] reverse 年数を降順にカウントする方式での暦元(0年)の通年(デフォルトは nil - 指定なし)
976
- # @param [Hash] options 以下の通り
977
- # @option options [String] :area 暦年代の使用地域の指定(デフォルトは nil - 指定なし)
978
- # @option options [String] :period 暦年代の使用時代の指定(デフォルトは nil - 指定なし)
979
- # @option options [Integer] :count 何件ヒットするまで検索するかを指定(デフォルトは 1件)
980
- # @option options [String] the_others 例えば When::TM::CalendarEra オブジェクトの epoch_of_use に 'name' などの
981
- # 指定がある場合、:name に指定しておけば、検索での絞り込みに使用できる。
982
- #
983
- # @note 検索用のインデクスはインスタンスの生成時に作成する。
984
- # 作成後にキーワードとなる年号(M17nクラス)にローケールが追加されても反映されない。
985
- #
986
- def _instance(*args)
987
- # パラメータ
988
- args = args.dup
989
- options = (args[-1].kind_of?(Hash)) ? args.pop.dup : {}
990
- key, epoch, reverse = options.delete(:label) || args
991
- key = When::Locale.ideographic_unification(key)
992
- if key.kind_of?(String)
993
- key, *parents = key.split(/::/).reverse
994
- else
995
- parents = []
996
- end
997
- area = options.delete(:area)
998
- period = options.delete(:period)
999
- count = options.delete(:count) || 1
1000
-
1001
- # 候補
1002
- _setup_ unless @_pool
1003
- pool = _candidates(options, area, period, key, epoch, false) +
1004
- _candidates(options, area, period, key, reverse, true)
1005
- _compact(pool, parents)
1006
- return pool unless pool.size < count
1007
-
1008
- order = @order
1009
- if /\?.+?=/ =~ parents[-1]
1010
- begin
1011
- head = When.CalendarEra(parents[-1])
1012
- order = order.dup.unshift(head)
1013
- rescue
1014
- end
1015
- end
1016
- order.each do |iri|
1017
- When.CalendarEra(iri)
1018
- pool = _candidates(options, area, period, key, epoch, false) +
1019
- _candidates(options, area, period, key, reverse, true)
1020
- _compact(pool, parents)
1021
- return pool unless pool.size < count
1022
- end
1023
-
1024
- return []
1025
- end
1026
-
1027
- private
1028
-
1029
- # 候補の抽出
1030
- def _candidates(options, area, period, key, epoch, reverse)
1031
- regexp, key = key if key.kind_of?(Regexp)
1032
- return [] unless @_pool[area] &&
1033
- @_pool[area][period] &&
1034
- @_pool[area][period][key] &&
1035
- @_pool[area][period][key][epoch]
1036
- pool = @_pool[area][period][key][epoch].dup
1037
- pool.delete_if {|e| regexp !~ e.name} if regexp
1038
- return pool if options.empty?
1039
- pool.delete_if {|e| !_match(e, reverse, options) }
1040
- end
1041
-
1042
- def _match(epoch, reverse, options)
1043
- return false unless epoch.reverse? == reverse
1044
- target = {:reference_event => epoch.reference_event,
1045
- :reference_date => epoch.reference_date,
1046
- :julian_reference => epoch.julian_reference
1047
- }
1048
- options.each_pair do |k,v|
1049
- return false unless (v === (target.key?(k) ? target[k] : epoch.options[k]))
1050
- end
1051
- return true
1052
- end
1053
-
1054
- def _compact(pool, parents)
1055
- pool.uniq!
1056
- return if parents.empty?
1057
- pool.delete_if {|e|
1058
- !parents.each do |parent|
1059
- e = e.parent
1060
- break false unless parent == e.name
1061
- end
1062
- }
1063
- end
1064
- end
1065
-
1066
- # @private
1067
- HashProperty =
1068
- [:reference_event, :reference_date, :julian_reference, :dating_system, # :epoch_of_use
1069
- :epoch, :epoch_year, :options]
1070
-
1071
- # この暦年代を識別する名称
1072
- #
1073
- # Name by which this calendar era is known
1074
- #
1075
- # @return [String]
1076
- # 通常 String のサブクラスである When::BasicTypes::M17n を使用する。
1077
- #
1078
- alias :name :label
1079
-
1080
- # この暦年代の基点となる事象
1081
- #
1082
- # Event used as the datum for this calendar era
1083
- #
1084
- # @return [String]
1085
- # 通常 String のサブクラスである When::BasicTypes::M17n を使用する。
1086
- #
1087
- # @note
1088
- # [Accession,代始]:: 天皇・皇帝などの代始めの改元,必ずしも践祚と連動しない。
1089
- # :: JIS X7108 附属書D表3に参照事象の例として「新しい天皇の即位」とある。
1090
- # :: これは践祚を意味するので、岡田芳朗氏によれば適切ではないとのこと。
1091
- # :: 英語には適切な訳語がないと思われる。
1092
- # [FelicitousEvent,祥瑞]:: 祥瑞の発生に伴う改元
1093
- # [NaturalDisaster,災異]:: 災異の発生に伴う改元
1094
- # [InauspiciousYear,革年]:: 甲子革令・辛酉革命説による改元
1095
- # [Foundation,創業]:: 建国による元号の制定
1096
- # [CalendarReform,改暦]:: 改暦に伴う epoch_of_use の境界
1097
- #
1098
- attr_reader :reference_event
1099
- alias :referenceEvent :reference_event
1100
-
1101
- # この暦による参照事象の日付
1102
- #
1103
- # Date of the reference event in the calendar being described
1104
- #
1105
- # @return [When::TM::CalDate]
1106
- #
1107
- # @note
1108
- # 明治以前の改元は当該年の初めにに遡って適用された。しかし、日記などの資料は当然旧年号で
1109
- # 記載されている。このような場合、reference_date の分解能を「年」とする。
1110
- # 本ライブラリでは、reference_date の分解能が「年」の場合、When::TM::TemporalPosition._instance
1111
- # の options[:lower] で年号使用の下限を年初とするか、epoch_of_use の下限とするかを
1112
- # 指定することができる。
1113
- #
1114
- attr_reader :reference_date
1115
- alias :referenceDate :reference_date
1116
-
1117
- # 参照事象のユリウス日
1118
- #
1119
- # Julian date of the reference event
1120
- #
1121
- # @return [When::TM::JulianDate]
1122
- #
1123
- attr_reader :julian_reference
1124
- alias :julianReference :julian_reference
1125
-
1126
- # この暦年代が日付の基礎として使用する期間
1127
- #
1128
- # Period for which the era was used as a basis for dating
1129
- #
1130
- # @return [Array<When::TM::TemporalPosition>]
1131
- #
1132
- # @note
1133
- # 途中の改暦を指定するために要素が必要になることがあり、要素数が2を超えることがある。
1134
- # 最初の要素が When::TimeValue::MIN(-Infinity)の場合、年数を降順にカウントする。
1135
- # 暦年代の分解能を“日”より細かくすることはできない。
1136
- #
1137
- attr_reader :epoch
1138
-
1139
- # この暦年代と関連付けられた暦 (relation - Basis)
1140
- #
1141
- # The calendar associated with the calendar eras being described
1142
- #
1143
- # @return [Array<When::TM::Calendar>]
1144
- #
1145
- attr_reader :dating_system
1146
- alias :datingSystem :dating_system
1147
-
1148
- # この暦年代の元年の年番号(additional attribute)
1149
- #
1150
- # The year number of the calendar associated with the first year of this calendar era
1151
- #
1152
- # @return [Integer]
1153
- #
1154
- attr_reader :epoch_year
1155
- alias :epochYear :epoch_year
1156
-
1157
- # その他の属性 - additional attribute
1158
- #
1159
- # @return [Hash]
1160
- # [ 'area' => 暦年代の使用地域 [When::BasicTypes::M17n] ]
1161
- # [ 'period' => 暦年代の使用時代 [When::BasicTypes::M17n] ]
1162
- # [ the others => epoch_of_use の 'name' などの指定を反映 [String] ]
1163
- #
1164
- attr_reader :options
1165
-
1166
- # この暦年代が日付の基礎として使用する期間
1167
- #
1168
- # Period for which the era was used as a basis for dating
1169
- #
1170
- # @return [When::TM::Period]
1171
- #
1172
- # @note
1173
- # epoch_of_use メソッドの戻り値は ISO19108 When::TM::Period と規定されている。
1174
- # このため変数 @epoch とは別に、本メソッドを提供する。
1175
- #
1176
- def epoch_of_use
1177
- @epoch_of_use ||=
1178
- Period.new(*([0, -1].map {|i|
1179
- date = @epoch[i]
1180
- if date.kind_of?(CalDate)
1181
- options = date._attr
1182
- options[:frame] = options.delete(:clock)
1183
- date = JulianDate.universal_time(date.universal_time, options)
1184
- end
1185
- Instant.new(date) # See JIS X7108 5.3.2.2 e) When::TM::Period は直接 JulianDate を保持できない
1186
- }))
1187
- end
1188
- alias :epochOfUse :epoch_of_use
1189
-
1190
- # 年数の数え方
1191
- #
1192
- # @return [Boolean]
1193
- # [ true - 降順 (Before Common Era 方式)]
1194
- # [ false - 昇順 (Common Era 方式) ]
1195
- #
1196
- def reverse?
1197
- @epoch[0].indeterminated_position == TimeValue::Min
1198
- end
1199
-
1200
- # 未来永劫使用するか?
1201
- #
1202
- # @return [Boolean]
1203
- # [ true - する ]
1204
- # [ false - しない ]
1205
- #
1206
- def unlimited?
1207
- @epoch[-1].indeterminated_position == TimeValue::Max
1208
- end
1209
-
1210
- # 時間の歩度
1211
- #
1212
- # @return [Numeric]
1213
- #
1214
- def rate_of_clock
1215
- _typical_epoch.time_standard.rate_of_clock
1216
- end
1217
-
1218
- # 当該の暦年代の日付に変換する
1219
- #
1220
- # @param [When::TM::TemporalPosition] date
1221
- # @param [Hash] trans_options 以下の通り
1222
- # @option trans_options [Hash] :lower 暦年代適用の下限
1223
- # [ true - epoch_of_use の始め(省略時) ]
1224
- # [ :reference_date - 参照事象の日付 ]
1225
- # @option trans_options [Hash] :upper 暦年代適用の上限
1226
- # [ true - epoch_of_use の終わり(省略時) ]
1227
- # [ :reference_date - 参照事象の日付 ]
1228
- #
1229
- # @return [When::TM::TemporalPosition]
1230
- # [ When::TimeValue::Before - 当該の暦年代より前の日付である ]
1231
- # [ When::TimeValue::After - 当該の暦年代より後の日付である ]
1232
- # [ その他 - 当該の暦年代の日付に変換された When::TM::TemporalPosition ]
1233
- #
1234
- def trans(date, trans_options={})
1235
- # 当該日付の決定
1236
- date = Position.any_other(date, trans_options) unless date.kind_of?(Array)
1237
- epoch, cal_date =
1238
- case date
1239
- when Array ; _trans_array(date)
1240
- when JulianDate, DateAndTime ; _trans_date(date, date.clock)
1241
- when TimeValue ; _trans_date(date)
1242
- when Numeric ; _trans_date(JulianDate.universal_time((date-JulianDate::JD19700101)*Duration::DAY))
1243
- else ; raise TypeError, "Irregal Seed Date Type"
1244
- end
1245
- return epoch unless cal_date
1246
-
1247
- # 上下限の確認
1248
- trans_options ||= {}
1249
- return TimeValue::Before if cal_date.to_i < lower_bound(trans_options[:lower] || :reference_date)
1250
- return TimeValue::After if cal_date.to_i > upper_bound(trans_options[:upper] || true)
1251
-
1252
- # 発見した日時の属性設定
1253
- cal_date.calendar_era = self
1254
- cal_date.calendar_era_name = [@label, @epoch_year, reverse?, cal_date.to_i < @epoch[0].to_i]
1255
- cal_date.cal_date[0] -= @epoch_year
1256
- cal_date.trans = trans_options.dup
1257
- cal_date.query = epoch.query.dup
1258
- return cal_date
1259
- end
1260
-
1261
- # When::TM::TemporalPosition ^ When::TM::CalendarEra で呼ばれる
1262
- # @private
1263
- def ^(date, options={})
1264
- date = Position.any_other(date, options)
1265
- cal_date = trans(date, (date.trans||{}).merge(options))
1266
- return nil unless cal_date.kind_of?(CalDate)
1267
- return cal_date
1268
- end
1269
-
1270
- # other と @julian_reference とを比較する
1271
- #
1272
- # @param [Comparable] other 比較対象
1273
- #
1274
- # @return [Integer] 比較結果を 負, 0, 正の値で返す
1275
- #
1276
- def <=>(other) #TODO @precision は?
1277
- @julian_reference.universal_time <=> other.julian_reference.universal_time
1278
- end
1279
-
1280
- # 暦年代の下限
1281
- # @private
1282
- def lower_bound(type=nil)
1283
- @lower_bound[type] ||= @epoch[0].indeterminated_position == TimeValue::Min ? -Float::MAX :
1284
- @reference_date.precision == YEAR &&
1285
- @reference_date.cal_date[YEAR-1] == 0 ? @epoch[0].ceil(YEAR).to_i :
1286
- case type
1287
- when true ; @epoch[0].to_i
1288
- when :reference_date ; @reference_date.to_i
1289
- else ; [@reference_date.to_i, @epoch[0].to_i].min
1290
- end
1291
- end
1292
-
1293
- # 暦年代の上限
1294
- # @private
1295
- def upper_bound(type=nil)
1296
- @upper_bound[type] ||= @epoch[-1].indeterminated_position == TimeValue::Max ? +Float::MAX :
1297
- begin
1298
- next_era = succ
1299
- next_ref = next_era.reference_date if next_era.respond_to?(:reference_date)
1300
- next_ref &&
1301
- next_ref.precision == YEAR &&
1302
- next_ref.cal_date[YEAR-1] == 0 ? @epoch[-1].ceil(YEAR).to_i - 1 :
1303
- case type
1304
- when true ; @epoch[-1].to_i - 1
1305
- when :reference_date ; @reference_date.to_i
1306
- else ; [@reference_date.to_i, @epoch[-1].to_i - 1].max
1307
- end
1308
- end
1309
- end
1310
-
1311
- protected
1312
-
1313
- # @private
1314
- def _normalize_leaf_era
1315
- # r_date and others
1316
- case @reference_date
1317
- when String ; format, r_date, r_era = When::BasicTypes::DateTime._to_array(@reference_date)
1318
- when Array ; r_era, *r_date = @reference_date
1319
- when CalDate ; r_era, r_date = @reference_date.calendar_era_name, @reference_date.cal_date
1320
- when nil ;
1321
- else ; raise TypeError, "ReferenceDate is invalid type"
1322
- end
1323
- r_era, r_year = r_era
1324
-
1325
- epochs = @epoch.dup
1326
- epochs.shift if (epochs[0].indeterminated_position == TimeValue::Min)
1327
- # j_date and calculated reference_date !((julian_reference == nil) && (reference_date != nil))
1328
- if @julian_reference
1329
- jdn = @julian_reference.to_i
1330
- epoch = epochs[0]
1331
- epochs.each do |e|
1332
- if (e.indeterminated_position == TimeValue::Max)
1333
- epoch = e
1334
- break
1335
- elsif (jdn < e.to_i)
1336
- break
1337
- else
1338
- epoch = e
1339
- end
1340
- end
1341
- @reference_date = epoch.frame.jul_trans(When.when?(jdn), {:frame=>epoch.frame})
1342
- j_date = @reference_date.cal_date
1343
- elsif (@reference_date == nil)
1344
- @reference_date = epochs[0].dup
1345
- j_date = @reference_date.cal_date
1346
- end
1347
-
1348
- # epoch_year
1349
- @epoch_year ||= r_year
1350
- @epoch_year = @epoch_year.to_i if (@epoch_year)
1351
- raise ArgumentError, "EpochYear mismatch" unless (@epoch_year == r_year)
1352
- unless @epoch_year
1353
- raise ArgumentError, "ReferenceDate is absent" unless r_date
1354
- @epoch_year = epochs[0].cal_date[0] * 1 - r_date[0] * 1
1355
- end
1356
-
1357
- # j_date and calculated reference_date ((julian_reference == nil) && (reference_date != nil))
1358
- unless j_date
1359
- j_date = [+r_date[0]+@epoch_year, *r_date[1..-1]]
1360
- epochs.each_index do |i|
1361
- e = epochs[i]
1362
- d = CalDate.new(j_date,{:frame=>e.frame})
1363
- if (e.indeterminated_position == TimeValue::Max)
1364
- @reference_date = d
1365
- break
1366
- elsif (d.to_i < e.to_i)
1367
- @reference_date = d if (i==0)
1368
- break
1369
- else
1370
- @reference_date = d
1371
- end
1372
- end
1373
- end
1374
-
1375
- # julian_reference and reference_date
1376
- @julian_reference = JulianDate.universal_time(@reference_date.universal_time)
1377
- @reference_date.cal_date[0] -= @epoch_year
1378
- @reference_date.calendar_era_name = [(r_era ? r_era : @label), @epoch_year]
1379
- if (r_date)
1380
- raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (@epoch_year == +j_date[0]-(+r_date[0]))
1381
- raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (j_date[1..-1] == r_date[1..-1])
1382
- #raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (j_date == r_date)
1383
- if (r_date[1] == nil)
1384
- @reference_date.precision = YEAR
1385
- elsif (r_date[2] == nil)
1386
- @reference_date.precision = MONTH
1387
- end
1388
- end
1389
- end
1390
-
1391
- # @private
1392
- def _register_calendar_era
1393
- return unless @label.kind_of?(When::BasicTypes::M17n)
1394
-
1395
- # dating_system
1396
- @dating_system = (@epoch.map {|e| e.frame}).compact.uniq
1397
- ancestors = hierarchy.inject(['']) {|list,era|
1398
- list << list[-1] + '::' + era.label.to_s
1399
- list
1400
- }
1401
-
1402
- if @epoch.length == 1
1403
- epoch[0].frame.synchronize {
1404
- range = When::Parts::GeometricComplex.new([[epoch[0],true]])
1405
- ancestors.each do |ancestor|
1406
- epoch[0].frame.domain[ancestor] |= range
1407
- end
1408
- } if epoch[0].frame
1409
- elsif reverse?
1410
- epoch[1].frame.synchronize {
1411
- range = When::Parts::GeometricComplex.new([[epoch[1],true]], true)
1412
- ancestors.each do |ancestor|
1413
- epoch[1].frame.domain[ancestor] |= range
1414
- end
1415
- } if epoch[1].frame
1416
- else
1417
- (epoch.length-1).times do |i|
1418
- epoch[i].frame.synchronize {
1419
- range = When::Parts::GeometricComplex.new([[epoch[i],true], [epoch[i+1],false]])
1420
- ancestors.each do |ancestor|
1421
- epoch[i].frame.domain[ancestor] |= range
1422
- end
1423
- } if epoch[i].frame
1424
- end
1425
- end
1426
-
1427
- @dating_system.each do |f|
1428
- f.synchronize do
1429
- f.reference_frame << self
1430
- f.reference_frame.uniq!
1431
- f.reference_frame.sort!
1432
- first = f.domain[''].first(When::MinusInfinity)
1433
- last = f.domain[''].last(When::PlusInfinity)
1434
- f.domain_of_validity = When::EX::Extent.new(
1435
- When::TM::Period.new(
1436
- When::TM::Instant.new(first),
1437
- When::TM::Instant.new(last))) if first && first <= last
1438
- end
1439
- end
1440
-
1441
- # インデクス登録
1442
- ((@area||{}).values + [nil]).each do |a|
1443
- self.class[a] ||= {}
1444
- ((@period||{}).values + [nil]).each do |p|
1445
- self.class[a][p] ||= {}
1446
- (@label.values + [nil]).each do |k|
1447
- self.class[a][p][k] ||= {}
1448
- [@epoch_year, nil].each do |e|
1449
- self.class[a][p][k][e] ||= []
1450
- self.class[a][p][k][e] << self
1451
- end
1452
- end
1453
- end
1454
- end
1455
- end
1456
-
1457
- private
1458
-
1459
- # オブジェクトの正規化
1460
- def _normalize(args=[], options={})
1461
- # area, period
1462
- term_options = {:namespace=>@namespace, :locale=>@locale, :options=>options}
1463
- @area = m17n(@area, nil, nil, term_options) if (@area.instance_of?(String))
1464
- @period = m17n(@period, nil, nil, term_options) if (@period.instance_of?(String))
1465
-
1466
- # 暦年代の上下限
1467
- @lower_bound = {}
1468
- @upper_bound = {}
1469
-
1470
- # other attributes
1471
- if (@child && @child.length>0)
1472
- _non_leaf_era(args, term_options)
1473
- _register_calendar_era unless _pool['..'].kind_of?(CalendarEra)
1474
- @child.each do |era|
1475
- era._register_calendar_era
1476
- end
1477
- else
1478
- _set_leaf_era(args, term_options)
1479
- _normalize_leaf_era unless @epoch[0].indeterminated_position == TimeValue::Min
1480
- end
1481
- end
1482
-
1483
- def _non_leaf_era(args, term_options)
1484
- @label = m17n(@label, nil, nil, term_options) if @label.instance_of?(String)
1485
- if @label
1486
- @label._pool['..'] = self # TODO ここを ||= にすると leaf? の判定がおかしくなる
1487
- @_pool[@label.to_s] = @label
1488
- end
1489
-
1490
- # reference_date, reference_event & label
1491
- @reference_date = @child[0].reference_date
1492
- @reference_event = @child[0].reference_event
1493
- @epoch_year = @child[0].epoch_year
1494
- @julian_reference = @child[0].julian_reference
1495
-
1496
- # options
1497
- @options ||= {}
1498
- @options.update({'area'=>@area, 'period'=>@period})
1499
-
1500
- # epoch
1501
- @epoch = []
1502
- @child.each do |c|
1503
- c.epoch.each do |e|
1504
- @epoch << e unless e == @epoch[-1]
1505
- end
1506
- end
1507
- end
1508
-
1509
- def _set_leaf_era(args, term_options)
1510
- # 変数の読み込み
1511
- reference_date, reference_event, *epochs = args
1512
-
1513
- # reference_date & label
1514
- if reference_date
1515
- @reference_date = reference_date
1516
- @label = m17n(@reference_date[/^\[[^\]]+\]|^[^-+0-9]+/], nil, nil, term_options)
1517
- end
1518
- @label._pool['..'] ||= self
1519
- @_pool[@label.to_s] = @label
1520
-
1521
- # reference_event
1522
- @reference_event = reference_event if reference_event
1523
- @reference_event ||= ""
1524
- @reference_event = DefaultEvents[@reference_event] if DefaultEvents[@reference_event]
1525
- @reference_event = m17n(@reference_event, nil, nil, term_options) if @reference_event.instance_of?(String)
1526
- @reference_event._pool['..'] ||= self
1527
- @_pool[@reference_event.to_s] = @reference_event
1528
-
1529
- # options
1530
- term_options[:options][:query] ||= {}
1531
- @options ||= {}
1532
- @options.update(term_options[:options][:query])
1533
- @options.update({'area'=>@area, 'period'=>@period})
1534
-
1535
- # epoch
1536
- @epoch ||= epochs
1537
- @epoch[0] ||= '-Infinity'
1538
- @epoch[1] ||= '+Infinity'
1539
- term_options[:options][:frame] ||= When::Gregorian
1540
- epoch = ''
1541
- @epoch = @epoch.map {|e|
1542
- case e
1543
- when ''
1544
- When.when?('+Infinity')
1545
- when String
1546
- d, f = e.split(/\^{1,2}/)
1547
- term_options[:options][:frame] = When.Resource(f, '_c:') if (f)
1548
- d.split(/;/).each do |v|
1549
- v.strip!
1550
- if (v =~ /^[-+\d]|^Now$|^Unknown$|^[-+]Infinity$/i)
1551
- epoch = v
1552
- break
1553
- end
1554
- k, c = v.split(/\s*[:=]\s*/, 2)
1555
- if (c.kind_of?(String))
1556
- code = m17n(c.gsub(/%2C/,','), nil, nil, term_options)
1557
- @_pool[code.to_s] ||= code
1558
- @options[k] = term_options[:options][:query][k] = @_pool[code.to_s]
1559
- else
1560
- @options.delete(k)
1561
- term_options[:options][:query].delete(k)
1562
- end
1563
- end
1564
- When.when?(epoch, @options.merge({:frame=>term_options[:options][:frame], :validate=>:epoch}))
1565
- else
1566
- e
1567
- end
1568
- }
1569
-
1570
- # normalize for last era
1571
- last_era = _pool['..'].child[-1] if _pool['..'].kind_of?(CalendarEra)
1572
- if last_era
1573
- if last_era.epoch[0].indeterminated_position == TimeValue::Min
1574
- last_era.epoch[0].frame = @epoch[0].frame
1575
- last_era.epoch[1] = @epoch[0]
1576
- last_era._normalize_leaf_era
1577
- elsif last_era.epoch[-1].indeterminated_position == TimeValue::Max
1578
- last_era.epoch[-1] = @epoch[0]
1579
- end
1580
- end
1581
- end
1582
-
1583
- # 配下のオブジェクトの前後関係の設定
1584
- def _sequence
1585
- return unless @child
1586
- prev = nil
1587
- if @_pool['..'].respond_to?(:child) && @_pool['..'].child
1588
- (@_pool['..'].child.length-1).downto(0) do |i|
1589
- v = @_pool['..'].child[i]
1590
- next if (v.epoch[0]>=self.epoch[0])
1591
- prev = v
1592
- break
1593
- end
1594
- end
1595
- @child.each do |v|
1596
- if prev
1597
- v._pool['.<-'] = prev
1598
- prev._pool['.->'] ||= v
1599
- while (prev.child && prev.child[-1]) do
1600
- prev = prev.child[-1]
1601
- prev._pool['.->'] ||= v
1602
- end
1603
- end
1604
- @_pool[v.label.to_s] = v
1605
- prev = v
1606
- end
1607
- end
1608
-
1609
- def _trans_array(date)
1610
- date = date.dup
1611
- cal_date = frame = nil
1612
- epoch = @epoch[0]
1613
- @epoch.each do |e|
1614
- cal_date = CalDate.new(date,{:frame=>e.frame}) unless (frame == e.frame)
1615
- frame = e.frame
1616
- break if (e.indeterminated_position == TimeValue::Max)
1617
- break if (cal_date < e)
1618
- epoch = e
1619
- end
1620
- cal_date = CalDate.new(date, {:frame=>epoch.frame}) unless (frame == epoch.frame)
1621
- return epoch, cal_date
1622
- end
1623
-
1624
- def _trans_date(date, clock=nil)
1625
- unless rate_of_clock == date.time_standard.rate_of_clock
1626
- date = JulianDate.dynamical_time(date.dynamical_time, {:time_standard=>_typical_epoch.time_standard})
1627
- clock = _typical_epoch.clock
1628
- end
1629
- frac = (clock) ? clock.universal_time : 0
1630
- sdn, time = (date.universal_time - frac).divmod(Duration::DAY)
1631
- sdn += JulianDate::JD19700101
1632
- epoch = @epoch[0]
1633
- return TimeValue::Before if sdn < lower_bound
1634
- return TimeValue::After if sdn > upper_bound
1635
- @epoch.each do |e|
1636
- break if (e.indeterminated_position == When::TimeValue::Max)
1637
- break if (sdn < e.to_i)
1638
- epoch = e
1639
- end
1640
- frame = epoch.frame
1641
- cal_date = frame.to_cal_date(sdn)
1642
- return epoch, CalDate.new(cal_date, {:frame=>frame}) unless clock
1643
- return epoch, DateAndTime.new(cal_date, time+frac, {:frame=>frame, :clock=>clock})
1644
- end
1645
-
1646
- # 代表的な元期
1647
- #
1648
- # @return [When::TM::TemporalPosition]
1649
- #
1650
- def _typical_epoch
1651
- @_typical_epoch ||= reverse? ? @epoch[-1] : @epoch[0]
1652
- end
1653
-
1654
- # その他のメソッド
1655
- #
1656
- # @note
1657
- # When::TM::CalendarEra で定義されていないメソッドは
1658
- # 処理を @reference_date (type: When::TM::TemporalPosition) に委譲する
1659
- #
1660
- def method_missing(name, *args, &block)
1661
- self.class.module_eval %Q{
1662
- def #{name}(*args, &block)
1663
- @reference_date.send("#{name}", *args, &block)
1664
- end
1665
- } unless When::Parts::MethodCash.escape(name)
1666
- @reference_date.send(name, *args, &block)
1667
- end
1668
- end
1669
- 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
+ #
9
+ # ISO 19108 Geographic information - Temporal schema
10
+ #
11
+ module When::TM
12
+ #
13
+ # (5.3) Temporal Reference System Package
14
+ #
15
+
16
+ # 時間参照系
17
+ #
18
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#AbstractTimeReferenceSystemType gml schema}
19
+ #
20
+ class ReferenceSystem < When::TM::Object
21
+
22
+ # この時間参照系が使用する空間と時間の制限(実装は時間のみ)
23
+ #
24
+ # Limits of space and time within which the temporal reference system is use
25
+ #
26
+ # @return [When::EX::Extent]
27
+ #
28
+ # @note
29
+ # domain の最小・最大から範囲を決定して domain_of_validity としている
30
+ # domain_of_validity は ISO19108との互換性を確保するため提供しているが、
31
+ # 有効期間が複数に断片化していることがあるので、より正確な情報を含む
32
+ # domain の使用を推奨する
33
+ #
34
+ # マルチスレッド動作時 CalendarEra の生成で Calendar の本属性が更新される
35
+ # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
36
+ #
37
+ attr_accessor :domain_of_validity
38
+ alias :domainOfValidity :domain_of_validity
39
+
40
+ # この時間参照系と関連付けられた時間位置 (relation - Reference)
41
+ #
42
+ # The temporal position associated with the time reference system being described
43
+ #
44
+ # @return [Array<When::TM::(Temporal)Position>]
45
+ #
46
+ attr_reader :position
47
+
48
+ # この時間参照系が使用する時間の範囲
49
+ #
50
+ # Range of time within which the temporal reference system is use
51
+ #
52
+ # @return [Hash<String=>When::Parts::GeomerticComplex>]
53
+ #
54
+ # @note
55
+ # マルチスレッド動作時 CalendarEra の生成で Calendar の本属性が更新される
56
+ # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
57
+ #
58
+ def domain
59
+ @domain ||= Hash.new {|hash, key| hash[key] = When::Parts::GeometricComplex.new([])}
60
+ end
61
+
62
+ # 時間参照系を識別する名称
63
+ #
64
+ # Name by which the temporal reference system is known
65
+ #
66
+ # @return [When::RS::Identifier]
67
+ #
68
+ def name
69
+ @name ||= When::RS::Identifier.new(label, @version, @remarks)
70
+ end
71
+ end
72
+
73
+ # 順序時間参照系
74
+ #
75
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeOrdinalReferenceSystemType gml schema}
76
+ #
77
+ class OrdinalReferenceSystem < ReferenceSystem
78
+
79
+ # この順序時間参照系の最上位レベルを構成する順序年代 (relation - Structure)
80
+ #
81
+ # Ordinal eras that make up the highest level of this ordinal reference system
82
+ #
83
+ # @return [Array<When::TM::OrdinalEra>]
84
+ #
85
+ alias :component :child
86
+
87
+ private
88
+
89
+ # オブジェクトの正規化
90
+ def _normalize(args=[], options={})
91
+ end
92
+ end
93
+
94
+ # 暦
95
+ #
96
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCalendarType gml schema}
97
+ #
98
+ class Calendar < ReferenceSystem
99
+
100
+ include When::Coordinates
101
+ include Spatial::Normalize
102
+ include Temporal
103
+ include When::TimeStandard::TimeBasis
104
+
105
+ # 初期化
106
+ #
107
+ # @return [void]
108
+ #
109
+ # @note
110
+ # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
111
+ # このため、本メソッド自体はスレッドセーフでない。
112
+ #
113
+ def self._setup_
114
+ @_lock_ = Mutex.new if When.multi_thread
115
+ @_pool = {}
116
+ end
117
+
118
+ # この暦と関連付けられた暦年代 (relation - Basis)
119
+ #
120
+ # The calendar eras associated with the calendar being described
121
+ #
122
+ # @return [Array<When::TM::CalendarEra>]
123
+ #
124
+ # @note
125
+ # マルチスレッド動作時 CalendarEra の生成で 本属性が更新される
126
+ # 参照・更新処理は synchronize { ... } の ... の部分に書く必要がある
127
+ #
128
+ attr_reader :reference_frame
129
+ alias :referenceFrame :reference_frame
130
+
131
+ # 一暦日の中の時間位置を定めるために、この暦とともに使用する時計 (relation - Resolution)
132
+ #
133
+ # The clock that is used with this calendar to define
134
+ # temporal position within a calendar day
135
+ #
136
+ # @return [Array<When::TM::Clock>]
137
+ #
138
+ attr_reader :time_basis
139
+ alias :timeBasis :time_basis
140
+
141
+ # 時刻制 - additional attribute
142
+ #
143
+ # @return [When::TimeStandard, nil]
144
+ #
145
+ def time_standard
146
+ @time_basis ? @time_basis.time_standard : nil
147
+ end
148
+
149
+ # 時間の歩度
150
+ #
151
+ # @return [Numeric]
152
+ #
153
+ def rate_of_clock
154
+ @time_basis ? @time_basis.time_standard.rate_of_clock : 1.0
155
+ end
156
+
157
+ # 日付と時刻をユリウス日(When::TM::JulianDate)に変換する
158
+ #
159
+ # @param [When::TM::CalDate] cal_date
160
+ # @param [When::TM::ClockTime] time
161
+ #
162
+ # @return [When::TM::JulianDate]
163
+ #
164
+ def date_trans(cal_date, time=nil, options={})
165
+ time = cal_date.clk_time if ((time == nil) && cal_date.kind_of?(DateAndTime))
166
+ frac = (time) ? time.universal_time : 0.0
167
+ jdn = to_julian_date(cal_date.cal_date)
168
+ return JulianDate.universal_time((jdn - JulianDate::JD19700101) * Duration::DAY + frac, options)
169
+ end
170
+ alias :dateTrans :date_trans
171
+
172
+ # ユリウス日(When::TM::JulianDate)を日付に変換する
173
+ #
174
+ # @param [When::TM::JulianDate] jdt
175
+ # @param [Hash] options see {When::TM::TemporalPosition._instance}
176
+ #
177
+ # @return [When::TM::CalDate] if (options[:clock or :tz] == nil)
178
+ # @return [When::TM::CalDateAndTime] if (options[:clock or :tz] != nil)
179
+ #
180
+ def jul_trans(jdt, options={})
181
+ options = TemporalPosition._options(options)
182
+ unless jdt.kind_of?(When::TimeValue)
183
+ options[:clock] ||= @time_basis unless rate_of_clock == 1.0
184
+ jdt = JulianDate.new(jdt, options)
185
+ end
186
+ cal_options = jdt._attr
187
+ cal_options.delete(:era_name)
188
+ cal_options.delete(:era)
189
+ unless rate_of_clock == jdt.time_standard.rate_of_clock
190
+ cal_options.delete(:time_standard)
191
+ cal_options[:clock] = @time_basis || When::UTC
192
+ jdt = JulianDate.dynamical_time(jdt.dynamical_time, {:time_standard=>time_standard})
193
+ end
194
+
195
+ cal_options.update(options)
196
+ cal_options[:frame] = self
197
+ clock = Clock.get_clock_option(cal_options) || jdt.clock
198
+
199
+ if clock
200
+ clock = When.Clock(clock) if clock.kind_of?(String)
201
+ clock = clock._daylight(jdt.universal_time) if clock._need_validate
202
+ frac = clock.universal_time
203
+ sdn, time = (jdt.universal_time - frac).divmod(Duration::DAY)
204
+ cal_options[:clock] = clock
205
+ cal_date = to_cal_date(sdn.to_i + JulianDate::JD19700101)
206
+ cal_date[0] -= cal_options[:era_name][1] if cal_options[:era_name]
207
+ DateAndTime.new(cal_date, time+frac, cal_options)
208
+ else
209
+ cal_date = to_cal_date(jdt.to_i)
210
+ cal_date[0] -= cal_options[:era_name][1] if cal_options[:era_name]
211
+ CalDate.new(cal_date, cal_options)
212
+ end
213
+ end
214
+ alias :julTrans :jul_trans
215
+ alias :^ :jul_trans
216
+
217
+ # ユリウス日(Numeric)を日付に変換する
218
+ #
219
+ # @param [Integer] jdn
220
+ #
221
+ # @return [Array<Numeric>]
222
+ #
223
+ def to_cal_date(jdn)
224
+ _encode(_number_to_coordinates(jdn))
225
+ end
226
+
227
+ # 日付をユリウス日(Numeric)に変換する
228
+ #
229
+ # @param [Array<Numeric>] cal_date
230
+ #
231
+ # @return [Integer] JulianDate
232
+ #
233
+ def to_julian_date(cal_date)
234
+ date = _decode(cal_date)
235
+ date[0] = +date[0]
236
+ _coordinates_to_number(*date)
237
+ end
238
+
239
+ # 日付・時刻をUniversal Time(Numeric)に変換する
240
+ #
241
+ # @param [Array<Numeric>] cal_date 日付
242
+ # @param [Array<Numeric>] clk_time 時刻
243
+ # @param [When::TM::Clock] clock 時法
244
+ #
245
+ # @return [Numeric] Universal Time
246
+ #
247
+ def to_universal_time(cal_date, clk_time, clock=When::UTC)
248
+ time = clk_time.dup
249
+ time[0] += _coordinates_to_number(*_decode(cal_date))
250
+ clock.to_universal_time(time) - When::TM::JulianDate::JD19700101 * When::TM::Duration::DAY
251
+ end
252
+
253
+ # 月初の通日
254
+ #
255
+ # @param [Integer] m 通月
256
+ #
257
+ # @return [Numeric] 月初の通日
258
+ #
259
+ def _new_month_(m)
260
+ date = @base.map {|d| d||0}
261
+ if @indices[-2].unit
262
+ date[-2] += m
263
+ _coordinates_to_number(*_decode(date))
264
+ else
265
+ d0 = _coordinates_to_number(*_decode(date))
266
+ date[-3] += (m * @mean_month / @mean_year).floor - 1
267
+ d1 = _coordinates_to_number(*_decode(date))
268
+ date[-2] += m - ((d1 - d0) / @mean_month + 0.5).floor
269
+ _coordinates_to_number(*_decode(date))
270
+ end
271
+ end
272
+
273
+ # 通日 - > [通月,月内日数,月の日数]
274
+ #
275
+ # @param [Integer] sdn 通日
276
+ #
277
+ # @return [Array<Numeric>] ( m, d, n )
278
+ # [ m - 通月]
279
+ # [ d - 月内の日数 (0 始まり)]
280
+ # [ n - 月の日数]
281
+ #
282
+ def _to_month_number_(sdn)
283
+ Residue.mod(sdn) {|m| _new_month(m)}
284
+ end
285
+
286
+ private
287
+
288
+ # オブジェクトの正規化
289
+ def _normalize(args=[], options={})
290
+ @indices ||= DefaultDateIndices
291
+ _normalize_spatial
292
+ _normalize_time_basis
293
+ _default_index_of_MSC
294
+ _normalize_temporal
295
+ @reference_frame ||= []
296
+ end
297
+ end
298
+
299
+ # 時計
300
+ #
301
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeClockType gml schema}
302
+ #
303
+ class Clock < ReferenceSystem
304
+
305
+ include When::Coordinates
306
+ include When::Parts::Timezone::Base
307
+ include Spatial::Normalize
308
+ include Temporal
309
+
310
+ class << self
311
+ include When::Parts::Resource::Pool
312
+
313
+ # When::TM::Clock Class のグローバルな設定を行う
314
+ #
315
+ # @param [When::Parts::Timezone::Base, String] local 地方時を使用する場合、指定する
316
+ #
317
+ # @return [void]
318
+ #
319
+ # @note
320
+ # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
321
+ # このため、本メソッド自体はスレッドセーフでない。
322
+ #
323
+ def _setup_(local=nil)
324
+ @_lock_ = Mutex.new if When.multi_thread
325
+ @_pool = {}
326
+ @local_time = local
327
+ end
328
+
329
+ # 設定情報を取得する
330
+ #
331
+ # @return [Hash] 設定情報
332
+ #
333
+ def _setup_info
334
+ {:local => _local_time}
335
+ end
336
+
337
+ # 地方時
338
+ #
339
+ # @param [When::Parts::Timezone::Base, String] local 地方時
340
+ #
341
+ # @return [When::Parts::Timezone::Base, String]
342
+ #
343
+ # @note
344
+ # @local_timeは、原則、ライブラリ立ち上げ時に _setup_ で初期化する。
345
+ # 以降、@local_timeに代入を行っても、すでに生成した When::TM::TemporalPosition 等には反映されない。
346
+ #
347
+ def local_time=(local)
348
+ if @_pool
349
+ @local_time = local
350
+ else
351
+ _setup_(local)
352
+ end
353
+ end
354
+
355
+ # When::TM::Clock のローカルタイムを読みだす
356
+ #
357
+ # @return [When::Parts::Timezone::Base]
358
+ #
359
+ def local_time
360
+ _local_time[1]
361
+ end
362
+
363
+ # When::TM::Clock のローカルタイムが設定されているか?
364
+ #
365
+ # @return [true, false]
366
+ #
367
+ def is_local_time_set?
368
+ _local_time[0]
369
+ end
370
+
371
+ # 共通処理
372
+ def _local_time
373
+ case @local_time
374
+ when Array ; @local_time
375
+ when nil ; @local_time = [false, When::UTC]
376
+ when String ; @local_time = [true, When::Parts::Timezone[@local_time] ||
377
+ When::V::Timezone[@local_time] ||
378
+ When.Clock(@local_time)]
379
+ else ; @local_time = [true, @local_time]
380
+ end
381
+ end
382
+ private :_local_time
383
+
384
+ # @private
385
+ def get_clock(options)
386
+ get_clock_option(options) || local_time
387
+ end
388
+
389
+ # @private
390
+ def get_clock_option(options)
391
+ clock = options.delete(:clock)
392
+ tz = options.delete(:tz)
393
+ tz ? (When::V::Timezone[tz] || When::Parts::Timezone[tz]) : clock
394
+ end
395
+
396
+ # @private
397
+ def to_hms(hms, extended=true)
398
+ case hms
399
+ when Numeric
400
+ sgn = (hms >= 0) ? '+' : '-'
401
+ hh, mm = hms.abs.divmod(3600)
402
+ mm, ss = mm.divmod(60)
403
+ ss, ff = ss.divmod(1)
404
+ ff = (ff == 0 || When::STRING <= When::SECOND) ? '' :
405
+ ("%.#{When::STRING - When::SECOND}f" % ff)[1..-1]
406
+ ss = (ss == 0 && ff == '') ? '' : ("%02d" % ss)
407
+ mm = "%02d" % mm
408
+ hh = "%02d" % hh
409
+ when /\A([-+])(\d{2})([:=*])?(\d{2})?([:=*])?(\d{2})?(\.\d+)?\z/
410
+ sgn, hh, d1, mm, d2, ss, ff = $~[1..7]
411
+ ff ||= ''
412
+ ss ||= ''
413
+ mm ||= ''
414
+ else
415
+ return nil
416
+ end
417
+
418
+ if (extended)
419
+ d1 ||= (mm=='') ? '' : ':'
420
+ d2 ||= (ss=='') ? '' : ':'
421
+ else
422
+ d1 = ''
423
+ d2 = ''
424
+ end
425
+ sgn + hh + d1 + mm + d2 + ss + ff
426
+ end
427
+ end
428
+
429
+ # この時法の基点となる事象
430
+ #
431
+ # Event used as the datum for this clock
432
+ #
433
+ # @return [String]
434
+ #
435
+ # @note
436
+ # new の options 引数に :reference_event があれば設定される。
437
+ # ライブラリとしては本変数を参照していない。下記の振る舞いを String で説明するため用いてもよい。
438
+ #
439
+ # @note
440
+ # 日付の境界が午前0時でない場合、When::Coordinates::Temporal.border により、境界が指定される。
441
+ # border, behavior メソッドをオーバーライドすることで、日の出、日の入りなど event 時刻が一定
442
+ # しない場合にも対応する。
443
+ #
444
+ attr_reader :reference_event
445
+ alias :referenceEvent :reference_event
446
+
447
+ # この時法による参照事象の時刻
448
+ #
449
+ # Time of the reference event for this clock
450
+ #
451
+ # @return [When::TM::ClockTime]
452
+ #
453
+ attr_reader :reference_time
454
+ alias :referenceTime :reference_time
455
+
456
+ # UTCによる参照事象の時刻
457
+ #
458
+ # UTC time of the reference event
459
+ #
460
+ # @return [When::TM::ClockTime]
461
+ #
462
+ attr_reader :utc_reference
463
+ alias :utcReference :utc_reference
464
+
465
+ # 一暦日の中の時間位置を定めるために、この時計とともに使用する暦 (relation - Resolution)
466
+ #
467
+ # The calendar that is used with this clock to define
468
+ # temporal position within a calendar day
469
+ #
470
+ # @return [Array<When::TM::Calendar>]
471
+ #
472
+ attr_reader :date_basis
473
+ alias :dateBasis :date_basis
474
+
475
+ # 時刻制 - additional attribute
476
+ #
477
+ # @return [When::TimeStandard]
478
+ #
479
+ attr_reader :time_standard
480
+
481
+ # この時法を生成した時間帯プロパティ - additional attribute
482
+ #
483
+ # @return [When::V::TimezoneProperty]
484
+ #
485
+ # @note
486
+ # When::TM::TemporalPosition に対して加減算を行うと、時間帯が変わる可能性がある。
487
+ # 本変数により、時間帯決定ルール(When::V::TimezoneProperty#rrule)を参照する。
488
+ #
489
+ attr_accessor :tz_prop
490
+ alias :tzProp :tz_prop
491
+
492
+ # universal_timeとこの時法の最小単位との比 - additional attribute
493
+ #
494
+ # @return [Numeric]
495
+ #
496
+ attr_reader :second
497
+
498
+ # この時法のUTCとの差(ISO 8601 extended format) - additional attribute
499
+ #
500
+ # @return [String] (±hh:mm)
501
+ #
502
+ attr_reader :zone
503
+ alias :to_extended :zone
504
+
505
+ # 時間の歩度
506
+ #
507
+ # @return [Numeric]
508
+ #
509
+ def rate_of_clock
510
+ @time_standard.rate_of_clock
511
+ end
512
+
513
+ # 128秒単位の実数による参照事象の時刻
514
+ #
515
+ # Fraction time of the reference event
516
+ #
517
+ # @param [Integer] sdn 参照事象の通し番号(ダミー)
518
+ #
519
+ # @return [Numeric]
520
+ #
521
+ # T00:00:00Z からの参照事象の経過時間 / 128秒
522
+ #
523
+ def universal_time(sdn=nil)
524
+ return @utc_reference.universal_time
525
+ end
526
+
527
+ # この時法の時刻をUTC時刻に変換する
528
+ #
529
+ # @param [When::TM::ClockTime] u_time
530
+ #
531
+ # @return [When::TM::ClockTime]
532
+ #
533
+ def utc_trans(u_time)
534
+ return When::UTC.to_clk_time(self.to_universal_time(u_time.clk_time))
535
+ end
536
+ alias :utcTrans :utc_trans
537
+
538
+ # UTC時刻をこの時法の時刻に変換する
539
+ #
540
+ # @param [When::TM::ClockTime] clk_time
541
+ #
542
+ # @return [When::TM::ClockTime]
543
+ #
544
+ def clk_trans(clk_time)
545
+ return self.to_clk_time(When::UTC.to_universal_time(u_time.clk_time))
546
+ end
547
+ alias :clkTrans :clk_trans
548
+
549
+ # この時法の時刻を128秒単位の実数に変換する
550
+ #
551
+ # @param [Array<Numeric>] clk_time
552
+ #
553
+ # @return [Numeric]
554
+ #
555
+ def to_universal_time(clk_time)
556
+ return _coordinates_to_number(_decode(clk_time)) / @second
557
+ end
558
+
559
+ # 128秒単位の実数をこの時法の時刻に変換する
560
+ #
561
+ # @param [Numeric] fod
562
+ #
563
+ # @return [When::TM::ClockTime]
564
+ #
565
+ def to_clk_time(fod, options={})
566
+ options[:frame] = self
567
+ fod, second = fod.trunk, fod.branch / fod.second if fod.kind_of?(When::Coordinates::LeapSeconds)
568
+ clk_time = ClockTime.new(_encode(_number_to_coordinates(fod * @second)), options)
569
+ return clk_time if (second||0) == 0
570
+ clk_time.clk_time[-1] += second
571
+ return clk_time
572
+ end
573
+
574
+ # 時刻をNumeric(serial time)に変換する
575
+ #
576
+ # @param [Numeric] clk_time
577
+ #
578
+ # @return [Numeric] serial time
579
+ #
580
+ def _coordinates_to_number(clk_time)
581
+ u = 1
582
+ s = 0
583
+ (@base.length-1).downto(1) do |i|
584
+ s += u * (+clk_time[i] - @base[i]) if (clk_time[i])
585
+ u *= @unit[i]
586
+ end
587
+ return s + u * (+clk_time[0]) + @origin_of_LSC
588
+ end
589
+
590
+ # Numeric(serial time)を時刻に変換する
591
+ #
592
+ # @param [Numeric] serial_time
593
+ #
594
+ # @return [Numeric]
595
+ #
596
+ def _number_to_coordinates(serial_time)
597
+ time = [serial_time-@origin_of_LSC]
598
+ (@base.length-1).downto(1) do |i|
599
+ carry, time[0] = (+time[0]).divmod(@unit[i])
600
+ time[0] += @base[i]
601
+ time.unshift(carry)
602
+ end
603
+ return time
604
+ end
605
+
606
+ #
607
+ # _m17n_form のための要素生成
608
+ #
609
+ # @param [Hash] options 下記のとおり
610
+ # @option options [Symbol] :method :to_m17n なら時間帯名を返す、その他は When::Parts::Resource#to_h 参照
611
+ #
612
+ # @private
613
+ def _to_hash_value(options={})
614
+ options[:method] == :to_m17n ? tzname(:hash)[0] : super
615
+ end
616
+
617
+ # この時法のUTCとの差(ISO 8601 basic format)
618
+ #
619
+ # @return [String] (±hhmm)
620
+ #
621
+ def to_basic
622
+ return '' unless @zone
623
+ @zone.gsub(/:/, '')
624
+ end
625
+
626
+ # 期間オブジェクトの桁数合わせ
627
+ # @private
628
+ def _arrange_length(period)
629
+ return period unless period.kind_of?(Array)
630
+ diff = @indices.length - period.length + 1
631
+ return period if (diff == 0)
632
+ return (diff > 0) ? period + Array.new(diff, 0) : period[0...diff]
633
+ end
634
+
635
+ # 時刻配列の分解能
636
+ # @private
637
+ def _precision(time, default=nil)
638
+ nil_index = time.index(nil) || time.length
639
+ precision = nil_index - 1 if (nil_index < @base.length || time[-1].kind_of?(Integer))
640
+ When::Coordinates::Index.precision(default || precision)
641
+ end
642
+
643
+ # 丸め量 / When::TM::Duration::SYSTEM
644
+ # @private
645
+ def _round_value(precision)
646
+ offset = When::TM::Duration::DAY / 2
647
+ precision.times do |i|
648
+ offset /= @unit[i+1] ? @unit[i+1] : 10
649
+ end
650
+ offset
651
+ end
652
+
653
+ # この時法の時間帯名
654
+ #
655
+ # @param [Symbol] format
656
+ # - :extended ISO 8601 extended format (default)
657
+ # - :basic ISO 8601 basic format
658
+ # - :hash 時間帯名の後ろにISO 8601 extended format を付加する
659
+ #
660
+ # @return [Array<String>]
661
+ #
662
+ # @note
663
+ # :extended または :basicが指定され、上記は時間帯名が定義されていない場合は、ISO 8601形式で返す
664
+ #
665
+ def tzname(format=:extended)
666
+ name = @tz_prop.tzname if @tz_prop.kind_of?(When::V::TimezoneProperty) && format != :hash
667
+ name ||= format == :basic ? to_basic : @zone
668
+ name = Array(name)
669
+ return name unless format == :hash
670
+ tzid = case @tz_prop
671
+ when When::V::TimezoneProperty ; @tz_prop['..'].property['tzid'].object
672
+ when When::Parts::Timezone ; @tz_prop.timezone.name
673
+ else ; ''
674
+ end
675
+ name[0] = tzid + name[0]
676
+ name
677
+ end
678
+
679
+ # 時間帯を代表する空間位置
680
+ # @return [When::Coordinates::Spatial]
681
+ def location
682
+ @location ||= @tz_prop.kind_of?(When::Parts::Timezone) ? @tz_prop.location :
683
+ When::Coordinates::Spatial.default_location
684
+ end
685
+
686
+ # 標準時間帯の時計
687
+ # @return [When::TM::Clock]
688
+ def standard
689
+ _tz_prop ? _tz_prop.standard : self
690
+ end
691
+
692
+ # 夏時間帯の時計
693
+ # @return [When::TM::Clock]
694
+ def daylight
695
+ _tz_prop ? _tz_prop.daylight : self
696
+ end
697
+
698
+ # 夏時間帯と標準時間帯の時間差
699
+ # @return [When::TM:IntervalLength]
700
+ def tz_difference
701
+ _tz_prop ? _tz_prop.tz_difference : 0
702
+ end
703
+
704
+ # 夏時間の有無
705
+ # @private
706
+ def _need_validate
707
+ _tz_prop ? _tz_prop._need_validate : false
708
+ end
709
+
710
+ # 夏時間
711
+ # @private
712
+ def _daylight(time)
713
+ _tz_prop ? _tz_prop._daylight(time) : self
714
+ end
715
+
716
+ private
717
+
718
+ # Timezone オブジェクト
719
+ def _tz_prop
720
+ @tz_prop.kind_of?(When::V::TimezoneProperty) ? @tz_prop._pool['..'] : @tz_prop
721
+ end
722
+
723
+ # オブジェクトの正規化
724
+ def _normalize(args=[], options={})
725
+ @indices ||= DefaultTimeIndices
726
+
727
+ # note
728
+ @note ||= 'JulianDay'
729
+
730
+ # normalize spatial module
731
+ _normalize_spatial
732
+
733
+ # normalize temporal module
734
+ _normalize_temporal
735
+
736
+ # second
737
+ @second = (@second||1/When::TM::Duration::SECOND).to_f
738
+
739
+ # zone
740
+ @zone = Clock.to_hms(@zone || @label || @reference_event)
741
+
742
+ # time_standard
743
+ @time_standard = When.Resource(@time_standard||'UniversalTime', '_t:') unless @time_standard.kind_of?(When::TimeStandard)
744
+
745
+ # utc_reference
746
+ @utc_reference ||= @zone ? ClockTime.new(@zone.to_s, {:frame=>When::UTC}) : Clock.local_time
747
+
748
+ # reference_time & origin_of_LSC
749
+ case @reference_time
750
+ when Array ; clk_time = @reference_time
751
+ when String ; clk_time = Pair._en_pair_date_time(@reference_time)
752
+ when ClockTime ; clk_time = @reference_time.clk_time
753
+ when nil ; clk_time = [0, 0]
754
+ else ; raise TypeError, "reference_time type mismatch"
755
+ end
756
+ @origin_of_LSC -= (to_universal_time(clk_time) - @utc_reference.universal_time) * @second
757
+ @reference_time = ClockTime.new(clk_time, {:frame=>self})
758
+ end
759
+ end
760
+
761
+ # 時間座標系
762
+ #
763
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCoordinateSystemType gml schema}
764
+ #
765
+ class CoordinateSystem < ReferenceSystem
766
+
767
+ # グレゴリオ暦の日付及びその日のUTCの時刻で表現する、座標参照系の尺度の原点位置
768
+ #
769
+ # Position of the origin of the scale on which the temporal coordinate
770
+ # system is based expressed as a date in the Gregorian calendar and
771
+ # time of day in UTC
772
+ #
773
+ # @return [When::BasicTypes::DateTime]
774
+ #
775
+ attr_reader :origin
776
+
777
+ # この参照系の軸で時間の参照単位とする間隔
778
+ #
779
+ # Standard unit of time used to measure duration
780
+ # on the axis of the coordinate system
781
+ #
782
+ # @return [When::TM::Duration] (changed from String)
783
+ #
784
+ attr_reader :interval
785
+
786
+ # この時間参照系の座標値をグレゴリオ暦及びUTC時刻に変換する
787
+ #
788
+ # @param [When::TM::Coordinate] c_value
789
+ #
790
+ # @return [When::TM::DateAndTime] (When::TM::BasicTypes::DateTimeはまちがい)
791
+ #
792
+ def transform_coord(c_value)
793
+ When::Gregorian.jul_trans(JulianDate.universal_time(c_value.universal_time, {:frame=>When::UTC}))
794
+ end
795
+ alias :transformCoord :transform_coord
796
+
797
+ # グレゴリオ暦及びUTC時刻をこの時間参照系の座標値に変換する
798
+ #
799
+ # @param [When::TM::DateAndTime (BasicTypes::DateTimeはまちがい)] date_time
800
+ #
801
+ # @return [When::TM::Coordinate]
802
+ #
803
+ def transform_date_time(date_time)
804
+ Coordinate.universal_time(date_time.universal_time, {:frame=>self})
805
+ end
806
+ alias :transformDateTime :transform_date_time
807
+
808
+ private
809
+
810
+ # オブジェクトの正規化
811
+ def _normalize(args=[], options={})
812
+ @origin = When.when?(@origin, options)
813
+ @interval = When.Duration(@interval)
814
+ raise TypeError, "Interval should be IntervalLength" unless @interval.kind_of?(IntervalLength)
815
+ end
816
+ end
817
+
818
+ # 順序時間参照系
819
+ #
820
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeOrdinalEraType gml schema}
821
+ #
822
+ class OrdinalEra < When::TM::Object
823
+
824
+ include Separation
825
+
826
+ # この順序年代を識別する名称
827
+ #
828
+ # Name that identifies a specific ordinal era
829
+ #
830
+ # @return [String]
831
+ #
832
+ alias :name :label
833
+
834
+ # この順序年代をさらに分割する順序年代
835
+ #
836
+ # Ordinal eras that subdivide this ordinal era
837
+ #
838
+ # @return [Array<When::TM::OrdinalEra>]
839
+ #
840
+ alias :member :child
841
+
842
+ # この順序年代が属する順序時間参照系 (relation - Structure)
843
+ #
844
+ # Ordinal reference system that contains this ordinal era
845
+ #
846
+ # @return [When::TM::OrdinalReferenceSystem, nil]
847
+ #
848
+ def system
849
+ _pool['..'].kind_of?(String) ? _pool['..'] : nil
850
+ end
851
+
852
+ # この順序年代が属する上位順序年代 (relation - Composition)
853
+ #
854
+ # Ordinal era that contains this ordinal era
855
+ #
856
+ # @return [When::TM::OrdinalEra, nil]
857
+ #
858
+ def group
859
+ _pool['..'].kind_of?(String)? nil : _pool['..']
860
+ end
861
+
862
+ # この順序年代が始まる時点
863
+ #
864
+ # Date at which the ordinal era began
865
+ #
866
+ # @return [When::BasicTypes::DateTime]
867
+ #
868
+ attr_reader :begin
869
+
870
+ # この順序年代が終わる時点
871
+ #
872
+ # Date at which the ordinal era ended
873
+ #
874
+ # @return [When::BasicTypes::DateTime]
875
+ #
876
+ def end
877
+ @end || (@_pool['.->'] ? @_pool['.->'].begin : nil)
878
+ end
879
+
880
+ # 日時が属するか?
881
+ #
882
+ # @param [When::TM::TemporalPosition] other
883
+ #
884
+ # @return [Boolean]
885
+ # [ true - 属する ]
886
+ # [ false - 属さない ]
887
+ #
888
+ def include?(other)
889
+ self.begin <= other && other < self.end
890
+ end
891
+
892
+ # When::TM::TemporalPosition ^ When::TM::OrdinalEra で呼ばれる
893
+ # @private
894
+ def ^(other)
895
+ include?(other) ? self : nil
896
+ end
897
+
898
+ private
899
+
900
+ # オブジェクトの正規化
901
+ def _normalize(args=[], options={})
902
+ # options
903
+ @options ||= {}
904
+ term_options = @options.merge({'namespace'=>@namespace, 'locale'=>@locale})
905
+
906
+ label, begun, ended = args
907
+
908
+ # label
909
+ @label = label if label
910
+ @label = m17n(@label, nil, nil, term_options) if (@label.instance_of?(String))
911
+ @label._pool['..'] ||= self
912
+ @_pool[@label.to_s] = @label
913
+
914
+ # begin
915
+ @begin = begun if begun
916
+ @begin = When.when?(@begin, @options) if @begin
917
+ @begin = @child[0].begin if !@begin && @child[0]
918
+
919
+ # end
920
+ @end = ended if ended
921
+ @end = When.when?(@end, @options) if @end
922
+ @end = @child[-1].end if !@end && @child[-1]
923
+ end
924
+
925
+ # その他のメソッド
926
+ #
927
+ # @note
928
+ # When::TM::OrdinalEra で定義されていないメソッドは
929
+ # 処理を @begin (type: When::TM::TemporalPosition) に委譲する
930
+ #
931
+ def method_missing(name, *args, &block)
932
+ self.class.module_eval %Q{
933
+ def #{name}(*args, &block)
934
+ @begin.send("#{name}", *args, &block)
935
+ end
936
+ } unless When::Parts::MethodCash.escape(name)
937
+ @begin.send(name, *args, &block)
938
+ end
939
+ end
940
+
941
+ # 暦年代
942
+ #
943
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalReferenceSystems.xsd#TimeCalendarEraType gml schema}
944
+ #
945
+ class CalendarEra < When::TM::Object
946
+
947
+ class << self
948
+
949
+ include When::Parts::Resource::Pool
950
+
951
+ # When::TM::CalendarEra Class のグローバルな設定を行う
952
+ #
953
+ # @param [Array<String>] order When::TM::CalendarEra の検索順序を指定するIRI文字列のArray
954
+ #
955
+ # @return [void]
956
+ #
957
+ # @note
958
+ # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
959
+ # このため、本メソッド自体はスレッドセーフでない。
960
+ #
961
+ def _setup_(order=nil)
962
+ @_lock_ = Mutex.new if When.multi_thread
963
+ @_pool = {}
964
+ @order = order || DefaultEpochs
965
+ end
966
+
967
+ # 設定情報を取得する
968
+ #
969
+ # @return [Hash] 設定情報
970
+ #
971
+ def _setup_info
972
+ {:order => @order || _setup_}
973
+ end
974
+
975
+ # When::TM::CalendarEra オブジェクトを検索し取得する
976
+ #
977
+ # @overload _instance(key, epoch=nil, reverse=nil, options={})
978
+ # @param [String, Regexp] key 検索する暦年代または、暦年代にマッチする正規表現
979
+ # @param [Integer] epoch 年数を昇順にカウントする方式での暦元(0年)の通年(デフォルトは nil - 指定なし)
980
+ # @param [Integer] reverse 年数を降順にカウントする方式での暦元(0年)の通年(デフォルトは nil - 指定なし)
981
+ # @param [Hash] options 以下の通り
982
+ # @option options [String] :area 暦年代の使用地域の指定(デフォルトは nil - 指定なし)
983
+ # @option options [String] :period 暦年代の使用時代の指定(デフォルトは nil - 指定なし)
984
+ # @option options [Integer] :count 何件ヒットするまで検索するかを指定(デフォルトは 1件)
985
+ # @option options [String] the_others 例えば When::TM::CalendarEra オブジェクトの epoch_of_use に 'name' などの
986
+ # 指定がある場合、:name に指定しておけば、検索での絞り込みに使用できる。
987
+ #
988
+ # @note 検索用のインデクスはインスタンスの生成時に作成する。
989
+ # 作成後にキーワードとなる年号(M17nクラス)にローケールが追加されても反映されない。
990
+ #
991
+ def _instance(*args)
992
+ # パラメータ
993
+ args = args.dup
994
+ options = (args[-1].kind_of?(Hash)) ? args.pop.dup : {}
995
+ key, epoch, reverse = options.delete(:label) || args
996
+ key = When::Locale.ideographic_unification(key)
997
+ if key.kind_of?(String)
998
+ key, *parents = key.split(/::/).reverse
999
+ else
1000
+ parents = []
1001
+ end
1002
+ area = options.delete(:area)
1003
+ period = options.delete(:period)
1004
+ count = options.delete(:count) || 1
1005
+
1006
+ # 候補
1007
+ _setup_ unless @_pool
1008
+ pool = _candidates(options, area, period, key, epoch, false) +
1009
+ _candidates(options, area, period, key, reverse, true)
1010
+ _compact(pool, parents)
1011
+ return pool unless pool.size < count
1012
+
1013
+ order = @order
1014
+ if /\?.+?=/ =~ parents[-1]
1015
+ begin
1016
+ head = When.CalendarEra(parents[-1])
1017
+ order = order.dup.unshift(head)
1018
+ rescue
1019
+ end
1020
+ end
1021
+ order.each do |iri|
1022
+ When.CalendarEra(iri)
1023
+ pool = _candidates(options, area, period, key, epoch, false) +
1024
+ _candidates(options, area, period, key, reverse, true)
1025
+ _compact(pool, parents)
1026
+ return pool unless pool.size < count
1027
+ end
1028
+
1029
+ return []
1030
+ end
1031
+
1032
+ private
1033
+
1034
+ # 候補の抽出
1035
+ def _candidates(options, area, period, key, epoch, reverse)
1036
+ regexp, key = key if key.kind_of?(Regexp)
1037
+ return [] unless @_pool[area] &&
1038
+ @_pool[area][period] &&
1039
+ @_pool[area][period][key] &&
1040
+ @_pool[area][period][key][epoch]
1041
+ pool = @_pool[area][period][key][epoch].dup
1042
+ pool.delete_if {|e| regexp !~ e.name} if regexp
1043
+ return pool if options.empty?
1044
+ pool.delete_if {|e| !_match(e, reverse, options) }
1045
+ end
1046
+
1047
+ def _match(epoch, reverse, options)
1048
+ return false unless epoch.reverse? == reverse
1049
+ target = {:reference_event => epoch.reference_event,
1050
+ :reference_date => epoch.reference_date,
1051
+ :julian_reference => epoch.julian_reference
1052
+ }
1053
+ options.each_pair do |k,v|
1054
+ return false unless (v === (target.key?(k) ? target[k] : epoch.options[k]))
1055
+ end
1056
+ return true
1057
+ end
1058
+
1059
+ def _compact(pool, parents)
1060
+ pool.uniq!
1061
+ return if parents.empty?
1062
+ pool.delete_if {|e|
1063
+ !parents.each do |parent|
1064
+ e = e.parent
1065
+ break false unless parent == e.name
1066
+ end
1067
+ }
1068
+ end
1069
+
1070
+ def _behalf_of(iri)
1071
+ base = iri.dup.sub!('/TM/CalendarEra/','/CalendarTypes/')
1072
+ return nil unless base
1073
+ calendar = When::Parts::Resource._instance(base)
1074
+ date = When.tm_pos(1, {:frame=>calendar}).floor
1075
+ label = calendar.label
1076
+ era = label.names['era']
1077
+ label = label.dup.send(:_copy, {:label=>era}) if era
1078
+ options = {:label=>label, '..'=>iri}
1079
+ %w(period area).each do |name|
1080
+ value = calendar.instance_variable_get('@' + name)
1081
+ options[name.to_sym] = value if value
1082
+ end
1083
+ era = self.new(date, '@CE', date, options)
1084
+ era.send(:_register_calendar_era)
1085
+ era
1086
+ end
1087
+ end
1088
+
1089
+ # @private
1090
+ HashProperty =
1091
+ [:reference_event, :reference_date, :julian_reference, :dating_system, # :epoch_of_use
1092
+ :epoch, :epoch_year, :options]
1093
+
1094
+ # この暦年代を識別する名称
1095
+ #
1096
+ # Name by which this calendar era is known
1097
+ #
1098
+ # @return [String]
1099
+ # 通常 String のサブクラスである When::BasicTypes::M17n を使用する。
1100
+ #
1101
+ alias :name :label
1102
+
1103
+ # この暦年代の基点となる事象
1104
+ #
1105
+ # Event used as the datum for this calendar era
1106
+ #
1107
+ # @return [String]
1108
+ # 通常 String のサブクラスである When::BasicTypes::M17n を使用する。
1109
+ #
1110
+ # @note
1111
+ # [Accession,代始]:: 天皇・皇帝などの代始めの改元,必ずしも践祚と連動しない。
1112
+ # :: JIS X7108 附属書D表3に参照事象の例として「新しい天皇の即位」とある。
1113
+ # :: これは践祚を意味するので、岡田芳朗氏によれば適切ではないとのこと。
1114
+ # :: 英語には適切な訳語がないと思われる。
1115
+ # [FelicitousEvent,祥瑞]:: 祥瑞の発生に伴う改元
1116
+ # [NaturalDisaster,災異]:: 災異の発生に伴う改元
1117
+ # [InauspiciousYear,革年]:: 甲子革令・辛酉革命説による改元
1118
+ # [Foundation,創業]:: 建国による元号の制定
1119
+ # [CalendarReform,改暦]:: 改暦に伴う epoch_of_use の境界
1120
+ #
1121
+ attr_reader :reference_event
1122
+ alias :referenceEvent :reference_event
1123
+
1124
+ # この暦による参照事象の日付
1125
+ #
1126
+ # Date of the reference event in the calendar being described
1127
+ #
1128
+ # @return [When::TM::CalDate]
1129
+ #
1130
+ # @note
1131
+ # 明治以前の改元は当該年の初めにに遡って適用された。しかし、日記などの資料は当然旧年号で
1132
+ # 記載されている。このような場合、reference_date の分解能を「年」とする。
1133
+ # 本ライブラリでは、reference_date の分解能が「年」の場合、When::TM::TemporalPosition._instance
1134
+ # options[:lower] で年号使用の下限を年初とするか、epoch_of_use の下限とするかを
1135
+ # 指定することができる。
1136
+ #
1137
+ attr_reader :reference_date
1138
+ alias :referenceDate :reference_date
1139
+
1140
+ # 参照事象のユリウス日
1141
+ #
1142
+ # Julian date of the reference event
1143
+ #
1144
+ # @return [When::TM::JulianDate]
1145
+ #
1146
+ attr_reader :julian_reference
1147
+ alias :julianReference :julian_reference
1148
+
1149
+ # この暦年代が日付の基礎として使用する期間
1150
+ #
1151
+ # Period for which the era was used as a basis for dating
1152
+ #
1153
+ # @return [Array<When::TM::TemporalPosition>]
1154
+ #
1155
+ # @note
1156
+ # 途中の改暦を指定するために要素が必要になることがあり、要素数が2を超えることがある。
1157
+ # 最初の要素が When::TimeValue::MIN(-Infinity)の場合、年数を降順にカウントする。
1158
+ # 暦年代の分解能を“日”より細かくすることはできない。
1159
+ #
1160
+ attr_reader :epoch
1161
+
1162
+ # この暦年代と関連付けられた暦 (relation - Basis)
1163
+ #
1164
+ # The calendar associated with the calendar eras being described
1165
+ #
1166
+ # @return [Array<When::TM::Calendar>]
1167
+ #
1168
+ attr_reader :dating_system
1169
+ alias :datingSystem :dating_system
1170
+
1171
+ # この暦年代の元年の年番号(additional attribute)
1172
+ #
1173
+ # The year number of the calendar associated with the first year of this calendar era
1174
+ #
1175
+ # @return [Integer]
1176
+ #
1177
+ attr_reader :epoch_year
1178
+ alias :epochYear :epoch_year
1179
+
1180
+ # その他の属性 - additional attribute
1181
+ #
1182
+ # @return [Hash]
1183
+ # [ 'area' => 暦年代の使用地域 [When::BasicTypes::M17n] ]
1184
+ # [ 'period' => 暦年代の使用時代 [When::BasicTypes::M17n] ]
1185
+ # [ the others => epoch_of_use 'name' などの指定を反映 [String] ]
1186
+ #
1187
+ attr_reader :options
1188
+
1189
+ # この暦年代が日付の基礎として使用する期間
1190
+ #
1191
+ # Period for which the era was used as a basis for dating
1192
+ #
1193
+ # @return [When::TM::Period]
1194
+ #
1195
+ # @note
1196
+ # epoch_of_use メソッドの戻り値は ISO19108 で When::TM::Period と規定されている。
1197
+ # このため変数 @epoch とは別に、本メソッドを提供する。
1198
+ #
1199
+ def epoch_of_use
1200
+ @epoch_of_use ||=
1201
+ Period.new(*([0, -1].map {|i|
1202
+ date = @epoch[i]
1203
+ if date.kind_of?(CalDate)
1204
+ options = date._attr
1205
+ options[:frame] = options.delete(:clock)
1206
+ date = JulianDate.universal_time(date.universal_time, options)
1207
+ end
1208
+ Instant.new(date) # See JIS X7108 5.3.2.2 e) When::TM::Period は直接 JulianDate を保持できない
1209
+ }))
1210
+ end
1211
+ alias :epochOfUse :epoch_of_use
1212
+
1213
+ # 年数の数え方
1214
+ #
1215
+ # @return [Boolean]
1216
+ # [ true - 降順 (Before Common Era 方式)]
1217
+ # [ false - 昇順 (Common Era 方式) ]
1218
+ #
1219
+ def reverse?
1220
+ @epoch[0].indeterminated_position == When::TimeValue::Min
1221
+ end
1222
+
1223
+ # 未来永劫使用するか?
1224
+ #
1225
+ # @return [Boolean]
1226
+ # [ true - する ]
1227
+ # [ false - しない ]
1228
+ #
1229
+ def unlimited?
1230
+ @epoch[-1].indeterminated_position == When::TimeValue::Max
1231
+ end
1232
+
1233
+ # 時間の歩度
1234
+ #
1235
+ # @return [Numeric]
1236
+ #
1237
+ def rate_of_clock
1238
+ _typical_epoch.time_standard.rate_of_clock
1239
+ end
1240
+
1241
+ # 当該の暦年代の日付に変換する
1242
+ #
1243
+ # @param [When::TM::TemporalPosition] date
1244
+ # @param [Hash] trans_options 以下の通り
1245
+ # @option trans_options [Hash] :lower 暦年代適用の下限
1246
+ # [ true - epoch_of_use の始め(省略時) ]
1247
+ # [ :reference_date - 参照事象の日付 ]
1248
+ # @option trans_options [Hash] :upper 暦年代適用の上限
1249
+ # [ true - epoch_of_use の終わり(省略時) ]
1250
+ # [ :reference_date - 参照事象の日付 ]
1251
+ #
1252
+ # @return [When::TM::TemporalPosition]
1253
+ # [ When::TimeValue::Before - 当該の暦年代より前の日付である ]
1254
+ # [ When::TimeValue::After - 当該の暦年代より後の日付である ]
1255
+ # [ その他 - 当該の暦年代の日付に変換された When::TM::TemporalPosition ]
1256
+ #
1257
+ def trans(date, trans_options={})
1258
+ # 当該日付の決定
1259
+ date = Position.any_other(date, trans_options) unless date.kind_of?(Array)
1260
+ epoch, cal_date =
1261
+ case date
1262
+ when Array ; _trans_array(date)
1263
+ when JulianDate, DateAndTime ; _trans_date(date, date.clock)
1264
+ when When::TimeValue ; _trans_date(date)
1265
+ when Numeric ; _trans_date(JulianDate.universal_time((date-JulianDate::JD19700101)*Duration::DAY))
1266
+ else ; raise TypeError, "Irregal Seed Date Type"
1267
+ end
1268
+ return epoch unless cal_date
1269
+
1270
+ # 上下限の確認
1271
+ trans_options ||= {}
1272
+ return When::TimeValue::Before if cal_date.to_i < lower_bound(trans_options[:lower] || :reference_date)
1273
+ return When::TimeValue::After if cal_date.to_i > upper_bound(trans_options[:upper] || true)
1274
+
1275
+ # 発見した日時の属性設定
1276
+ cal_date.send(:calendar_era=, self)
1277
+ cal_date.send(:calendar_era_name=, [@label, @epoch_year, reverse?, cal_date.to_i < @epoch[0].to_i])
1278
+ cal_date.cal_date[0] -= @epoch_year
1279
+ cal_date.trans = trans_options.dup
1280
+ cal_date.query = (epoch.query || {}).dup
1281
+ return cal_date
1282
+ end
1283
+
1284
+ # When::TM::TemporalPosition ^ When::TM::CalendarEra で呼ばれる
1285
+ # @private
1286
+ def ^(date, options={})
1287
+ date = Position.any_other(date, options)
1288
+ cal_date = trans(date, (date.trans||{}).merge(options))
1289
+ return nil unless cal_date.kind_of?(CalDate)
1290
+ return cal_date
1291
+ end
1292
+
1293
+ # other と @julian_reference とを比較する
1294
+ #
1295
+ # @param [Comparable] other 比較対象
1296
+ #
1297
+ # @return [Integer] 比較結果を 負, 0, 正の値で返す
1298
+ #
1299
+ def <=>(other) #TODO @precision は?
1300
+ @julian_reference.universal_time <=> other.julian_reference.universal_time
1301
+ end
1302
+
1303
+ # 暦年代の下限
1304
+ # @private
1305
+ def lower_bound(type=nil)
1306
+ @lower_bound[type] ||= @epoch[0].indeterminated_position == When::TimeValue::Min ? -Float::MAX :
1307
+ @reference_date.precision == When::YEAR &&
1308
+ @reference_date.cal_date[When::YEAR-1] == 0 ? @epoch[0].ceil(When::YEAR).to_i :
1309
+ case type
1310
+ when true ; @epoch[0].to_i
1311
+ when :reference_date ; @reference_date.to_i
1312
+ else ; [@reference_date.to_i, @epoch[0].to_i].min
1313
+ end
1314
+ end
1315
+
1316
+ # 暦年代の上限
1317
+ # @private
1318
+ def upper_bound(type=nil)
1319
+ @upper_bound[type] ||= @epoch[-1].indeterminated_position == When::TimeValue::Max ? +Float::MAX :
1320
+ begin
1321
+ next_era = succ
1322
+ next_ref = next_era.reference_date if next_era.respond_to?(:reference_date)
1323
+ next_ref &&
1324
+ next_ref.precision == When::YEAR &&
1325
+ next_ref.cal_date[When::YEAR-1] == 0 ? @epoch[-1].ceil(When::YEAR).to_i - 1 :
1326
+ case type
1327
+ when true ; @epoch[-1].to_i - 1
1328
+ when :reference_date ; @reference_date.to_i
1329
+ else ; [@reference_date.to_i, @epoch[-1].to_i - 1].max
1330
+ end
1331
+ end
1332
+ end
1333
+
1334
+ protected
1335
+
1336
+ # @private
1337
+ def _normalize_leaf_era
1338
+ # r_date and others
1339
+ case @reference_date
1340
+ when String ; format, r_date, r_era = When::BasicTypes::DateTime._to_array(@reference_date)
1341
+ when Array ; r_era, *r_date = @reference_date
1342
+ when CalDate ; r_era, r_date = @reference_date.calendar_era_name, @reference_date.cal_date
1343
+ when nil ;
1344
+ else ; raise TypeError, "ReferenceDate is invalid type"
1345
+ end
1346
+ r_era, r_year = r_era
1347
+
1348
+ epochs = @epoch.dup
1349
+ epochs.shift if (epochs[0].indeterminated_position == When::TimeValue::Min)
1350
+ # j_date and calculated reference_date !((julian_reference == nil) && (reference_date != nil))
1351
+ if @julian_reference
1352
+ jdn = @julian_reference.to_i
1353
+ epoch = epochs[0]
1354
+ epochs.each do |e|
1355
+ if (e.indeterminated_position == When::TimeValue::Max)
1356
+ epoch = e
1357
+ break
1358
+ elsif (jdn < e.to_i)
1359
+ break
1360
+ else
1361
+ epoch = e
1362
+ end
1363
+ end
1364
+ @reference_date = epoch.frame.jul_trans(When.when?(jdn), {:frame=>epoch.frame})
1365
+ j_date = @reference_date.cal_date
1366
+ elsif (@reference_date == nil)
1367
+ @reference_date = epochs[0].dup
1368
+ j_date = @reference_date.cal_date
1369
+ end
1370
+
1371
+ # epoch_year
1372
+ @epoch_year ||= r_year
1373
+ @epoch_year = @epoch_year.to_i if (@epoch_year)
1374
+ raise ArgumentError, "EpochYear mismatch" unless (@epoch_year == r_year)
1375
+ unless @epoch_year
1376
+ raise ArgumentError, "ReferenceDate is absent" unless r_date
1377
+ @epoch_year = epochs[0].cal_date[0] * 1 - r_date[0] * 1
1378
+ end
1379
+
1380
+ # j_date and calculated reference_date ((julian_reference == nil) && (reference_date != nil))
1381
+ unless j_date
1382
+ j_date = [+r_date[0]+@epoch_year, *r_date[1..-1]]
1383
+ epochs.each_index do |i|
1384
+ e = epochs[i]
1385
+ d = CalDate.new(j_date,{:frame=>e.frame})
1386
+ if (e.indeterminated_position == When::TimeValue::Max)
1387
+ @reference_date = d
1388
+ break
1389
+ elsif (d.to_i < e.to_i)
1390
+ @reference_date = d if (i==0)
1391
+ break
1392
+ else
1393
+ @reference_date = d
1394
+ end
1395
+ end
1396
+ end
1397
+
1398
+ # julian_reference and reference_date
1399
+ @julian_reference = JulianDate.universal_time(@reference_date.universal_time)
1400
+ @reference_date.cal_date[0] -= @epoch_year
1401
+ @reference_date.send(:calendar_era_name=, [(r_era ? r_era : @label), @epoch_year])
1402
+ if (r_date)
1403
+ raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (@epoch_year == +j_date[0]-(+r_date[0]))
1404
+ raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (j_date[1..-1] == r_date[1..-1])
1405
+ #raise ArgumentError, "JulianReference and ReferenceDate are mismatch" unless (j_date == r_date)
1406
+ if (r_date[1] == nil)
1407
+ @reference_date.precision = When::YEAR
1408
+ elsif (r_date[2] == nil)
1409
+ @reference_date.precision = When::MONTH
1410
+ end
1411
+ end
1412
+ end
1413
+
1414
+ # @private
1415
+ def _register_calendar_era
1416
+ return unless @label.kind_of?(When::BasicTypes::M17n)
1417
+
1418
+ # dating_system
1419
+ @dating_system = (@epoch.map {|e| e.frame}).compact.uniq
1420
+ ancestors = hierarchy.inject(['']) {|list,era|
1421
+ list << list[-1] + '::' + era.label.to_s
1422
+ list
1423
+ }
1424
+
1425
+ if @epoch.length == 1
1426
+ epoch[0].frame.synchronize {
1427
+ range = When::Parts::GeometricComplex.new([[epoch[0],true]])
1428
+ ancestors.each do |ancestor|
1429
+ epoch[0].frame.domain[ancestor] |= range
1430
+ end
1431
+ } if epoch[0].frame
1432
+ elsif reverse?
1433
+ epoch[1].frame.synchronize {
1434
+ range = When::Parts::GeometricComplex.new([[epoch[1],true]], true)
1435
+ ancestors.each do |ancestor|
1436
+ epoch[1].frame.domain[ancestor] |= range
1437
+ end
1438
+ } if epoch[1].frame
1439
+ else
1440
+ (epoch.length-1).times do |i|
1441
+ epoch[i].frame.synchronize {
1442
+ range = When::Parts::GeometricComplex.new([[epoch[i],true], [epoch[i+1],false]])
1443
+ ancestors.each do |ancestor|
1444
+ epoch[i].frame.domain[ancestor] |= range
1445
+ end
1446
+ } if epoch[i].frame
1447
+ end
1448
+ end
1449
+
1450
+ @dating_system.each do |f|
1451
+ f.synchronize do
1452
+ f.reference_frame << self
1453
+ f.reference_frame.uniq!
1454
+ f.reference_frame.sort!
1455
+ first = f.domain[''].first(When::MinusInfinity)
1456
+ last = f.domain[''].last(When::PlusInfinity)
1457
+ f.domain_of_validity = When::EX::Extent.new(
1458
+ When::TM::Period.new(
1459
+ When::TM::Instant.new(first),
1460
+ When::TM::Instant.new(last))) if first && first <= last
1461
+ end
1462
+ end
1463
+
1464
+ # インデクス登録
1465
+ ((@area||{}).values + [nil]).each do |a|
1466
+ self.class[a] ||= {}
1467
+ ((@period||{}).values + [nil]).each do |p|
1468
+ self.class[a][p] ||= {}
1469
+ (@label.values + [nil]).each do |k|
1470
+ self.class[a][p][k] ||= {}
1471
+ [@epoch_year, nil].each do |e|
1472
+ self.class[a][p][k][e] ||= []
1473
+ self.class[a][p][k][e] << self
1474
+ end
1475
+ end
1476
+ end
1477
+ end
1478
+ end
1479
+
1480
+ private
1481
+
1482
+ # オブジェクトの正規化
1483
+ def _normalize(args=[], options={})
1484
+ # area, period
1485
+ term_options = {:namespace=>@namespace, :locale=>@locale, :options=>options}
1486
+ @area = m17n(@area, nil, nil, term_options) if @area.instance_of?(String)
1487
+ @period = m17n(@period, nil, nil, term_options) if @period.instance_of?(String)
1488
+
1489
+ # 暦年代の上下限
1490
+ @lower_bound = {}
1491
+ @upper_bound = {}
1492
+
1493
+ # other attributes
1494
+ if @child && @child.length>0
1495
+ _non_leaf_era(args, term_options)
1496
+ _register_calendar_era unless _pool['..'].kind_of?(CalendarEra)
1497
+ @child.each do |era|
1498
+ era._register_calendar_era
1499
+ end
1500
+ else
1501
+ _set_leaf_era(args, term_options)
1502
+ _normalize_leaf_era unless @epoch[0].indeterminated_position == When::TimeValue::Min
1503
+ end
1504
+ end
1505
+
1506
+ def _non_leaf_era(args, term_options)
1507
+ @label = m17n(@label, nil, nil, term_options) if @label.instance_of?(String)
1508
+ if @label
1509
+ @label._pool['..'] = self # TODO ここを ||= にすると leaf? の判定がおかしくなる
1510
+ @_pool[@label.to_s] = @label
1511
+ end
1512
+
1513
+ # reference_date, reference_event & label
1514
+ @reference_date = @child[0].reference_date
1515
+ @reference_event = @child[0].reference_event
1516
+ @epoch_year = 0 # @child[0].epoch_year
1517
+ @julian_reference = @child[0].julian_reference
1518
+
1519
+ # options
1520
+ @options ||= {}
1521
+ @options.update({'area'=>@area, 'period'=>@period})
1522
+
1523
+ # epoch
1524
+ @epoch = []
1525
+ @child.each do |c|
1526
+ c.epoch.each do |e|
1527
+ @epoch << e unless e == @epoch[-1]
1528
+ end
1529
+ end
1530
+ end
1531
+
1532
+ def _set_leaf_era(args, term_options)
1533
+ # 変数の読み込み
1534
+ reference_date, reference_event, *epochs = args
1535
+
1536
+ # reference_date & label
1537
+ if reference_date
1538
+ @reference_date = reference_date
1539
+ @label ||= m17n(@reference_date[/\A\[[^\]]+\]|^[^-+0-9]+/], nil, nil, term_options)
1540
+ end
1541
+ @label._pool['..'] ||= self
1542
+ @_pool[@label.to_s] = @label
1543
+
1544
+ # reference_event
1545
+ @reference_event = reference_event if reference_event
1546
+ @reference_event ||= ""
1547
+ @reference_event = DefaultEvents[@reference_event] if DefaultEvents[@reference_event]
1548
+ @reference_event = m17n(@reference_event, nil, nil, term_options) if @reference_event.instance_of?(String)
1549
+ @reference_event._pool['..'] ||= self
1550
+ @_pool[@reference_event.to_s] = @reference_event
1551
+
1552
+ # options
1553
+ term_options[:options][:query] ||= {}
1554
+ @options ||= {}
1555
+ @options.update(term_options[:options][:query])
1556
+ @options.update({'area'=>@area, 'period'=>@period})
1557
+
1558
+ # epoch
1559
+ @epoch ||= epochs
1560
+ @epoch[0] ||= '-Infinity'
1561
+ @epoch[1] ||= '+Infinity'
1562
+ term_options[:options][:frame] ||= When::Gregorian
1563
+ epoch = ''
1564
+ @epoch = @epoch.map {|e|
1565
+ case e
1566
+ when ''
1567
+ When.when?('+Infinity')
1568
+ when String
1569
+ d, f = e.split(/\^{1,2}/)
1570
+ term_options[:options][:frame] = When.Resource(f, '_c:') if (f)
1571
+ d.split(/;/).each do |v|
1572
+ v.strip!
1573
+ if (v =~ /\A[-+\d]|^Now$|^Unknown$|^[-+]Infinity\z/i)
1574
+ epoch = v
1575
+ break
1576
+ end
1577
+ k, c = v.split(/\s*[:=]\s*/, 2)
1578
+ if (c.kind_of?(String))
1579
+ code = m17n(c.gsub(/%2C/,','), nil, nil, term_options)
1580
+ @_pool[code.to_s] ||= code
1581
+ @options[k] = term_options[:options][:query][k] = @_pool[code.to_s]
1582
+ else
1583
+ @options.delete(k)
1584
+ term_options[:options][:query].delete(k)
1585
+ end
1586
+ end
1587
+ When.when?(epoch, @options.merge({:frame=>term_options[:options][:frame], :validate=>:epoch}))
1588
+ else
1589
+ e
1590
+ end
1591
+ }
1592
+
1593
+ # normalize for last era
1594
+ last_era = _pool['..'].child[-1] if _pool['..'].kind_of?(CalendarEra)
1595
+ if last_era
1596
+ if last_era.epoch[0].indeterminated_position == When::TimeValue::Min
1597
+ last_era.epoch[0].frame = @epoch[0].frame
1598
+ last_era.epoch[1] = @epoch[0]
1599
+ last_era._normalize_leaf_era
1600
+ elsif last_era.epoch[-1].indeterminated_position == When::TimeValue::Max
1601
+ last_era.epoch[-1] = @epoch[0]
1602
+ end
1603
+ end
1604
+ end
1605
+
1606
+ # 配下のオブジェクトの前後関係の設定
1607
+ def _sequence
1608
+ return unless @child
1609
+ prev = nil
1610
+ if @_pool['..'].respond_to?(:child) && @_pool['..'].child
1611
+ (@_pool['..'].child.length-1).downto(0) do |i|
1612
+ v = @_pool['..'].child[i]
1613
+ next if (v.epoch[0]>=self.epoch[0])
1614
+ prev = v
1615
+ break
1616
+ end
1617
+ end
1618
+ @child.each do |v|
1619
+ if prev
1620
+ v._pool['.<-'] = prev
1621
+ prev._pool['.->'] ||= v
1622
+ while (prev.child && prev.child[-1]) do
1623
+ prev = prev.child[-1]
1624
+ prev._pool['.->'] ||= v
1625
+ end
1626
+ end
1627
+ @_pool[v.label.to_s] = v
1628
+ prev = v
1629
+ end
1630
+ end
1631
+
1632
+ def _trans_array(date)
1633
+ date = date.dup
1634
+ epoch0 = @epoch.first
1635
+ date0 = date1 = CalDate.new(date, {:frame=>epoch0.frame}) if epoch0.frame
1636
+ @epoch.each do |epoch1|
1637
+ date1 = CalDate.new(date, {:frame=>epoch1.frame}) unless epoch0.frame.equal?(epoch1.frame)
1638
+ return [epoch0, date0] if epoch1.indeterminated_position == When::TimeValue::Max
1639
+ if date1.to_i < epoch1.to_i
1640
+ return [epoch0, date0] if date0.to_i < epoch1.to_i
1641
+ return [epoch1, epoch1.frame ^ date0.to_i]
1642
+ end
1643
+ date0, epoch0 = date1, epoch1
1644
+ end
1645
+ return [@epoch.last, date1]
1646
+ end
1647
+
1648
+ def _trans_date(date, clock=nil)
1649
+ unless rate_of_clock == date.time_standard.rate_of_clock
1650
+ date = JulianDate.dynamical_time(date.dynamical_time, {:time_standard=>_typical_epoch.time_standard})
1651
+ clock = _typical_epoch.clock
1652
+ end
1653
+ frac = clock ? clock.universal_time : 0
1654
+ sdn, time = (date.universal_time - frac).divmod(Duration::DAY)
1655
+ sdn += JulianDate::JD19700101
1656
+ epoch = @epoch[0]
1657
+ return When::TimeValue::Before if sdn < lower_bound
1658
+ return When::TimeValue::After if sdn > upper_bound
1659
+ @epoch.each do |e|
1660
+ break if e.indeterminated_position == When::TimeValue::Max
1661
+ break if sdn < e.to_i
1662
+ epoch = e
1663
+ end
1664
+ frame = epoch.frame
1665
+ cal_date = frame.to_cal_date(sdn)
1666
+ return epoch, CalDate.new(cal_date, {:frame=>frame}) unless clock
1667
+ return epoch, DateAndTime.new(cal_date, time+frac, {:frame=>frame, :clock=>clock})
1668
+ end
1669
+
1670
+ # 代表的な元期
1671
+ #
1672
+ # @return [When::TM::TemporalPosition]
1673
+ #
1674
+ def _typical_epoch
1675
+ @_typical_epoch ||= reverse? ? @epoch[-1] : @epoch[0]
1676
+ end
1677
+
1678
+ # その他のメソッド
1679
+ #
1680
+ # @note
1681
+ # When::TM::CalendarEra で定義されていないメソッドは
1682
+ # 処理を @reference_date (type: When::TM::TemporalPosition) に委譲する
1683
+ #
1684
+ def method_missing(name, *args, &block)
1685
+ self.class.module_eval %Q{
1686
+ def #{name}(*args, &block)
1687
+ @reference_date.send("#{name}", *args, &block)
1688
+ end
1689
+ } unless When::Parts::MethodCash.escape(name)
1690
+ @reference_date.send(name, *args, &block)
1691
+ end
1692
+ end
1693
+ end