when_exe 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  =begin
3
- Copyright (C) 2011-2014 Takashi SUGA
3
+ Copyright (C) 2011-2015 Takashi SUGA
4
4
 
5
5
  You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
6
  =end
@@ -207,6 +207,9 @@ module When
207
207
  #
208
208
  CalendarDepend = false
209
209
 
210
+ # @private
211
+ HashProperty = [:event]
212
+
210
213
  # デフォルトイベント名
211
214
  #
212
215
  # @return [String]
@@ -435,14 +438,15 @@ module When
435
438
 
436
439
  # return Array of Hash
437
440
  focused_notes.map {|note|
441
+ nobj = note_objects[note]
438
442
  case notes[note]
439
443
  when nil, false ; {}
440
- when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
444
+ when Hash ; {:note=>nobj.label}.merge(notes[note])
441
445
  else
442
- if note_objects[note].respond_to?(:to_note_hash)
443
- note_objects[note].to_note_hash(notes[note], dates)
446
+ if nobj.respond_to?(:to_note_hash)
447
+ nobj.to_note_hash(notes[note], dates)
444
448
  else
445
- {:note=>note_objects[note].label, :value=>notes[note]}
449
+ {:note=>nobj.kind_of?(String) ? nobj : nobj.label, :value=>notes[note]}
446
450
  end
447
451
  end
448
452
  }
