when_exe 0.3.7 → 0.3.8

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