when_exe 0.4.0 → 0.4.1

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