@@ -1,2446 +1,2437 @@
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
- # 座標の記述に用いる諸オブジェクト
10
- #
11
- module When::Coordinates
12
- # 変換テーブル
13
- PRECISION = {'YEAR'=>When::YEAR, 'MONTH' =>When::MONTH, 'WEEK' =>When::WEEK, 'DAY' =>When::DAY,
14
- 'HOUR'=>When::HOUR, 'MINUTE'=>When::MINUTE, 'SECOND'=>When::SECOND, 'SYSTEM'=>When::SYSTEM}
15
- PERIOD = {'P1Y' =>When::YEAR, 'P1M' =>When::MONTH, 'P1W' =>When::WEEK, 'P1D' =>When::DAY,
16
- 'PT1H'=>When::HOUR, 'PT1M' =>When::MINUTE, 'PT1S' =>When::SECOND,
17
- '1Y' =>When::YEAR, '1M' =>When::MONTH, '1W' =>When::WEEK, '1D' =>When::DAY,
18
- '1h' =>When::HOUR, '1m' =>When::MINUTE, '1s' =>When::SECOND}
19
- VALUE = {'DATE'=>When::DAY, 'TIME' =>When::SYSTEM, 'DATE-TIME'=>When::SYSTEM} # RFC 5545
20
- PRECISION_NAME = PRECISION.invert
21
- PERIOD_NAME = {When::YEAR=>'P1Y' , When::MONTH=>'P1M', When::WEEK =>'P1W', When::DAY=>'P1D',
22
- When::HOUR=>'PT1H', When::MINUTE=>'PT1M', When::SECOND=>'PT1S'}
23
- MATCH = {'NS'=>/(N|S|北緯|南緯)/, 'EW'=>/(E|W|東経|西経)/}
24
-
25
- # 60進->10進変換(1/225度単位)
26
- #
27
- # @param [String] src 60進法で表した方向付きの数値
28
- # @param [String] dir 方向 ('NS' または 'EW')
29
- #
30
- # @return [Numeric] 10進変換した数値 (src が nil なら0.0を、Numeric なら 225*src を返す)
31
- #
32
- def self.to_deg_225(src, dir)
33
- case src
34
- when String
35
- src = src.gsub(/_+/,'').gsub(/@/, '.')
36
- return src.to_r * Spatial::DEGREE if (src =~ /E[-+]/ || src !~ MATCH[dir])
37
- sign = ($1 == dir[1..1]) ? -1 : +1
38
- value = src.gsub(MATCH[dir], '').strip
39
- if ((value + "00000") =~ /\A(\d+)\.(\d{2})(\d{2})(\d+)\z/)
40
- deg, min, sec, frac = $~[1..4]
41
- sec += "." + frac
42
- else
43
- deg, min, sec = value.split(/[^\d.]+/)
44
- end
45
- return sign * (deg.to_i * Spatial::DEGREE +
46
- (min||0).to_f * (Spatial::DEGREE/60.0) +
47
- (sec||0).to_f * (Spatial::DEGREE/3600.0))
48
- when NilClass
49
- 0.0
50
- when Numeric
51
- src * Spatial::DEGREE
52
- else
53
- raise TypeError, "Invalid Location Type"
54
- end
55
- end
56
-
57
- # 60進->10進変換(度単位)
58
- #
59
- # @param [String] src 60進法で表した方向付きの数値
60
- # @param [String] dir 方向 ('NS' または 'EW')
61
- #
62
- # @return [Numeric] 10進変換した数値 (src が nil なら0.0を、Numeric ならそのままsrcを返す)
63
- #
64
- def self.to_deg(src, dir)
65
- to_deg_225(src, dir) / Spatial::DEGREE
66
- end
67
-
68
- # 10進->60進変換
69
- #
70
- # @param [Numeric] src 数値
71
- # @param [String] dir 方向 ('NS' または 'EW')
72
- # @param [Integer] round 秒の小数点以下最大桁数
73
- #
74
- # @return [String] 60進変換した数値
75
- #
76
- def self.to_dms(src, dir, round=6)
77
- dir = (src >= 0) ? dir[0..0] : dir[1..1]
78
- deg, min = src.abs.divmod(1)
79
- min, sec = (60*min).divmod(1)
80
- sec = (60*10**round*sec).round
81
- fig = round + 2
82
- round.times do
83
- div, mod = sec.divmod(10)
84
- if mod == 0
85
- fig -= 1
86
- sec = div
87
- else
88
- break
89
- end
90
- end
91
- (['N','S'].include?(dir) ? "%02d.%02d%0#{fig}d%s" : "%03d.%02d%0#{fig}d%s") % [deg, min, sec, dir]
92
- end
93
-
94
- #
95
- # 剰余類
96
- #
97
- class Residue < When::BasicTypes::Object
98
-
99
- LabelProperty = 'label'
100
-
101
- class << self
102
- #
103
- # 曜日(剰余類)
104
- #
105
- # @param [Numeric] day 月曜を 0 とする七曜(剰余類)を返します
106
- # @param [String] day 最初の3文字から決定した七曜(剰余類)を返します。
107
- # 一致する七曜(剰余類)がない場合、名前の一致するその他の剰余類を探して返します。
108
- #
109
- # @return [When::Coordinates::Residue]
110
- #
111
- def day_of_week(day)
112
- return day if day.kind_of?(self)
113
-
114
- day ||= 0
115
- week = When.Resource('_co:Common::Week')
116
- dow = week.child
117
- case day
118
- when Numeric ; return dow[day]
119
- when /\AWeek\z/ ; return week
120
- when String ; day = When::EncodingConversion.to_internal_encoding(day)
121
- else ; return nil
122
- end
123
-
124
- day, shift = day.split(':', 2)
125
- residue = day.split('&').inject(nil) {|res,d|
126
- r = _day_of_week(d.strip, dow)
127
- return nil unless r
128
- res ? res & r : r
129
- }
130
- return residue unless shift
131
- shift = shift.to_i
132
- shift -= 1 if shift > 0
133
- residue >> shift
134
- end
135
- alias :to_residue :day_of_week
136
-
137
- def _day_of_week(day, dow)
138
- match = day[/\A...|^.{1,2}\z/]
139
- if match
140
- dow.size.times do |i|
141
- return dow[i] if dow[i].label.=~(/\A#{match}/i)
142
- end
143
- end
144
-
145
- ObjectSpace.each_object(self) do |object|
146
- return object if object.registered? && object.label.=~(/\A#{day}\z/)
147
- end
148
-
149
- return nil
150
- end
151
- private :_day_of_week
152
-
153
- # 汎用の mod
154
- #
155
- # @param [Numeric] nn 被除数
156
- # @param [Block] dd 手続き
157
- #
158
- # nn = rem + dd(quot)
159
- #
160
- # div = dd(quot+1)-dd(quot)
161
- #
162
- # となるように rem, div, quot を決めたい手続き
163
- #
164
- # @return [Array<Numeric>] [ quot, rem, div ]
165
- # nn を dd で「割った」商(quot), 余り(rem)および除数(div)を返します。
166
- # remは非負、quotは Integer。
167
- #
168
- def mod(nn, &dd)
169
- u = dd.call(0)
170
- y = ((nn-u)*256).divmod(dd.call(256)-u)[0] - 1
171
- w1, w2 = dd.call(y), dd.call(y+1)
172
- until w1 <= nn && nn < w2
173
- if w2 <= nn then y, w1, w2 = y+1, w2, dd.call(y+2)
174
- else y, w1, w2 = y-1, dd.call(y-1), w1
175
- end
176
- end
177
- return y, nn-w1, w2-w1
178
- end
179
-
180
- # 中国剰余
181
- #
182
- # @param [Array<Numeric>] a [ num, den ] den で割って num 余ることを示す配列
183
- # @param [Array<Numeric>] b [ num, den ] den で割って num 余ることを示す配列
184
- #
185
- # @return [Array<Numeric>] a と b の条件をともに満たす [ num, den ]
186
- #
187
- def _china(a, b)
188
- b, a = a, b if (a[1] <= b[1])
189
- g, p, q = _gcd(a[1], b[1])
190
- return [((b[0]*a[1]*q-a[0]*b[1]*p)*(a[1]*q-b[1]*p)) % (a[1]*b[1]), a[1]*b[1]] if (g == 1)
191
- r = a[0] % g
192
- s = b[0] % g
193
- return nil unless (r == s)
194
- m = _china([(a[0]-r).div(g), a[1].div(g)], [(b[0]-s).div(g), b[1].div(g)])
195
- return nil unless (m)
196
- return [m[0]*g+r, m[1]*g]
197
- end
198
-
199
- private
200
-
201
- # 最大公約数
202
- def _gcd(a,b)
203
- c, x = a.divmod(b)
204
- g = [[a, 1, 0],
205
- [b, c, 1]]
206
-
207
- until (x == 0) do
208
- k = g[1][0].div(x)
209
- g << [x, k * g[1][1] + g[0][1], k * g[1][2] + g[0][2]]
210
- g.shift
211
- x = g[0][0] % g[1][0]
212
- end
213
-
214
- return [g[1][0]] + g[0][1..2]
215
- end
216
- end
217
-
218
- # 剰余
219
- #
220
- # @return [Numeric]
221
- #
222
- attr_accessor :remainder
223
- protected :remainder=
224
-
225
- #
226
- #
227
- # @return [Integer] (>0)
228
- #
229
- attr_reader :divisor
230
-
231
- # 繰り上がり
232
- #
233
- # @return [Integer]
234
- #
235
- attr_accessor :carry
236
- protected :carry=
237
-
238
- # 単位
239
- #
240
- # @return [Hash] { String=>Numeric }
241
- #
242
- # Example : { 'day'=>11, 'year'=4 }
243
- #
244
- # 通日に適用するときは、11 ずらして計算 - 甲子日 = ユリウス日 11 + 60n
245
- #
246
- # 通年に適用するときは、 4 ずらして計算 - 甲子年 = 西暦 4 + 60n
247
- #
248
- attr_reader :units
249
-
250
- # units の指定
251
- #
252
- # @param [String] arg あらかじめ units に指定した単位を用いる
253
- #
254
- # @return [When::Coordinates::Residue]
255
- #
256
- def to(arg)
257
- return nil unless @units[arg]
258
- self.class.new(@remainder, @divisor, @carry, {arg=>@units[arg]})
259
- end
260
- alias :/ :to
261
-
262
- # オブジェクトの単位
263
- #
264
- # @return [String] 現在使用中の単位を返す
265
- #
266
- def event
267
- keys = @units.keys
268
- return (keys.size == 1) ? keys[0] : 'day'
269
- end
270
-
271
- # remainder の指定
272
- #
273
- # @param [Numeric] remainder 指定値を@remainderとする
274
- # @param [String] remainder When::Parts::Resource の has-a 関係によりString に対応する子供オブジェクトを取得
275
- #
276
- # @return [When::Coordinates::Residue]
277
- #
278
- def [](remainder)
279
- return super if !remainder.kind_of?(Numeric) || (child && child.length == @divisor)
280
- return self if remainder == 0 && !child
281
- remainder *= 1
282
- return self.class.new(@remainder+remainder, @divisor, @carry, @label, @format, @units) unless (child && child.length > 0)
283
- carry, remainder = (@remainder+remainder).divmod(@divisor)
284
- base = child.reverse.each do |residue|
285
- break residue if residue.remainder <= remainder
286
- end
287
- raise ArgumentError, "remainder out of range: #{remainder}" unless base.kind_of?(self.class)
288
- base = base.dup
289
- base.remainder = remainder
290
- base.carry += carry
291
- return base
292
- end
293
-
294
- # 典型的なイベントの発生間隔
295
- #
296
- # @param [String] event
297
- #
298
- # @return [When::TM::PeriodDuration]
299
- #
300
- def duration(event=self.event)
301
- When::TM::PeriodDuration.new(@divisor, When::Coordinates::PRECISION[event.upcase])
302
- end
303
-
304
- # 派生オブジェクトと元オブジェクトの remainder の差
305
- # (派生オブジェクトとは、元オブジェクトに[]演算を施して @remainder を変えたオブジェクト)
306
- #
307
- # @return [Integer]
308
- #
309
- # 派生オブジェクトでない場合 : 自身の remainder
310
- #
311
- # 派生オブジェクトである場合 : 派生オブジェクトと元オブジェクトの remainder の差
312
- #
313
- def difference
314
- @difference ||= (registered? || iri !~ /:/) ? @remainder : @remainder - When.Resource(iri).remainder
315
- end
316
-
317
- # remainderの加算
318
- #
319
- # other : Numeric
320
- #
321
- # @return [When::Coordinates::Residue]
322
- #
323
- def +(other)
324
- carry, remainder = (@remainder + other).divmod(@divisor)
325
- return self.class.new(remainder, @divisor, @carry+carry, @units)
326
- end
327
-
328
- # remainderの減算
329
- #
330
- # @param [Numeric] other
331
- #
332
- # @return [When::Coordinates::Residue]
333
- #
334
- def -(other)
335
- carry, remainder = (@remainder - other).divmod(@divisor)
336
- return self.class.new(remainder, @divisor, @carry+carry, @units)
337
- end
338
-
339
- # carryの加算
340
- #
341
- # @param [Numeric] other
342
- #
343
- # @return [When::Coordinates::Residue]
344
- #
345
- def >>(other)
346
- return self.class.new(@remainder, @divisor, @carry+other, @units)
347
- end
348
-
349
- # carryの減算
350
- #
351
- # @param [Numeric] other
352
- #
353
- # @return [When::Coordinates::Residue]
354
- #
355
- def <<(other)
356
- return self.class.new(@remainder, @divisor, @carry-other, @units)
357
- end
358
-
359
- # 剰余類の共通集合
360
- #
361
- # @param [When::Coordinates::Residue] other
362
- #
363
- # @return [When::Coordinates::Residue]
364
- #
365
- def &(other)
366
- case other
367
- when Residue
368
- if self.units == other.units
369
- m = self.class._china([@remainder, @divisor], [other.remainder, other.divisor])
370
- u = units.dup
371
- else
372
- keys = units.keys & other.units.keys
373
- keys = ['day'] if keys == []
374
- return nil unless (keys.size==1)
375
- self_base = self.units[keys[0]] || 0
376
- other_base = other.units[keys[0]] || 0
377
- m = self.class._china([@remainder, @divisor],
378
- [(other.remainder+other_base-self_base) % other.divisor, other.divisor])
379
- u = {keys[0]=>self_base}
380
- end
381
- return nil unless (m)
382
- return self.class.new(m[0], m[1], @carry, u)
383
- when Pair
384
- return Pair.new(self & other.trunk, other.branch)
385
- when Numeric
386
- keys = @units.keys
387
- d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
388
- c, m = (other-d).divmod(@divisor)
389
- c += 1 if (m > @remainder)
390
- return (c + @carry) * @divisor + @remainder + d
391
- else
392
- position = When::TM::Position.any_other(other)
393
- raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
394
- return position & self
395
- end
396
- end
397
-
398
- # 剰余
399
- #
400
- # @param [When::TM::TemporalPosition, Numeric, When::Coordinates::Pair] other
401
- #
402
- # @return ['other' と同じクラス]
403
- #
404
- def %(other)
405
- case other
406
- when Pair
407
- return Pair.new(self % other.trunk, other.branch)
408
- when Numeric
409
- keys = @units.keys
410
- d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
411
- return (other-d) % @divisor
412
- else
413
- position = When::TM::Position.any_other(other)
414
- raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
415
- return self[position % self]
416
- end
417
- end
418
-
419
- # Enumerator の生成
420
- #
421
- # @overload initialize()
422
- #
423
- # @overload initialize(range, count_limit=nil)
424
- # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
425
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
426
- #
427
- # @overload initialize(first, direction, count_limit)
428
- # @param [When::TM::TemporalPosition] first 始点
429
- # @param [Symbol] direction :forward - 昇順, :reverse - 降順
430
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
431
- #
432
- # @return [When::Coordinates::Residue::BestRationalApproximations] 引数なしの場合
433
- # @return [When::Coordinates::Residue::Enumerator] 引数ありの場合
434
- #
435
- def _enumerator(*args)
436
- length = args.length
437
- length -= 1 if args[-1].kind_of?(Hash)
438
- args.unshift(self)
439
- (length==0) ? BestRationalApproximations.new(self, *args) : Enumerator.new(*args)
440
- end
441
- alias :to_enum :_enumerator
442
- alias :enum_for :_enumerator
443
-
444
- # オブジェクトの生成
445
- #
446
- # @overload initialize(remainder, divisor, carry=0, label=nil, format=nil, units={})
447
- # @param [Numeric] remainder 剰余
448
- # @param [Integer] divisor 法
449
- # @param [Integer] carry 繰り上がり
450
- # @param [String, When::BasicTypes::M17n] label 名前
451
- # @param [String, When::BasicTypes::M17n] format 名前の書式
452
- # @param [Hash] units 単位(下記の通り)
453
- # @option units [Numeric] 'day' 通日に対して使用する場合のオフセット
454
- # @option units [Numeric] 'year' 通年に対して使用する場合のオフセット
455
- #
456
- def initialize(*args)
457
- # units の取得
458
- options =_get_options(args).dup
459
- units = {}
460
- list = []
461
- options.each_pair do |key, value|
462
- if (PRECISION[key.upcase])
463
- list << key
464
- units[key.downcase] = _to_num(value)
465
- end
466
- end
467
- list.each do |key|
468
- options.delete(key)
469
- end
470
- options['units'] ||= {}
471
- options['units'].update(units)
472
- _set_variables(options)
473
- @units ||= {}
474
-
475
- # その他の変数
476
- remainder, divisor, carry, label, format = args
477
- @label = label || @label
478
- @label = m17n(@label, nil, nil, options) if (@label)
479
- @format = format || @format
480
- @format = m17n(@format, nil, nil, options) if (@format)
481
- _sequence
482
- @remainder = _to_num(remainder || @remainder)
483
- @divisor = _to_num(divisor || @divisor )
484
- @carry = _to_num(carry || @carry )
485
- raise RangeError, "Divisor shoud be Positive Numeric" if (@divisor <= 0)
486
- carry, @remainder = @remainder.divmod(@divisor)
487
- @carry += carry
488
- end
489
-
490
- private
491
-
492
- alias :_method_missing :method_missing
493
-
494
- # その他のメソッド
495
- #
496
- # When::Coordinate::Residue で定義されていないメソッドは
497
- # 指定の桁での剰余算とみなす
498
- #
499
- def method_missing(name, *args, &block)
500
- return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name) ||
501
- !@units.key?(name.to_s.downcase)
502
- instance_eval %Q{
503
- def #{name}(*args, &block)
504
- self[args[0] % self.to("#{name.to_s.downcase}")]
505
- end
506
- } unless When::Parts::MethodCash.escape(name)
507
- self[args[0] % self.to(name.to_s.downcase)]
508
- end
509
-
510
- # 数値化
511
- def _to_num(s)
512
- case s
513
- when Numeric ; return s
514
- when nil ; return 0
515
- when /\.|E/ ; return s.to_f
516
- else ; return s.to_i
517
- end
518
- end
519
-
520
- #
521
- # 最良近似分数の系列を生成する Enumerator
522
- #
523
- class BestRationalApproximations < When::Parts::Enumerator
524
-
525
- # Enumerator の巻き戻し
526
- #
527
- # @return [void]
528
- #
529
- def _rewind
530
- @z = @x/@y
531
- @k = @z.floor
532
- @p = [1,@k]
533
- @q = [0, 1]
534
- super
535
- end
536
-
537
- # 最良近似分数を生成する
538
- #
539
- # @return [Array<Numeric>] ( remainder, divisor, error )
540
- # [ remainder (Integer) 分子 ]
541
- # [ divisor (Integer) 分母 ]
542
- # [ error (Float) 誤差 ]
543
- #
544
- def succ
545
- value = @current
546
- if (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ||
547
- (@error.kind_of?(Numeric) && @e && @error >= @e.abs)
548
- @current = nil
549
- else
550
- if @z==@k
551
- @e = 0
552
- @current = [@p[1], @q[1], 0]
553
- else
554
- @z = 1.0/(@z-@k)
555
- @k = @z.floor
556
- @e = @p[1].to_f/@q[1]-@x.to_f/@y
557
- @current = [@p[1], @q[1], @e]
558
- @p = [@p[1], @p[1]*@k + @p[0]]
559
- @q = [@q[1], @q[1]*@k + @q[0]]
560
- end
561
- @count += 1
562
- end
563
- return value
564
- end
565
-
566
- # オブジェクトの生成
567
- #
568
- # @overload initialize(parent, options={})
569
- # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
570
- # @param [Hash] options 下記の通り
571
- # @option options [Numeric] :error 収束とみなす誤差(デフォルト 1E-5)
572
- # @option options [Integer] :count_limit 最大繰り返し回数
573
- #
574
- def initialize(*args)
575
- @y = args[0].divisor
576
- @x = args[0].remainder + @y * args[0].carry
577
- super
578
- @error = @options[:error] || 1e-15
579
- @count_limit = @options[:count_limit]
580
- end
581
- end
582
-
583
- #
584
- # 指定の剰余となる通日or通年を生成する Enumerator
585
- #
586
- class Enumerator < When::Parts::Enumerator
587
-
588
- #
589
- # 次の通日or通年を得る
590
- #
591
- # @return [When::TM::TemporalPosition]
592
- #
593
- def succ
594
- value = @current
595
- @current = (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ? nil :
596
- (@current==:first) ? @first :
597
- (@direction==:forward) ? @first & @parent >> @count : @first & @parent << @count
598
- @count += 1
599
- return value
600
- end
601
-
602
- # オブジェクトの生成
603
- #
604
- # @overload initialize(parent, range, count_limit=nil)
605
- # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
606
- # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
607
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
608
- #
609
- # @overload initialize(parent, first, direction, count_limit)
610
- # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
611
- # @param [When::TM::TemporalPosition] first 始点
612
- # @param [Symbol] direction :forward - 昇順, :reverse - 降順
613
- # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
614
- #
615
- def initialize(*args)
616
- case args[1]
617
- when When::TimeValue
618
- first = args[1] & args[0]
619
- first = args[1] & (args[0] << 1) if args[2] == :reverse && first > args[1]
620
- args[1] = first
621
- when Range
622
- first = args[1].first & args[0]
623
- args[1] = (args[1].exclude_end?) ? (first...args[1].last) : (first..args[1].last)
624
- else
625
- raise TypeError, "Second Argument should be 'When::TM::(Temporal)Position'"
626
- end
627
- @period = When::TM::PeriodDuration.new(args[0].divisor,
628
- When::Coordinates::PRECISION[args[0].units] || When::DAY)
629
- super(*args)
630
- end
631
- end
632
- end
633
-
634
- # 暦座標
635
- #
636
- # 暦座標の特性を定義する
637
- #
638
- class Index < When::BasicTypes::Object
639
-
640
- # 日時要素の要素数
641
- #
642
- # @return [Integer]
643
- #
644
- # 「時」なら 24, 「分」なら 60
645
- #
646
- # 日数や太陰太陽暦の月数のように不定の場合は nil
647
- #
648
- attr_reader :unit
649
-
650
- # 日時要素の下限
651
- #
652
- # @return [Integer]
653
- #
654
- # 年月日は 1, 時分秒は 0
655
- #
656
- attr_reader :base
657
-
658
- # 日時要素名(幹)
659
- #
660
- # @return [When::BasicTypes::M17n]
661
- #
662
- # 月の名称の配列などに用いる
663
- #
664
- attr_reader :trunk
665
-
666
- # 座標値のシフト
667
- #
668
- # @return [Integer](デフォルト 0)
669
- #
670
- # 月 - 名称を trunk 配列の何番目(0オリジン)からとるかを指定
671
- #
672
- # 日 - 剰余類のシフトを指定
673
- #
674
- attr_reader :shift
675
-
676
- # 日時要素名(枝)
677
- #
678
- # @return [Hash] { Numeric=>When::BasicTypes::M17n }
679
- #
680
- # When::Coordinates::Pair の branch の値からprefix
681
- #
682
- # (「閏」などの文字列)を引くための Hash
683
- #
684
- attr_reader :branch
685
-
686
- # 日時要素名(幹枝)
687
- #
688
- # @return [Hash] { When::Coordinates::Pair=>When::BasicTypes::M17n }
689
- #
690
- # When::Coordinates::Pair から 日時要素名を引くための Hash
691
- #
692
- # 初期化時に @trunk, @branch から自動的に生成する
693
- #
694
- attr_reader :trunk_branch
695
-
696
- # インデクス(幹枝)
697
- #
698
- # @return [Hash] { String=>When::Coordinates::Pair }
699
- #
700
- # 日時要素名から When::Coordinates::Pair を引くための Hash
701
- #
702
- # 初期化時に @trunk, @branch から自動的に生成する
703
- #
704
- attr_reader :index
705
-
706
- # @private
707
- def self.precision(specification)
708
- return specification.to_i if (specification.kind_of?(Numeric))
709
- return (PRECISION[specification] || VALUE[specification] || When::SYSTEM)
710
- end
711
-
712
- private
713
-
714
- def _normalize(args=[], options={})
715
- @label = m17n(@label, nil, nil, @options) if @label
716
- @unit = @unit.to_i if @unit
717
- @base = (@base || 1).to_i
718
- @trunk = When::BasicTypes::M17n.labels(args[0]) unless args.empty?
719
- @shift = (@shift || 0).to_i
720
- @keys = []
721
- if @trunk
722
- if @trunk.kind_of?(Array)
723
- m17n = {}
724
- @trunk.length.times do |i|
725
- m17n[(i-@shift) % @trunk.length + @base] = @trunk[i]
726
- end
727
- @trunk = m17n
728
- end
729
- raise TypeError, "Trunk must be Hash" unless @trunk.kind_of?(Hash)
730
- @trunk.values.each do |v|
731
- @keys |= v.keys if v.kind_of?(When::Locale)
732
- end
733
- end
734
- if @branch
735
- if @branch.kind_of?(Array)
736
- m17n = {}
737
- @branch.length.times do |i|
738
- m17n[i] = @branch[i]
739
- end
740
- @branch = m17n
741
- end
742
- raise TypeError, "Branch must be Hash" unless @branch.kind_of?(Hash)
743
- @branch.each_pair do |key, value|
744
- value = When::BasicTypes::M17n.label(value)
745
- @keys |= value.keys if value.kind_of?(When::Locale)
746
- @branch[key] = value
747
- end
748
- end
749
- @trunk_branch = {}
750
- @index = {}
751
- if @trunk
752
- if @branch
753
- @trunk.keys.each do |t|
754
- @branch.keys.each do |b|
755
- @trunk_branch[Pair._en_pair(t,b)] = @trunk[t].prefix(@branch[b])
756
- end
757
- end
758
- else
759
- @trunk_branch = @trunk
760
- end
761
- @trunk_branch.each_pair do |k,v|
762
- v.values.each do |s|
763
- @index[s] = k
764
- end
765
- end
766
- end
767
- end
768
- end
769
-
770
- # 暦座標値
771
- #
772
- # 暦座標の値を表現する
773
- #
774
- class Pair < Numeric
775
- DL0 = {'-'=> 0, '.'=> 0, ':'=> 0, ','=> 0, ' '=> 0, '@'=>-2 }
776
- DL1 = {'!'=>-2.5, '%'=>-2, '&'=>-1.5, '*'=>-1, '+'=>-0.5,
777
- '<'=> 0.5, '='=> 1, '>'=> 1.5, '?'=> 2 }
778
- DL2 = DL1.invert
779
-
780
- class << self
781
- #
782
- # source を Numeric に変換する
783
- #
784
- # @param [Object] source
785
- # @param [Numeric] default source nil の場合の代替値
786
- #
787
- # @return [Numeric]
788
- #
789
- def _en_number(source, default=0)
790
- return default unless(source)
791
- integer = source.to_i
792
- float = source.to_f
793
- return (integer==float) ? integer : float
794
- end
795
-
796
- #
797
- # (source, branch) を When::Coordinates::Pair に変換する
798
- #
799
- # @param [Object] source
800
- # @param [Numeric] branch
801
- #
802
- # @return [When::Coordinates::Pair]
803
- #
804
- def _force_pair(source, branch=nil)
805
- case source
806
- when self
807
- return source
808
- when Numeric
809
- return new(_en_number(source), branch)
810
- when String
811
- tail = source[-1..-1]
812
- branch = DL0[tail] || DL1[tail]
813
- source = (branch) ? source[0..-2] : source.dup
814
- trunk = (source =~ /\.|E/i) ? source.to_f : source.to_i
815
- return new(trunk, branch)
816
- else
817
- raise TypeError, "Irregal type for #{self} :#{source}"
818
- end
819
- end
820
-
821
- #
822
- # branch が有効なら (source, branch) を When::Coordinates::Pair に変換する。
823
- #
824
- # @param [Object] trunk
825
- # @param [Numeric, String] branch
826
- #
827
- # @return [trunk自体] branch が無効
828
- # @return [When::Coordinates::Pair] branch が有効(非0)
829
- #
830
- def _en_pair(trunk, branch=nil)
831
- return trunk if (trunk.kind_of?(self))
832
- branch = DL0[branch] || DL1[branch] || branch
833
- trunk = _en_number(trunk) if (trunk)
834
- return trunk unless (branch && branch != 0)
835
- return new(trunk, branch)
836
- end
837
-
838
- #
839
- # When::Coordinates::Pair の trunk と branch の和をとって Numeric 化する
840
- #
841
- # @param [When::Coordinates::Pair] source trunk と branch の和を返す
842
- # @param [Array] source 各要素を _de_pair した Arrayを返す
843
- # @param [その他 Numeric] source をそのまま返す
844
- #
845
- # @return [Numeric, Array<Numeric>]
846
- #
847
- def _de_pair(source)
848
- case source
849
- when Array
850
- return (source.map!{|v| _de_pair(v)})
851
- when self
852
- return source.sum
853
- when Numeric
854
- return source
855
- else
856
- raise TypeError, "Irregal type for #{self}"
857
- end
858
- end
859
-
860
- #
861
- # 文字列を When::Coordinates::Pair のArray 化する
862
- #
863
- # @param [String] source
864
- #
865
- # @return [Array<When::Coordinates::Pair>]
866
- #
867
- def _en_pair_array(source)
868
- source = $1 if source=~/\A\s*\[?(.+?)\]?\s*\z/
869
- source.split(/,/).map {|v|
870
- v =~ /\A\s*(.+?)([^\d\s])?\s*\z/
871
- _en_pair($1, $2)
872
- }
873
- end
874
-
875
- #
876
- # 日付を When::Coordinates::Pair のArray 化する
877
- #
878
- # @param [String] source
879
- #
880
- # @return [Array<When::Coordinates::Pair>]
881
- #
882
- def _en_pair_date_time(source)
883
- source = $1 if source =~ /\A\s*\[(.+)\]\s*\z/
884
- trunk, branch, *rest = source.strip.split(/([^\d])/)
885
- if trunk == ''
886
- sign = branch
887
- trunk, branch, *rest = rest
888
- trunk = sign + trunk if trunk
889
- end
890
- pairs = []
891
- while (trunk)
892
- pairs << _en_pair(trunk, branch)
893
- trunk, branch, *rest = rest
894
- end
895
- return pairs
896
- end
897
-
898
- #
899
- # branch文字を意識して、書式文字列により When::Coordinates::Pair の Array を文字列化
900
- #
901
- # @overload _format(format, list)
902
- # @param [String] format 書式文字列
903
- # @param [Array] list 書式に従って文字列化されるオブジェクトの Array
904
- #
905
- # @return [String]
906
- #
907
- # @note %0s は文字列引数の表示を抑制するための指定となる。C言語のprintfと異なるので注意。
908
- #
909
- def _format(m17ns)
910
- index = 1
911
- m17ns[0].scan(/(%(?:(\d)\$)?[-+0# ]?[*\d.$]*[cspdiuboxXfgeEG])([-.:])?|(%%|.)/).inject('') { |form, m17n|
912
- t,i,s,c = m17n
913
- case t
914
- when '%0s'
915
- m17n[index..index] = nil
916
- when nil
917
- form += c
918
- else
919
- suffix = DL2[m17ns[i ? i.to_i : index]*0] || s if s
920
- form += t + ({nil=>'', '%'=>'%%'}[suffix] || suffix)
921
- index += t.length - t.gsub(/\*/,'').length + 1
922
- end
923
- form
924
- } % m17ns[1..-1].map { |v| v.kind_of?(self) ? v.trunk : v }
925
- end
926
- end
927
-
928
- #
929
- # 暦要素の幹
930
- #
931
- # @return [Numeric] 年番号,月番号,日番号など暦要素の幹となる部分
932
- #
933
- attr_accessor :trunk
934
-
935
- #
936
- # 暦要素の枝
937
- #
938
- # @return [Numeric] 暦要素のうち(閏であるかなど)幹で表現しきれない枝の部分
939
- #
940
- attr_accessor :branch
941
-
942
- #
943
- # 暦要素の幹と枝の和
944
- #
945
- # @return [Numeric]
946
- #
947
- # @note
948
- # 個別の実装において、本変数が When::TM::Calendar や When::TM::Clock が扱うべき
949
- # 座標値を表すように配慮されている。
950
- #
951
- # 例: 会計年度など年の変わり目が暦年と異なる場合、trunk+=1, branch-=1 として、
952
- # trunk が会計年度, sum が暦年を表現するようにできる。この場合、trunk は表記上の
953
- # 年、branch は会計年度と暦年にずれがあるという情報を表現していることになる。
954
- #
955
- attr_reader :sum
956
-
957
- #
958
- # trunk の更新
959
- #
960
- # @param [Numeric] trunk 新しい trunk
961
- #
962
- # @return [When::Coordinates::Pair] 更新結果
963
- #
964
- def trunk=(trunk)
965
- @trunk = trunk
966
- _normalize
967
- self
968
- end
969
-
970
- #
971
- # branch の更新
972
- #
973
- # @param [Numeric] branch 新しい branch
974
- #
975
- # @return [When::Coordinates::Pair] 更新結果
976
- #
977
- def branch=(branch)
978
- @branch = branch
979
- _normalize
980
- self
981
- end
982
-
983
- # 属性 @sum を取得する
984
- #
985
- # @return [Numeric]
986
- #
987
- # @note
988
- # When::Coordinates::Pair 以外の Numeric では、単項演算 + は恒等変換になる。
989
- # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
990
- # 否かを判断することなく、暦要素に単項演算 + を施すことによって、必要な暦要素を取得できる。
991
- def +@
992
- return @sum
993
- end
994
-
995
- # trunk の符号を反転する
996
- #
997
- # @return [When::Coordinates::Pair] Pair.new(-@trunk, @branch)
998
- #
999
- def -@
1000
- return self.class.new(-@trunk, @branch)
1001
- end
1002
-
1003
- # @trunk, @branch を取得する
1004
- #
1005
- # @param [Integer] other (1, 0, -1)
1006
- #
1007
- # @return [Numeric]
1008
- # [ other == 1 - @trunk ]
1009
- # [ other == 0 - @branch ]
1010
- # [ other == -1 - -@trunk ]
1011
- #
1012
- # @note
1013
- # When::Coordinates::Pair 以外の Numeric では、1 による乗算は恒等変換になる。
1014
- # また、0 による乗算は恒に 0になる。
1015
- # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
1016
- # 否かを判断することなく、暦要素に 1 による乗算を施すことによって、trunk に相当する値を、
1017
- # 0 による乗算を施すことによって、branch に相当する値を取得できる。
1018
- def *(other)
1019
- case other
1020
- when 1 ; @trunk
1021
- when 0 ; @branch
1022
- when -1 ; -@trunk
1023
- else ; raise ArgumentError, "Irregal designation : #{other}"
1024
- end
1025
- end
1026
-
1027
- # 加算
1028
- #
1029
- # @param [Numeric] other
1030
- #
1031
- # @return [When::Coordinates::Pair]
1032
- # other When::Coordinates::Pair でない場合、trunk に対する加算となる
1033
- #
1034
- def +(other)
1035
- return self.class.new(@trunk + other, @branch) unless other.kind_of?(self.class)
1036
- return self.class.new(@trunk + other.trunk, @branch + other.branch)
1037
- end
1038
-
1039
- # 減算
1040
- #
1041
- # @param [Numeric] other
1042
- #
1043
- # @return [When::Coordinates::Pair]
1044
- # other When::Coordinates::Pair でない場合、trunk に対する減算となる
1045
- #
1046
- def -(other)
1047
- return self.class.new(@trunk - other, @branch) unless other.kind_of?(self.class)
1048
- return self.class.new(@trunk - other.trunk, @branch - other.branch)
1049
- end
1050
-
1051
- # 商と剰余
1052
- #
1053
- # @param [Numeric] other
1054
- #
1055
- # @return [When::Coordinates::Pair]
1056
- # trunk に対する divmod となる
1057
- #
1058
- def divmod(other)
1059
- div, mod = @trunk.divmod(other)
1060
- return div, self.class.new(mod, @branch)
1061
- end
1062
-
1063
- # 剰余
1064
- #
1065
- # @param [Numeric] other
1066
- #
1067
- # @return [When::Coordinates::Pair]
1068
- # trunk に対する % となる
1069
- #
1070
- def %(other)
1071
- self.class.new(@trunk % other, @branch)
1072
- end
1073
-
1074
- # 比較
1075
- #
1076
- # @param [Numeric] other
1077
- #
1078
- # @return [Integer] (負,0,正)
1079
- # trunk の比較が優先される
1080
- #
1081
- def <=>(other)
1082
- other = self.class._force_pair(other)
1083
- (@trunk <=> other.trunk).nonzero? || (@branch <=> other.branch)
1084
- end
1085
-
1086
- # 文字列化
1087
- #
1088
- # @param [String] zero @branch == 0 を表現する文字列
1089
- #
1090
- # @return [String]
1091
- #
1092
- def to_s(zero='')
1093
- return @trunk.to_s + (@branch==0 ? zero : DL2[@branch])
1094
- end
1095
-
1096
- # 強制型変換
1097
- # @private
1098
- def coerce(other)
1099
- [self.class._force_pair(other), self]
1100
- end
1101
-
1102
- private
1103
-
1104
- # オブジェクトの生成
1105
- #
1106
- # @param [Numeric] trunk 幹
1107
- # @param [Numeric] branch 枝
1108
- #
1109
- def initialize(trunk, branch=nil)
1110
- @trunk = trunk || 0
1111
- @branch = branch || 0
1112
- _normalize
1113
- end
1114
-
1115
- def _normalize
1116
- @trunk = @trunk.to_i if (@trunk.kind_of?(Float) && @trunk.to_i == @trunk.to_f)
1117
- @sum = @trunk
1118
- if (@branch.kind_of?(Numeric))
1119
- @branch = @branch.to_i if (@branch.to_i == @branch.to_f)
1120
- @sum += @branch if (@trunk)
1121
- end
1122
- end
1123
-
1124
- # その他のメソッド
1125
- # When::Coordinates:Pair で定義されていないメソッドは
1126
- # 処理を @sum (type:Numeric) に委譲する
1127
- #
1128
- def method_missing(name, *args, &block)
1129
- self.class.module_eval %Q{
1130
- def #{name}(*args, &block)
1131
- @sum.send("#{name}", *args, &block)
1132
- end
1133
- } unless When::Parts::MethodCash.escape(name)
1134
- @sum.send(name, *args, &block)
1135
- end
1136
- end
1137
-
1138
- # 暦座標値
1139
- #
1140
- # 閏秒のある暦座標の値を表現する
1141
- #
1142
- class LeapSeconds < Pair
1143
-
1144
- #
1145
- # 閏秒の単位 / When::TM::Duration::SYSTEM
1146
- #
1147
- # @return [Numeric]
1148
- #
1149
- attr_reader :second
1150
-
1151
- #
1152
- # trunk の整数部
1153
- #
1154
- # @return [Integer]
1155
- #
1156
- attr_reader :int
1157
-
1158
- #
1159
- # trunk の小数部
1160
- #
1161
- # @return [Float]
1162
- #
1163
- attr_reader :frac
1164
-
1165
- # 加算
1166
- #
1167
- # @param [Numeric] other
1168
- #
1169
- # @return [When::Coordinates::LeapSeconds]
1170
- # other When::Coordinates::LeapSeconds でない場合、trunk に対する加算となる
1171
- #
1172
- def +(other)
1173
- return self.class.new(@trunk + +other, @branch, @second) unless other.kind_of?(self.class)
1174
- return self.class.new(@trunk + other.trunk, @branch + other.branch, @second)
1175
- end
1176
-
1177
- # 減算
1178
- #
1179
- # @param [Numeric] other
1180
- #
1181
- # @return [When::Coordinates::LeapSeconds]
1182
- # other When::Coordinates::LeapSeconds でない場合、trunk に対する減算となる
1183
- #
1184
- def -(other)
1185
- return self.class.new(@trunk - +other, @branch, @second) unless other.kind_of?(self.class)
1186
- return self.class.new(@trunk - other.trunk, @branch - other.branch, @second)
1187
- end
1188
-
1189
- # 商と剰余
1190
- #
1191
- # @param [Numeric] other
1192
- #
1193
- # @return [When::Coordinates::LeapSeconds]
1194
- # trunk に対する divmod となる
1195
- #
1196
- def divmod(other)
1197
- div, mod = @trunk.divmod(other)
1198
- return div, self.class.new(mod, @branch, @second)
1199
- end
1200
-
1201
- # 比較
1202
- #
1203
- # @param [Numeric] other
1204
- #
1205
- # @return [Integer] (負,0,正)
1206
- # trunk の整数部, branch, trunk の小数部の順に比較する
1207
- #
1208
- def <=>(other)
1209
- other = self.class.new(+other, 0, @second) unless other.kind_of?(self.class)
1210
- raise ArgumentError, "length of unit 'second' mismatch" unless @second == other.second
1211
- (@int <=> other.int).nonzero? || (@branch <=> other.branch).nonzero? || (@frac <=> other.frac)
1212
- end
1213
-
1214
- # 文字列化
1215
- #
1216
- # @param [String] zero @branch==0 を表現する文字列
1217
- #
1218
- # @return [String]
1219
- #
1220
- def to_s(zero='')
1221
- return @trunk.to_s + (@branch==0 ? zero : DL2[(@branch/@second).floor])
1222
- end
1223
-
1224
- # 強制型変換
1225
- # @private
1226
- def coerce(other)
1227
- [self.class.new(other, 0, @second), self]
1228
- end
1229
-
1230
- # オブジェクトの生成
1231
- #
1232
- # @param [Numeric] trunk 幹
1233
- # @param [Numeric] branch 枝
1234
- # @param [Numeric] second 閏秒の単位
1235
- #
1236
- def initialize(trunk, branch, second)
1237
- @second = second
1238
- @int, @frac = trunk.divmod(second)
1239
- super(trunk, branch)
1240
- end
1241
- end
1242
-
1243
- #
1244
- # 空間位置
1245
- #
1246
- class Spatial < When::BasicTypes::Object
1247
-
1248
- LabelProperty = 'label'
1249
-
1250
- include When::Ephemeris
1251
-
1252
- require 'when_exe/ephemeris/eclipse'
1253
-
1254
- class << self
1255
- # When::Coordinates::Spatial のグローバルな設定を行う
1256
- #
1257
- # @param [When::Coordinates::Spatial, String] location デフォルトの空間位置を使用する場合、指定する
1258
- #
1259
- # @return [void]
1260
- #
1261
- # @note
1262
- # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
1263
- # このため、本メソッド自体はスレッドセーフでない。
1264
- #
1265
- def _setup_(location=nil)
1266
- @_lock_ = Mutex.new if When.multi_thread
1267
- @_pool = {}
1268
- @default_location = location
1269
- end
1270
-
1271
- # デフォルトの空間位置
1272
- #
1273
- # @param [When::Coordinates::Spatial, String] local デフォルトの空間位置
1274
- #
1275
- # @return [When::Coordinates::Spatial, String]
1276
- #
1277
- # @note
1278
- # @default_locationは、原則、ライブラリ立ち上げ時に _setup_ で初期化する。
1279
- # 以降、@default_locationに代入を行っても、すでに生成した When::TM::TemporalPosition 等には反映されない。
1280
- #
1281
- def default_location=(local)
1282
- if @_pool
1283
- @default_location = local
1284
- else
1285
- _setup_(local)
1286
- end
1287
- end
1288
-
1289
- # 設定情報を取得する
1290
- #
1291
- # @return [Hash] 設定情報
1292
- #
1293
- def _setup_info
1294
- {:location => @default_location}
1295
- end
1296
-
1297
- # デフォルトの空間位置を読みだす
1298
- #
1299
- # @return [When::Coordinates::Spatial]
1300
- #
1301
- def default_location
1302
- _default_location[1]
1303
- end
1304
-
1305
- # デフォルトの空間位置が When::TM::Clock のローカルタイムから生成されたか?
1306
- #
1307
- # @return [true, false]
1308
- #
1309
- def is_default_location_derived?
1310
- location = _default_location
1311
- location[0] && location[1]
1312
- end
1313
-
1314
- private
1315
-
1316
- # 共通処理
1317
- def _default_location
1318
- case @default_location
1319
- when nil ;
1320
- when Array ; return @default_location unless @default_location[0]
1321
- when String ; return (@default_location = [false, When.Resource(@default_location)])
1322
- else ; return (@default_location = [false, @default_location])
1323
- end
1324
- timezone = When::TM::Clock.local_time
1325
- location = timezone.location if timezone.kind_of?(When::Parts::Timezone)
1326
- return [true, location]
1327
- end
1328
- end
1329
-
1330
- # @private
1331
- HashProperty = [:label, [:alt, 0.0], [:datum, When::Ephemeris::Earth], :ref]
1332
-
1333
- # Degree / Internal Location Unit(16")
1334
- #
1335
- # (3600 を 2 の因数で割りつくした値を単位とする)
1336
- DEGREE = 225
1337
-
1338
- # 黄道座標 (ecliptic coordinate system)
1339
- ECLIPTIC = 0
1340
-
1341
- # 赤道座標 (equatorial coordinate system)
1342
- EQUATORIAL = 1
1343
-
1344
- # 赤道座標[時角] (equatorial coordinate system with hour angle)
1345
- EQUATORIAL_HA = 2
1346
-
1347
- # 地平座標 (horizontal coordinate system)
1348
- HORIZONTAL = 3
1349
-
1350
- # 惑星中心の高度
1351
- CENTER = :center
1352
-
1353
- # 北緯を正とする緯度 / 16秒
1354
- #
1355
- # @return [Numeric]
1356
- #
1357
- attr_reader :lat
1358
-
1359
- # 東経を正とする経度 / 16秒
1360
- #
1361
- # @return [Numeric]
1362
- #
1363
- attr_reader :long
1364
-
1365
- # 高度 / m
1366
- #
1367
- # @return [Numeric]
1368
- # @return [:Center] 天体の中心の場合
1369
- #
1370
- attr_reader :alt
1371
-
1372
- # 座標系
1373
- #
1374
- # @return [When::Ephemeris::Datum]
1375
- #
1376
- attr_reader :datum
1377
-
1378
- # 参照
1379
- #
1380
- # @return [String] URI
1381
- #
1382
- attr_reader :ref
1383
-
1384
- # 時間帯(オプショナル)
1385
- #
1386
- # @return [TZInfo::CountryTimezone]
1387
- #
1388
- # TZInfoライブラリから経緯度を取得して使用する
1389
- #
1390
- attr_reader :tz
1391
-
1392
- # 緯度文字列
1393
- #
1394
- # @param [Integer] round 秒の小数点以下最大桁数
1395
- #
1396
- # @return [String] 緯度文字列(DD.MMSSsss[NS])
1397
- #
1398
- def lat_s
1399
- When::Coordinates.to_dms(lat / When::Coordinates::Spatial::DEGREE, 'NS', round=6)
1400
- end
1401
-
1402
- # 経度文字列
1403
- #
1404
- # @param [Integer] round 秒の小数点以下最大桁数
1405
- #
1406
- # @return [String] 経度文字列(DDD.MMSSsss[EW])
1407
- #
1408
- def long_s
1409
- When::Coordinates.to_dms(long / When::Coordinates::Spatial::DEGREE, 'EW', round=6)
1410
- end
1411
-
1412
- # 高度 / m
1413
- #
1414
- # @return [Numeric]
1415
- # @return [:Center] 天体の中心の場合
1416
- #
1417
- attr_reader :alt
1418
-
1419
- # 観測地の地心距離 / kmを返します。
1420
- #
1421
- # @return [Numeric]
1422
- #
1423
- def obserber_distance
1424
- l = PI / (90 * DEGREE) * @lat
1425
- @datum.surface_radius * (@datum.shape[0]+@datum.shape[1]*cos(l)+@datum.shape[2]*cos(2*l))
1426
- end
1427
-
1428
- # 観測地での‘大地’の視半径
1429
- #
1430
- # @return [Numeric]
1431
- #
1432
- def horizon
1433
- # 地面以下なら 90度とみなす
1434
- return 0.25 if @alt == :Center || @alt <= 0
1435
-
1436
- # 観測地の地心距離 / m
1437
- r = obserber_distance * 1000.0
1438
-
1439
- # 大気効果
1440
- air_effect = @datum.air[1] * @alt / (@datum.air[2] * @alt + r)
1441
-
1442
- # ‘大地’の視半径
1443
- asin((1.0+air_effect) * r / (r+@alt)) / CIRCLE
1444
- end
1445
-
1446
- # 観測地の地方恒星時 / 時を返します。
1447
- #
1448
- # @param [Numeric] t ユリウス日(Terrestrial Time)
1449
- # @param [When::TM::TemporalPosition] t
1450
- #
1451
- # @return [Numeric]
1452
- #
1453
- def local_sidereal_time(t)
1454
- t = +t
1455
- c = julian_century_from_2000(t)
1456
- result = @datum.sid[0] + c * (@datum.sid[1] + c * @datum.sid[2]) + @long / (15.0 * DEGREE)
1457
- result += (cosc(obl(c)) * delta_p(c) +
1458
- (t-When::TimeStandard.delta_t_observed(t)/86400+0.5) % 1) * 24 if @datum.kind_of?(Earth)
1459
- result
1460
- end
1461
-
1462
- # 観測地の日心三次元座標(黄道座標)
1463
- #
1464
- # @param [Numeric] t ユリウス日(Terrestrial Time)
1465
- # @param [When::TM::TemporalPosition] t
1466
- #
1467
- # @return [Numeric]
1468
- #
1469
- def _coords(t)
1470
- t = +t
1471
- @datum._coords(t) + _coords_diff(t)
1472
- end
1473
-
1474
- private
1475
-
1476
- # 要素の正規化
1477
- def _normalize(args=[], options={})
1478
-
1479
- # 時間帯による指定
1480
- @tz = When::Parts::Timezone.tz_info[@tz] if @tz.kind_of?(String)
1481
- if @tz
1482
- @label ||= When.m17n(@tz.identifier)
1483
- @long ||= @tz.longitude
1484
- @lat ||= @tz.latitude
1485
- end
1486
-
1487
- # データの整形
1488
- @label = When::BasicTypes::M17n.new(@label) if @label.kind_of?(Hash)
1489
- @long = When::Coordinates.to_deg_225(@long, 'EW') if @long
1490
- @lat = When::Coordinates.to_deg_225(@lat, 'NS') if @lat
1491
- @datum = When.Resource(@datum || 'Earth', '_ep:')
1492
- @long ||= 0.0
1493
- @lat ||= 0.0
1494
- @alt =
1495
- case @alt
1496
- when String ; @alt.gsub(/@/, '.').to_f
1497
- when Numeric ; @alt.to_f
1498
- when Symbol ; @alt
1499
- else ; 0.0
1500
- end
1501
-
1502
- # 日月食用作業変数
1503
- @mean = When.Resource(@mean || '_ep:Formula?formula=1L')
1504
- @ecls = {}
1505
- end
1506
-
1507
- #
1508
- # 空間位置オブジェクトの内容を Hash 化
1509
- #
1510
- # @param [Object] options When::Parts::Resource#to_h を参照
1511
- # @option options [Symbol] :method :to_h を label の内容の Hash 書き出しのために追加
1512
- #
1513
- # @return [Hash] Hash 化した空間位置オブジェクト
1514
- #
1515
- def _to_h(options={})
1516
- hash = super
1517
- hash[:long] = long_s
1518
- hash[:lat] = lat_s
1519
- hash
1520
- end
1521
-
1522
- # 観測地の惑星中心を原点とする三次元座標
1523
- #
1524
- # @param [Numeric] t ユリウス日(Terrestrial Time)
1525
- # @param [When::TM::TemporalPosition] t
1526
- # @param [Integer] system : 座標系
1527
- # [ ECLIPTIC = 黄道座標 ]
1528
- # [ EQUATORIAL = 赤道座標 ]
1529
- # [ EQUATORIAL_HA = 赤道座標[時角] ]
1530
- # [ HORIZONTAL = 地平座標 ]
1531
- #
1532
- def _coords_diff(t, system=ECLIPTIC)
1533
- return Coords.polar(0,0,0) if alt == :Center
1534
- t = +t
1535
- lat = @lat.to_f / DEGREE
1536
- coords = Coords.polar(
1537
- local_sidereal_time(t) / 24.0,
1538
- (lat + @datum.shape[3] * sind(2*lat)) / 360.0,
1539
- (obserber_distance + @alt/1000.0) / AU)
1540
- case system
1541
- when ECLIPTIC ; coords.r_to_y(t, @datum)
1542
- when EQUATORIAL ; coords
1543
- when EQUATORIAL_HA ; coords.r_to_rh(t, self)
1544
- when HORIZONTAL ; coords.r_to_h(t, self)
1545
- end
1546
- end
1547
-
1548
- # その他のメソッド
1549
- # When::Coordinates::Spatial で定義されていないメソッドは
1550
- # 処理を @datum (type: When::Ephemeris::Datum) に委譲する
1551
- #
1552
- def method_missing(name, *args, &block)
1553
- self.class.module_eval %Q{
1554
- def #{name}(*args, &block)
1555
- @datum.send("#{name}", *args, &block)
1556
- end
1557
- } unless When::Parts::MethodCash.escape(name)
1558
- @datum.send(name, *args, &block)
1559
- end
1560
-
1561
- # @private
1562
- module Normalize
1563
-
1564
- private
1565
-
1566
- # 位置情報
1567
- #
1568
- # @return [When::Coordinates::Spatial]
1569
- #
1570
- #attr_reader :location
1571
-
1572
- # 日時要素の境界オブジェクト
1573
- #
1574
- # @return [When::Coordinates::Border]
1575
- #
1576
- #attr_reader :border
1577
-
1578
- # 境界計算用の計算オブジェクト
1579
- #
1580
- # @return [When::Ephemeris::Formula]
1581
- #
1582
- #attr_reader :formula
1583
-
1584
- #
1585
- # Temporal Module の Spatial Parts の初期化
1586
- #
1587
- def _normalize_spatial
1588
-
1589
- # Location
1590
- if (@location||@long||@lat||@alt).kind_of?(String)
1591
- @location ||= "_l:long=#{@long||0}&lat=#{@lat||0}&alt=#{@alt||0}"
1592
- @location = When.Resource(@location)
1593
- end
1594
- @location ||= @tz_prop.location if @tz_prop
1595
-
1596
- # Border
1597
- @border = When.Border(@border) if @border.kind_of?(String)
1598
-
1599
- # Formula
1600
- instance_eval('class << self; attr_reader :formula; end') if @location && @border
1601
- if respond_to?(:formula)
1602
- extend When::Ephemeris::Formula::ForwardedFormula
1603
- unless @formula
1604
- options = {:formula => kind_of?(When::CalendarTypes::SolarYearTableBased) ? '1S' : '1L'}
1605
- options[:location] = @location if @location
1606
- @formula = When::Ephemeris::Formula.new(options)
1607
- end
1608
- @formula = When.Resource(Array(@formula), '_ep:')
1609
- end
1610
- end
1611
- end
1612
- end
1613
-
1614
- #
1615
- # 暦座標を扱う処理をまとめたモジュール
1616
- #
1617
- # When::TM::Calendar と When::TM::Clock に共通する処理だが、ISO 19108 で両者の
1618
- # 直接の superclass である、When::TM::ReferenceSystem は、これらの処理を持たない
1619
- # こととなっているため、When::TM::Calendar When::TM::Clock の共通部分を
1620
- # モジュールとしてまとめた。
1621
- #
1622
- module Temporal
1623
-
1624
- include When::Parts::MethodCash
1625
-
1626
- # @private
1627
- HashProperty =
1628
- [[:origin_of_MSC, 0], [:origin_of_LSC, 0], [:index_of_MSC, 0], [:epoch_in_CE, 0],
1629
- :unit, :base, :pair, :note,
1630
- :location, :time_basis, :border, :formula, :domain]
1631
-
1632
- # 年/日の原点(origin of most significant coordinate)
1633
- #
1634
- # @return [Integer]
1635
- #
1636
- attr_reader :origin_of_MSC
1637
-
1638
- # 日/秒の原点(origin of least significant coordinate)
1639
- #
1640
- # @return [Integer]
1641
- #
1642
- attr_reader :origin_of_LSC
1643
-
1644
- # インデクスオブジェクト
1645
- #
1646
- # @return [Array<When::Coordinates::Index>]
1647
- #
1648
- attr_reader :indices
1649
-
1650
- # 年/日のインデクス(index of most significant coordinate)
1651
- #
1652
- # @return [Integer]
1653
- #
1654
- attr_reader :index_of_MSC
1655
-
1656
- # 日時要素の要素数
1657
- #
1658
- # @return [Array<Integer, nil>]
1659
- #
1660
- # Ex. [nil, 12], [nil, 24, 60, 60]
1661
- #
1662
- # 初期化時に indices から自動生成する
1663
- #
1664
- attr_reader :unit
1665
-
1666
- # 日時要素の下限
1667
- #
1668
- # @return [Array<Integer, nil>]
1669
- #
1670
- # Ex. [nil, 1, 1], [nil, 0, 0, 0]
1671
- #
1672
- # 初期化時に indices から自動生成する
1673
- #
1674
- # @note 日付/日時の外部表現の「下限」を指定する。内部表現の下限は常に 0 である。
1675
- # サブクラスが定義するメソッド _coordinates_to_number, _number_to_coordinates は内部表現を使用する。
1676
- #
1677
- attr_reader :base
1678
-
1679
- # 日時要素がPairであるべきか
1680
- #
1681
- # @return [Array<Boolean>]
1682
- #
1683
- # Ex. [false] * 3, [false] * 4
1684
- #
1685
- # 初期化時に indices から自動生成する
1686
- #
1687
- attr_reader :pair
1688
-
1689
- # 代表暦注
1690
- #
1691
- # @return [When::CalendarNote]
1692
- # @return [Array<Array<klass, Array<klass, method, block>>>] 最外側のArray要素は年・月・日に対応
1693
- #
1694
- # klass [String, When::CalendarNote, When::Coordinates::Residue]
1695
- #
1696
- # method [String, Symbol] (デフォルト 'day', 'month' or 'year' (対応する桁による))
1697
- #
1698
- # block [Block] (デフォルト なし)
1699
- #
1700
- def note
1701
- case @note
1702
- when String ; @note = When.CalendarNote(@note)
1703
- when Array ; @note = When::CalendarNote.new(*@note)
1704
- end
1705
- @note
1706
- end
1707
-
1708
- #
1709
- # 日時要素の正規化
1710
- #
1711
- # @param [Array<Numeric>] source 正規化しようとしている日時要素の Array
1712
- # @param [Array<Numeric>] other 日時要素ごとに加減算を行う場合、加減算量の Array を指定する
1713
- # @param [Block] block
1714
- # @note
1715
- # 日付要素と時刻要素に関連がある場合、block を指定して、両者の
1716
- # 情報をやり取りする( yield で通日を渡し、通日を返してもらう)。
1717
- #
1718
- # 例1: 夏時間制を採用している場合、日付によって時刻の正規化の仕方が影響を受ける
1719
- #
1720
- # 例2: 日の境界が日没の場合、当該時刻が日没の前か後かで日付が変わる
1721
- #
1722
- # @return [Array<Numeric>] 正規化された日時要素の Array
1723
- #
1724
- # 日時要素は、それぞれの When::TM::Calendar や When::TM::Clock の実装に応じて有効な値となっている。
1725
- #
1726
- def _validate(source, other=nil, &block)
1727
- return _encode(_decode(source, other, &block))
1728
- end
1729
-
1730
- # 期間指定用 Array の桁数合わせ
1731
- #
1732
- # @param [Array<Numeric>] period
1733
- #
1734
- # @return [Array<Numeric>] 桁数合わせをした Array
1735
- #
1736
- def _arrange_length(period)
1737
- return period unless period.kind_of?(Array)
1738
- diff = @indices.length - period.length + 1
1739
- return period if (diff == 0)
1740
- return (diff > 0) ? Array.new(diff, 0) + period : period[(-diff)..-1]
1741
- end
1742
-
1743
- # @private
1744
- #
1745
- # 対応する ::Date の start 属性
1746
- def _default_start
1747
- ::Date::GREGORIAN
1748
- end
1749
-
1750
- # protected
1751
-
1752
- #
1753
- # 日時要素の encode
1754
- #
1755
- # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
1756
- #
1757
- # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
1758
- #
1759
- def _encode(source, border=@border)
1760
- # source は非破壊
1761
- date = source.dup
1762
-
1763
- # 外部表現に戻す
1764
- date[0] = +date[0]
1765
- (@base.length-1).downto(@unit.length-1) do |i|
1766
- date[i] = _from_index(date[0..i]) || date[i] + (@base[i]||0)
1767
- end
1768
- date[0] = source[0]
1769
-
1770
- # 結果を反映
1771
- date = border._adjust_epoch(date, self) if border
1772
- _encode_upper_structure(date)
1773
- end
1774
-
1775
- private
1776
-
1777
- #
1778
- # 日時要素の decode
1779
- #
1780
- # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
1781
- #
1782
- # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
1783
- #
1784
- def _decode(source, other=nil)
1785
-
1786
- # other の正規化
1787
- period = other ? other.dup : Array.new(@indices.length+1,0)
1788
-
1789
- # 上の位の集約
1790
- date = _decode_upper_structure(source.dup)
1791
- if (@index_of_MSC > 0) && other
1792
- u = 1
1793
- s = period[@index_of_MSC]
1794
- (@index_of_MSC-1).downto(0) do |i|
1795
- u *= @indices[i].unit
1796
- s += u * period[i]
1797
- end
1798
- period = [s] + period[(@index_of_MSC+1)..-1]
1799
- end
1800
-
1801
- # 下の位の既定値
1802
- unless date[1] || !@border
1803
- date[0...@base.length] = @border.border([@border._date_adjust(date[0])], self)
1804
- end
1805
-
1806
- # 要素数固定部分の正規化(上 -> 下) - ISO8601 の 小数要素(ex. "T23:20.8")の処理
1807
- coordinates = [date]
1808
- coordinates << period if other
1809
- coordinates.each do |coord|
1810
- carry = 0
1811
- @unit.length.times do |i|
1812
- if carry == 0
1813
- break unless coord[i]
1814
- else
1815
- coord[i] ||= 0
1816
- coord[i] += carry * unit[i]
1817
- end
1818
- if coord[i].kind_of?(Integer)
1819
- carry = 0
1820
- else
1821
- if i < @unit.length-1
1822
- carry = (coord[i].kind_of?(Pair) ? coord[i].trunk : coord[i]) % 1
1823
- coord[i] -= carry
1824
- end
1825
- coord[i] = coord[i].to_i if coord[i].kind_of?(Float) && coord[i] == coord[i].to_i
1826
- end
1827
- end
1828
- end
1829
-
1830
- # 要素数固定部分の正規化(下 -> 上)
1831
- carry = 0
1832
- (@unit.length-1).downto(1) do |i|
1833
- if date[i]
1834
- digit = date[i] + ((other || date[i]>=0) ? period[i]-@base[i] : @unit[i])
1835
- else
1836
- digit = period[i]
1837
- end
1838
- carry, date[i] = (digit + carry).divmod(@unit[i])
1839
- end
1840
- year = date[0] + period[0] + carry
1841
-
1842
- # 要素数可変部分の正規化
1843
- limit = @base.length-1
1844
- count = nil
1845
- date[0] = +year
1846
- if @base.length > @unit.length
1847
- @unit.length.upto(limit) do |i|
1848
- len = _length(date[0...i])
1849
- if date[i]
1850
- plus = date[i]>=0
1851
- digit = !plus ? +date[i] : _to_index(date[0..i]) || +date[i] - @base[i]
1852
- digit += (plus || other) ? period[i] : len
1853
- else
1854
- digit = period[i]
1855
- end
1856
-
1857
- if i==limit then
1858
- # 最下位
1859
- if (0...len) === digit
1860
- # 要素が範囲内
1861
- date[i] = digit
1862
- elsif other && period[i] == 0
1863
- # 要素が範囲外で、加算自体はあるが“日”の加算なし
1864
- date[i] = len-1
1865
- else
1866
- # 要素が範囲外で、加算自体がないか“日”の加算あり
1867
- date[i] = 0
1868
- count = _coordinates_to_number(*date)+digit
1869
- count = yield(count) if block_given?
1870
- date = _number_to_coordinates(count)
1871
- end
1872
-
1873
- else
1874
- # 最下位以外
1875
- # 要素が大きすぎる場合
1876
- while digit >= len do
1877
- digit -= len
1878
- carry = 1
1879
- (i-1).downto(1) do |k|
1880
- if date[k] >= _length(date[0...k])-1
1881
- date[k] = 0
1882
- carry = 1
1883
- else
1884
- date[k] += 1
1885
- carry = 0
1886
- break
1887
- end
1888
- end
1889
- date[0] += carry
1890
- len = _length(date[0...i])
1891
- end
1892
-
1893
- # 要素が小さすぎる場合
1894
- while digit < 0 do
1895
- date[i-1] -= 1
1896
- digit += _length(date[0...i])
1897
- (i-1).downto(1) do |k|
1898
- break if (date[k] >= 0)
1899
- date[k-1] -= 1
1900
- date[k] = _length(date[0...k]) - 1
1901
- end
1902
- end
1903
-
1904
- # 要素が範囲内
1905
- date[i] = digit
1906
- end
1907
- end
1908
- end
1909
- date[0] = year + (date[0] - +year)
1910
-
1911
- # 時刻部分による補正が入る場合
1912
- if block_given? && !count
1913
- count = _coordinates_to_number(*date)
1914
- modified = yield(count)
1915
- date = _number_to_coordinates(modified) unless count == modified
1916
- end
1917
-
1918
- # 結果を返す
1919
- return date
1920
- end
1921
-
1922
- #
1923
- # 日時要素の下限を取得する
1924
- #
1925
- # @param [Array<Numeric>] date 日時要素
1926
- #
1927
- # @return [Integer] 日時要素の下限
1928
- #
1929
- def _base(date)
1930
- return @base[date.length]
1931
- end
1932
-
1933
- #
1934
- # 日時要素の番号から要素を取得する配列を返す
1935
- #
1936
- #def _ids(date)
1937
- # return nil
1938
- #end
1939
-
1940
- #
1941
- # 日時要素の要素数を取得する(直下の要素)
1942
- #
1943
- #def _length(date)
1944
- # return @unit[date.length]
1945
- #end
1946
-
1947
- #
1948
- # 日時要素の要素数を取得する(全要素)
1949
- #
1950
- # @param [Array<Numeric>] date 日時要素(内部表現)
1951
- #
1952
- # @return [Integer] 日時要素の要素数
1953
- # @note 一般に date 1要素ならその要素の年の日数、2要素ならその要素の年月の日数になる。
1954
- #
1955
- def _sum_(date)
1956
- return @unit[date.length..-1].inject(1) {|p, u| p * u }
1957
- end
1958
-
1959
- def _normalize_temporal
1960
-
1961
- # method cash
1962
- @_m_cash_lock_ = Mutex.new if When.multi_thread
1963
-
1964
- # label
1965
- @label = When::BasicTypes::M17n.label(@label)
1966
-
1967
- # Origin and Upper Digits
1968
- @origin_of_MSC ||= - @border.behavior * 1 if @border
1969
- @origin_of_MSC = Pair._en_number(@origin_of_MSC)
1970
- @origin_of_LSC = Pair._en_number(@origin_of_LSC)
1971
- @index_of_MSC = Pair._en_number(@index_of_MSC)
1972
- if @index_of_MSC != 0
1973
- extend OriginAndUpperDigits
1974
- elsif @origin_of_MSC != 0
1975
- extend OriginOnly
1976
- end
1977
-
1978
- # unit
1979
- @unit = [nil]
1980
- (@index_of_MSC...@indices.length).each do |i|
1981
- break unless @indices[i].unit
1982
- @unit << @indices[i].unit
1983
- end
1984
-
1985
- # base & pair
1986
- @base = [nil]
1987
- @pair = [false]
1988
- (@index_of_MSC...@indices.length).each do |i|
1989
- raise ArgumentError, "Base not defined" unless @indices[i].base
1990
- @base << @indices[i].base
1991
- @pair << (@indices[i].branch != nil)
1992
- end
1993
- extend IndexConversion unless @pair.uniq == [false]
1994
-
1995
- # note
1996
- @note ||= 'Default'
1997
-
1998
- # keys
1999
- @keys = @indices.inject(label.instance_of?(When::Locale) ? label.keys : []) {|key, index| key |= index.keys}
2000
- end
2001
-
2002
- def _default_index_of_MSC
2003
- unless @index_of_MSC
2004
- [:_coordinates_to_number, :_coordinates_to_number_].each do |to_n|
2005
- if respond_to?(to_n)
2006
- @index_of_MSC = @indices.length - method(to_n).arity + 1
2007
- break
2008
- end
2009
- end
2010
- end
2011
- end
2012
-
2013
- # 何もしない
2014
- def _return_nil(source); nil end
2015
- alias :_from_index :_return_nil
2016
- alias :_to_index :_return_nil
2017
-
2018
- def _do_nothing(source); source end
2019
- alias :_encode_upper_structure :_do_nothing
2020
- alias :_decode_upper_structure :_do_nothing
2021
-
2022
- # @private
2023
- module IndexConversion
2024
- #
2025
- # indexのPair化
2026
- #
2027
- # @param [Array<Numeric>] date 最下位が index になっている日時要素
2028
- #
2029
- # @return [When::Coordinates::Pair] 最下位の index に対応する When::Coordinates::Pair
2030
- #
2031
- def _from_index(date)
2032
- return nil unless @pair[date.size-1]
2033
- ids = _ids(date[0..-2])
2034
- m = ids[date[-1]] if (ids)
2035
- return Pair._force_pair(m) if (ids && m)
2036
- return Pair.new(+date[-1]+@base[date.length-1], 0)
2037
- rescue ArgumentError
2038
- nil
2039
- end
2040
-
2041
- #
2042
- # Pairのindex
2043
- #
2044
- # @param [Array<Numeric>] date 最下位が When::Coordinates::Pair になっている日時要素
2045
- #
2046
- # @return [When::Coordinates::Pair] 最下位の When::Coordinates::Pair に対応する index
2047
- #
2048
- def _to_index(date)
2049
- return nil unless @pair[date.size-1]
2050
- ids = _ids(date[0..-2])
2051
- i = ids.index(date[-1]) if ids
2052
- return i if i
2053
- return nil unless ids && date[-1].kind_of?(Pair)
2054
- digit = Pair.new(date[-1].trunk, date[-1].branch)
2055
- while digit.branch > 0
2056
- digit.branch -= 1
2057
- i = ids.index(digit)
2058
- return i + date[-1].branch - digit.branch if i
2059
- end
2060
- return nil
2061
- rescue ArgumentError
2062
- nil
2063
- end
2064
- end
2065
-
2066
- alias :_method_missing :method_missing
2067
-
2068
- # その他のメソッド
2069
- # When::Coordinates::Temporal で定義されていないメソッドは
2070
- # 処理を下記に移譲する(番号は優先順位)
2071
- # When::CalendarNote
2072
- # (1) @note
2073
- # (2) SolarTerms
2074
- # (3) LunarPhases
2075
- # When::Ephemeris::Formula
2076
- # (4)@formula[0]
2077
- #
2078
- def method_missing(name, *args, &block)
2079
- unless When::Parts::MethodCash::Escape.key?(name)
2080
- if note.respond_to?(name)
2081
- if note.class::CalendarDepend
2082
- instance_eval %Q{
2083
- def #{name}(*args, &block)
2084
- @note.send("#{name}", *(args + [self]), &block)
2085
- end
2086
- } unless When::Parts::MethodCash.escape(name)
2087
- return @note.send(name, *(args + [self]), &block)
2088
- else
2089
- instance_eval %Q{
2090
- def #{name}(*args, &block)
2091
- @note.send("#{name}", *args, &block)
2092
- end
2093
- } unless When::Parts::MethodCash.escape(name)
2094
- return @note.send(name, *args, &block)
2095
- end
2096
- end
2097
- ['SolarTerms', 'LunarPhases'].each do |note|
2098
- if When.CalendarNote(note).respond_to?(name)
2099
- instance_eval %Q{
2100
- def #{name}(*args, &block)
2101
- When.CalendarNote("#{note}").send("#{name}", *args, &block)
2102
- end
2103
- } unless When::Parts::MethodCash.escape(name)
2104
- return When.CalendarNote(note).send(name, *args, &block)
2105
- end
2106
- end
2107
- if When::Ephemeris::Formula.method_defined?(name)
2108
- unless respond_to?(:forwarded_formula, true)
2109
- extend When::Ephemeris::Formula::ForwardedFormula
2110
- @formula ||= When::Ephemeris::Formula.new({:location=>@location})
2111
- @formula = When.Resource(Array(@formula), '_ep:')
2112
- end
2113
- instance_eval %Q{
2114
- def #{name}(*args, &block)
2115
- forward = forwarded_formula("#{name}", args[0])
2116
- return forward.send("#{name}", *args, &block) if forward
2117
- _method_missing("#{name}", *args, &block)
2118
- end
2119
- } unless When::Parts::MethodCash.escape(name)
2120
- forward = forwarded_formula(name, args[0])
2121
- return forward.send(name, *args, &block) if forward
2122
- end
2123
- end
2124
- _method_missing(name, *args, &block)
2125
- end
2126
-
2127
- # @private
2128
- module OriginOnly
2129
- # 上の位の付加
2130
- #
2131
- # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
2132
- #
2133
- # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
2134
- #
2135
- def _encode_upper_structure(source)
2136
- date = source.dup
2137
- date[0] += @origin_of_MSC
2138
- return date
2139
- end
2140
-
2141
- # 上の位の除去
2142
- #
2143
- # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
2144
- #
2145
- # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
2146
- #
2147
- def _decode_upper_structure(source)
2148
- date = source.dup
2149
- date[0] -= @origin_of_MSC
2150
- return date
2151
- end
2152
- end
2153
-
2154
- # @private
2155
- module OriginAndUpperDigits
2156
- # 上の位の付加
2157
- #
2158
- # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
2159
- #
2160
- # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
2161
- #
2162
- def _encode_upper_structure(source)
2163
- date = source.dup
2164
- date[0] += @origin_of_MSC
2165
- @index_of_MSC.downto(1) do |i|
2166
- carry, date[0] = (+date[0]).divmod(@indices[i-1].unit)
2167
- date[0] += @indices[i-1].base
2168
- date.unshift(carry)
2169
- end
2170
- return date
2171
- end
2172
-
2173
- # 上の位の除去
2174
- #
2175
- # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
2176
- #
2177
- # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
2178
- #
2179
- def _decode_upper_structure(source)
2180
- date = source.dup
2181
- u = 1
2182
- s = 0
2183
- @index_of_MSC.downto(1) do |i|
2184
- s += u * (+date[i] - @indices[i-1].base) if (date[i])
2185
- u *= @indices[i-1].unit
2186
- end
2187
- date[@index_of_MSC] = s + u * (+date[0]) - @origin_of_MSC
2188
- return date[@index_of_MSC..-1]
2189
- end
2190
- end
2191
- end
2192
-
2193
- #
2194
- # 日時要素の境界 - Border
2195
- #
2196
- class Border < When::BasicTypes::Object
2197
- #
2198
- # 境界の振舞
2199
- #
2200
- # @return [Numeric]
2201
- #
2202
- # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
2203
- #
2204
- # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
2205
- #
2206
- def behavior
2207
- @border[0]
2208
- end
2209
-
2210
- # 境界の取得
2211
- #
2212
- # @param [Array<Numeric>] date 境界を計算する年/日
2213
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2214
- #
2215
- # @return [Array<Numeric>] その年/日の境界
2216
- #
2217
- def border(date=[], frame=nil)
2218
- last = date.length-1
2219
- return @border if last<0
2220
- b_date = date[0..last] + @border[(last+1)..-1]
2221
- branch = @border[last] * 0
2222
- b_date[last] = When::Coordinates::Pair.new(date[last] * 1, branch)
2223
- return b_date
2224
- end
2225
-
2226
- # 境界の正規化
2227
- #
2228
- # @param [Array<Numeric>] date 境界を計算する年/日
2229
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2230
- #
2231
- # @return [Array<Numeric>] その年/日の境界
2232
- #
2233
- def _adjust_epoch(date, frame=nil)
2234
- s_date = date.dup
2235
- e_date = border([+date[0]], frame)
2236
- branch = behavior * 0
2237
- branch += 1 if (s_date[1..-1] <=> e_date[1..-1]) < 0
2238
- s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
2239
- return s_date
2240
- end
2241
-
2242
- # 日付の補正
2243
- # @private
2244
- def _date_adjust(source)
2245
- source
2246
- end
2247
-
2248
- private
2249
-
2250
- # 要素の正規化
2251
- def _normalize(args=[], options={})
2252
- @border = When::Coordinates::Pair._en_pair_date_time(@border) if @border.kind_of?(String)
2253
- end
2254
- end
2255
-
2256
- #
2257
- # 日時要素の境界 - 年/日によって、異なる境界を使用する場合
2258
- #
2259
- class MultiBorder < Border
2260
-
2261
- #
2262
- # 境界の配列
2263
- # @return [Array<When::Coordinates::Border>]
2264
- attr_reader :borders
2265
-
2266
- #
2267
- # 境界の振舞
2268
- #
2269
- # @return [Numeric]
2270
- #
2271
- # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
2272
- #
2273
- # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
2274
- #
2275
- def behavior
2276
- @borders[0][:border].behavior
2277
- end
2278
-
2279
- # 境界の取得
2280
- #
2281
- # @param [Array<Numeric>] date 境界を計算する年/日
2282
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2283
- #
2284
- # @return [Array<Numeric>] その年/日の境界
2285
- #
2286
- def border(date=[], frame=nil)
2287
- last = date.length-1
2288
- return @borders[0][:boder] if (last<0)
2289
- @borders.each do |border|
2290
- return border[:border].border(date, frame) if date[0] >= border[:key]
2291
- end
2292
- date[0..last]
2293
- end
2294
-
2295
- # 境界の正規化
2296
- #
2297
- # @param [Array<Numeric>] date 境界を計算する年/日
2298
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2299
- #
2300
- # @return [Array<Numeric>] その年/日の境界
2301
- #
2302
- def _adjust_epoch(date, frame=nil)
2303
- @borders.each do |border|
2304
- next unless date[0] >= border[:key]
2305
- s_date = date.dup
2306
- e_date = border[:border].border(date[0..0], frame)
2307
- branch = border[:border].behavior * 0
2308
- branch += 1 if ((s_date[1..-1] <=> e_date[1..-1]) < 0)
2309
- s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
2310
- return s_date
2311
- end
2312
- date
2313
- end
2314
-
2315
- private
2316
-
2317
- # 要素の正規化
2318
- def _normalize(args=[], options={})
2319
- if @borders.kind_of?(String)
2320
- list = @borders.split(/(\(.+?\))/)
2321
- list.shift if list[0]==''
2322
- list.unshift(-Float::MAX) unless list[0] =~ /\(/
2323
- list.push('0-1-1') if list[-1] =~ /\(/
2324
- @borders = []
2325
- loop do
2326
- key, border, *list = list
2327
- break unless key
2328
- key = $1.to_i if key.kind_of?(String) && /\((.+?)\)/ =~ key
2329
- @borders << {:key=>key, :border=>When.Border(border)}
2330
- end
2331
- end
2332
- @borders = @borders.sort_by {|border| -border[:key]}
2333
- end
2334
- end
2335
-
2336
- #
2337
- # 基準暦法の新年による境界
2338
- #
2339
- class CalendarBorder < Border
2340
-
2341
- class << self
2342
-
2343
- private
2344
-
2345
- def _behalf_of(iri)
2346
- base = iri.dup.sub!('/Coordinates/','/CalendarTypes/')
2347
- return nil unless base
2348
- self.new({'engine'=>When::Parts::Resource._instance(base)})
2349
- end
2350
- end
2351
-
2352
- # 境界の取得
2353
- #
2354
- # @param [Array<Numeric>] date 境界を計算する年
2355
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2356
- #
2357
- # @return [Array<Numeric>] その年の境界
2358
- #
2359
- def border(date=[], frame=nil)
2360
- last = date.length-1
2361
- return @border if last<0
2362
- args = date.dup << {:frame=>@engine}
2363
- args[0] += frame.origin_of_MSC + @border[last] * 1 + (frame.epoch_in_CE - @engine.epoch_in_CE)
2364
- b_date = frame._encode(frame._number_to_coordinates(When.tm_pos(*args).to_i), nil)
2365
- branch = @border[last] * 0
2366
- b_date[last] = When::Coordinates::Pair.new(date[last] * 1, branch)
2367
- return b_date
2368
- end
2369
-
2370
- private
2371
-
2372
- # 要素の正規化
2373
- def _normalize(args=[], options={})
2374
- @border ||= [When::Coordinates::Pair.new(+1,-1),0,0]
2375
- @border = When::Coordinates::Pair._en_pair_date_time(@border) if @border.kind_of?(String)
2376
- @engine = When.Calendar(@engine)
2377
- end
2378
- end
2379
-
2380
- #
2381
- # 日時要素の境界 - 日の出,日の入り
2382
- #
2383
- class DayBorder < Border
2384
-
2385
- # 境界の取得
2386
- #
2387
- # @param [Array<Numeric>] date 境界を計算する日
2388
- # @param [When::TM::ReferenceSystem] clock 使用する時法
2389
- #
2390
- # @return [Array<Numeric>] その日の境界
2391
- #
2392
- # @note 属性 @event によって境界を計算する (see {When::Ephemeris::Formula#day_event})
2393
- #
2394
- def border(date=[], clock=When::UTC)
2395
- return @border unless date[0] && clock.formula
2396
-
2397
- time =
2398
- clock._number_to_coordinates(clock.second *
2399
- clock.time_standard.from_dynamical_time(
2400
- When::TM::JulianDate._d_to_t(
2401
- clock.formula.first.day_event(
2402
- clock.time_standard.to_dynamical_date(date[0] + @border[0]*0), @event, When.Resource('_ep:Sun'), @height
2403
- ))))
2404
-
2405
- time[0] += When::TM::JulianDate::JD19700101
2406
- time[0] = When::Coordinates::Pair.new(time[0]-@border[0]*0, @border[0]*0) unless @border[0]*0 == 0
2407
- clock._encode(time, false)
2408
- end
2409
-
2410
- # 日付の補正
2411
- # @private
2412
- def _date_adjust(source)
2413
- source * 1 + @border[0] * 0
2414
- end
2415
- end
2416
-
2417
- #
2418
- # 日時要素の境界 - 日の出
2419
- #
2420
- class Sunrise < DayBorder
2421
-
2422
- private
2423
-
2424
- # 要素の正規化
2425
- def _normalize(args=[], options={})
2426
- @border = [0,0,0,0]
2427
- @event = -1
2428
- @height ||= '0'
2429
- end
2430
- end
2431
-
2432
- #
2433
- # 日時要素の境界 - 日の入り
2434
- #
2435
- class Sunset < DayBorder
2436
-
2437
- private
2438
-
2439
- # 要素の正規化
2440
- def _normalize(args=[], options={})
2441
- @border = [When::Coordinates::Pair.new(+1,-1),0,0,0]
2442
- @event = +1
2443
- @height ||= '0'
2444
- end
2445
- end
2446
- 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
+ # 座標の記述に用いる諸オブジェクト
10
+ #
11
+ module When::Coordinates
12
+ # 変換テーブル
13
+ PRECISION = {'YEAR'=>When::YEAR, 'MONTH' =>When::MONTH, 'WEEK' =>When::WEEK, 'DAY' =>When::DAY,
14
+ 'HOUR'=>When::HOUR, 'MINUTE'=>When::MINUTE, 'SECOND'=>When::SECOND, 'SYSTEM'=>When::SYSTEM}
15
+ PERIOD = {'P1Y' =>When::YEAR, 'P1M' =>When::MONTH, 'P1W' =>When::WEEK, 'P1D' =>When::DAY,
16
+ 'PT1H'=>When::HOUR, 'PT1M' =>When::MINUTE, 'PT1S' =>When::SECOND,
17
+ '1Y' =>When::YEAR, '1M' =>When::MONTH, '1W' =>When::WEEK, '1D' =>When::DAY,
18
+ '1h' =>When::HOUR, '1m' =>When::MINUTE, '1s' =>When::SECOND}
19
+ VALUE = {'DATE'=>When::DAY, 'TIME' =>When::SYSTEM, 'DATE-TIME'=>When::SYSTEM} # RFC 5545
20
+ PRECISION_NAME = PRECISION.invert
21
+ PERIOD_NAME = {When::YEAR=>'P1Y' , When::MONTH=>'P1M', When::WEEK =>'P1W', When::DAY=>'P1D',
22
+ When::HOUR=>'PT1H', When::MINUTE=>'PT1M', When::SECOND=>'PT1S'}
23
+ MATCH = {'NS'=>/(N|S|北緯|南緯)/, 'EW'=>/(E|W|東経|西経)/}
24
+
25
+ # 60進->10進変換(1/225度単位)
26
+ #
27
+ # @param [String] src 60進法で表した方向付きの数値
28
+ # @param [String] dir 方向 ('NS' または 'EW')
29
+ #
30
+ # @return [Numeric] 10進変換した数値 (src が nil なら0.0を、Numeric なら 225*src を返す)
31
+ #
32
+ def self.to_deg_225(src, dir)
33
+ case src
34
+ when String
35
+ src = src.gsub(/_+/,'').gsub(/@/, '.')
36
+ return src.to_r * Spatial::DEGREE if (src =~ /E[-+]/ || src !~ MATCH[dir])
37
+ sign = ($1 == dir[1..1]) ? -1 : +1
38
+ value = src.gsub(MATCH[dir], '').strip
39
+ if ((value + "00000") =~ /\A(\d+)\.(\d{2})(\d{2})(\d+)\z/)
40
+ deg, min, sec, frac = $~[1..4]
41
+ sec += "." + frac
42
+ else
43
+ deg, min, sec = value.split(/[^\d.]+/)
44
+ end
45
+ return sign * (deg.to_i * Spatial::DEGREE +
46
+ (min||0).to_f * (Spatial::DEGREE/60.0) +
47
+ (sec||0).to_f * (Spatial::DEGREE/3600.0))
48
+ when NilClass
49
+ 0.0
50
+ when Numeric
51
+ src * Spatial::DEGREE
52
+ else
53
+ raise TypeError, "Invalid Location Type"
54
+ end
55
+ end
56
+
57
+ # 60進->10進変換(度単位)
58
+ #
59
+ # @param [String] src 60進法で表した方向付きの数値
60
+ # @param [String] dir 方向 ('NS' または 'EW')
61
+ #
62
+ # @return [Numeric] 10進変換した数値 (src が nil なら0.0を、Numeric ならそのままsrcを返す)
63
+ #
64
+ def self.to_deg(src, dir)
65
+ to_deg_225(src, dir) / Spatial::DEGREE
66
+ end
67
+
68
+ # 10進->60進変換
69
+ #
70
+ # @param [Numeric] src 数値
71
+ # @param [String] dir 方向 ('NS' または 'EW')
72
+ # @param [Integer] round 秒の小数点以下最大桁数
73
+ #
74
+ # @return [String] 60進変換した数値
75
+ #
76
+ def self.to_dms(src, dir, round=6)
77
+ dir = (src >= 0) ? dir[0..0] : dir[1..1]
78
+ deg, min = src.abs.divmod(1)
79
+ min, sec = (60*min).divmod(1)
80
+ sec = (60*10**round*sec).round
81
+ fig = round + 2
82
+ round.times do
83
+ div, mod = sec.divmod(10)
84
+ if mod == 0
85
+ fig -= 1
86
+ sec = div
87
+ else
88
+ break
89
+ end
90
+ end
91
+ (['N','S'].include?(dir) ? "%02d.%02d%0#{fig}d%s" : "%03d.%02d%0#{fig}d%s") % [deg, min, sec, dir]
92
+ end
93
+
94
+ #
95
+ # 剰余類
96
+ #
97
+ class Residue < When::BasicTypes::Object
98
+
99
+ LabelProperty = 'label'
100
+
101
+ class << self
102
+ #
103
+ # 曜日(剰余類)
104
+ #
105
+ # @param [Numeric] day 月曜を 0 とする七曜(剰余類)を返します
106
+ # @param [String] day 最初の3文字から決定した七曜(剰余類)を返します。
107
+ # 一致する七曜(剰余類)がない場合、名前の一致するその他の剰余類を探して返します。
108
+ #
109
+ # @return [When::Coordinates::Residue]
110
+ #
111
+ def day_of_week(day)
112
+ return day if day.kind_of?(self)
113
+
114
+ day ||= 0
115
+ week = When.Resource('_co:Common::Week')
116
+ dow = week.child
117
+ case day
118
+ when Numeric ; return dow[day]
119
+ when /\AWeek\z/ ; return week
120
+ when String ; day = When::EncodingConversion.to_internal_encoding(day)
121
+ else ; return nil
122
+ end
123
+
124
+ day, shift = day.split(':', 2)
125
+ residue = day.split('&').inject(nil) {|res,d|
126
+ r = _day_of_week(d.strip, dow)
127
+ return nil unless r
128
+ res ? res & r : r
129
+ }
130
+ return residue unless shift
131
+ shift = shift.to_i
132
+ shift -= 1 if shift > 0
133
+ residue >> shift
134
+ end
135
+ alias :to_residue :day_of_week
136
+
137
+ def _day_of_week(day, dow)
138
+ match = day[/\A...|^.{1,2}\z/]
139
+ if match
140
+ dow.size.times do |i|
141
+ return dow[i] if dow[i].label.=~(/\A#{match}/i)
142
+ end
143
+ end
144
+
145
+ ObjectSpace.each_object(self) do |object|
146
+ return object if object.registered? && object.label.=~(/\A#{day}\z/)
147
+ end
148
+
149
+ return nil
150
+ end
151
+ private :_day_of_week
152
+
153
+ # 汎用の mod
154
+ #
155
+ # @param [Numeric] nn 被除数
156
+ # @param [Block] dd 手続き
157
+ #
158
+ # nn = rem + dd(quot)
159
+ #
160
+ # div = dd(quot+1)-dd(quot)
161
+ #
162
+ # となるように rem, div, quot を決めたい手続き
163
+ #
164
+ # @return [Array<Numeric>] [ quot, rem, div ]
165
+ # nn を dd で「割った」商(quot), 余り(rem)および除数(div)を返します。
166
+ # remは非負、quotは Integer。
167
+ #
168
+ def mod(nn, &dd)
169
+ u = dd.call(0)
170
+ y = ((nn-u)*256).divmod(dd.call(256)-u)[0] - 1
171
+ w1, w2 = dd.call(y), dd.call(y+1)
172
+ until w1 <= nn && nn < w2
173
+ if w2 <= nn then y, w1, w2 = y+1, w2, dd.call(y+2)
174
+ else y, w1, w2 = y-1, dd.call(y-1), w1
175
+ end
176
+ end
177
+ return y, nn-w1, w2-w1
178
+ end
179
+
180
+ # 中国剰余
181
+ #
182
+ # @param [Array<Numeric>] a [ num, den ] den で割って num 余ることを示す配列
183
+ # @param [Array<Numeric>] b [ num, den ] den で割って num 余ることを示す配列
184
+ #
185
+ # @return [Array<Numeric>] a と b の条件をともに満たす [ num, den ]
186
+ #
187
+ def _china(a, b)
188
+ b, a = a, b if (a[1] <= b[1])
189
+ g, p, q = _gcd(a[1], b[1])
190
+ return [((b[0]*a[1]*q-a[0]*b[1]*p)*(a[1]*q-b[1]*p)) % (a[1]*b[1]), a[1]*b[1]] if (g == 1)
191
+ r = a[0] % g
192
+ s = b[0] % g
193
+ return nil unless (r == s)
194
+ m = _china([(a[0]-r).div(g), a[1].div(g)], [(b[0]-s).div(g), b[1].div(g)])
195
+ return nil unless (m)
196
+ return [m[0]*g+r, m[1]*g]
197
+ end
198
+
199
+ private
200
+
201
+ # 最大公約数
202
+ def _gcd(a,b)
203
+ c, x = a.divmod(b)
204
+ g = [[a, 1, 0],
205
+ [b, c, 1]]
206
+
207
+ until (x == 0) do
208
+ k = g[1][0].div(x)
209
+ g << [x, k * g[1][1] + g[0][1], k * g[1][2] + g[0][2]]
210
+ g.shift
211
+ x = g[0][0] % g[1][0]
212
+ end
213
+
214
+ return [g[1][0]] + g[0][1..2]
215
+ end
216
+ end
217
+
218
+ # @private
219
+ HashProperty = [:label, :remainder, :divisor]
220
+
221
+ # 剰余
222
+ #
223
+ # @return [Numeric]
224
+ #
225
+ attr_accessor :remainder
226
+ protected :remainder=
227
+
228
+ #
229
+ #
230
+ # @return [Integer] (>0)
231
+ #
232
+ attr_reader :divisor
233
+
234
+ # 繰り上がり
235
+ #
236
+ # @return [Integer]
237
+ #
238
+ attr_accessor :carry
239
+ protected :carry=
240
+
241
+ # 単位
242
+ #
243
+ # @return [Hash] { String=>Numeric }
244
+ #
245
+ # Example : { 'day'=>11, 'year'=4 }
246
+ #
247
+ # 通日に適用するときは、11 ずらして計算 - 甲子日 = ユリウス日 11 + 60n
248
+ #
249
+ # 通年に適用するときは、 4 ずらして計算 - 甲子年 = 西暦 4 + 60n
250
+ #
251
+ attr_reader :units
252
+
253
+ # units の指定
254
+ #
255
+ # @param [String] arg あらかじめ units に指定した単位を用いる
256
+ #
257
+ # @return [When::Coordinates::Residue]
258
+ #
259
+ def to(arg)
260
+ return nil unless @units[arg]
261
+ self.class.new(@remainder, @divisor, @carry, {arg=>@units[arg]})
262
+ end
263
+ alias :/ :to
264
+
265
+ # オブジェクトの単位
266
+ #
267
+ # @return [String] 現在使用中の単位を返す
268
+ #
269
+ def event
270
+ keys = @units.keys
271
+ return (keys.size == 1) ? keys[0] : 'day'
272
+ end
273
+
274
+ # remainder の指定
275
+ #
276
+ # @param [Numeric] remainder 指定値を@remainderとする
277
+ # @param [String] remainder When::Parts::Resource の has-a 関係によりString に対応する子供オブジェクトを取得
278
+ #
279
+ # @return [When::Coordinates::Residue]
280
+ #
281
+ def [](remainder)
282
+ return super if !remainder.kind_of?(Numeric) || (child && child.length == @divisor)
283
+ return self if remainder == 0 && !child
284
+ remainder *= 1
285
+ return self.class.new(@remainder+remainder, @divisor, @carry, @label, @format, @units) unless (child && child.length > 0)
286
+ carry, remainder = (@remainder+remainder).divmod(@divisor)
287
+ base = child.reverse.each do |residue|
288
+ break residue if residue.remainder <= remainder
289
+ end
290
+ raise ArgumentError, "remainder out of range: #{remainder}" unless base.kind_of?(self.class)
291
+ base = base.dup
292
+ base.remainder = remainder
293
+ base.carry += carry
294
+ return base
295
+ end
296
+
297
+ # 典型的なイベントの発生間隔
298
+ #
299
+ # @param [String] event
300
+ #
301
+ # @return [When::TM::PeriodDuration]
302
+ #
303
+ def duration(event=self.event)
304
+ When::TM::PeriodDuration.new(@divisor, When::Coordinates::PRECISION[event.upcase])
305
+ end
306
+
307
+ # 派生オブジェクトと元オブジェクトの remainder の差
308
+ # (派生オブジェクトとは、元オブジェクトに[]演算を施して @remainder を変えたオブジェクト)
309
+ #
310
+ # @return [Integer]
311
+ #
312
+ # 派生オブジェクトでない場合 : 自身の remainder
313
+ #
314
+ # 派生オブジェクトである場合 : 派生オブジェクトと元オブジェクトの remainder の差
315
+ #
316
+ def difference
317
+ @difference ||= (registered? || iri !~ /:/) ? @remainder : @remainder - When.Resource(iri).remainder
318
+ end
319
+
320
+ # remainderの加算
321
+ #
322
+ # other : Numeric
323
+ #
324
+ # @return [When::Coordinates::Residue]
325
+ #
326
+ def +(other)
327
+ carry, remainder = (@remainder + other).divmod(@divisor)
328
+ return self.class.new(remainder, @divisor, @carry+carry, @units)
329
+ end
330
+
331
+ # remainderの減算
332
+ #
333
+ # @param [Numeric] other
334
+ #
335
+ # @return [When::Coordinates::Residue]
336
+ #
337
+ def -(other)
338
+ carry, remainder = (@remainder - other).divmod(@divisor)
339
+ return self.class.new(remainder, @divisor, @carry+carry, @units)
340
+ end
341
+
342
+ # carryの加算
343
+ #
344
+ # @param [Numeric] other
345
+ #
346
+ # @return [When::Coordinates::Residue]
347
+ #
348
+ def >>(other)
349
+ return self.class.new(@remainder, @divisor, @carry+other, @units)
350
+ end
351
+
352
+ # carryの減算
353
+ #
354
+ # @param [Numeric] other
355
+ #
356
+ # @return [When::Coordinates::Residue]
357
+ #
358
+ def <<(other)
359
+ return self.class.new(@remainder, @divisor, @carry-other, @units)
360
+ end
361
+
362
+ # 剰余類の共通集合
363
+ #
364
+ # @param [When::Coordinates::Residue] other
365
+ #
366
+ # @return [When::Coordinates::Residue]
367
+ #
368
+ def &(other)
369
+ case other
370
+ when Residue
371
+ if self.units == other.units
372
+ m = self.class._china([@remainder, @divisor], [other.remainder, other.divisor])
373
+ u = units.dup
374
+ else
375
+ keys = units.keys & other.units.keys
376
+ keys = ['day'] if keys == []
377
+ return nil unless (keys.size==1)
378
+ self_base = self.units[keys[0]] || 0
379
+ other_base = other.units[keys[0]] || 0
380
+ m = self.class._china([@remainder, @divisor],
381
+ [(other.remainder+other_base-self_base) % other.divisor, other.divisor])
382
+ u = {keys[0]=>self_base}
383
+ end
384
+ return nil unless (m)
385
+ return self.class.new(m[0], m[1], @carry, u)
386
+ when Pair
387
+ return Pair.new(self & other.trunk, other.branch)
388
+ when Numeric
389
+ keys = @units.keys
390
+ d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
391
+ c, m = (other-d).divmod(@divisor)
392
+ c += 1 if (m > @remainder)
393
+ return (c + @carry) * @divisor + @remainder + d
394
+ else
395
+ position = When::TM::Position.any_other(other)
396
+ raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
397
+ return position & self
398
+ end
399
+ end
400
+
401
+ # 剰余
402
+ #
403
+ # @param [When::TM::TemporalPosition, Numeric, When::Coordinates::Pair] other
404
+ #
405
+ # @return ['other' と同じクラス]
406
+ #
407
+ def %(other)
408
+ case other
409
+ when Pair
410
+ return Pair.new(self % other.trunk, other.branch)
411
+ when Numeric
412
+ keys = @units.keys
413
+ d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
414
+ return (other-d) % @divisor
415
+ else
416
+ position = When::TM::Position.any_other(other)
417
+ raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
418
+ return self[position % self]
419
+ end
420
+ end
421
+
422
+ # Enumerator の生成
423
+ #
424
+ # @overload initialize()
425
+ #
426
+ # @overload initialize(range, count_limit=nil)
427
+ # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
428
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
429
+ #
430
+ # @overload initialize(first, direction, count_limit)
431
+ # @param [When::TM::TemporalPosition] first 始点
432
+ # @param [Symbol] direction :forward - 昇順, :reverse - 降順
433
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
434
+ #
435
+ # @return [When::Coordinates::Residue::BestRationalApproximations] 引数なしの場合
436
+ # @return [When::Coordinates::Residue::Enumerator] 引数ありの場合
437
+ #
438
+ def _enumerator(*args)
439
+ length = args.length
440
+ length -= 1 if args[-1].kind_of?(Hash)
441
+ args.unshift(self)
442
+ (length==0) ? BestRationalApproximations.new(self, *args) : Enumerator.new(*args)
443
+ end
444
+ alias :to_enum :_enumerator
445
+ alias :enum_for :_enumerator
446
+
447
+ # オブジェクトの生成
448
+ #
449
+ # @overload initialize(remainder, divisor, carry=0, label=nil, format=nil, units={})
450
+ # @param [Numeric] remainder 剰余
451
+ # @param [Integer] divisor 法
452
+ # @param [Integer] carry 繰り上がり
453
+ # @param [String, When::BasicTypes::M17n] label 名前
454
+ # @param [String, When::BasicTypes::M17n] format 名前の書式
455
+ # @param [Hash] units 単位(下記の通り)
456
+ # @option units [Numeric] 'day' 通日に対して使用する場合のオフセット
457
+ # @option units [Numeric] 'year' 通年に対して使用する場合のオフセット
458
+ #
459
+ def initialize(*args)
460
+ # units の取得
461
+ options =_get_options(args).dup
462
+ units = {}
463
+ list = []
464
+ options.each_pair do |key, value|
465
+ if (PRECISION[key.upcase])
466
+ list << key
467
+ units[key.downcase] = _to_num(value)
468
+ end
469
+ end
470
+ list.each do |key|
471
+ options.delete(key)
472
+ end
473
+ options['units'] ||= {}
474
+ options['units'].update(units)
475
+ _set_variables(options)
476
+ @units ||= {}
477
+
478
+ # その他の変数
479
+ remainder, divisor, carry, label, format = args
480
+ @label = label || @label
481
+ @label = m17n(@label, nil, nil, options) if (@label)
482
+ @format = format || @format
483
+ @format = m17n(@format, nil, nil, options) if (@format)
484
+ _sequence
485
+ @remainder = _to_num(remainder || @remainder)
486
+ @divisor = _to_num(divisor || @divisor )
487
+ @carry = _to_num(carry || @carry )
488
+ raise RangeError, "Divisor shoud be Positive Numeric" if (@divisor <= 0)
489
+ carry, @remainder = @remainder.divmod(@divisor)
490
+ @carry += carry
491
+ end
492
+
493
+ private
494
+
495
+ alias :_method_missing :method_missing
496
+
497
+ # その他のメソッド
498
+ #
499
+ # When::Coordinate::Residue で定義されていないメソッドは
500
+ # 指定の桁での剰余算とみなす
501
+ #
502
+ def method_missing(name, *args, &block)
503
+ return _method_missing(name, *args, &block) if When::Parts::MethodCash::Escape.key?(name) ||
504
+ !@units.key?(name.to_s.downcase)
505
+ instance_eval %Q{
506
+ def #{name}(*args, &block)
507
+ self[args[0] % self.to("#{name.to_s.downcase}")]
508
+ end
509
+ } unless When::Parts::MethodCash.escape(name)
510
+ self[args[0] % self.to(name.to_s.downcase)]
511
+ end
512
+
513
+ # 数値化
514
+ def _to_num(s)
515
+ case s
516
+ when Numeric ; return s
517
+ when nil ; return 0
518
+ when /\.|E/ ; return s.to_f
519
+ else ; return s.to_i
520
+ end
521
+ end
522
+
523
+ #
524
+ # 最良近似分数の系列を生成する Enumerator
525
+ #
526
+ class BestRationalApproximations < When::Parts::Enumerator
527
+
528
+ # Enumerator の巻き戻し
529
+ #
530
+ # @return [void]
531
+ #
532
+ def _rewind
533
+ @z = @x/@y
534
+ @k = @z.floor
535
+ @p = [1,@k]
536
+ @q = [0, 1]
537
+ super
538
+ end
539
+
540
+ # 最良近似分数を生成する
541
+ #
542
+ # @return [Array<Numeric>] ( remainder, divisor, error )
543
+ # [ remainder (Integer) 分子 ]
544
+ # [ divisor (Integer) 分母 ]
545
+ # [ error (Float) 誤差 ]
546
+ #
547
+ def succ
548
+ value = @current
549
+ if (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ||
550
+ (@error.kind_of?(Numeric) && @e && @error >= @e.abs)
551
+ @current = nil
552
+ else
553
+ if @z==@k
554
+ @e = 0
555
+ @current = [@p[1], @q[1], 0]
556
+ else
557
+ @z = 1.0/(@z-@k)
558
+ @k = @z.floor
559
+ @e = @p[1].to_f/@q[1]-@x.to_f/@y
560
+ @current = [@p[1], @q[1], @e]
561
+ @p = [@p[1], @p[1]*@k + @p[0]]
562
+ @q = [@q[1], @q[1]*@k + @q[0]]
563
+ end
564
+ @count += 1
565
+ end
566
+ return value
567
+ end
568
+
569
+ # オブジェクトの生成
570
+ #
571
+ # @overload initialize(parent, options={})
572
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
573
+ # @param [Hash] options 下記の通り
574
+ # @option options [Numeric] :error 収束とみなす誤差(デフォルト 1E-5)
575
+ # @option options [Integer] :count_limit 最大繰り返し回数
576
+ #
577
+ def initialize(*args)
578
+ @y = args[0].divisor
579
+ @x = args[0].remainder + @y * args[0].carry
580
+ super
581
+ @error = @options[:error] || 1e-15
582
+ @count_limit = @options[:count_limit]
583
+ end
584
+ end
585
+
586
+ #
587
+ # 指定の剰余となる通日or通年を生成する Enumerator
588
+ #
589
+ class Enumerator < When::Parts::Enumerator
590
+
591
+ #
592
+ # 次の通日or通年を得る
593
+ #
594
+ # @return [When::TM::TemporalPosition]
595
+ #
596
+ def succ
597
+ value = @current
598
+ @current = (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ? nil :
599
+ (@current==:first) ? @first :
600
+ (@direction==:forward) ? @first & @parent >> @count : @first & @parent << @count
601
+ @count += 1
602
+ return value
603
+ end
604
+
605
+ # オブジェクトの生成
606
+ #
607
+ # @overload initialize(parent, range, count_limit=nil)
608
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
609
+ # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
610
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
611
+ #
612
+ # @overload initialize(parent, first, direction, count_limit)
613
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
614
+ # @param [When::TM::TemporalPosition] first 始点
615
+ # @param [Symbol] direction :forward - 昇順, :reverse - 降順
616
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
617
+ #
618
+ def initialize(*args)
619
+ case args[1]
620
+ when When::TimeValue
621
+ first = args[1] & args[0]
622
+ first = args[1] & (args[0] << 1) if args[2] == :reverse && first > args[1]
623
+ args[1] = first
624
+ when Range
625
+ first = args[1].first & args[0]
626
+ args[1] = (args[1].exclude_end?) ? (first...args[1].last) : (first..args[1].last)
627
+ else
628
+ raise TypeError, "Second Argument should be 'When::TM::(Temporal)Position'"
629
+ end
630
+ @period = When::TM::PeriodDuration.new(args[0].divisor,
631
+ When::Coordinates::PRECISION[args[0].units] || When::DAY)
632
+ super(*args)
633
+ end
634
+ end
635
+ end
636
+
637
+ # 暦座標
638
+ #
639
+ # 暦座標の特性を定義する
640
+ #
641
+ class Index < When::BasicTypes::Object
642
+
643
+ # 日時要素の要素数
644
+ #
645
+ # @return [Integer]
646
+ #
647
+ # 「時」なら 24, 「分」なら 60
648
+ #
649
+ # 日数や太陰太陽暦の月数のように不定の場合は nil
650
+ #
651
+ attr_reader :unit
652
+
653
+ # 日時要素の下限
654
+ #
655
+ # @return [Integer]
656
+ #
657
+ # 年月日は 1, 時分秒は 0
658
+ #
659
+ attr_reader :base
660
+
661
+ # 日時要素名(幹)
662
+ #
663
+ # @return [When::BasicTypes::M17n]
664
+ #
665
+ # 月の名称の配列などに用いる
666
+ #
667
+ attr_reader :trunk
668
+
669
+ # 座標値のシフト
670
+ #
671
+ # @return [Integer](デフォルト 0)
672
+ #
673
+ # 月 - 名称を trunk 配列の何番目(0オリジン)からとるかを指定
674
+ #
675
+ # 日 - 剰余類のシフトを指定
676
+ #
677
+ attr_reader :shift
678
+
679
+ # 日時要素名(枝)
680
+ #
681
+ # @return [Hash] { Numeric=>When::BasicTypes::M17n }
682
+ #
683
+ # When::Coordinates::Pair の branch の値からprefix
684
+ #
685
+ # (「閏」などの文字列)を引くための Hash
686
+ #
687
+ attr_reader :branch
688
+
689
+ # 日時要素名(幹枝)
690
+ #
691
+ # @return [Hash] { When::Coordinates::Pair=>When::BasicTypes::M17n }
692
+ #
693
+ # When::Coordinates::Pair から 日時要素名を引くための Hash
694
+ #
695
+ # 初期化時に @trunk, @branch から自動的に生成する
696
+ #
697
+ attr_reader :trunk_branch
698
+
699
+ # インデクス(幹枝)
700
+ #
701
+ # @return [Hash] { String=>When::Coordinates::Pair }
702
+ #
703
+ # 日時要素名から When::Coordinates::Pair を引くための Hash
704
+ #
705
+ # 初期化時に @trunk, @branch から自動的に生成する
706
+ #
707
+ attr_reader :index
708
+
709
+ # @private
710
+ def self.precision(specification)
711
+ return specification.to_i if (specification.kind_of?(Numeric))
712
+ return (PRECISION[specification] || VALUE[specification] || When::SYSTEM)
713
+ end
714
+
715
+ private
716
+
717
+ def _normalize(args=[], options={})
718
+ @label = m17n(@label, nil, nil, @options) if @label
719
+ @unit = @unit.to_i if @unit
720
+ @base = (@base || 1).to_i
721
+ @trunk = When::BasicTypes::M17n.labels(args[0]) unless args.empty?
722
+ @shift = (@shift || 0).to_i
723
+ @keys = []
724
+ if @trunk
725
+ if @trunk.kind_of?(Array)
726
+ m17n = {}
727
+ @trunk.length.times do |i|
728
+ m17n[(i-@shift) % @trunk.length + @base] = @trunk[i]
729
+ end
730
+ @trunk = m17n
731
+ end
732
+ raise TypeError, "Trunk must be Hash" unless @trunk.kind_of?(Hash)
733
+ @trunk.values.each do |v|
734
+ @keys |= v.keys if v.kind_of?(When::Locale)
735
+ end
736
+ end
737
+ if @branch
738
+ if @branch.kind_of?(Array)
739
+ m17n = {}
740
+ @branch.length.times do |i|
741
+ m17n[i] = @branch[i]
742
+ end
743
+ @branch = m17n
744
+ end
745
+ raise TypeError, "Branch must be Hash" unless @branch.kind_of?(Hash)
746
+ @branch.each_pair do |key, value|
747
+ value = When::BasicTypes::M17n.label(value)
748
+ @keys |= value.keys if value.kind_of?(When::Locale)
749
+ @branch[key] = value
750
+ end
751
+ end
752
+ @trunk_branch = {}
753
+ @index = {}
754
+ if @trunk
755
+ if @branch
756
+ @trunk.keys.each do |t|
757
+ @branch.keys.each do |b|
758
+ @trunk_branch[Pair._en_pair(t,b)] = @trunk[t].prefix(@branch[b])
759
+ end
760
+ end
761
+ else
762
+ @trunk_branch = @trunk
763
+ end
764
+ @trunk_branch.each_pair do |k,v|
765
+ v.values.each do |s|
766
+ @index[s] = k
767
+ end
768
+ end
769
+ end
770
+ end
771
+ end
772
+
773
+ # 暦座標値
774
+ #
775
+ # 暦座標の値を表現する
776
+ #
777
+ class Pair < Numeric
778
+ DL0 = {'-'=> 0, '.'=> 0, ':'=> 0, ','=> 0, ' '=> 0, '@'=>-2 }
779
+ DL1 = {'!'=>-2.5, '%'=>-2, '&'=>-1.5, '*'=>-1, '+'=>-0.5,
780
+ '<'=> 0.5, '='=> 1, '>'=> 1.5, '?'=> 2 }
781
+ DL2 = DL1.invert
782
+
783
+ class << self
784
+ #
785
+ # source Numeric に変換する
786
+ #
787
+ # @param [Object] source
788
+ # @param [Numeric] default source が nil の場合の代替値
789
+ #
790
+ # @return [Numeric]
791
+ #
792
+ def _en_number(source, default=0)
793
+ return default unless(source)
794
+ integer = source.to_i
795
+ float = source.to_f
796
+ return (integer==float) ? integer : float
797
+ end
798
+
799
+ #
800
+ # (source, branch) を When::Coordinates::Pair に変換する
801
+ #
802
+ # @param [Object] source
803
+ # @param [Numeric] branch
804
+ #
805
+ # @return [When::Coordinates::Pair]
806
+ #
807
+ def _force_pair(source, branch=nil)
808
+ case source
809
+ when self
810
+ return source
811
+ when Numeric
812
+ return new(_en_number(source), branch)
813
+ when String
814
+ tail = source[-1..-1]
815
+ branch = DL0[tail] || DL1[tail]
816
+ source = (branch) ? source[0..-2] : source.dup
817
+ trunk = (source =~ /\.|E/i) ? source.to_f : source.to_i
818
+ return new(trunk, branch)
819
+ else
820
+ raise TypeError, "Irregal type for #{self} :#{source}"
821
+ end
822
+ end
823
+
824
+ #
825
+ # branch が有効なら (source, branch) を When::Coordinates::Pair に変換する。
826
+ #
827
+ # @param [Object] trunk
828
+ # @param [Numeric, String] branch
829
+ #
830
+ # @return [trunk自体] branch が無効
831
+ # @return [When::Coordinates::Pair] branch が有効(非0)
832
+ #
833
+ def _en_pair(trunk, branch=nil)
834
+ return trunk if (trunk.kind_of?(self))
835
+ branch = DL0[branch] || DL1[branch] || branch
836
+ trunk = _en_number(trunk) if (trunk)
837
+ return trunk unless (branch && branch != 0)
838
+ return new(trunk, branch)
839
+ end
840
+
841
+ #
842
+ # When::Coordinates::Pair trunk branch の和をとって Numeric 化する
843
+ #
844
+ # @param [When::Coordinates::Pair] source trunk と branch の和を返す
845
+ # @param [Array] source 各要素を _de_pair した Arrayを返す
846
+ # @param [その他 Numeric] source をそのまま返す
847
+ #
848
+ # @return [Numeric, Array<Numeric>]
849
+ #
850
+ def _de_pair(source)
851
+ case source
852
+ when Array
853
+ return (source.map!{|v| _de_pair(v)})
854
+ when self
855
+ return source.sum
856
+ when Numeric
857
+ return source
858
+ else
859
+ raise TypeError, "Irregal type for #{self}"
860
+ end
861
+ end
862
+
863
+ #
864
+ # 文字列を When::Coordinates::Pair のArray 化する
865
+ #
866
+ # @param [String] source
867
+ #
868
+ # @return [Array<When::Coordinates::Pair>]
869
+ #
870
+ def _en_pair_array(source)
871
+ source = $1 if source=~/\A\s*\[?(.+?)\]?\s*\z/
872
+ source.split(/,/).map {|v|
873
+ v =~ /\A\s*(.+?)([^\d\s])?\s*\z/
874
+ _en_pair($1, $2)
875
+ }
876
+ end
877
+
878
+ #
879
+ # 日付を When::Coordinates::Pair のArray 化する
880
+ #
881
+ # @param [String] source
882
+ #
883
+ # @return [Array<When::Coordinates::Pair>]
884
+ #
885
+ def _en_pair_date_time(source)
886
+ source = $1 if source =~ /\A\s*\[(.+)\]\s*\z/
887
+ trunk, branch, *rest = source.strip.split(/([^\d])/)
888
+ if trunk == ''
889
+ sign = branch
890
+ trunk, branch, *rest = rest
891
+ trunk = sign + trunk if trunk
892
+ end
893
+ pairs = []
894
+ while (trunk)
895
+ pairs << _en_pair(trunk, branch)
896
+ trunk, branch, *rest = rest
897
+ end
898
+ return pairs
899
+ end
900
+
901
+ #
902
+ # branch文字を意識して、書式文字列により When::Coordinates::Pair の Array を文字列化
903
+ #
904
+ # @overload _format(format, list)
905
+ # @param [String] format 書式文字列
906
+ # @param [Array] list 書式に従って文字列化されるオブジェクトの Array
907
+ #
908
+ # @return [String]
909
+ #
910
+ # @note %0s は文字列引数の表示を抑制するための指定となる。C言語のprintfと異なるので注意。
911
+ #
912
+ def _format(m17ns)
913
+ index = 1
914
+ m17ns[0].scan(/(%(?:(\d)\$)?[-+0# ]?[*\d.$]*[cspdiuboxXfgeEG])([-.:])?|(%%|.)/).inject('') { |form, m17n|
915
+ t,i,s,c = m17n
916
+ case t
917
+ when '%0s'
918
+ m17n[index..index] = nil
919
+ when nil
920
+ form += c
921
+ else
922
+ suffix = DL2[m17ns[i ? i.to_i : index]*0] || s if s
923
+ form += t + ({nil=>'', '%'=>'%%'}[suffix] || suffix)
924
+ index += t.length - t.gsub(/\*/,'').length + 1
925
+ end
926
+ form
927
+ } % m17ns[1..-1].map { |v| v.kind_of?(self) ? v.trunk : v }
928
+ end
929
+ end
930
+
931
+ #
932
+ # 暦要素の幹
933
+ #
934
+ # @return [Numeric] 年番号,月番号,日番号など暦要素の幹となる部分
935
+ #
936
+ attr_accessor :trunk
937
+
938
+ #
939
+ # 暦要素の枝
940
+ #
941
+ # @return [Numeric] 暦要素のうち(閏であるかなど)幹で表現しきれない枝の部分
942
+ #
943
+ attr_accessor :branch
944
+
945
+ #
946
+ # 暦要素の幹と枝の和
947
+ #
948
+ # @return [Numeric]
949
+ #
950
+ # @note
951
+ # 個別の実装において、本変数が When::TM::Calendar When::TM::Clock が扱うべき
952
+ # 座標値を表すように配慮されている。
953
+ #
954
+ # 例: 会計年度など年の変わり目が暦年と異なる場合、trunk+=1, branch-=1 として、
955
+ # trunk が会計年度, sum が暦年を表現するようにできる。この場合、trunk は表記上の
956
+ # 年、branch は会計年度と暦年にずれがあるという情報を表現していることになる。
957
+ #
958
+ attr_reader :sum
959
+
960
+ #
961
+ # trunk の更新
962
+ #
963
+ # @param [Numeric] trunk 新しい trunk
964
+ #
965
+ # @return [When::Coordinates::Pair] 更新結果
966
+ #
967
+ def trunk=(trunk)
968
+ @trunk = trunk
969
+ _normalize
970
+ self
971
+ end
972
+
973
+ #
974
+ # branch の更新
975
+ #
976
+ # @param [Numeric] branch 新しい branch
977
+ #
978
+ # @return [When::Coordinates::Pair] 更新結果
979
+ #
980
+ def branch=(branch)
981
+ @branch = branch
982
+ _normalize
983
+ self
984
+ end
985
+
986
+ # 属性 @sum を取得する
987
+ #
988
+ # @return [Numeric]
989
+ #
990
+ # @note
991
+ # When::Coordinates::Pair 以外の Numeric では、単項演算 + は恒等変換になる。
992
+ # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
993
+ # 否かを判断することなく、暦要素に単項演算 + を施すことによって、必要な暦要素を取得できる。
994
+ def +@
995
+ return @sum
996
+ end
997
+
998
+ # trunk の符号を反転する
999
+ #
1000
+ # @return [When::Coordinates::Pair] Pair.new(-@trunk, @branch)
1001
+ #
1002
+ def -@
1003
+ return self.class.new(-@trunk, @branch)
1004
+ end
1005
+
1006
+ # @trunk, @branch を取得する
1007
+ #
1008
+ # @param [Integer] other (1, 0, -1)
1009
+ #
1010
+ # @return [Numeric]
1011
+ # [ other == 1 - @trunk ]
1012
+ # [ other == 0 - @branch ]
1013
+ # [ other == -1 - -@trunk ]
1014
+ #
1015
+ # @note
1016
+ # When::Coordinates::Pair 以外の Numeric では、1 による乗算は恒等変換になる。
1017
+ # また、0 による乗算は恒に 0になる。
1018
+ # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
1019
+ # 否かを判断することなく、暦要素に 1 による乗算を施すことによって、trunk に相当する値を、
1020
+ # 0 による乗算を施すことによって、branch に相当する値を取得できる。
1021
+ def *(other)
1022
+ case other
1023
+ when 1 ; @trunk
1024
+ when 0 ; @branch
1025
+ when -1 ; -@trunk
1026
+ else ; raise ArgumentError, "Irregal designation : #{other}"
1027
+ end
1028
+ end
1029
+
1030
+ # 加算
1031
+ #
1032
+ # @param [Numeric] other
1033
+ #
1034
+ # @return [When::Coordinates::Pair]
1035
+ # other When::Coordinates::Pair でない場合、trunk に対する加算となる
1036
+ #
1037
+ def +(other)
1038
+ return self.class.new(@trunk + other, @branch) unless other.kind_of?(self.class)
1039
+ return self.class.new(@trunk + other.trunk, @branch + other.branch)
1040
+ end
1041
+
1042
+ # 減算
1043
+ #
1044
+ # @param [Numeric] other
1045
+ #
1046
+ # @return [When::Coordinates::Pair]
1047
+ # other When::Coordinates::Pair でない場合、trunk に対する減算となる
1048
+ #
1049
+ def -(other)
1050
+ return self.class.new(@trunk - other, @branch) unless other.kind_of?(self.class)
1051
+ return self.class.new(@trunk - other.trunk, @branch - other.branch)
1052
+ end
1053
+
1054
+ # 商と剰余
1055
+ #
1056
+ # @param [Numeric] other
1057
+ #
1058
+ # @return [When::Coordinates::Pair]
1059
+ # trunk に対する divmod となる
1060
+ #
1061
+ def divmod(other)
1062
+ div, mod = @trunk.divmod(other)
1063
+ return div, self.class.new(mod, @branch)
1064
+ end
1065
+
1066
+ # 剰余
1067
+ #
1068
+ # @param [Numeric] other
1069
+ #
1070
+ # @return [When::Coordinates::Pair]
1071
+ # trunk に対する % となる
1072
+ #
1073
+ def %(other)
1074
+ self.class.new(@trunk % other, @branch)
1075
+ end
1076
+
1077
+ # 比較
1078
+ #
1079
+ # @param [Numeric] other
1080
+ #
1081
+ # @return [Integer] (負,0,正)
1082
+ # trunk の比較が優先される
1083
+ #
1084
+ def <=>(other)
1085
+ other = self.class._force_pair(other)
1086
+ (@trunk <=> other.trunk).nonzero? || (@branch <=> other.branch)
1087
+ end
1088
+
1089
+ # 文字列化
1090
+ #
1091
+ # @param [String] zero @branch == 0 を表現する文字列
1092
+ #
1093
+ # @return [String]
1094
+ #
1095
+ def to_s(zero='')
1096
+ return @trunk.to_s + (@branch==0 ? zero : DL2[@branch])
1097
+ end
1098
+
1099
+ # 強制型変換
1100
+ # @private
1101
+ def coerce(other)
1102
+ [self.class._force_pair(other), self]
1103
+ end
1104
+
1105
+ private
1106
+
1107
+ # オブジェクトの生成
1108
+ #
1109
+ # @param [Numeric] trunk
1110
+ # @param [Numeric] branch 枝
1111
+ #
1112
+ def initialize(trunk, branch=nil)
1113
+ @trunk = trunk || 0
1114
+ @branch = branch || 0
1115
+ _normalize
1116
+ end
1117
+
1118
+ def _normalize
1119
+ @trunk = @trunk.to_i if (@trunk.kind_of?(Float) && @trunk.to_i == @trunk.to_f)
1120
+ @sum = @trunk
1121
+ if (@branch.kind_of?(Numeric))
1122
+ @branch = @branch.to_i if (@branch.to_i == @branch.to_f)
1123
+ @sum += @branch if (@trunk)
1124
+ end
1125
+ end
1126
+
1127
+ # その他のメソッド
1128
+ # When::Coordinates:Pair で定義されていないメソッドは
1129
+ # 処理を @sum (type:Numeric) に委譲する
1130
+ #
1131
+ def method_missing(name, *args, &block)
1132
+ self.class.module_eval %Q{
1133
+ def #{name}(*args, &block)
1134
+ @sum.send("#{name}", *args, &block)
1135
+ end
1136
+ } unless When::Parts::MethodCash.escape(name)
1137
+ @sum.send(name, *args, &block)
1138
+ end
1139
+ end
1140
+
1141
+ # 暦座標値
1142
+ #
1143
+ # 閏秒のある暦座標の値を表現する
1144
+ #
1145
+ class LeapSeconds < Pair
1146
+
1147
+ #
1148
+ # 閏秒の単位 / When::TM::Duration::SYSTEM
1149
+ #
1150
+ # @return [Numeric]
1151
+ #
1152
+ attr_reader :second
1153
+
1154
+ #
1155
+ # trunk の整数部
1156
+ #
1157
+ # @return [Integer]
1158
+ #
1159
+ attr_reader :int
1160
+
1161
+ #
1162
+ # trunk の小数部
1163
+ #
1164
+ # @return [Float]
1165
+ #
1166
+ attr_reader :frac
1167
+
1168
+ # 加算
1169
+ #
1170
+ # @param [Numeric] other
1171
+ #
1172
+ # @return [When::Coordinates::LeapSeconds]
1173
+ # other When::Coordinates::LeapSeconds でない場合、trunk に対する加算となる
1174
+ #
1175
+ def +(other)
1176
+ return self.class.new(@trunk + +other, @branch, @second) unless other.kind_of?(self.class)
1177
+ return self.class.new(@trunk + other.trunk, @branch + other.branch, @second)
1178
+ end
1179
+
1180
+ # 減算
1181
+ #
1182
+ # @param [Numeric] other
1183
+ #
1184
+ # @return [When::Coordinates::LeapSeconds]
1185
+ # other When::Coordinates::LeapSeconds でない場合、trunk に対する減算となる
1186
+ #
1187
+ def -(other)
1188
+ return self.class.new(@trunk - +other, @branch, @second) unless other.kind_of?(self.class)
1189
+ return self.class.new(@trunk - other.trunk, @branch - other.branch, @second)
1190
+ end
1191
+
1192
+ # 商と剰余
1193
+ #
1194
+ # @param [Numeric] other
1195
+ #
1196
+ # @return [When::Coordinates::LeapSeconds]
1197
+ # trunk に対する divmod となる
1198
+ #
1199
+ def divmod(other)
1200
+ div, mod = @trunk.divmod(other)
1201
+ return div, self.class.new(mod, @branch, @second)
1202
+ end
1203
+
1204
+ # 比較
1205
+ #
1206
+ # @param [Numeric] other
1207
+ #
1208
+ # @return [Integer] (負,0,正)
1209
+ # trunk の整数部, branch, trunk の小数部の順に比較する
1210
+ #
1211
+ def <=>(other)
1212
+ other = self.class.new(+other, 0, @second) unless other.kind_of?(self.class)
1213
+ raise ArgumentError, "length of unit 'second' mismatch" unless @second == other.second
1214
+ (@int <=> other.int).nonzero? || (@branch <=> other.branch).nonzero? || (@frac <=> other.frac)
1215
+ end
1216
+
1217
+ # 文字列化
1218
+ #
1219
+ # @param [String] zero @branch==0 を表現する文字列
1220
+ #
1221
+ # @return [String]
1222
+ #
1223
+ def to_s(zero='')
1224
+ return @trunk.to_s + (@branch==0 ? zero : DL2[(@branch/@second).floor])
1225
+ end
1226
+
1227
+ # 強制型変換
1228
+ # @private
1229
+ def coerce(other)
1230
+ [self.class.new(other, 0, @second), self]
1231
+ end
1232
+
1233
+ # オブジェクトの生成
1234
+ #
1235
+ # @param [Numeric] trunk 幹
1236
+ # @param [Numeric] branch
1237
+ # @param [Numeric] second 閏秒の単位
1238
+ #
1239
+ def initialize(trunk, branch, second)
1240
+ @second = second
1241
+ @int, @frac = trunk.divmod(second)
1242
+ super(trunk, branch)
1243
+ end
1244
+ end
1245
+
1246
+ #
1247
+ # 空間位置
1248
+ #
1249
+ class Spatial < When::BasicTypes::Object
1250
+
1251
+ LabelProperty = 'label'
1252
+
1253
+ include When::Ephemeris
1254
+
1255
+ require 'when_exe/ephemeris/eclipse'
1256
+
1257
+ class << self
1258
+ # When::Coordinates::Spatial のグローバルな設定を行う
1259
+ #
1260
+ # @param [When::Coordinates::Spatial, String] location デフォルトの空間位置を使用する場合、指定する
1261
+ #
1262
+ # @return [void]
1263
+ #
1264
+ # @note
1265
+ # 本メソッドでマルチスレッド対応の管理変数の初期化を行っている。
1266
+ # このため、本メソッド自体はスレッドセーフでない。
1267
+ #
1268
+ def _setup_(location=nil)
1269
+ @_lock_ = Mutex.new if When.multi_thread
1270
+ @_pool = {}
1271
+ @default_location = location
1272
+ end
1273
+
1274
+ # デフォルトの空間位置
1275
+ #
1276
+ # @param [When::Coordinates::Spatial, String] local デフォルトの空間位置
1277
+ #
1278
+ # @return [When::Coordinates::Spatial, String]
1279
+ #
1280
+ # @note
1281
+ # @default_locationは、原則、ライブラリ立ち上げ時に _setup_ で初期化する。
1282
+ # 以降、@default_locationに代入を行っても、すでに生成した When::TM::TemporalPosition 等には反映されない。
1283
+ #
1284
+ def default_location=(local)
1285
+ if @_pool
1286
+ @default_location = local
1287
+ else
1288
+ _setup_(local)
1289
+ end
1290
+ end
1291
+
1292
+ # 設定情報を取得する
1293
+ #
1294
+ # @return [Hash] 設定情報
1295
+ #
1296
+ def _setup_info
1297
+ {:location => @default_location}
1298
+ end
1299
+
1300
+ # デフォルトの空間位置を読みだす
1301
+ #
1302
+ # @return [When::Coordinates::Spatial]
1303
+ #
1304
+ def default_location
1305
+ _default_location[1]
1306
+ end
1307
+
1308
+ # デフォルトの空間位置が When::TM::Clock のローカルタイムから生成されたか?
1309
+ #
1310
+ # @return [true, false]
1311
+ #
1312
+ def is_default_location_derived?
1313
+ location = _default_location
1314
+ location[0] && location[1]
1315
+ end
1316
+
1317
+ private
1318
+
1319
+ # 共通処理
1320
+ def _default_location
1321
+ case @default_location
1322
+ when nil ;
1323
+ when Array ; return @default_location unless @default_location[0]
1324
+ when String ; return (@default_location = [false, When.Resource(@default_location)])
1325
+ else ; return (@default_location = [false, @default_location])
1326
+ end
1327
+ timezone = When::TM::Clock.local_time
1328
+ location = timezone.location if timezone.kind_of?(When::Parts::Timezone)
1329
+ return [true, location]
1330
+ end
1331
+ end
1332
+
1333
+ # @private
1334
+ HashProperty = [:label, :longitude, :latitude, [:altitude, 0.0], [:datum, When::Ephemeris::Earth], :ref]
1335
+
1336
+ # Degree / Internal Location Unit(16")
1337
+ #
1338
+ # (3600 2 の因数で割りつくした値を単位とする)
1339
+ DEGREE = 225
1340
+
1341
+ # 黄道座標 (ecliptic coordinate system)
1342
+ ECLIPTIC = 0
1343
+
1344
+ # 赤道座標 (equatorial coordinate system)
1345
+ EQUATORIAL = 1
1346
+
1347
+ # 赤道座標[時角] (equatorial coordinate system with hour angle)
1348
+ EQUATORIAL_HA = 2
1349
+
1350
+ # 地平座標 (horizontal coordinate system)
1351
+ HORIZONTAL = 3
1352
+
1353
+ # 惑星中心の高度
1354
+ CENTER = :center
1355
+
1356
+ # 北緯を正とする緯度 / 16秒
1357
+ #
1358
+ # @return [Numeric]
1359
+ #
1360
+ attr_reader :lat
1361
+
1362
+ # 東経を正とする経度 / 16秒
1363
+ #
1364
+ # @return [Numeric]
1365
+ #
1366
+ attr_reader :long
1367
+
1368
+ # 高度 / m
1369
+ #
1370
+ # @return [Numeric]
1371
+ # @return [:Center] 天体の中心の場合
1372
+ #
1373
+ attr_reader :alt
1374
+ alias :altitude :alt
1375
+
1376
+ # 座標系
1377
+ #
1378
+ # @return [When::Ephemeris::Datum]
1379
+ #
1380
+ attr_reader :datum
1381
+
1382
+ # 参照
1383
+ #
1384
+ # @return [String] URI
1385
+ #
1386
+ attr_reader :ref
1387
+
1388
+ # 時間帯(オプショナル)
1389
+ #
1390
+ # @return [TZInfo::CountryTimezone]
1391
+ #
1392
+ # TZInfoライブラリから経緯度を取得して使用する
1393
+ #
1394
+ attr_reader :tz
1395
+
1396
+ # 緯度文字列
1397
+ #
1398
+ # @param [Integer] round 秒の小数点以下最大桁数
1399
+ #
1400
+ # @return [String] 緯度文字列(DD.MMSSsss[NS])
1401
+ #
1402
+ def lat_s
1403
+ When::Coordinates.to_dms(lat / When::Coordinates::Spatial::DEGREE, 'NS', round=6)
1404
+ end
1405
+ alias :latitude :lat_s
1406
+
1407
+ # 経度文字列
1408
+ #
1409
+ # @param [Integer] round 秒の小数点以下最大桁数
1410
+ #
1411
+ # @return [String] 経度文字列(DDD.MMSSsss[EW])
1412
+ #
1413
+ def long_s
1414
+ When::Coordinates.to_dms(long / When::Coordinates::Spatial::DEGREE, 'EW', round=6)
1415
+ end
1416
+ alias :longitude :long_s
1417
+
1418
+ # 高度 / m
1419
+ #
1420
+ # @return [Numeric]
1421
+ # @return [:Center] 天体の中心の場合
1422
+ #
1423
+ attr_reader :alt
1424
+
1425
+ # 観測地の地心距離 / kmを返します。
1426
+ #
1427
+ # @return [Numeric]
1428
+ #
1429
+ def obserber_distance
1430
+ l = PI / (90 * DEGREE) * @lat
1431
+ @datum.surface_radius * (@datum.shape[0]+@datum.shape[1]*cos(l)+@datum.shape[2]*cos(2*l))
1432
+ end
1433
+
1434
+ # 観測地での‘大地’の視半径
1435
+ #
1436
+ # @return [Numeric]
1437
+ #
1438
+ def horizon
1439
+ # 地面以下なら 90度とみなす
1440
+ return 0.25 if @alt == :Center || @alt <= 0
1441
+
1442
+ # 観測地の地心距離 / m
1443
+ r = obserber_distance * 1000.0
1444
+
1445
+ # 大気効果
1446
+ air_effect = @datum.air[1] * @alt / (@datum.air[2] * @alt + r)
1447
+
1448
+ # ‘大地’の視半径
1449
+ asin((1.0+air_effect) * r / (r+@alt)) / CIRCLE
1450
+ end
1451
+
1452
+ # 観測地の地方恒星時 / 時を返します。
1453
+ #
1454
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1455
+ # @param [When::TM::TemporalPosition] t
1456
+ #
1457
+ # @return [Numeric]
1458
+ #
1459
+ def local_sidereal_time(t)
1460
+ t = +t
1461
+ c = julian_century_from_2000(t)
1462
+ result = @datum.sid[0] + c * (@datum.sid[1] + c * @datum.sid[2]) + @long / (15.0 * DEGREE)
1463
+ result += (cosc(obl(c)) * delta_p(c) +
1464
+ (t-When::TimeStandard.delta_t_observed(t)/86400+0.5) % 1) * 24 if @datum.kind_of?(Earth)
1465
+ result
1466
+ end
1467
+
1468
+ # 観測地の日心三次元座標(黄道座標)
1469
+ #
1470
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1471
+ # @param [When::TM::TemporalPosition] t
1472
+ #
1473
+ # @return [Numeric]
1474
+ #
1475
+ def _coords(t)
1476
+ t = +t
1477
+ @datum._coords(t) + _coords_diff(t)
1478
+ end
1479
+
1480
+ private
1481
+
1482
+ # 要素の正規化
1483
+ def _normalize(args=[], options={})
1484
+
1485
+ # 時間帯による指定
1486
+ @tz = When::Parts::Timezone.tz_info[@tz] if @tz.kind_of?(String)
1487
+ if @tz
1488
+ @label ||= When.m17n(@tz.identifier)
1489
+ @long ||= @tz.longitude
1490
+ @lat ||= @tz.latitude
1491
+ end
1492
+
1493
+ # データの整形
1494
+ @label = When::BasicTypes::M17n.new(@label) if @label.kind_of?(Hash)
1495
+ @long = When::Coordinates.to_deg_225(@long, 'EW') if @long
1496
+ @lat = When::Coordinates.to_deg_225(@lat, 'NS') if @lat
1497
+ @datum = When.Resource(@datum || 'Earth', '_ep:')
1498
+ @long ||= 0.0
1499
+ @lat ||= 0.0
1500
+ @alt =
1501
+ case @alt
1502
+ when String ; @alt.gsub(/@/, '.').to_f
1503
+ when Numeric ; @alt.to_f
1504
+ when Symbol ; @alt
1505
+ else ; 0.0
1506
+ end
1507
+
1508
+ # 日月食用作業変数
1509
+ @mean = When.Resource(@mean || '_ep:Formula?formula=1L')
1510
+ @ecls = {}
1511
+ end
1512
+
1513
+ # 観測地の惑星中心を原点とする三次元座標
1514
+ #
1515
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1516
+ # @param [When::TM::TemporalPosition] t
1517
+ # @param [Integer] system : 座標系
1518
+ # [ ECLIPTIC = 黄道座標 ]
1519
+ # [ EQUATORIAL = 赤道座標 ]
1520
+ # [ EQUATORIAL_HA = 赤道座標[時角] ]
1521
+ # [ HORIZONTAL = 地平座標 ]
1522
+ #
1523
+ def _coords_diff(t, system=ECLIPTIC)
1524
+ return Coords.polar(0,0,0) if alt == :Center
1525
+ t = +t
1526
+ lat = @lat.to_f / DEGREE
1527
+ coords = Coords.polar(
1528
+ local_sidereal_time(t) / 24.0,
1529
+ (lat + @datum.shape[3] * sind(2*lat)) / 360.0,
1530
+ (obserber_distance + @alt/1000.0) / AU)
1531
+ case system
1532
+ when ECLIPTIC ; coords.r_to_y(t, @datum)
1533
+ when EQUATORIAL ; coords
1534
+ when EQUATORIAL_HA ; coords.r_to_rh(t, self)
1535
+ when HORIZONTAL ; coords.r_to_h(t, self)
1536
+ end
1537
+ end
1538
+
1539
+ # その他のメソッド
1540
+ # When::Coordinates::Spatial で定義されていないメソッドは
1541
+ # 処理を @datum (type: When::Ephemeris::Datum) に委譲する
1542
+ #
1543
+ def method_missing(name, *args, &block)
1544
+ self.class.module_eval %Q{
1545
+ def #{name}(*args, &block)
1546
+ @datum.send("#{name}", *args, &block)
1547
+ end
1548
+ } unless When::Parts::MethodCash.escape(name)
1549
+ @datum.send(name, *args, &block)
1550
+ end
1551
+
1552
+ # @private
1553
+ module Normalize
1554
+
1555
+ private
1556
+
1557
+ # 位置情報
1558
+ #
1559
+ # @return [When::Coordinates::Spatial]
1560
+ #
1561
+ #attr_reader :location
1562
+
1563
+ # 日時要素の境界オブジェクト
1564
+ #
1565
+ # @return [When::Coordinates::Border]
1566
+ #
1567
+ #attr_reader :border
1568
+
1569
+ # 境界計算用の計算オブジェクト
1570
+ #
1571
+ # @return [When::Ephemeris::Formula]
1572
+ #
1573
+ #attr_reader :formula
1574
+
1575
+ #
1576
+ # Temporal Module の Spatial Parts の初期化
1577
+ #
1578
+ def _normalize_spatial
1579
+
1580
+ # Location
1581
+ if (@location||@long||@lat||@alt).kind_of?(String)
1582
+ @location ||= "_l:long=#{@long||0}&lat=#{@lat||0}&alt=#{@alt||0}"
1583
+ @location = When.Resource(@location)
1584
+ end
1585
+ @location ||= @tz_prop.location if @tz_prop
1586
+
1587
+ # Border
1588
+ @border = When.Border(@border) if @border.kind_of?(String)
1589
+
1590
+ # Formula
1591
+ instance_eval('class << self; attr_reader :formula; end') if @location && @border
1592
+ if respond_to?(:formula)
1593
+ extend When::Ephemeris::Formula::ForwardedFormula
1594
+ unless @formula
1595
+ options = {:formula => kind_of?(When::CalendarTypes::SolarYearTableBased) ? '1S' : '1L'}
1596
+ options[:location] = @location if @location
1597
+ @formula = When::Ephemeris::Formula.new(options)
1598
+ end
1599
+ @formula = When.Resource(Array(@formula), '_ep:')
1600
+ end
1601
+ end
1602
+ end
1603
+ end
1604
+
1605
+ #
1606
+ # 暦座標を扱う処理をまとめたモジュール
1607
+ #
1608
+ # When::TM::Calendar When::TM::Clock に共通する処理だが、ISO 19108 で両者の
1609
+ # 直接の superclass である、When::TM::ReferenceSystem は、これらの処理を持たない
1610
+ # こととなっているため、When::TM::Calendar と When::TM::Clock の共通部分を
1611
+ # モジュールとしてまとめた。
1612
+ #
1613
+ module Temporal
1614
+
1615
+ include When::Parts::MethodCash
1616
+
1617
+ # @private
1618
+ HashProperty =
1619
+ [[:origin_of_MSC, 0], [:origin_of_LSC, 0], [:index_of_MSC, 0], [:epoch_in_CE, 0],
1620
+ :unit, :base, :pair, :note,
1621
+ :location, :time_basis, :border, :formula, :domain]
1622
+
1623
+ # 年/日の原点(origin of most significant coordinate)
1624
+ #
1625
+ # @return [Integer]
1626
+ #
1627
+ attr_reader :origin_of_MSC
1628
+
1629
+ # 日/秒の原点(origin of least significant coordinate)
1630
+ #
1631
+ # @return [Integer]
1632
+ #
1633
+ attr_reader :origin_of_LSC
1634
+
1635
+ # インデクスオブジェクト
1636
+ #
1637
+ # @return [Array<When::Coordinates::Index>]
1638
+ #
1639
+ attr_reader :indices
1640
+
1641
+ # 年/日のインデクス(index of most significant coordinate)
1642
+ #
1643
+ # @return [Integer]
1644
+ #
1645
+ attr_reader :index_of_MSC
1646
+
1647
+ # 日時要素の要素数
1648
+ #
1649
+ # @return [Array<Integer, nil>]
1650
+ #
1651
+ # Ex. [nil, 12], [nil, 24, 60, 60]
1652
+ #
1653
+ # 初期化時に indices から自動生成する
1654
+ #
1655
+ attr_reader :unit
1656
+
1657
+ # 日時要素の下限
1658
+ #
1659
+ # @return [Array<Integer, nil>]
1660
+ #
1661
+ # Ex. [nil, 1, 1], [nil, 0, 0, 0]
1662
+ #
1663
+ # 初期化時に indices から自動生成する
1664
+ #
1665
+ # @note 日付/日時の外部表現の「下限」を指定する。内部表現の下限は常に 0 である。
1666
+ # サブクラスが定義するメソッド _coordinates_to_number, _number_to_coordinates は内部表現を使用する。
1667
+ #
1668
+ attr_reader :base
1669
+
1670
+ # 日時要素がPairであるべきか
1671
+ #
1672
+ # @return [Array<Boolean>]
1673
+ #
1674
+ # Ex. [false] * 3, [false] * 4
1675
+ #
1676
+ # 初期化時に indices から自動生成する
1677
+ #
1678
+ attr_reader :pair
1679
+
1680
+ # 代表暦注
1681
+ #
1682
+ # @return [When::CalendarNote]
1683
+ # @return [Array<Array<klass, Array<klass, method, block>>>] 最外側のArray要素は年・月・日に対応
1684
+ #
1685
+ # klass [String, When::CalendarNote, When::Coordinates::Residue]
1686
+ #
1687
+ # method [String, Symbol] (デフォルト 'day', 'month' or 'year' (対応する桁による))
1688
+ #
1689
+ # block [Block] (デフォルト なし)
1690
+ #
1691
+ def note
1692
+ case @note
1693
+ when String ; @note = When.CalendarNote(@note)
1694
+ when Array ; @note = When::CalendarNote.new(*@note)
1695
+ end
1696
+ @note
1697
+ end
1698
+
1699
+ #
1700
+ # 日時要素の正規化
1701
+ #
1702
+ # @param [Array<Numeric>] source 正規化しようとしている日時要素の Array
1703
+ # @param [Array<Numeric>] other 日時要素ごとに加減算を行う場合、加減算量の Array を指定する
1704
+ # @param [Block] block
1705
+ # @note
1706
+ # 日付要素と時刻要素に関連がある場合、block を指定して、両者の
1707
+ # 情報をやり取りする( yield で通日を渡し、通日を返してもらう)。
1708
+ #
1709
+ # 例1: 夏時間制を採用している場合、日付によって時刻の正規化の仕方が影響を受ける
1710
+ #
1711
+ # 例2: 日の境界が日没の場合、当該時刻が日没の前か後かで日付が変わる
1712
+ #
1713
+ # @return [Array<Numeric>] 正規化された日時要素の Array
1714
+ #
1715
+ # 日時要素は、それぞれの When::TM::Calendar や When::TM::Clock の実装に応じて有効な値となっている。
1716
+ #
1717
+ def _validate(source, other=nil, &block)
1718
+ return _encode(_decode(source, other, &block))
1719
+ end
1720
+
1721
+ # 期間指定用 Array の桁数合わせ
1722
+ #
1723
+ # @param [Array<Numeric>] period
1724
+ #
1725
+ # @return [Array<Numeric>] 桁数合わせをした Array
1726
+ #
1727
+ def _arrange_length(period)
1728
+ return period unless period.kind_of?(Array)
1729
+ diff = @indices.length - period.length + 1
1730
+ return period if (diff == 0)
1731
+ return (diff > 0) ? Array.new(diff, 0) + period : period[(-diff)..-1]
1732
+ end
1733
+
1734
+ # @private
1735
+ #
1736
+ # 対応する ::Date の start 属性
1737
+ def _default_start
1738
+ ::Date::GREGORIAN
1739
+ end
1740
+
1741
+ # protected
1742
+
1743
+ #
1744
+ # 日時要素の encode
1745
+ #
1746
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
1747
+ #
1748
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
1749
+ #
1750
+ def _encode(source, border=@border)
1751
+ # source は非破壊
1752
+ date = source.dup
1753
+
1754
+ # 外部表現に戻す
1755
+ date[0] = +date[0]
1756
+ (@base.length-1).downto(@unit.length-1) do |i|
1757
+ date[i] = _from_index(date[0..i]) || date[i] + (@base[i]||0)
1758
+ end
1759
+ date[0] = source[0]
1760
+
1761
+ # 結果を反映
1762
+ date = border._adjust_epoch(date, self) if border
1763
+ _encode_upper_structure(date)
1764
+ end
1765
+
1766
+ private
1767
+
1768
+ #
1769
+ # 日時要素の decode
1770
+ #
1771
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
1772
+ #
1773
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
1774
+ #
1775
+ def _decode(source, other=nil)
1776
+
1777
+ # other の正規化
1778
+ period = other ? other.dup : Array.new(@indices.length+1,0)
1779
+
1780
+ # 上の位の集約
1781
+ date = _decode_upper_structure(source.dup)
1782
+ if (@index_of_MSC > 0) && other
1783
+ u = 1
1784
+ s = period[@index_of_MSC]
1785
+ (@index_of_MSC-1).downto(0) do |i|
1786
+ u *= @indices[i].unit
1787
+ s += u * period[i]
1788
+ end
1789
+ period = [s] + period[(@index_of_MSC+1)..-1]
1790
+ end
1791
+
1792
+ # 下の位の既定値
1793
+ unless date[1] || !@border
1794
+ date[0...@base.length] = @border.border([@border._date_adjust(date[0])], self)
1795
+ end
1796
+
1797
+ # 要素数固定部分の正規化(上 -> 下) - ISO8601 の 小数要素(ex. "T23:20.8")の処理
1798
+ coordinates = [date]
1799
+ coordinates << period if other
1800
+ coordinates.each do |coord|
1801
+ carry = 0
1802
+ @unit.length.times do |i|
1803
+ if carry == 0
1804
+ break unless coord[i]
1805
+ else
1806
+ coord[i] ||= 0
1807
+ coord[i] += carry * unit[i]
1808
+ end
1809
+ if coord[i].kind_of?(Integer)
1810
+ carry = 0
1811
+ else
1812
+ if i < @unit.length-1
1813
+ carry = (coord[i].kind_of?(Pair) ? coord[i].trunk : coord[i]) % 1
1814
+ coord[i] -= carry
1815
+ end
1816
+ coord[i] = coord[i].to_i if coord[i].kind_of?(Float) && coord[i] == coord[i].to_i
1817
+ end
1818
+ end
1819
+ end
1820
+
1821
+ # 要素数固定部分の正規化(下 -> 上)
1822
+ carry = 0
1823
+ (@unit.length-1).downto(1) do |i|
1824
+ if date[i]
1825
+ digit = date[i] + ((other || date[i]>=0) ? period[i]-@base[i] : @unit[i])
1826
+ else
1827
+ digit = period[i]
1828
+ end
1829
+ carry, date[i] = (digit + carry).divmod(@unit[i])
1830
+ end
1831
+ year = date[0] + period[0] + carry
1832
+
1833
+ # 要素数可変部分の正規化
1834
+ limit = @base.length-1
1835
+ count = nil
1836
+ date[0] = +year
1837
+ if @base.length > @unit.length
1838
+ @unit.length.upto(limit) do |i|
1839
+ len = _length(date[0...i])
1840
+ if date[i]
1841
+ plus = date[i]>=0
1842
+ digit = !plus ? +date[i] : _to_index(date[0..i]) || +date[i] - @base[i]
1843
+ digit += (plus || other) ? period[i] : len
1844
+ else
1845
+ digit = period[i]
1846
+ end
1847
+
1848
+ if i==limit then
1849
+ # 最下位
1850
+ if (0...len) === digit
1851
+ # 要素が範囲内
1852
+ date[i] = digit
1853
+ elsif other && period[i] == 0
1854
+ # 要素が範囲外で、加算自体はあるが“日”の加算なし
1855
+ date[i] = len-1
1856
+ else
1857
+ # 要素が範囲外で、加算自体がないか“日”の加算あり
1858
+ date[i] = 0
1859
+ count = _coordinates_to_number(*date)+digit
1860
+ count = yield(count) if block_given?
1861
+ date = _number_to_coordinates(count)
1862
+ end
1863
+
1864
+ else
1865
+ # 最下位以外
1866
+ # 要素が大きすぎる場合
1867
+ while digit >= len do
1868
+ digit -= len
1869
+ carry = 1
1870
+ (i-1).downto(1) do |k|
1871
+ if date[k] >= _length(date[0...k])-1
1872
+ date[k] = 0
1873
+ carry = 1
1874
+ else
1875
+ date[k] += 1
1876
+ carry = 0
1877
+ break
1878
+ end
1879
+ end
1880
+ date[0] += carry
1881
+ len = _length(date[0...i])
1882
+ end
1883
+
1884
+ # 要素が小さすぎる場合
1885
+ while digit < 0 do
1886
+ date[i-1] -= 1
1887
+ digit += _length(date[0...i])
1888
+ (i-1).downto(1) do |k|
1889
+ break if (date[k] >= 0)
1890
+ date[k-1] -= 1
1891
+ date[k] = _length(date[0...k]) - 1
1892
+ end
1893
+ end
1894
+
1895
+ # 要素が範囲内
1896
+ date[i] = digit
1897
+ end
1898
+ end
1899
+ end
1900
+ date[0] = year + (date[0] - +year)
1901
+
1902
+ # 時刻部分による補正が入る場合
1903
+ if block_given? && !count
1904
+ count = _coordinates_to_number(*date)
1905
+ modified = yield(count)
1906
+ date = _number_to_coordinates(modified) unless count == modified
1907
+ end
1908
+
1909
+ # 結果を返す
1910
+ return date
1911
+ end
1912
+
1913
+ #
1914
+ # 日時要素の下限を取得する
1915
+ #
1916
+ # @param [Array<Numeric>] date 日時要素
1917
+ #
1918
+ # @return [Integer] 日時要素の下限
1919
+ #
1920
+ def _base(date)
1921
+ return @base[date.length]
1922
+ end
1923
+
1924
+ #
1925
+ # 日時要素の番号から要素を取得する配列を返す
1926
+ #
1927
+ #def _ids(date)
1928
+ # return nil
1929
+ #end
1930
+
1931
+ #
1932
+ # 日時要素の要素数を取得する(直下の要素)
1933
+ #
1934
+ #def _length(date)
1935
+ # return @unit[date.length]
1936
+ #end
1937
+
1938
+ #
1939
+ # 日時要素の要素数を取得する(全要素)
1940
+ #
1941
+ # @param [Array<Numeric>] date 日時要素(内部表現)
1942
+ #
1943
+ # @return [Integer] 日時要素の要素数
1944
+ # @note 一般に date が 1要素ならその要素の年の日数、2要素ならその要素の年月の日数になる。
1945
+ #
1946
+ def _sum_(date)
1947
+ return @unit[date.length..-1].inject(1) {|p, u| p * u }
1948
+ end
1949
+
1950
+ def _normalize_temporal
1951
+
1952
+ # method cash
1953
+ @_m_cash_lock_ = Mutex.new if When.multi_thread
1954
+
1955
+ # label
1956
+ @label = When::BasicTypes::M17n.label(@label)
1957
+
1958
+ # Origin and Upper Digits
1959
+ @origin_of_MSC ||= - @border.behavior * 1 if @border
1960
+ @origin_of_MSC = Pair._en_number(@origin_of_MSC)
1961
+ @origin_of_LSC = Pair._en_number(@origin_of_LSC)
1962
+ @index_of_MSC = Pair._en_number(@index_of_MSC)
1963
+ if @index_of_MSC != 0
1964
+ extend OriginAndUpperDigits
1965
+ elsif @origin_of_MSC != 0
1966
+ extend OriginOnly
1967
+ end
1968
+
1969
+ # unit
1970
+ @unit = [nil]
1971
+ (@index_of_MSC...@indices.length).each do |i|
1972
+ break unless @indices[i].unit
1973
+ @unit << @indices[i].unit
1974
+ end
1975
+
1976
+ # base & pair
1977
+ @base = [nil]
1978
+ @pair = [false]
1979
+ (@index_of_MSC...@indices.length).each do |i|
1980
+ raise ArgumentError, "Base not defined" unless @indices[i].base
1981
+ @base << @indices[i].base
1982
+ @pair << (@indices[i].branch != nil)
1983
+ end
1984
+ extend IndexConversion unless @pair.uniq == [false]
1985
+
1986
+ # note
1987
+ @note ||= 'Default'
1988
+
1989
+ # keys
1990
+ @keys = @indices.inject(label.instance_of?(When::Locale) ? label.keys : []) {|key, index| key |= index.keys}
1991
+ end
1992
+
1993
+ def _default_index_of_MSC
1994
+ unless @index_of_MSC
1995
+ [:_coordinates_to_number, :_coordinates_to_number_].each do |to_n|
1996
+ if respond_to?(to_n)
1997
+ @index_of_MSC = @indices.length - method(to_n).arity + 1
1998
+ break
1999
+ end
2000
+ end
2001
+ end
2002
+ end
2003
+
2004
+ # 何もしない
2005
+ def _return_nil(source); nil end
2006
+ alias :_from_index :_return_nil
2007
+ alias :_to_index :_return_nil
2008
+
2009
+ def _do_nothing(source); source end
2010
+ alias :_encode_upper_structure :_do_nothing
2011
+ alias :_decode_upper_structure :_do_nothing
2012
+
2013
+ # @private
2014
+ module IndexConversion
2015
+ #
2016
+ # indexのPair化
2017
+ #
2018
+ # @param [Array<Numeric>] date 最下位が index になっている日時要素
2019
+ #
2020
+ # @return [When::Coordinates::Pair] 最下位の index に対応する When::Coordinates::Pair
2021
+ #
2022
+ def _from_index(date)
2023
+ return nil unless @pair[date.size-1]
2024
+ ids = _ids(date[0..-2])
2025
+ m = ids[date[-1]] if (ids)
2026
+ return Pair._force_pair(m) if (ids && m)
2027
+ return Pair.new(+date[-1]+@base[date.length-1], 0)
2028
+ rescue ArgumentError
2029
+ nil
2030
+ end
2031
+
2032
+ #
2033
+ # Pairのindex化
2034
+ #
2035
+ # @param [Array<Numeric>] date 最下位が When::Coordinates::Pair になっている日時要素
2036
+ #
2037
+ # @return [When::Coordinates::Pair] 最下位の When::Coordinates::Pair に対応する index
2038
+ #
2039
+ def _to_index(date)
2040
+ return nil unless @pair[date.size-1]
2041
+ ids = _ids(date[0..-2])
2042
+ i = ids.index(date[-1]) if ids
2043
+ return i if i
2044
+ return nil unless ids && date[-1].kind_of?(Pair)
2045
+ digit = Pair.new(date[-1].trunk, date[-1].branch)
2046
+ while digit.branch > 0
2047
+ digit.branch -= 1
2048
+ i = ids.index(digit)
2049
+ return i + date[-1].branch - digit.branch if i
2050
+ end
2051
+ return nil
2052
+ rescue ArgumentError
2053
+ nil
2054
+ end
2055
+ end
2056
+
2057
+ alias :_method_missing :method_missing
2058
+
2059
+ # その他のメソッド
2060
+ # When::Coordinates::Temporal で定義されていないメソッドは
2061
+ # 処理を下記に移譲する(番号は優先順位)
2062
+ # When::CalendarNote
2063
+ # (1) @note
2064
+ # (2) SolarTerms
2065
+ # (3) LunarPhases
2066
+ # When::Ephemeris::Formula
2067
+ # (4)@formula[0]
2068
+ #
2069
+ def method_missing(name, *args, &block)
2070
+ unless When::Parts::MethodCash::Escape.key?(name)
2071
+ if note.respond_to?(name)
2072
+ if note.class::CalendarDepend
2073
+ instance_eval %Q{
2074
+ def #{name}(*args, &block)
2075
+ @note.send("#{name}", *(args + [self]), &block)
2076
+ end
2077
+ } unless When::Parts::MethodCash.escape(name)
2078
+ return @note.send(name, *(args + [self]), &block)
2079
+ else
2080
+ instance_eval %Q{
2081
+ def #{name}(*args, &block)
2082
+ @note.send("#{name}", *args, &block)
2083
+ end
2084
+ } unless When::Parts::MethodCash.escape(name)
2085
+ return @note.send(name, *args, &block)
2086
+ end
2087
+ end
2088
+ ['SolarTerms', 'LunarPhases'].each do |note|
2089
+ if When.CalendarNote(note).respond_to?(name)
2090
+ instance_eval %Q{
2091
+ def #{name}(*args, &block)
2092
+ When.CalendarNote("#{note}").send("#{name}", *args, &block)
2093
+ end
2094
+ } unless When::Parts::MethodCash.escape(name)
2095
+ return When.CalendarNote(note).send(name, *args, &block)
2096
+ end
2097
+ end
2098
+ if When::Ephemeris::Formula.method_defined?(name)
2099
+ unless respond_to?(:forwarded_formula, true)
2100
+ extend When::Ephemeris::Formula::ForwardedFormula
2101
+ @formula ||= When::Ephemeris::Formula.new({:location=>@location})
2102
+ @formula = When.Resource(Array(@formula), '_ep:')
2103
+ end
2104
+ instance_eval %Q{
2105
+ def #{name}(*args, &block)
2106
+ forward = forwarded_formula("#{name}", args[0])
2107
+ return forward.send("#{name}", *args, &block) if forward
2108
+ _method_missing("#{name}", *args, &block)
2109
+ end
2110
+ } unless When::Parts::MethodCash.escape(name)
2111
+ forward = forwarded_formula(name, args[0])
2112
+ return forward.send(name, *args, &block) if forward
2113
+ end
2114
+ end
2115
+ _method_missing(name, *args, &block)
2116
+ end
2117
+
2118
+ # @private
2119
+ module OriginOnly
2120
+ # 上の位の付加
2121
+ #
2122
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
2123
+ #
2124
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
2125
+ #
2126
+ def _encode_upper_structure(source)
2127
+ date = source.dup
2128
+ date[0] += @origin_of_MSC
2129
+ return date
2130
+ end
2131
+
2132
+ # 上の位の除去
2133
+ #
2134
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
2135
+ #
2136
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
2137
+ #
2138
+ def _decode_upper_structure(source)
2139
+ date = source.dup
2140
+ date[0] -= @origin_of_MSC
2141
+ return date
2142
+ end
2143
+ end
2144
+
2145
+ # @private
2146
+ module OriginAndUpperDigits
2147
+ # 上の位の付加
2148
+ #
2149
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
2150
+ #
2151
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
2152
+ #
2153
+ def _encode_upper_structure(source)
2154
+ date = source.dup
2155
+ date[0] += @origin_of_MSC
2156
+ @index_of_MSC.downto(1) do |i|
2157
+ carry, date[0] = (+date[0]).divmod(@indices[i-1].unit)
2158
+ date[0] += @indices[i-1].base
2159
+ date.unshift(carry)
2160
+ end
2161
+ return date
2162
+ end
2163
+
2164
+ # 上の位の除去
2165
+ #
2166
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
2167
+ #
2168
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
2169
+ #
2170
+ def _decode_upper_structure(source)
2171
+ date = source.dup
2172
+ u = 1
2173
+ s = 0
2174
+ @index_of_MSC.downto(1) do |i|
2175
+ s += u * (+date[i] - @indices[i-1].base) if (date[i])
2176
+ u *= @indices[i-1].unit
2177
+ end
2178
+ date[@index_of_MSC] = s + u * (+date[0]) - @origin_of_MSC
2179
+ return date[@index_of_MSC..-1]
2180
+ end
2181
+ end
2182
+ end
2183
+
2184
+ #
2185
+ # 日時要素の境界 - Border
2186
+ #
2187
+ class Border < When::BasicTypes::Object
2188
+ #
2189
+ # 境界の振舞
2190
+ #
2191
+ # @return [Numeric]
2192
+ #
2193
+ # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
2194
+ #
2195
+ # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
2196
+ #
2197
+ def behavior
2198
+ @border[0]
2199
+ end
2200
+
2201
+ # 境界の取得
2202
+ #
2203
+ # @param [Array<Numeric>] date 境界を計算する年/日
2204
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2205
+ #
2206
+ # @return [Array<Numeric>] その年/日の境界
2207
+ #
2208
+ def border(date=[], frame=nil)
2209
+ last = date.length-1
2210
+ return @border if last<0
2211
+ b_date = date[0..last] + @border[(last+1)..-1]
2212
+ branch = @border[last] * 0
2213
+ b_date[last] = When::Coordinates::Pair.new(date[last] * 1, branch)
2214
+ return b_date
2215
+ end
2216
+
2217
+ # 境界の正規化
2218
+ #
2219
+ # @param [Array<Numeric>] date 境界を計算する年/日
2220
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2221
+ #
2222
+ # @return [Array<Numeric>] その年/日の境界
2223
+ #
2224
+ def _adjust_epoch(date, frame=nil)
2225
+ s_date = date.dup
2226
+ e_date = border([+date[0]], frame)
2227
+ branch = behavior * 0
2228
+ branch += 1 if (s_date[1..-1] <=> e_date[1..-1]) < 0
2229
+ s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
2230
+ return s_date
2231
+ end
2232
+
2233
+ # 日付の補正
2234
+ # @private
2235
+ def _date_adjust(source)
2236
+ source
2237
+ end
2238
+
2239
+ private
2240
+
2241
+ # 要素の正規化
2242
+ def _normalize(args=[], options={})
2243
+ @border = When::Coordinates::Pair._en_pair_date_time(@border) if @border.kind_of?(String)
2244
+ end
2245
+ end
2246
+
2247
+ #
2248
+ # 日時要素の境界 - 年/日によって、異なる境界を使用する場合
2249
+ #
2250
+ class MultiBorder < Border
2251
+
2252
+ #
2253
+ # 境界の配列
2254
+ # @return [Array<When::Coordinates::Border>]
2255
+ attr_reader :borders
2256
+
2257
+ #
2258
+ # 境界の振舞
2259
+ #
2260
+ # @return [Numeric]
2261
+ #
2262
+ # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
2263
+ #
2264
+ # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
2265
+ #
2266
+ def behavior
2267
+ @borders[0][:border].behavior
2268
+ end
2269
+
2270
+ # 境界の取得
2271
+ #
2272
+ # @param [Array<Numeric>] date 境界を計算する年/日
2273
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2274
+ #
2275
+ # @return [Array<Numeric>] その年/日の境界
2276
+ #
2277
+ def border(date=[], frame=nil)
2278
+ last = date.length-1
2279
+ return @borders[0][:boder] if (last<0)
2280
+ @borders.each do |border|
2281
+ return border[:border].border(date, frame) if date[0] >= border[:key]
2282
+ end
2283
+ date[0..last]
2284
+ end
2285
+
2286
+ # 境界の正規化
2287
+ #
2288
+ # @param [Array<Numeric>] date 境界を計算する年/日
2289
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2290
+ #
2291
+ # @return [Array<Numeric>] その年/日の境界
2292
+ #
2293
+ def _adjust_epoch(date, frame=nil)
2294
+ @borders.each do |border|
2295
+ next unless date[0] >= border[:key]
2296
+ s_date = date.dup
2297
+ e_date = border[:border].border(date[0..0], frame)
2298
+ branch = border[:border].behavior * 0
2299
+ branch += 1 if ((s_date[1..-1] <=> e_date[1..-1]) < 0)
2300
+ s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
2301
+ return s_date
2302
+ end
2303
+ date
2304
+ end
2305
+
2306
+ private
2307
+
2308
+ # 要素の正規化
2309
+ def _normalize(args=[], options={})
2310
+ if @borders.kind_of?(String)
2311
+ list = @borders.split(/(\(.+?\))/)
2312
+ list.shift if list[0]==''
2313
+ list.unshift(-Float::MAX) unless list[0] =~ /\(/
2314
+ list.push('0-1-1') if list[-1] =~ /\(/
2315
+ @borders = []
2316
+ loop do
2317
+ key, border, *list = list
2318
+ break unless key
2319
+ key = $1.to_i if key.kind_of?(String) && /\((.+?)\)/ =~ key
2320
+ @borders << {:key=>key, :border=>When.Border(border)}
2321
+ end
2322
+ end
2323
+ @borders = @borders.sort_by {|border| -border[:key]}
2324
+ end
2325
+ end
2326
+
2327
+ #
2328
+ # 基準暦法の新年による境界
2329
+ #
2330
+ class CalendarBorder < Border
2331
+
2332
+ class << self
2333
+
2334
+ private
2335
+
2336
+ def _behalf_of(iri)
2337
+ base = iri.dup.sub!('/Coordinates/','/CalendarTypes/')
2338
+ return nil unless base
2339
+ self.new({'engine'=>When::Parts::Resource._instance(base)})
2340
+ end
2341
+ end
2342
+
2343
+ # 境界の取得
2344
+ #
2345
+ # @param [Array<Numeric>] date 境界を計算する年
2346
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
2347
+ #
2348
+ # @return [Array<Numeric>] その年の境界
2349
+ #
2350
+ def border(date=[], frame=nil)
2351
+ last = date.length-1
2352
+ return @border if last<0
2353
+ args = date.dup << {:frame=>@engine}
2354
+ args[0] += frame.origin_of_MSC + @border[last] * 1 + (frame.epoch_in_CE - @engine.epoch_in_CE)
2355
+ b_date = frame._encode(frame._number_to_coordinates(When.tm_pos(*args).to_i), nil)
2356
+ branch = @border[last] * 0
2357
+ b_date[last] = When::Coordinates::Pair.new(date[last] * 1, branch)
2358
+ return b_date
2359
+ end
2360
+
2361
+ private
2362
+
2363
+ # 要素の正規化
2364
+ def _normalize(args=[], options={})
2365
+ @border ||= [When::Coordinates::Pair.new(+1,-1),0,0]
2366
+ @border = When::Coordinates::Pair._en_pair_date_time(@border) if @border.kind_of?(String)
2367
+ @engine = When.Calendar(@engine)
2368
+ end
2369
+ end
2370
+
2371
+ #
2372
+ # 日時要素の境界 - 日の出,日の入り
2373
+ #
2374
+ class DayBorder < Border
2375
+
2376
+ # 境界の取得
2377
+ #
2378
+ # @param [Array<Numeric>] date 境界を計算する日
2379
+ # @param [When::TM::ReferenceSystem] clock 使用する時法
2380
+ #
2381
+ # @return [Array<Numeric>] その日の境界
2382
+ #
2383
+ # @note 属性 @event によって境界を計算する (see {When::Ephemeris::Formula#day_event})
2384
+ #
2385
+ def border(date=[], clock=When::UTC)
2386
+ return @border unless date[0] && clock.formula
2387
+
2388
+ time =
2389
+ clock._number_to_coordinates(clock.second *
2390
+ clock.time_standard.from_dynamical_time(
2391
+ When::TM::JulianDate._d_to_t(
2392
+ clock.formula.first.day_event(
2393
+ clock.time_standard.to_dynamical_date(date[0] + @border[0]*0), @event, When.Resource('_ep:Sun'), @height
2394
+ ))))
2395
+
2396
+ time[0] += When::TM::JulianDate::JD19700101
2397
+ time[0] = When::Coordinates::Pair.new(time[0]-@border[0]*0, @border[0]*0) unless @border[0]*0 == 0
2398
+ clock._encode(time, false)
2399
+ end
2400
+
2401
+ # 日付の補正
2402
+ # @private
2403
+ def _date_adjust(source)
2404
+ source * 1 + @border[0] * 0
2405
+ end
2406
+ end
2407
+
2408
+ #
2409
+ # 日時要素の境界 - 日の出
2410
+ #
2411
+ class Sunrise < DayBorder
2412
+
2413
+ private
2414
+
2415
+ # 要素の正規化
2416
+ def _normalize(args=[], options={})
2417
+ @border = [0,0,0,0]
2418
+ @event = -1
2419
+ @height ||= '0'
2420
+ end
2421
+ end
2422
+
2423
+ #
2424
+ # 日時要素の境界 - 日の入り
2425
+ #
2426
+ class Sunset < DayBorder
2427
+
2428
+ private
2429
+
2430
+ # 要素の正規化
2431
+ def _normalize(args=[], options={})
2432
+ @border = [When::Coordinates::Pair.new(+1,-1),0,0,0]
2433
+ @event = +1
2434
+ @height ||= '0'
2435
+ end
2436
+ end
2437
+ end