when_exe 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +171 -0
  3. data/lib/when_exe.rb +78 -47
  4. data/lib/when_exe/basictypes.rb +752 -747
  5. data/lib/when_exe/calendarnote.rb +805 -801
  6. data/lib/when_exe/calendartypes.rb +1583 -1531
  7. data/lib/when_exe/coordinates.rb +16 -15
  8. data/lib/when_exe/core/duration.rb +114 -110
  9. data/lib/when_exe/core/extension.rb +504 -504
  10. data/lib/when_exe/ephemeris.rb +1917 -1913
  11. data/lib/when_exe/ephemeris/moon.rb +333 -333
  12. data/lib/when_exe/ephemeris/notes.rb +389 -387
  13. data/lib/when_exe/ephemeris/planets.rb +585 -585
  14. data/lib/when_exe/ephemeris/sun.rb +214 -214
  15. data/lib/when_exe/googlecalendar.rb +144 -140
  16. data/lib/when_exe/icalendar.rb +1636 -1636
  17. data/lib/when_exe/inspect.rb +46 -22
  18. data/lib/when_exe/locales/akt.rb +176 -176
  19. data/lib/when_exe/locales/encoding_conversion.rb +134 -126
  20. data/lib/when_exe/locales/iast.rb +90 -90
  21. data/lib/when_exe/locales/locale.rb +750 -746
  22. data/lib/when_exe/locales/transliteration_table.rb +62 -62
  23. data/lib/when_exe/mini_application.rb +307 -305
  24. data/lib/when_exe/parts/enumerator.rb +2 -2
  25. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  26. data/lib/when_exe/parts/method_cash.rb +224 -224
  27. data/lib/when_exe/parts/resource.rb +1069 -1071
  28. data/lib/when_exe/parts/timezone.rb +240 -230
  29. data/lib/when_exe/region/armenian.rb +56 -56
  30. data/lib/when_exe/region/babylonian.rb +405 -0
  31. data/lib/when_exe/region/bahai.rb +146 -146
  32. data/lib/when_exe/region/balinese.rb +622 -622
  33. data/lib/when_exe/region/chinese.rb +95 -25
  34. data/lib/when_exe/region/chinese/calendars.rb +1016 -1016
  35. data/lib/when_exe/region/chinese/epochs.rb +1 -1
  36. data/lib/when_exe/region/chinese/twins.rb +803 -795
  37. data/lib/when_exe/region/christian.rb +824 -824
  38. data/lib/when_exe/region/coptic.rb +106 -87
  39. data/lib/when_exe/region/discordian.rb +225 -225
  40. data/lib/when_exe/region/far_east.rb +188 -188
  41. data/lib/when_exe/region/french.rb +56 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +58 -58
  44. data/lib/when_exe/region/indian.rb +1254 -1251
  45. data/lib/when_exe/region/iranian.rb +8 -8
  46. data/lib/when_exe/region/islamic.rb +3 -3
  47. data/lib/when_exe/region/japanese.rb +93 -99
  48. data/lib/when_exe/region/japanese/calendars.rb +396 -397
  49. data/lib/when_exe/region/japanese/epochs.rb +26 -26
  50. data/lib/when_exe/region/japanese/nihon_shoki.rb +71 -71
  51. data/lib/when_exe/region/japanese/notes.rb +1383 -1386
  52. data/lib/when_exe/region/japanese/residues.rb +1306 -1306
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -0
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +126 -126
  57. data/lib/when_exe/region/korean.rb +378 -378
  58. data/lib/when_exe/region/m17n.rb +114 -113
  59. data/lib/when_exe/region/martian.rb +258 -255
  60. data/lib/when_exe/region/mayan.rb +32 -32
  61. data/lib/when_exe/region/residue.rb +89 -89
  62. data/lib/when_exe/region/roman.rb +36 -24
  63. data/lib/when_exe/region/ryukyu.rb +97 -97
  64. data/lib/when_exe/region/shire.rb +240 -240
  65. data/lib/when_exe/region/soviet.rb +209 -0
  66. data/lib/when_exe/region/symmetry.rb +50 -50
  67. data/lib/when_exe/region/thai.rb +336 -335
  68. data/lib/when_exe/region/tibetan.rb +316 -315
  69. data/lib/when_exe/region/vietnamese.rb +440 -439
  70. data/lib/when_exe/region/weekdate.rb +80 -80
  71. data/lib/when_exe/region/world.rb +175 -175
  72. data/lib/when_exe/region/yerm.rb +14 -14
  73. data/lib/when_exe/region/zoroastrian.rb +203 -203
  74. data/lib/when_exe/timestandard.rb +707 -681
  75. data/lib/when_exe/tmduration.rb +338 -330
  76. data/lib/when_exe/tmobjects.rb +1346 -1325
  77. data/lib/when_exe/tmposition.rb +2115 -2072
  78. data/lib/when_exe/tmreference.rb +1693 -1669
  79. data/lib/when_exe/version.rb +1 -1
  80. data/link_to_online_documents +1 -1
  81. data/test/examples/JapanHolidaysRFC6350.ics +1 -1
  82. data/test/test.rb +67 -61
  83. data/test/test/basictypes.rb +409 -409
  84. data/test/test/calendarnote.rb +86 -69
  85. data/test/test/calendartypes.rb +97 -97
  86. data/test/test/coordinates.rb +396 -396
  87. data/test/test/ephemeris.rb +83 -74
  88. data/test/test/ephemeris/moon.rb +14 -14
  89. data/test/test/ephemeris/planets.rb +14 -14
  90. data/test/test/ephemeris/sun.rb +14 -14
  91. data/test/test/googlecalendar.rb +194 -176
  92. data/test/test/icalendar.rb +867 -858
  93. data/test/test/inspect.rb +117 -117
  94. data/test/test/parts.rb +487 -487
  95. data/test/test/region/balinese.rb +34 -0
  96. data/test/test/region/chinese.rb +218 -206
  97. data/test/test/region/christian.rb +245 -245
  98. data/test/test/region/coptic.rb +27 -27
  99. data/test/test/region/french.rb +33 -33
  100. data/test/test/region/geologicalage.rb +17 -17
  101. data/test/test/region/indian.rb +57 -57
  102. data/test/test/region/iran.rb +54 -54
  103. data/test/test/region/islamic.rb +18 -18
  104. data/test/test/region/japanese.rb +237 -219
  105. data/test/test/region/jewish.rb +61 -61
  106. data/test/test/region/m17n.rb +184 -184
  107. data/test/test/region/mayan.rb +195 -195
  108. data/test/test/region/residue.rb +147 -139
  109. data/test/test/region/thai.rb +116 -116
  110. data/test/test/region/tibetan.rb +30 -30
  111. data/test/test/region/vietnamese.rb +102 -102
  112. data/test/test/region/yerm.rb +146 -146
  113. data/test/test/timestandard.rb +81 -81
  114. data/test/test/tmobjects.rb +328 -328
  115. data/test/test/tmposition.rb +397 -284
  116. data/test/test/tmreference.rb +157 -157
  117. metadata +13 -10
@@ -1,1531 +1,1583 @@
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
- autoload :Rational, 'Rational' unless Object.const_defined?(:Rational)
9
-
10
- #
11
- # 具体的な When::TM::ReferenceSystem のサブクラスの実装
12
- #
13
- module When::CalendarTypes
14
-
15
- #
16
- # Universal Time, Coordinated
17
- #
18
- class UTC < When::TM::Clock
19
-
20
- # この時法の時刻をUTC時刻に変換する
21
- #
22
- # Description of an operation for
23
- # converting a time on this clock to a UTC time
24
- #
25
- # @param [When::TM::ClockTime] u_time
26
- # @return [When::TM::ClockTime]
27
- #
28
- def utc_trans(u_time)
29
- return u_time
30
- end
31
- alias :utcTrans :utc_trans
32
-
33
- # UTC時刻をこの時法の時刻に変換する
34
- #
35
- # Description of an operation for
36
- # converting a UTC time to a time on this clock
37
- #
38
- # @param [When::TM::ClockTime] clk_time
39
- # @return [When::TM::ClockTime]
40
- #
41
- def clk_trans(clk_time)
42
- return clk_time
43
- end
44
- alias :clkTrans :clk_trans
45
-
46
- #
47
- # Zone 名
48
- #
49
- # @return [String]
50
- #
51
- def zone
52
- @label.to_s
53
- end
54
-
55
- private
56
-
57
- # オブジェクトの正規化
58
- def _normalize(args=[], options={})
59
- @label ||= m17n('Z')
60
- @indices ||= When::Coordinates::DefaultTimeIndices
61
- @note ||= 'JulianDay'
62
- _normalize_spatial
63
- _normalize_temporal
64
- @second = (@second||1/When::TM::Duration::SECOND).to_f
65
- @zone = '+00:00'
66
- @time_standard ||= When.Resource('_t:UniversalTime')
67
- @utc_reference = When::TM::ClockTime.new([0,0,0,0], {:frame=>self})
68
- end
69
- end
70
-
71
- #
72
- # Abstract Local Time
73
- #
74
- class LocalTime < UTC
75
-
76
- # 128秒単位の実数による参照事象の時刻
77
- #
78
- # Fraction time of the reference event
79
- #
80
- # @param [Integer] sdn 参照事象の通し番号
81
- #
82
- # @return [Numeric]
83
- #
84
- # T00:00:00Z からの参照事象の経過時間 / 128秒
85
- #
86
- def universal_time(sdn=nil)
87
- return super unless sdn
88
- time = When::TM::JulianDate._d_to_t(sdn-0.5)
89
- @time_standard.to_dynamical_time(time) - When::TimeStandard.to_dynamical_time(time)
90
- end
91
-
92
- #
93
- # Zone 名
94
- #
95
- # @return [String]
96
- #
97
- def zone
98
- iri.split('/')[-1]
99
- end
100
- end
101
-
102
- #
103
- # Local Mean Time
104
- #
105
- class LMT < LocalTime
106
-
107
- private
108
-
109
- # オブジェクトの正規化
110
- def _normalize(args=[], options={})
111
- @label = m17n('LMT')
112
- @time_standard = When.Resource("_t:LocalMeanTime?location=_l:long=#{@long||0}")
113
- super
114
- end
115
- end
116
-
117
- #
118
- # Local Apparent Time
119
- #
120
- class LAT < LocalTime
121
-
122
- private
123
-
124
- # オブジェクトの正規化
125
- def _normalize(args=[], options={})
126
- @label = m17n('LAT')
127
- @time_standard = When.Resource("_t:LocalApparentTime?location=_l:long=#{@long||0}")
128
- super
129
- end
130
- end
131
-
132
- #
133
- # Temporal Hour System
134
- #
135
- class THS < LocalTime
136
-
137
- private
138
-
139
- # オブジェクトの正規化
140
- def _normalize(args=[], options={})
141
- @label = m17n('THS')
142
- @time_standard = When.Resource("_t:TemporalHourSystem?location=(_l:long=#{@long||0}&lat=#{@lat||0}&alt=#{@alt||0})")
143
- super
144
- end
145
- end
146
-
147
- #
148
- # 太陰(太陽)暦の朔閏パターンを扱うモジュール
149
- #
150
- module Lunar
151
-
152
- # @private
153
- Pattern = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
154
-
155
- # 朔閏表を生成する
156
- #
157
- # @param [Range] range 生成範囲(西暦年)
158
- # @param [Integer] length 大の月の日数
159
- # @param [When::TM::Duration] duration チェックする月の間隔
160
- #
161
- # @return [Hash] 朔閏表
162
- #
163
- def lunar_table(range, length=30, duration=When::P1M)
164
- date = When.TemporalPosition(range.first, {:frame=>self}).floor
165
- table = []
166
- hash = {
167
- 'origin_of_MSC' => range.first,
168
- 'origin_of_LSC' => date.to_i,
169
- 'rule_table' => table
170
- }
171
- list = ''
172
- while range.include?(date[YEAR])
173
- month = date[MONTH] * 1
174
- char = Pattern[month..month]
175
- char = char.downcase unless date.length(MONTH) == length
176
- list += char
177
- succ = date + duration
178
- unless date[YEAR] == succ[YEAR]
179
- table << list
180
- list = ''
181
- end
182
- date = succ
183
- end
184
- hash
185
- end
186
-
187
- # 朔閏表を比較する
188
- #
189
- # @param [When::TM::Calendar] base 基準とする暦法
190
- # @param [Range] range 比較範囲(西暦年)
191
- # @param [Integer] length 大の月の日数
192
- # @param [When::TM::Duration] duration チェックする月の間隔
193
- #
194
- # @return [Hash] 朔閏表の差分
195
- #
196
- def verify(base, range=base.range, length=30, duration=When::P1M)
197
- range = When::Parts::GeometricComplex.new(range) & When::Parts::GeometricComplex.new(self.range) if respond_to?(:range)
198
- base_table = base.lunar_table(range, length, duration)
199
- self_table = self.lunar_table(range, length, duration)
200
- hash = {}
201
- range.each do |year|
202
- difference = _verify(base_table['rule_table'][year-range.first],
203
- self_table['rule_table'][year-range.first])
204
- hash[year] = difference if difference
205
- end
206
- hash
207
- end
208
-
209
- # @private
210
- def _verify(source, target)
211
- return nil if source == target
212
- return {source => target} unless source.length == target.length
213
- indices = []
214
- index = []
215
- source.length.times do |i|
216
- if source[i..i] == target[i..i]
217
- unless index.empty?
218
- indices << index
219
- index = []
220
- end
221
- else
222
- index << i
223
- end
224
- end
225
- indices << index unless index.empty?
226
- ranges = []
227
- indices.each do |index|
228
- if ranges.empty? || index.first > ranges.last.last + 2
229
- ranges << index
230
- else
231
- ranges[-1] = [ranges.last.first,index.last]
232
- end
233
- end
234
- hash = {}
235
- ranges.each do |index|
236
- range = index.first..index.last
237
- hash[source[range]] = target[range]
238
- end
239
- test = source.dup
240
- hash.each_pair do |key, value|
241
- test.sub!(key, value)
242
- end
243
- # raise ArgumentError, "can't replace '#{source}'=>'#{target}' by #{hash}." unless test == target
244
- return hash if test == target
245
- {source => target}
246
- end
247
- end
248
-
249
- #
250
- # 朔閏パターンの表の拡張
251
- #
252
- module TableExtend
253
-
254
- # 年月日 -> 通日
255
- #
256
- # @param [Numeric] y 年
257
- # @param [Integer] m 月 (0 始まり)
258
- # @param [Integer] d 日 (0 始まり)
259
- #
260
- # @return [Integer] 通日
261
- #
262
- def _coordinates_to_number(y, m, d)
263
- if @after && y >= @rule_table[@entry_key]['Years']
264
- _normalize_after
265
- return @after._coordinates_to_number(y + @_after_offset, m, d)
266
- end
267
- if @before && y < 0
268
- _normalize_before
269
- return @before._coordinates_to_number(y + @_before_offset, m, d)
270
- end
271
- super
272
- end
273
-
274
- # 通日 - > 年月日
275
- #
276
- # @param [Integer] sdn 通日
277
- #
278
- # @return [Array<Integer>] [ y, m, d ]
279
- # y 年
280
- # m 月 (0 始まり)
281
- # d 日 (0 始まり)
282
- #
283
- def _number_to_coordinates(sdn)
284
- if @after && sdn >= @origin_of_LSC + @rule_table[@entry_key]['Days']
285
- _normalize_after
286
- y, m, d = @after._number_to_coordinates(sdn)
287
- return [y - @_after_offset, m, d]
288
- end
289
- if @before && sdn < @origin_of_LSC
290
- _normalize_before
291
- y, m, d = @before._number_to_coordinates(sdn)
292
- return [y - @_before_offset, m, d]
293
- end
294
- super
295
- end
296
-
297
- #
298
- # その他のテーブル参照
299
- #
300
- %w(ids_ length).each do |method|
301
- module_eval %Q{
302
- def _#{method}(date)
303
- if @after && +date[0] >= @rule_table[@entry_key]['Years']
304
- _normalize_after
305
- date[0] += @_after_offset
306
- return @after.send(:_#{method}, date)
307
- end
308
- if @before && +date[0] < 0
309
- _normalize_before
310
- date[0] += @_before_offset
311
- return @before.send(:_#{method}, date)
312
- end
313
- super
314
- end
315
- }
316
- end
317
-
318
- private
319
-
320
- def _normalize_after
321
- raise RangeError, "Out of range: #{iri}" if @after.kind_of?(Symbol)
322
- @after = When.Calendar(@after)
323
- @_after_offset = @origin_of_MSC - @after.origin_of_MSC
324
- class << self; alias :_normalize_after :_normalize_non end
325
- end
326
-
327
- def _normalize_before
328
- raise RangeError, "Out of range: #{iri}" if @before.kind_of?(Symbol)
329
- @before = When.Calendar(@before)
330
- @_before_offset = @origin_of_MSC - @before.origin_of_MSC
331
- class << self; alias :_normalize_before :_normalize_non end
332
- end
333
-
334
- def _normalize_non
335
- end
336
- end
337
-
338
- # 月日の配当パターンの種類が限定されている暦の抽象基底クラス
339
- #
340
- # Calendar which has some fixed arrangement rules for under year
341
- #
342
- # 新年の日付が専用メソッドで与えられ、月日の配当が1年の日数等
343
- # で決まる暦。いわゆる Rule-Based な暦はほとんど該当します。
344
- class TableBased < When::TM::Calendar
345
-
346
- # 年月日 -> 通日
347
- #
348
- # @param [Numeric] y
349
- # @param [Integer] m 月 (0 始まり)
350
- # @param [Integer] d 日 (0 始まり)
351
- #
352
- # @return [Integer] 通日
353
- #
354
- def _coordinates_to_number(y, m, d)
355
- sdn = _sdn([+y])
356
- rule = _rule(_key([+y]))
357
- sdn += d + rule['Offset'][m]
358
- return sdn if d >= 0
359
- return sdn + rule['Length'][m % rule['Length'].length]
360
- end
361
-
362
- # 通日 - > 年月日
363
- #
364
- # @param [Integer] sdn 通日
365
- #
366
- # @return [Array<Integer>] [ y, m, d ]
367
- # y
368
- # m 月 (0 始まり)
369
- # d 日 (0 始まり)
370
- #
371
- def _number_to_coordinates(sdn)
372
- y, d = Residue.mod(sdn) {|n| _sdn([n])}
373
- rule = _rule(_key([y]))
374
- (rule['Months']-1).downto(0) do |m|
375
- if d >=rule['Offset'][m]
376
- d -= rule['Offset'][m]
377
- return [y, m, d]
378
- end
379
- end
380
- return nil
381
- end
382
-
383
- # 暦要素数
384
- #
385
- # @overload _length(date)
386
- # @param [Array<Integer>] date ( y )
387
- #
388
- # y
389
- #
390
- # @return [Integer] その年の月数
391
- #
392
- # @overload _length(date)
393
- # @param [Array<Integer>] date ( y, m )
394
- #
395
- # y 年
396
- #
397
- # m 月 (0 始まり)
398
- #
399
- # @return [Integer] その年月の日数
400
- #
401
- def _length(date)
402
- y, m = date
403
- if (m)
404
- # 指定した月に含まれる日の数を返します。
405
- return @unit[2] if @unit[2]
406
- rule = _rule(_key([y]))
407
- return rule['Length'][m % rule['Length'].length]
408
- else
409
- # 指定した年に含まれる月の数を返します。
410
- return @unit[1] if @unit[1]
411
- return _rule(_key([y]))['Months']
412
- end
413
- end
414
-
415
- private
416
-
417
- # オブジェクトの正規化
418
- def _normalize(args=[], options={})
419
- super
420
-
421
- # rule_table
422
- # @rule_table = @rule_table.dup
423
- @rule_table = {'T' => {'Rule' => @rule_table }} if (@rule_table.kind_of?(Array))
424
- @_m_cash_ = {}
425
- @_m_cash_["_rule"] = @rule_table
426
-
427
- # unit length
428
- unit = @unit[1..2]
429
- @rule_table.each do |key, rule|
430
- _make_rule(key, rule, unit) if rule.kind_of?(Hash)
431
- end
432
-
433
- # mean month length
434
- if @entry_key
435
- ::Rational
436
- @mean_month = Rational(@rule_table[@entry_key]['Days'], @rule_table[@entry_key]['Months'])
437
- @mean_year = Rational(@rule_table[@entry_key]['Days'], @rule_table[@entry_key]['Years' ])
438
- end
439
-
440
- # range extension
441
- extend(TableExtend) if @before || @after
442
- end
443
-
444
- # rule の正規化
445
- def _make_rule(key, rule, unit=[])
446
- # @rule_table[key]['Years', 'Months', 'Offset', 'Days']
447
- rule['IDs'] = Pair._en_pair_array(rule['IDs']) if rule['IDs'].kind_of?(String)
448
- rule['Years'] ||= 1
449
- rule['Months'] ||= (rule['IDs']||rule['Length']).length
450
- rule['Offset'] = []
451
- sum, len = 0, rule['Length'].length
452
- rule['Months'].times do |k|
453
- rule['Offset'] << sum
454
- sum += rule['Length'][k % len]
455
- end
456
- rule['Days'] ||= sum
457
-
458
- # Months in Year
459
- unit[0] ||= rule['Months']
460
- unit[0] = 0 unless (unit[0]==rule['Months'])
461
-
462
- # Days in Month
463
- len = rule['Length'][0]
464
- if rule['Length'].length == 1 && (rule['Days'] % len) == 0
465
- unit[1] ||= len
466
- unit[1] = 0 unless (unit[1]==len)
467
- else
468
- unit[1] = 0
469
- end
470
- end
471
-
472
- # 年初の通日によるセットアップ
473
- def _sdn_setup(c_key, c_date)
474
- n_date = c_date.dup
475
- n_date[-1] += 1
476
- n_key = (n_date.length<=1) ? n_date[0] : n_date
477
- c_sdn = (@_m_cash_["_sdn"][c_key] ||= _sdn_(c_date))
478
- n_sdn = (@_m_cash_["_sdn"][n_key] ||= _sdn_(n_date))
479
- key = n_sdn - c_sdn
480
- rule = (@_m_cash_["_rule"][key] ||= _rule_(key))
481
- @_m_cash_["_key"] ||= {}
482
- @_m_cash_["_ids"] ||= {}
483
- @_m_cash_["_key"][c_key] ||= key
484
- @_m_cash_["_ids"][c_key] ||= rule['IDs']
485
- return c_sdn
486
- end
487
-
488
- # 年初の通日
489
- # このメソッドは subclass で定義します
490
- #
491
- # @param [Array<Numeric>] date ( 年 )
492
- #
493
- # @return [Integer] 年初の通日
494
- #
495
- def _sdn_(date)
496
- raise TypeError, "Abstract TableBased Calendar Type"
497
- end
498
-
499
- # 暦日表のキー取得
500
- #
501
- # @param [Array<Numeric>] date ( 年 )
502
- #
503
- # @return [Integer] 暦日表のキー 本暦法では当該年の日数を暦日表のキーとします
504
- #
505
- def _key_(date)
506
- n_date = date.dup
507
- n_date[-1] += 1
508
- _sdn(n_date) - _sdn(date)
509
- end
510
-
511
- # 日時要素の翻訳表の取得
512
- #
513
- # @param [Array<Numeric>] date ( 年 )
514
- #
515
- # @return [Array<When::Coordinates::Pair>] 日時要素の翻訳表
516
- #
517
- def _ids_(date)
518
- _rule(_key(date))['IDs']
519
- end
520
-
521
- # 暦要素数
522
- #
523
- # @param [Array<Numeric>] date ( 年 )
524
- #
525
- # @return [Integer] その年の日数
526
- #
527
- def _sum_(date)
528
- return _rule(_key([date[0]]))['Days']
529
- end
530
-
531
- # 月日の配当
532
- #
533
- # @param [Numeric] year 年
534
- #
535
- # @return [Array<Integer>] [ 月の日数 ]
536
- #
537
- def month_arrangement_(year)
538
- _rule(_key([year * 1 - @origin_of_MSC]))['Length']
539
- end
540
-
541
- # rule の遅延生成
542
- def _rule_(key)
543
- rule = {
544
- 'Years' => 1,
545
- 'Months' => key.length,
546
- 'Days' => _year_length(key),
547
- 'IDs' => [],
548
- 'Length' => [],
549
- 'Offset' => []
550
- }
551
-
552
- key.length.times do |m|
553
- rule['Length'] << _month_length(key, m)
554
- rule['Offset'] << (m == 0 ? 0 : rule['Offset'][m-1]+rule['Length'][m-1])
555
- rule['IDs'] << _month_id(key, m)
556
- end
557
- return rule
558
- end
559
- end
560
-
561
- # 表引きにより実現する太陰太陽暦
562
- #
563
- # Luni-Solar calendar which uses year / month /day table
564
- #
565
- class PatternTableBasedLuniSolar < TableBased
566
-
567
- include Lunar
568
-
569
- class << self
570
- #
571
- # ひとつのひな型朔閏表からの差分で朔閏表を生成する
572
- #
573
- # @param [Array] definition ひな型朔閏表
574
- # @param [Range] range 生成する朔閏表の年代範囲
575
- # @param [Hash{Integer=>(String or Hash{String or Regexp=>String})}] difference 差分情報
576
- #
577
- # @return [Array] 生成された朔閏表定義
578
- #
579
- def patch(definition, range=nil, difference={})
580
- When.Calendar(definition)
581
- base = When::CalendarTypes.const_get(definition)
582
- hash = base[-1].dup
583
- range ||= hash['origin_of_MSC']...(hash['origin_of_MSC']+hash['rule_table'].size)
584
- range = range.to_a
585
- hash['origin_of_LSC'] += hash['rule_table'][range[0]-hash['origin_of_MSC']][1]
586
- hash['rule_table'] = range.map {|year|
587
- original = hash['rule_table'][year-hash['origin_of_MSC']][0]
588
- case difference[year]
589
- when String ; next difference[year]
590
- when nil ; next original
591
- end
592
- original = original.dup
593
- difference[year].each_pair {|key,value|
594
- raise ArgumentError, "Can't patch \"#{original}\" by {#{key}=>#{value}} at #{year}" unless original.sub!(key,value)
595
- }
596
- original
597
- }
598
- hash['origin_of_MSC'] = range[0]
599
- base[0..-2] + [hash]
600
- end
601
-
602
- #
603
- # 複数のひな型朔閏表からの差分で朔閏表を生成する
604
- #
605
- # @param [[Array<Array<String, Range>>]] definitions ひな型朔閏表
606
- # - String - もとにする太陰太陽暦のIRI文字列
607
- # - Range - 朔閏表の年代範囲(デフォルトはもとにする太陰太陽暦の年代範囲)
608
- # @param [Hash{Integer=>(String or Hash{String or Regexp=>String})}] difference 差分情報
609
- #
610
- # @return [Array] 生成された朔閏表定義
611
- #
612
- def join(definitions, difference={})
613
- if definitions.first.kind_of?(Array)
614
- base = When::CalendarTypes.const_get(definitions.first[0]).dup
615
- else
616
- base = []
617
- base << definitions.shift until definitions.first.kind_of?(Array)
618
- end
619
- tables = definitions.map {|definition|
620
- When.Calendar(definition[0]).lunar_table(definition[1])
621
- }
622
- hash = base.pop.merge({
623
- 'origin_of_MSC' => tables.first['origin_of_MSC'],
624
- 'origin_of_LSC' => tables.first['origin_of_LSC'],
625
- 'rule_table' => tables.inject([]) {|rules, table| rules += table['rule_table']}
626
- })
627
- difference.each_pair do |year, pattern|
628
- offset = year - hash['origin_of_MSC']
629
- hash['rule_table'][offset] =
630
- if pattern.kind_of?(Hash)
631
- rule = hash['rule_table'][offset].dup
632
- pattern.each_pair do |key,value|
633
- raise ArgumentError, "Can't patch \"#{rule}\" by {#{key}=>#{value}} at #{year}" unless rule.sub!(key,value)
634
- end
635
- rule
636
- else
637
- pattern
638
- end
639
- end
640
- base << hash
641
- end
642
- end
643
-
644
- # 朔閏表を生成する
645
- #
646
- # @param [Range] sub_range 生成範囲(西暦年) デフォルトは self.range
647
- # @param [Integer] length 大の月の日数(ダミー)
648
- # @param [When::TM::Duration] duration チェックする月の間隔(ダミー)
649
- #
650
- # @return [Hash] 朔閏表
651
- #
652
- def lunar_table(sub_range=nil, length=nil, duration=nil)
653
- sub_range ||= range
654
- last = sub_range.last
655
- last -= 1 if sub_range.exclude_end?
656
- [sub_range.first, last].each do |edge|
657
- raise RangeError, 'Range exceeded: ' + sub_range.to_s unless range.include?(edge)
658
- end
659
- {
660
- 'origin_of_MSC' => sub_range.first,
661
- 'origin_of_LSC' => @origin_of_LSC + @rule_table['T']['Rule'][sub_range.first-@origin_of_MSC][1],
662
- 'rule_table' => sub_range.to_a.map {|year|
663
- @rule_table['T']['Rule'][year-@origin_of_MSC][0]
664
- }
665
- }
666
- end
667
-
668
- # 朔閏表の有効範囲
669
- #
670
- # @return [Range] 有効範囲(西暦年)
671
- #
672
- def range
673
- @origin_of_MSC...(@origin_of_MSC+@rule_table['T']['Rule'].length)
674
- end
675
-
676
- private
677
-
678
- # new で指定された月日配当規則をプログラムで利用可能にします。
679
- #
680
- # key 年月日配当規則のハッシュキー
681
- # rule 年月日配当規則
682
- #
683
- # インスタンス変数 ハッシュのハッシュ@rule_table の要素
684
- # Years => the period length / year
685
- # Months => the period length / month
686
- # Days => the period length / day
687
- # Rule => Array of sub rules' key and offset
688
- def _make_rule(key, rule, unit=nil)
689
-
690
- offsets = [0, 0]
691
- rule['Rule'].each_index do |k|
692
- subkey = rule['Rule'][k]
693
- if subkey.kind_of?(Array)
694
- subkey, *offsets = rule['Rule'][k]
695
- else
696
- rule['Rule'][k] = [subkey] + offsets
697
- end
698
- _increment_offsets(offsets, subkey)
699
- end
700
-
701
- rule['Years'] ||= rule['Rule'].length # 年数
702
- rule['Months'] ||= offsets[1] # 月数
703
- rule['Days'] ||= offsets[0] # 日数
704
-
705
- @entry_key ||= key
706
- end
707
-
708
- # オフセットの更新
709
- def _increment_offsets(offsets, subkey)
710
- offsets[1] += subkey.length # 月のオフセットを月数分進める
711
- offsets[0] += _year_length(subkey) # 日のオフセットを日数分進める
712
- end
713
-
714
- # 年初の通日によるセットアップ
715
- def _sdn_setup(c_key, c_date)
716
- root_rule = @rule_table[@entry_key]
717
- count, year = c_date[0].divmod(root_rule['Years'])
718
- key, dd, mm = root_rule['Rule'][year]
719
- rule = (@_m_cash_["_rule"][key] ||= _rule_(key))
720
- @_m_cash_["_key"] ||= {}
721
- @_m_cash_["_ids"] ||= {}
722
- @_m_cash_["_key"][c_key] ||= key
723
- @_m_cash_["_ids"][c_key] ||= rule['IDs']
724
- @_m_cash_["_sdn"][c_key] ||= @origin_of_LSC + dd + count * root_rule['Days']
725
- end
726
-
727
- # 年初の通日
728
- #
729
- # @param [Array<Numeric>] date ( y )
730
- #
731
- # y
732
- #
733
- # @return [Integer] 年初の通日
734
- #
735
- def _sdn_(date)
736
- rule = @rule_table[@entry_key]
737
- count, year = date[0].divmod(rule['Years'])
738
- return @origin_of_LSC + rule['Rule'][year][1] + count * rule['Days']
739
- end
740
-
741
- # 暦日表のキー取得
742
- #
743
- # @param [Array<Numeric>] date ( y )
744
- #
745
- # y
746
- #
747
- # @return [Integer] 暦日表のキー
748
- #
749
- def _key_(date)
750
- rule = @rule_table[@entry_key]
751
- count, year = date[0].divmod(rule['Years'])
752
- return rule['Rule'][year][0]
753
- end
754
-
755
- # 朔閏パターン -> 日数/年
756
- #
757
- # @param [String] key 朔閏パターン
758
- #
759
- # @return [Integer] 日数/年
760
- #
761
- def _year_length(key)
762
- key.length * 29 + key.gsub(/[a-z]/,'').length
763
- end
764
-
765
- # 朔閏パターン -> 日数/年
766
- #
767
- # @param [String] key 朔閏パターン
768
- # @param [Integer] m 月番号(0始まり)
769
- #
770
- # @return [Integer] 日数/月
771
- #
772
- def _month_length(key, m)
773
- key[m,1] =~ /[a-z]/ ? 29 : 30
774
- end
775
-
776
- # 朔閏パターン -> 月のID
777
- #
778
- # @param [String] key 朔閏パターン
779
- # @param [Integer] m 月番号(0始まり)
780
- #
781
- # @return [Integer] 月のID
782
- #
783
- def _month_id(key, m)
784
- trunk = key.upcase[m]
785
- branch = trunk == key.upcase[m-1] ? 1 : 0
786
- trunk = trunk.ord if trunk.kind_of?(String)
787
- trunk -= 64
788
- branch == 0 ? trunk : When::Coordinates::Pair.new(trunk, branch)
789
- end
790
-
791
- # オブジェクトの正規化
792
- #
793
- # @note インスタンス変数 @note は to_a でデフォルトとして用いる暦注
794
- #
795
- def _normalize(args=[], options={})
796
- @note ||= When.CalendarNote('Chinese') # See when.rb
797
- super
798
- end
799
- end
800
-
801
- # 表引きにより実現する太陰太陽暦(29,30日以外の月がある場合)
802
- #
803
- # Luni-Solar calendar which uses year / month /day table
804
- #
805
- class PatternTableBasedLuniSolarExtended < PatternTableBasedLuniSolar
806
-
807
- private
808
-
809
- # rule の遅延生成
810
- def _rule_(key)
811
- key.kind_of?(Hash) ? key : super
812
- end
813
-
814
- # オフセットの更新
815
- def _increment_offsets(offsets, subkey)
816
- return super unless subkey.kind_of?(Hash)
817
- offsets[1] += subkey['Months'] # 月のオフセットを月数分進める
818
- offsets[0] += subkey['Days'] # 日のオフセットを日数分進める
819
- end
820
- end
821
-
822
- # 表引きにより実現する太陽暦(閏月なし, 5,6,27~34日の月に対応)
823
- #
824
- # Solar calendar which uses year / month /day table
825
- #
826
- class PatternTableBasedSolar < PatternTableBasedLuniSolar
827
-
828
- private
829
-
830
- # 朔閏パターン -> 日数/年
831
- #
832
- # @param [String] key 朔閏パターン
833
- #
834
- # @return [Integer] 日数/年
835
- #
836
- def _year_length(key)
837
- length = 0
838
- key.length.times do |m|
839
- length += _month_length(key, m)
840
- end
841
- length
842
- end
843
-
844
- # 朔閏パターン -> 日数/年
845
- #
846
- # @param [String] key 朔閏パターン
847
- # @param [Integer] m 月番号(0始まり)
848
- #
849
- # @return [Integer] 日数/月
850
- #
851
- def _month_length(key, m)
852
- trunk = key.upcase[m]
853
- trunk = trunk.ord if trunk.kind_of?(String)
854
- trunk -= 48
855
- case trunk
856
- when 5, 6 ; trunk
857
- when 7..9 ; trunk + 20
858
- else ; trunk + 30
859
- end
860
- end
861
-
862
- # 朔閏パターン -> 月のID
863
- #
864
- # @param [String] key 朔閏パターン
865
- # @param [Integer] m 月番号(0始まり)
866
- #
867
- # @return [Integer] 月のID
868
- #
869
- def _month_id(key, m)
870
- m + 1
871
- end
872
- end
873
-
874
- # 年の配当パターンが限定されている暦
875
- #
876
- # Calendar which has some fixed arrangement rules of year pattern
877
- #
878
- class CyclicTableBased < TableBased
879
-
880
- # 通日 - > 年月日
881
- #
882
- # @param [Integer] sdn 通日
883
- #
884
- # @return [Array<Integer>] [ y, m, d ]
885
- # y 年
886
- # m (0 始まり)
887
- # d 日 (0 始まり)
888
- #
889
- def _number_to_coordinates(sdn)
890
- root_rule = @rule_table[@entry_key]
891
- count, value = (sdn-@origin_of_LSC).divmod(root_rule['Days'])
892
- y, d, key = _read_period(@entry_key,
893
- 'Days', value,
894
- 'Years', count * root_rule['Years'])
895
- rule = _rule(key)
896
- (rule['Months']-1).downto(0) do |m|
897
- if d >=rule['Offset'][m]
898
- d -= rule['Offset'][m]
899
- return [y, m, d]
900
- end
901
- end
902
- return nil
903
- end
904
-
905
- private
906
-
907
- #
908
- # new で指定された月日配当規則をプログラムで利用可能にします。
909
- #
910
- # key 年月日配当規則のハッシュキー
911
- # rule 年月日配当規則
912
- #
913
- # インスタンス変数 ハッシュのハッシュ@rule_table の要素
914
- # Years => the period length / year
915
- # Months => the period length / month
916
- # Days => the period length / day
917
- # Rule => Array of sub rules' key
918
- #
919
- def _make_rule(key, rule, unit=nil)
920
- if rule.key?('Rule') # Table of Many Years
921
- ['Years', 'Months', 'Days'].each do |u|
922
- rule[u] ||=
923
- begin
924
- s = 0
925
- rule['Rule'].each do |part|
926
- subkey, count = part
927
- subrule = @rule_table[subkey]
928
- _make_rule(subkey, subrule, unit) unless subrule[u]
929
- s += (count||1) * subrule[u]
930
- end
931
- s
932
- end
933
- end
934
- if !@entry_key ||
935
- @rule_table[@entry_key]['Days'] < rule['Days']
936
- @entry_key = key
937
- end
938
- else # Table of One Year
939
- super
940
- end
941
- end
942
-
943
- # 年初の通日によるセットアップ
944
- def _sdn_setup(c_key, c_date)
945
- root_rule = @rule_table[@entry_key]
946
- count, value = c_date[0].divmod(root_rule['Years'])
947
- sdn, y, key = _read_period(@entry_key,
948
- 'Years', value,
949
- 'Days', @origin_of_LSC + count * root_rule['Days'])
950
- @_m_cash_["_key"] ||= {}
951
- @_m_cash_["_ids"] ||= {}
952
- @_m_cash_["_key"][c_key] ||= key
953
- @_m_cash_["_ids"][c_key] ||= @_m_cash_["_rule"][key]['IDs']
954
- @_m_cash_["_sdn"][c_key] ||= sdn
955
- end
956
-
957
- # 年初の通日
958
- #
959
- # @param [Array<Numeric>] date ( y )
960
- #
961
- # y
962
- #
963
- # @return [Integer] 年初の通日
964
- #
965
- def _sdn_(date)
966
- root_rule = @rule_table[@entry_key]
967
- count, value = date[0].divmod(root_rule['Years'])
968
- sdn, y, key = _read_period(@entry_key,
969
- 'Years', value,
970
- 'Days', @origin_of_LSC + count * root_rule['Days'])
971
- return sdn
972
- end
973
-
974
- # 暦日表のキー取得
975
- #
976
- # @param [Array<Numeric>] date ( y )
977
- #
978
- # y 年
979
- #
980
- # @return [Integer] 暦日表のキー
981
- #
982
- def _key_(date)
983
- root_rule = @rule_table[@entry_key]
984
- count, value = date[0].divmod(root_rule['Years'])
985
- sdn, y, key = _read_period(@entry_key,
986
- 'Years', value,
987
- 'Days', @origin_of_LSC + count * root_rule['Days'])
988
- return key
989
- end
990
-
991
- # 年の配当規則を読み出します。
992
- #
993
- # key 年の配当規則のキー
994
- # akey 入力が'Days'か'Years'かを指定
995
- # avalue 入力の'Days'または'Years'の値
996
- # zkey 出力が'Years'か'Days'かを指定
997
- # zvalue 出力の'Years'または'Days'の値
998
- def _read_period(key, akey, avalue, zkey, zvalue)
999
- rule = @rule_table
1000
- rule[key]['Rule'].each do |part|
1001
- subkey, count, = [*part] << 1
1002
- if avalue >= count * rule[subkey][akey]
1003
- avalue -= count * rule[subkey][akey]
1004
- zvalue += count * rule[subkey][zkey]
1005
- else
1006
- count, avalue = avalue.divmod(rule[subkey][akey])
1007
- zvalue += count * rule[subkey][zkey]
1008
- return zvalue, avalue, subkey unless rule[subkey].key?('Rule')
1009
- return _read_period(subkey, akey, avalue, zkey, zvalue)
1010
- end
1011
- end
1012
- end
1013
- end
1014
-
1015
- #
1016
- # 年初を太陽黄経で決定する暦
1017
- #
1018
- class YearLengthTableBased < TableBased
1019
-
1020
- # 天体暦アルゴリズム
1021
- #
1022
- # @return [Array<When::Ephemeris::Formula>]
1023
- #
1024
- attr_reader :formula
1025
-
1026
- #protected
1027
-
1028
- private
1029
-
1030
- # 年初の通日
1031
- #
1032
- # @param [Array<Numeric>] date ( y )
1033
- #
1034
- # y
1035
- #
1036
- # @return [Integer] 年初の通日
1037
- #
1038
- def _sdn_(date)
1039
- y = +date[0]
1040
- t = @formula[0].cn_to_time(y.to_f + @cycle_offset)
1041
- return solar_sdn(t + @day_offset)
1042
- end
1043
-
1044
- # オブジェクトの正規化
1045
- #
1046
- # YearLengthTableBased+オブジェクトの性質定義を初期設定します。
1047
- #
1048
- def _normalize(args=[], options={})
1049
-
1050
- Rational
1051
- @cycle_offset = (@cycle_offset||0).to_r
1052
- @day_offset = (@day_offset||0).to_r
1053
- @formula = 'Formula?formula=1S'
1054
-
1055
- super
1056
- end
1057
- end
1058
-
1059
- # 月日の配当が太陽または月の位置によって決定される暦
1060
- #
1061
- # Calendar based on the ephemeris of the Sun or the Moon
1062
- #
1063
- class EphemerisBased < When::TM::Calendar
1064
-
1065
- # 天体暦
1066
- #
1067
- # @return [When::Ephmeris::Formula]
1068
- #
1069
- attr_reader :formula
1070
-
1071
- #protected
1072
-
1073
- # 年月日 -> 通日
1074
- #
1075
- # @param [Numeric] y 年
1076
- # @param [Integer] m 月 (0 始まり)
1077
- # @param [Integer] d 日 (0 始まり)
1078
- #
1079
- # @return [Integer] 通日
1080
- #
1081
- def _coordinates_to_number(y, m, d)
1082
- _new_month(@months_in_year * (+y) + m) + d
1083
- end
1084
-
1085
- # 通日 - > 年月日
1086
- #
1087
- # @param [Integer] sdn 通日
1088
- #
1089
- # @return [Array<Integer>] [ y, m, d ]
1090
- # y
1091
- # m 月 (0 始まり)
1092
- # d 日 (0 始まり)
1093
- #
1094
- def _number_to_coordinates(sdn)
1095
- m, d = Residue.mod(sdn) {|m| _new_month(m)}
1096
- y, m = m.divmod(@months_in_year)
1097
- return y, m, d
1098
- end
1099
-
1100
- # 暦要素数
1101
- #
1102
- # @overload _length(date)
1103
- # @param [Array<Integer>] date ( y )
1104
- #
1105
- # y 年
1106
- #
1107
- # @return [Integer] その年の月数
1108
- #
1109
- # @overload _length(date)
1110
- # @param [Array<Integer>] date ( y, m )
1111
- #
1112
- # y 年
1113
- #
1114
- # m (0 始まり)
1115
- #
1116
- # @return [Integer] その年月の日数
1117
- #
1118
- def _length(date)
1119
- y, m = date
1120
- if (m)
1121
- # 指定した月に含まれる日の数を返します。
1122
- m += @months_in_year * +y
1123
- return _new_month(m+1) - _new_month(m)
1124
- else
1125
- # 指定した年に含まれる月の数を返します。
1126
- return @months_in_year
1127
- end
1128
- end
1129
-
1130
- # 暦要素数
1131
- #
1132
- # @param [Array<Numeric>] date ( y )
1133
- #
1134
- # y
1135
- #
1136
- # @return [Integer] その年の日数
1137
- #
1138
- def _sum_(date)
1139
- y, = date
1140
- m = @months_in_year * +y
1141
- return _new_month(m+@months_in_year) - _new_month(m)
1142
- end
1143
-
1144
- private
1145
-
1146
- # オブジェクトの正規化
1147
- #
1148
- # @months_in_year = 1年の月の数
1149
- #
1150
- def _normalize(args=[], options={})
1151
- @months_in_year ||= 12
1152
- @formula = When::Parts::Resource._instantiate(@formula)
1153
- super
1154
- end
1155
- end
1156
-
1157
- # 月日の配当が太陽の位置によって決定される太陽暦
1158
- #
1159
- # Calendar based on the ephemeris of the Sun
1160
- #
1161
- class EphemerisBasedSolar < EphemerisBased
1162
-
1163
- #protected
1164
-
1165
- # 月初の通日
1166
- #
1167
- # @param [Integer] m 通月
1168
- #
1169
- # @return [Integer] 月初の通日
1170
- #
1171
- def _new_month_(m)
1172
- return solar_sdn(@formula[0].cn_to_time(m + @cycle_offset))
1173
- end
1174
-
1175
- private
1176
-
1177
- # オブジェクトの正規化
1178
- # cycle_offset = 位相のオフセット / 1か月分の角度
1179
- # formula = 位相の計算に用いる太陽の Formula
1180
- #
1181
- def _normalize(args=[], options={})
1182
- @cycle_offset ||= -1.5
1183
- @formula ||= "Formula?formula=#{@months_in_year||12}S"
1184
- super
1185
- end
1186
- end
1187
-
1188
- # 月日の配当が月の位相によって決定される純太陰暦
1189
- #
1190
- # Calendar based on the ephemeris of the Moon
1191
- #
1192
- class EphemerisBasedLunar < EphemerisBased
1193
-
1194
- include Lunar
1195
-
1196
- #protected
1197
-
1198
- # 月初の通日
1199
- #
1200
- # @param [Integer] m 通月
1201
- #
1202
- # @return [Integer] 月初の通日
1203
- #
1204
- def _new_month_(m)
1205
- return lunar_sdn(@formula[-1].cn_to_time(m + @cycle_offset))
1206
- end
1207
-
1208
- private
1209
-
1210
- # オブジェクトの正規化
1211
- # cycle_offset = Goldstein Number に対する暦元の補正
1212
- #
1213
- def _normalize(args=[], options={})
1214
- @cycle_offset ||= 1671 * 12 + 4
1215
- super
1216
- end
1217
- end
1218
-
1219
- # 月日の配当が太陽および月の位置によって決定される太陰太陽暦
1220
- #
1221
- # Calendar based on the ephemeris of the Sun and the Moon
1222
- #
1223
- class EphemerisBasedLuniSolar < EphemerisBasedSolar
1224
-
1225
- include Lunar
1226
-
1227
- # 計算方法
1228
- # @return [Array<When::Ephemeris::Formula>]
1229
- attr_reader :formula
1230
-
1231
- #protected
1232
-
1233
- # 年月日 -> 通日
1234
- #
1235
- # @param [Numeric] yy 年
1236
- # @param [Integer] mm 月 (0 始まり)
1237
- # @param [Integer] dd 日 (0 始まり)
1238
- #
1239
- # @return [Integer] 通日
1240
- #
1241
- def _coordinates_to_number(yy, mm, dd)
1242
- _new_month(_new_year_month(+yy) + mm) + dd
1243
- end
1244
-
1245
- # 通日 - > 年月日
1246
- #
1247
- # @param [Integer] sdn 通日
1248
- #
1249
- # @return [Array<Integer>] ( y, m, d )
1250
- # [ y 年 ]
1251
- # [ m (0 始まり) ]
1252
- # [ d 日 (0 始まり) ]
1253
- #
1254
- def _number_to_coordinates(sdn)
1255
- nn, dd = Residue.mod(sdn) {|m| _new_month(m)}
1256
- yy, mm = Residue.mod(nn) {|y| _new_year_month(y)}
1257
- [yy, mm, dd]
1258
- end
1259
-
1260
- # 暦要素数
1261
- #
1262
- # @overload _length(date)
1263
- # @param [Array<Integer>] date ( 年 )
1264
- # @return [Integer] その年の月数
1265
- #
1266
- # @overload _length(date)
1267
- # @param [Array<Integer>] date ( 年, 月 )
1268
- # @note 月は 0 始まり
1269
- # @return [Integer] その年月の日数
1270
- #
1271
- def _length(date)
1272
- y, m = date
1273
- if (m)
1274
- # 指定した月に含まれる日の数を返します。
1275
- m += _new_year_month(+y)
1276
- return _new_month(m+1) - _new_month(m)
1277
- else
1278
- # 指定した年に含まれる月の数を返します。
1279
- return _ids([y]).length
1280
- end
1281
- end
1282
-
1283
- private
1284
-
1285
- # 暦要素数
1286
- #
1287
- # @param [Array<Numeric>] date ( y )
1288
- #
1289
- # y
1290
- #
1291
- # @return [Integer] その年の日数
1292
- #
1293
- def _sum_(date)
1294
- y = +date[0]
1295
- return _new_month(_new_year_month(y+1)) - _new_month(_new_year_month(y))
1296
- end
1297
-
1298
- # 太陽月初の通日
1299
- #
1300
- #
1301
- alias :_new_epoch_ :_new_month_
1302
-
1303
- # 太陰月初の通日
1304
- #
1305
- # @param [Integer] m 通月
1306
- #
1307
- # @return [Integer] 月初の通日
1308
- #
1309
- def _new_month_(m)
1310
- lunar_sdn(@formula[-1].cn_to_time(m))
1311
- end
1312
-
1313
- # 年初の通月
1314
- #
1315
- # @param [Integer] y 年
1316
- #
1317
- # @return [Integer] 年初の通月
1318
- #
1319
- def _new_year_month_(y)
1320
- raise TypeError, 'EphemerisBasedLuniSolar is abstract class'
1321
- end
1322
-
1323
- # オブジェクトの正規化
1324
- #
1325
- # cycle_offset = 雨水の場合 -1
1326
- # formula = 位相の計算に用いる太陽と月の Formula
1327
- # notes = to_a でデフォルトとして用いる暦注
1328
- #
1329
- def _normalize(args=[], options={})
1330
- @formula ||= ['Formula?formula=12S', 'Formula?formula=1L']
1331
- super
1332
- end
1333
- end
1334
-
1335
- #
1336
- # 日時要素の境界 - Border
1337
- #
1338
- class Border < When::TM::ReferenceSystem
1339
- #
1340
- # 境界の振舞
1341
- #
1342
- # @return [Numeric]
1343
- #
1344
- # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
1345
- #
1346
- # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
1347
- #
1348
- def behavior
1349
- @border[0]
1350
- end
1351
-
1352
- # 境界の取得
1353
- #
1354
- # @param [Array<Numeric>] date 境界を計算する年/日
1355
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1356
- #
1357
- # @return [Array<Numeric>] その年/日の境界
1358
- #
1359
- def border(date=[], frame=nil)
1360
- last = date.length-1
1361
- return @border if (last<0)
1362
- bDate = date[0..last] + @border[(last+1)..-1]
1363
- branch = @border[last] * 0
1364
- return bDate if (branch==0)
1365
- bDate[last] = When::Coordinates::Pair.new(+date[last]-branch, branch)
1366
- return bDate
1367
- end
1368
-
1369
- # 境界の正規化
1370
- #
1371
- # @param [Array<Numeric>] date 境界を計算する年/日
1372
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1373
- #
1374
- # @return [Array<Numeric>] その年/日の境界
1375
- #
1376
- def _adjust_epoch(date, frame=nil)
1377
- s_date = date.dup
1378
- e_date = border(date[0..0], frame)
1379
- branch = behavior * 0
1380
- branch += 1 if ((s_date[1..-1] <=> e_date[1..-1]) < 0)
1381
- s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
1382
- return s_date
1383
- end
1384
-
1385
- private
1386
-
1387
- # 要素の正規化
1388
- def _normalize(args=[], options={})
1389
- @border = When::Coordinates::Pair._en_pair_date_time(@border) if (@border.kind_of?(String))
1390
- end
1391
- end
1392
-
1393
- #
1394
- # 日時要素の境界 - 年/日によって、異なる境界を使用する場合
1395
- #
1396
- class MultiBorder < Border
1397
-
1398
- #
1399
- # 境界の配列
1400
- # @return [Array<When::CalendarTypes::Border>]
1401
- attr_reader :borders
1402
-
1403
- #
1404
- # 境界の振舞
1405
- #
1406
- # @return [Numeric]
1407
- #
1408
- # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
1409
- #
1410
- # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
1411
- #
1412
- def behavior
1413
- @borders[0][:border].behavior
1414
- end
1415
-
1416
- # 境界の取得
1417
- #
1418
- # @param [Array<Numeric>] date 境界を計算する年/日
1419
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1420
- #
1421
- # @return [Array<Numeric>] その年/日の境界
1422
- #
1423
- def border(date=[], frame=nil)
1424
- last = date.length-1
1425
- return @borders[0][:boder] if (last<0)
1426
- @borders.each do |border|
1427
- return border[:border].border(date, frame) if date[0] >= border[:key]
1428
- end
1429
- date[0..last]
1430
- end
1431
-
1432
- # 境界の正規化
1433
- #
1434
- # @param [Array<Numeric>] date 境界を計算する年/日
1435
- # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1436
- #
1437
- # @return [Array<Numeric>] その年/日の境界
1438
- #
1439
- def _adjust_epoch(date, frame=nil)
1440
- @borders.each do |border|
1441
- next unless date[0] >= border[:key]
1442
- s_date = date.dup
1443
- e_date = border[:border].border(date[0..0], frame)
1444
- branch = border[:border].behavior * 0
1445
- branch += 1 if ((s_date[1..-1] <=> e_date[1..-1]) < 0)
1446
- s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
1447
- return s_date
1448
- end
1449
- date
1450
- end
1451
-
1452
- private
1453
-
1454
- # 要素の正規化
1455
- def _normalize(args=[], options={})
1456
- if @borders.kind_of?(String)
1457
- list = @borders.split(/(\(.+?\))/)
1458
- list.shift if list[0]==''
1459
- list.unshift(-Float::MAX) unless list[0] =~ /\(/
1460
- list.push('0-1-1') if list[-1] =~ /\(/
1461
- @borders = []
1462
- loop do
1463
- key, border, *list = list
1464
- break unless key
1465
- key = $1.to_i if key.kind_of?(String) && /\((.+?)\)/ =~ key
1466
- border = "_c:Border?border=#{border}" unless border =~ /^[A-Z_]/i
1467
- border = When.Calendar(border)
1468
- @borders << {:key=>key, :border=>border}
1469
- end
1470
- end
1471
- @borders = @borders.sort_by {|border| -border[:key]}
1472
- end
1473
- end
1474
-
1475
- #
1476
- # 日時要素の境界 - 日の出,日の入り
1477
- #
1478
- class DayBorder < Border
1479
-
1480
- # 境界の取得
1481
- #
1482
- # @param [Array<Numeric>] date 境界を計算する日
1483
- # @param [When::TM::ReferenceSystem] clock 使用する時法
1484
- #
1485
- # @return [Array<Numeric>] その日の境界
1486
- #
1487
- # @note 属性 @event によって境界を計算する (see {When::Ephemeris::Formula#day_event})
1488
- #
1489
- def border(date=[], clock=When::UTC)
1490
- return @border unless date[0] && clock.formula
1491
-
1492
- clock._encode(
1493
- clock._number_to_coordinates(clock.second *
1494
- clock.time_standard.from_dynamical_time(
1495
- When::TM::JulianDate._d_to_t(
1496
- clock.formula[-1].day_event(
1497
- clock.time_standard.to_dynamical_date(date[0]), @event, When.Resource('_ep:Sun'), @height
1498
- )))), false)
1499
- end
1500
- end
1501
-
1502
- #
1503
- # 日時要素の境界 - 日の出
1504
- #
1505
- class SunRise < DayBorder
1506
-
1507
- private
1508
-
1509
- # 要素の正規化
1510
- def _normalize(args=[], options={})
1511
- @border = [0,0,0,0]
1512
- @event = -1
1513
- @height ||= '0'
1514
- end
1515
- end
1516
-
1517
- #
1518
- # 日時要素の境界 - 日の入り
1519
- #
1520
- class SunSet < DayBorder
1521
-
1522
- private
1523
-
1524
- # 要素の正規化
1525
- def _normalize(args=[], options={})
1526
- @border = [When::Coordinates::Pair.new(+1,-1),0,0,0]
1527
- @event = +1
1528
- @height ||= '0'
1529
- end
1530
- end
1531
- 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
+ autoload :Rational, 'Rational' unless Object.const_defined?(:Rational)
9
+
10
+ #
11
+ # 具体的な When::TM::ReferenceSystem のサブクラスの実装
12
+ #
13
+ module When::CalendarTypes
14
+
15
+ #
16
+ # Universal Time, Coordinated
17
+ #
18
+ class UTC < When::TM::Clock
19
+
20
+ # この時法の時刻をUTC時刻に変換する
21
+ #
22
+ # Description of an operation for
23
+ # converting a time on this clock to a UTC time
24
+ #
25
+ # @param [When::TM::ClockTime] u_time
26
+ # @return [When::TM::ClockTime]
27
+ #
28
+ def utc_trans(u_time)
29
+ return u_time
30
+ end
31
+ alias :utcTrans :utc_trans
32
+
33
+ # UTC時刻をこの時法の時刻に変換する
34
+ #
35
+ # Description of an operation for
36
+ # converting a UTC time to a time on this clock
37
+ #
38
+ # @param [When::TM::ClockTime] clk_time
39
+ # @return [When::TM::ClockTime]
40
+ #
41
+ def clk_trans(clk_time)
42
+ return clk_time
43
+ end
44
+ alias :clkTrans :clk_trans
45
+
46
+ #
47
+ # Zone 名
48
+ #
49
+ # @return [String]
50
+ #
51
+ def zone
52
+ @label.to_s
53
+ end
54
+
55
+ private
56
+
57
+ # オブジェクトの正規化
58
+ def _normalize(args=[], options={})
59
+ @label ||= m17n('Z')
60
+ @indices ||= When::Coordinates::DefaultTimeIndices
61
+ @note ||= 'JulianDay'
62
+ _normalize_spatial
63
+ _normalize_temporal
64
+ @second = (@second||1/When::TM::Duration::SECOND).to_f
65
+ @zone = '+00:00'
66
+ @time_standard ||= When.Resource('_t:UniversalTime')
67
+ @utc_reference = When::TM::ClockTime.new([0,0,0,0], {:frame=>self})
68
+ end
69
+ end
70
+
71
+ #
72
+ # Abstract Local Time
73
+ #
74
+ class LocalTime < UTC
75
+
76
+ # 128秒単位の実数による参照事象の時刻
77
+ #
78
+ # Fraction time of the reference event
79
+ #
80
+ # @param [Integer] sdn 参照事象の通し番号
81
+ #
82
+ # @return [Numeric]
83
+ #
84
+ # T00:00:00Z からの参照事象の経過時間 / 128秒
85
+ #
86
+ def universal_time(sdn=nil)
87
+ return super - @time_standard.localtime_difference unless sdn
88
+ time = When::TM::JulianDate._d_to_t(sdn-0.5)
89
+ @time_standard.to_dynamical_time(time) - When::TimeStandard.to_dynamical_time(time) - @time_standard.localtime_difference
90
+ end
91
+
92
+ #
93
+ # Zone 名
94
+ #
95
+ # @return [String]
96
+ #
97
+ def zone
98
+ iri.split('/')[-1]
99
+ end
100
+
101
+ private
102
+
103
+ # オブジェクトの正規化
104
+ def _normalize(args=[], options={})
105
+ @origin_of_LSC = - @time_standard.localtime_difference / When::TM::Duration::SECOND
106
+ super
107
+ end
108
+ end
109
+
110
+ #
111
+ # Local Mean Time
112
+ #
113
+ class LMT < LocalTime
114
+
115
+ private
116
+
117
+ # オブジェクトの正規化
118
+ def _normalize(args=[], options={})
119
+ @label = m17n('LMT')
120
+ @time_standard = When.Resource("_t:LocalMeanTime?location=_l:long=#{@long||0}")
121
+ super
122
+ end
123
+ end
124
+
125
+ #
126
+ # Local Apparent Time
127
+ #
128
+ class LAT < LocalTime
129
+
130
+ private
131
+
132
+ # オブジェクトの正規化
133
+ def _normalize(args=[], options={})
134
+ @label = m17n('LAT')
135
+ @time_standard = When.Resource("_t:LocalApparentTime?location=_l:long=#{@long||0}")
136
+ super
137
+ end
138
+ end
139
+
140
+ #
141
+ # Temporal Hour System
142
+ #
143
+ class THS < LocalTime
144
+
145
+ private
146
+
147
+ # オブジェクトの正規化
148
+ def _normalize(args=[], options={})
149
+ @label = m17n('THS')
150
+ @time_standard = When.Resource("_t:TemporalHourSystem?location=(_l:long=#{@long||0}&lat=#{@lat||0}&alt=#{@alt||0})")
151
+ super
152
+ end
153
+ end
154
+
155
+ #
156
+ # 太陰(太陽)暦の朔閏パターンを扱うモジュール
157
+ #
158
+ module Lunar
159
+
160
+ # @private
161
+ Pattern = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ'
162
+
163
+ # 朔閏表を生成する
164
+ #
165
+ # @param [Range] range 生成範囲(西暦年)
166
+ # @param [Integer] length 大の月の日数
167
+ # @param [When::TM::Duration] duration チェックする月の間隔
168
+ #
169
+ # @return [Hash] 朔閏表
170
+ #
171
+ def lunar_table(range, length=30, duration=When::P1M)
172
+ date = When.TemporalPosition(range.first, {:frame=>self}).floor
173
+ table = []
174
+ hash = {
175
+ 'origin_of_MSC' => range.first,
176
+ 'origin_of_LSC' => date.to_i,
177
+ 'rule_table' => table
178
+ }
179
+ list = ''
180
+ while range.include?(date[YEAR])
181
+ month = date[MONTH] * 1
182
+ char = Pattern[month..month]
183
+ char = char.downcase unless date.length(MONTH) == length
184
+ list += char
185
+ succ = date + duration
186
+ unless date[YEAR] == succ[YEAR]
187
+ table << list
188
+ list = ''
189
+ end
190
+ date = succ
191
+ end
192
+ hash
193
+ end
194
+
195
+ # 朔閏表を比較する
196
+ #
197
+ # @param [When::TM::Calendar] base 基準とする暦法
198
+ # @param [Range] range 比較範囲(西暦年)
199
+ # @param [Integer] length 大の月の日数
200
+ # @param [When::TM::Duration] duration チェックする月の間隔
201
+ #
202
+ # @return [Hash] 朔閏表の差分
203
+ #
204
+ def verify(base, range=base.range, length=30, duration=When::P1M)
205
+ range = When::Parts::GeometricComplex.new(range) & When::Parts::GeometricComplex.new(self.range) if respond_to?(:range)
206
+ base_table = base.lunar_table(range, length, duration)
207
+ self_table = self.lunar_table(range, length, duration)
208
+ hash = {}
209
+ range.each do |year|
210
+ difference = _verify(base_table['rule_table'][year-range.first],
211
+ self_table['rule_table'][year-range.first])
212
+ hash[year] = difference if difference
213
+ end
214
+ hash
215
+ end
216
+
217
+ # @private
218
+ def _verify(source, target)
219
+ return nil if source == target
220
+ return {source => target} unless source.length == target.length
221
+ indices = []
222
+ index = []
223
+ source.length.times do |i|
224
+ if source[i..i] == target[i..i]
225
+ unless index.empty?
226
+ indices << index
227
+ index = []
228
+ end
229
+ else
230
+ index << i
231
+ end
232
+ end
233
+ indices << index unless index.empty?
234
+ ranges = []
235
+ indices.each do |index|
236
+ if ranges.empty? || index.first > ranges.last.last + 2
237
+ ranges << index
238
+ else
239
+ ranges[-1] = [ranges.last.first,index.last]
240
+ end
241
+ end
242
+ hash = {}
243
+ ranges.each do |index|
244
+ range = index.first..index.last
245
+ hash[source[range]] = target[range]
246
+ end
247
+ test = source.dup
248
+ hash.each_pair do |key, value|
249
+ test.sub!(key, value)
250
+ end
251
+ # raise ArgumentError, "can't replace '#{source}'=>'#{target}' by #{hash}." unless test == target
252
+ return hash if test == target
253
+ {source => target}
254
+ end
255
+ end
256
+
257
+ #
258
+ # 朔閏パターンの表の拡張
259
+ #
260
+ module TableExtend
261
+
262
+ # 年月日 -> 通日
263
+ #
264
+ # @param [Numeric] y 年
265
+ # @param [Integer] m (0 始まり)
266
+ # @param [Integer] d 日 (0 始まり)
267
+ #
268
+ # @return [Integer] 通日
269
+ #
270
+ def _coordinates_to_number(y, m, d)
271
+ if @after && y >= @rule_table[@entry_key]['Years']
272
+ _normalize_after
273
+ return @after._coordinates_to_number(y + @_after_offset, m, d)
274
+ end
275
+ if @before && y < 0
276
+ _normalize_before
277
+ return @before._coordinates_to_number(y + @_before_offset, m, d)
278
+ end
279
+ super
280
+ end
281
+
282
+ # 通日 - > 年月日
283
+ #
284
+ # @param [Integer] sdn 通日
285
+ #
286
+ # @return [Array<Integer>] [ y, m, d ]
287
+ # y
288
+ # m 月 (0 始まり)
289
+ # d (0 始まり)
290
+ #
291
+ def _number_to_coordinates(sdn)
292
+ if @after && sdn >= @origin_of_LSC + @rule_table[@entry_key]['Days']
293
+ _normalize_after
294
+ y, m, d = @after._number_to_coordinates(sdn)
295
+ return [y - @_after_offset, m, d]
296
+ end
297
+ if @before && sdn < @origin_of_LSC
298
+ _normalize_before
299
+ y, m, d = @before._number_to_coordinates(sdn)
300
+ return [y - @_before_offset, m, d]
301
+ end
302
+ super
303
+ end
304
+
305
+ #
306
+ # その他のテーブル参照
307
+ #
308
+ %w(ids_ length).each do |method|
309
+ module_eval %Q{
310
+ def _#{method}(date)
311
+ if @after && +date[0] >= @rule_table[@entry_key]['Years']
312
+ _normalize_after
313
+ date[0] += @_after_offset
314
+ return @after.send(:_#{method}, date)
315
+ end
316
+ if @before && +date[0] < 0
317
+ _normalize_before
318
+ date[0] += @_before_offset
319
+ return @before.send(:_#{method}, date)
320
+ end
321
+ super
322
+ end
323
+ }
324
+ end
325
+
326
+ private
327
+
328
+ def _normalize_after
329
+ raise RangeError, "Out of range: #{iri}" if @after.kind_of?(Symbol)
330
+ @after = When.Calendar(@after)
331
+ @_after_offset = @origin_of_MSC - @after.origin_of_MSC
332
+ class << self; alias :_normalize_after :_normalize_non end
333
+ end
334
+
335
+ def _normalize_before
336
+ raise RangeError, "Out of range: #{iri}" if @before.kind_of?(Symbol)
337
+ @before = When.Calendar(@before)
338
+ @_before_offset = @origin_of_MSC - @before.origin_of_MSC
339
+ class << self; alias :_normalize_before :_normalize_non end
340
+ end
341
+
342
+ def _normalize_non
343
+ end
344
+ end
345
+
346
+ # 月日の配当パターンの種類が限定されている暦の抽象基底クラス
347
+ #
348
+ # Calendar which has some fixed arrangement rules for under year
349
+ #
350
+ # 新年の日付が専用メソッドで与えられ、月日の配当が1年の日数等
351
+ # で決まる暦。いわゆる Rule-Based な暦はほとんど該当します。
352
+ class TableBased < When::TM::Calendar
353
+
354
+ # 年月日 -> 通日
355
+ #
356
+ # @param [Numeric] y
357
+ # @param [Integer] m (0 始まり)
358
+ # @param [Integer] d (0 始まり)
359
+ #
360
+ # @return [Integer] 通日
361
+ #
362
+ def _coordinates_to_number(y, m, d)
363
+ sdn = _sdn([+y])
364
+ rule = _rule(_key([+y]))
365
+ sdn += d + rule['Offset'][m]
366
+ return sdn if d >= 0
367
+ return sdn + rule['Length'][m % rule['Length'].length]
368
+ end
369
+
370
+ # 通日 - > 年月日
371
+ #
372
+ # @param [Integer] sdn 通日
373
+ #
374
+ # @return [Array<Integer>] [ y, m, d ]
375
+ # y
376
+ # m (0 始まり)
377
+ # d (0 始まり)
378
+ #
379
+ def _number_to_coordinates(sdn)
380
+ y, d = Residue.mod(sdn) {|n| _sdn([n])}
381
+ rule = _rule(_key([y]))
382
+ (rule['Months']-1).downto(0) do |m|
383
+ if d >=rule['Offset'][m]
384
+ d -= rule['Offset'][m]
385
+ return [y, m, d]
386
+ end
387
+ end
388
+ return nil
389
+ end
390
+
391
+ # 暦要素数
392
+ #
393
+ # @overload _length(date)
394
+ # @param [Array<Integer>] date ( y )
395
+ #
396
+ # y 年
397
+ #
398
+ # @return [Integer] その年の月数
399
+ #
400
+ # @overload _length(date)
401
+ # @param [Array<Integer>] date ( y, m )
402
+ #
403
+ # y
404
+ #
405
+ # m (0 始まり)
406
+ #
407
+ # @return [Integer] その年月の日数
408
+ #
409
+ def _length(date)
410
+ y, m = date
411
+ if (m)
412
+ # 指定した月に含まれる日の数を返します。
413
+ return @unit[2] if @unit[2]
414
+ rule = _rule(_key([y]))
415
+ return rule['Length'][m % rule['Length'].length]
416
+ else
417
+ # 指定した年に含まれる月の数を返します。
418
+ return @unit[1] if @unit[1]
419
+ return _rule(_key([y]))['Months']
420
+ end
421
+ end
422
+
423
+ private
424
+
425
+ # オブジェクトの正規化
426
+ def _normalize(args=[], options={})
427
+ # range extension
428
+ extend(TableExtend) if @before || @after
429
+
430
+ super
431
+
432
+ # rule_table
433
+ # @rule_table = @rule_table.dup
434
+ @rule_table = {'T' => {'Rule' => @rule_table }} if @rule_table.kind_of?(Array)
435
+ @_m_cash_ = {}
436
+ @_m_cash_["_rule"] = @rule_table
437
+
438
+ # unit length
439
+ unit = @unit[1..2]
440
+ @rule_table.each do |key, rule|
441
+ _make_rule(key, rule, unit) if rule.kind_of?(Hash)
442
+ end
443
+
444
+ # mean month length
445
+ if @entry_key
446
+ ::Rational
447
+ @mean_month = Rational(@rule_table[@entry_key]['Days'], @rule_table[@entry_key]['Months'])
448
+ @mean_year = Rational(@rule_table[@entry_key]['Days'], @rule_table[@entry_key]['Years' ])
449
+ end
450
+ end
451
+
452
+ # rule の正規化
453
+ def _make_rule(key, rule, unit=[])
454
+ # @rule_table[key]['Years', 'Months', 'Offset', 'Days']
455
+ rule['IDs'] = Pair._en_pair_array(rule['IDs']) if rule['IDs'].kind_of?(String)
456
+ rule['Years'] ||= 1
457
+ rule['Months'] ||= (rule['IDs']||rule['Length']).length
458
+ rule['Offset'] = []
459
+ sum, len = 0, rule['Length'].length
460
+ rule['Months'].times do |k|
461
+ rule['Offset'] << sum
462
+ sum += rule['Length'][k % len]
463
+ end
464
+ rule['Days'] ||= sum
465
+
466
+ # Months in Year
467
+ unit[0] ||= rule['Months']
468
+ unit[0] = 0 unless (unit[0]==rule['Months'])
469
+
470
+ # Days in Month
471
+ len = rule['Length'][0]
472
+ if rule['Length'].length == 1 && (rule['Days'] % len) == 0
473
+ unit[1] ||= len
474
+ unit[1] = 0 unless (unit[1]==len)
475
+ else
476
+ unit[1] = 0
477
+ end
478
+ end
479
+
480
+ # 年初の通日によるセットアップ
481
+ def _sdn_setup(c_key, c_date)
482
+ n_date = c_date.dup
483
+ n_date[-1] += 1
484
+ n_key = (n_date.length<=1) ? n_date[0] : n_date
485
+ c_sdn = (@_m_cash_["_sdn"][c_key] ||= _sdn_(c_date))
486
+ n_sdn = (@_m_cash_["_sdn"][n_key] ||= _sdn_(n_date))
487
+ key = (n_sdn - c_sdn).to_i
488
+ rule = (@_m_cash_["_rule"][key] ||= _rule_(key))
489
+ @_m_cash_["_key"] ||= {}
490
+ @_m_cash_["_ids"] ||= {}
491
+ @_m_cash_["_key"][c_key] ||= key
492
+ @_m_cash_["_ids"][c_key] ||= rule['IDs']
493
+ return c_sdn
494
+ end
495
+
496
+ # 年初の通日
497
+ # このメソッドは subclass で定義します
498
+ #
499
+ # @param [Array<Numeric>] date ( 年 )
500
+ #
501
+ # @return [Integer] 年初の通日
502
+ #
503
+ def _sdn_(date)
504
+ raise TypeError, "Abstract TableBased Calendar Type"
505
+ end
506
+
507
+ # 暦日表のキー取得
508
+ #
509
+ # @param [Array<Numeric>] date ( 年 )
510
+ #
511
+ # @return [Integer] 暦日表のキー 本暦法では当該年の日数を暦日表のキーとします
512
+ #
513
+ def _key_(date)
514
+ n_date = date.dup
515
+ n_date[-1] += 1
516
+ (_sdn(n_date) - _sdn(date)).to_i
517
+ end
518
+
519
+ # 日時要素の翻訳表の取得
520
+ #
521
+ # @param [Array<Numeric>] date ( 年 )
522
+ #
523
+ # @return [Array<When::Coordinates::Pair>] 日時要素の翻訳表
524
+ #
525
+ def _ids_(date)
526
+ _rule(_key(date))['IDs']
527
+ end
528
+
529
+ # 暦要素数
530
+ #
531
+ # @param [Array<Numeric>] date ( 年 )
532
+ #
533
+ # @return [Integer] その年の日数
534
+ #
535
+ def _sum_(date)
536
+ return _rule(_key([date[0]]))['Days']
537
+ end
538
+
539
+ # 月日の配当
540
+ #
541
+ # @param [Numeric] year 年
542
+ #
543
+ # @return [Array<Integer>] [ 月の日数 ]
544
+ #
545
+ def month_arrangement_(year)
546
+ _rule(_key([year * 1 - @origin_of_MSC]))['Length']
547
+ end
548
+
549
+ # rule の遅延生成
550
+ def _rule_(key)
551
+ rule = {
552
+ 'Years' => 1,
553
+ 'Months' => key.length,
554
+ 'Days' => _year_length(key),
555
+ 'IDs' => [],
556
+ 'Length' => [],
557
+ 'Offset' => []
558
+ }
559
+
560
+ key.length.times do |m|
561
+ rule['Length'] << _month_length(key, m)
562
+ rule['Offset'] << (m == 0 ? 0 : rule['Offset'][m-1]+rule['Length'][m-1])
563
+ rule['IDs'] << _month_id(key, m)
564
+ end
565
+ return rule
566
+ end
567
+ end
568
+
569
+ # 表引きにより実現する太陰太陽暦
570
+ #
571
+ # Luni-Solar calendar which uses year / month /day table
572
+ #
573
+ class PatternTableBasedLuniSolar < TableBased
574
+
575
+ include Lunar
576
+
577
+ class << self
578
+ #
579
+ # ひとつのひな型朔閏表からの差分で朔閏表を生成する
580
+ #
581
+ # @param [Array] definition ひな型朔閏表
582
+ # @param [Range] range 生成する朔閏表の年代範囲
583
+ # @param [Hash{Integer=>(String or Hash{String or Regexp=>String})}] difference 差分情報
584
+ #
585
+ # @return [Array] 生成された朔閏表定義
586
+ #
587
+ def patch(definition, range=nil, difference={})
588
+ When.Calendar(definition)
589
+ base = When::CalendarTypes.const_get(definition)
590
+ hash = base[-1].dup
591
+ range ||= hash['origin_of_MSC']...(hash['origin_of_MSC']+hash['rule_table'].size)
592
+ range = range.to_a
593
+ hash['origin_of_LSC'] += hash['rule_table'][range[0]-hash['origin_of_MSC']][1]
594
+ hash['rule_table'] = range.map {|year|
595
+ original = hash['rule_table'][year-hash['origin_of_MSC']][0]
596
+ case difference[year]
597
+ when String ; next difference[year]
598
+ when nil ; next original
599
+ end
600
+ original = original.dup
601
+ difference[year].each_pair {|key,value|
602
+ raise ArgumentError, "Can't patch \"#{original}\" by {#{key}=>#{value}} at #{year}" unless original.sub!(key,value)
603
+ }
604
+ original
605
+ }
606
+ hash['origin_of_MSC'] = range[0]
607
+ base[0..-2] + [hash]
608
+ end
609
+
610
+ #
611
+ # 複数のひな型朔閏表からの差分で朔閏表を生成する
612
+ #
613
+ # @param [[Array<Array<String, Range>>]] definitions ひな型朔閏表
614
+ # - String - もとにする太陰太陽暦のIRI文字列
615
+ # - Range - 朔閏表の年代範囲(デフォルトはもとにする太陰太陽暦の年代範囲)
616
+ # @param [Hash{Integer=>(String or Hash{String or Regexp=>String})}] difference 差分情報
617
+ #
618
+ # @return [Array] 生成された朔閏表定義
619
+ #
620
+ def join(definitions, difference={})
621
+ if definitions.first.kind_of?(Array)
622
+ base = When::CalendarTypes.const_get(definitions.first[0]).dup
623
+ else
624
+ base = []
625
+ base << definitions.shift until definitions.first.kind_of?(Array)
626
+ end
627
+ tables = definitions.map {|definition|
628
+ When.Calendar(definition[0]).lunar_table(definition[1])
629
+ }
630
+ hash = base.pop.merge({
631
+ 'origin_of_MSC' => tables.first['origin_of_MSC'],
632
+ 'origin_of_LSC' => tables.first['origin_of_LSC'],
633
+ 'rule_table' => tables.inject([]) {|rules, table| rules += table['rule_table']}
634
+ })
635
+ difference.each_pair do |year, pattern|
636
+ offset = year - hash['origin_of_MSC']
637
+ hash['rule_table'][offset] =
638
+ if pattern.kind_of?(Hash)
639
+ rule = hash['rule_table'][offset].dup
640
+ pattern.each_pair do |key,value|
641
+ raise ArgumentError, "Can't patch \"#{rule}\" by {#{key}=>#{value}} at #{year}" unless rule.sub!(key,value)
642
+ end
643
+ rule
644
+ else
645
+ pattern
646
+ end
647
+ end
648
+ base << hash
649
+ end
650
+ end
651
+
652
+ # 朔閏表を生成する
653
+ #
654
+ # @param [Range] sub_range 生成範囲(西暦年) デフォルトは self.range
655
+ # @param [Integer] length 大の月の日数(ダミー)
656
+ # @param [When::TM::Duration] duration チェックする月の間隔(ダミー)
657
+ #
658
+ # @return [Hash] 朔閏表
659
+ #
660
+ def lunar_table(sub_range=nil, length=nil, duration=nil)
661
+ sub_range ||= range
662
+ last = sub_range.last
663
+ last -= 1 if sub_range.exclude_end?
664
+ [sub_range.first, last].each do |edge|
665
+ raise RangeError, 'Range exceeded: ' + sub_range.to_s unless range.include?(edge)
666
+ end
667
+ {
668
+ 'origin_of_MSC' => sub_range.first,
669
+ 'origin_of_LSC' => @origin_of_LSC + @rule_table['T']['Rule'][sub_range.first-@origin_of_MSC][1],
670
+ 'rule_table' => sub_range.to_a.map {|year|
671
+ @rule_table['T']['Rule'][year-@origin_of_MSC][0]
672
+ }
673
+ }
674
+ end
675
+
676
+ # 朔閏表の有効範囲
677
+ #
678
+ # @return [Range] 有効範囲(西暦年)
679
+ #
680
+ def range
681
+ @origin_of_MSC...(@origin_of_MSC+@rule_table['T']['Rule'].length)
682
+ end
683
+
684
+ private
685
+
686
+ # new で指定された月日配当規則をプログラムで利用可能にします。
687
+ #
688
+ # key 年月日配当規則のハッシュキー
689
+ # rule 年月日配当規則
690
+ #
691
+ # インスタンス変数 ハッシュのハッシュ@rule_table の要素
692
+ # Years => the period length / year
693
+ # Months => the period length / month
694
+ # Days => the period length / day
695
+ # Rule => Array of sub rules' key and offset
696
+ def _make_rule(key, rule, unit=nil)
697
+
698
+ offsets = [0, 0]
699
+ rule['Rule'].each_index do |k|
700
+ subkey = rule['Rule'][k]
701
+ if subkey.kind_of?(Array)
702
+ subkey, *offsets = rule['Rule'][k]
703
+ else
704
+ subkey = _make_subkey(subkey)
705
+ rule['Rule'][k] = [subkey] + offsets
706
+ end
707
+ _increment_offsets(offsets, subkey)
708
+ end
709
+
710
+ rule['Years'] ||= rule['Rule'].length # 年数
711
+ rule['Months'] ||= offsets[1] # 月数
712
+ rule['Days'] ||= offsets[0] # 日数
713
+
714
+ @entry_key ||= key
715
+ end
716
+
717
+ # 恒等変換
718
+ alias :_make_subkey :_do_nothing
719
+
720
+ # オフセットの更新
721
+ def _increment_offsets(offsets, subkey)
722
+ offsets[1] += subkey.length # 月のオフセットを月数分進める
723
+ offsets[0] += _year_length(subkey) # 日のオフセットを日数分進める
724
+ end
725
+
726
+ # 年初の通日によるセットアップ
727
+ def _sdn_setup(c_key, c_date)
728
+ root_rule = @rule_table[@entry_key]
729
+ count, year = c_date[0].divmod(root_rule['Years'])
730
+ key, dd, mm = root_rule['Rule'][year]
731
+ rule = (@_m_cash_["_rule"][key] ||= _rule_(key))
732
+ @_m_cash_["_key"] ||= {}
733
+ @_m_cash_["_ids"] ||= {}
734
+ @_m_cash_["_key"][c_key] ||= key
735
+ @_m_cash_["_ids"][c_key] ||= rule['IDs']
736
+ @_m_cash_["_sdn"][c_key] ||= @origin_of_LSC + dd + count * root_rule['Days']
737
+ end
738
+
739
+ # 年初の通日
740
+ #
741
+ # @param [Array<Numeric>] date ( y )
742
+ #
743
+ # y
744
+ #
745
+ # @return [Integer] 年初の通日
746
+ #
747
+ def _sdn_(date)
748
+ rule = @rule_table[@entry_key]
749
+ count, year = date[0].divmod(rule['Years'])
750
+ return @origin_of_LSC + rule['Rule'][year][1] + count * rule['Days']
751
+ end
752
+
753
+ # 暦日表のキー取得
754
+ #
755
+ # @param [Array<Numeric>] date ( y )
756
+ #
757
+ # y
758
+ #
759
+ # @return [Integer] 暦日表のキー
760
+ #
761
+ def _key_(date)
762
+ rule = @rule_table[@entry_key]
763
+ count, year = date[0].divmod(rule['Years'])
764
+ return rule['Rule'][year][0]
765
+ end
766
+
767
+ # 朔閏パターン -> 日数/年
768
+ #
769
+ # @param [String] key 朔閏パターン
770
+ #
771
+ # @return [Integer] 日数/年
772
+ #
773
+ def _year_length(key)
774
+ key.length * 29 + key.gsub(/[a-z]/,'').length
775
+ end
776
+
777
+ # 朔閏パターン -> 日数/年
778
+ #
779
+ # @param [String] key 朔閏パターン
780
+ # @param [Integer] m 月番号(0始まり)
781
+ #
782
+ # @return [Integer] 日数/月
783
+ #
784
+ def _month_length(key, m)
785
+ key[m,1] =~ /[a-z]/ ? 29 : 30
786
+ end
787
+
788
+ # 朔閏パターン -> 月のID
789
+ #
790
+ # @param [String] key 朔閏パターン
791
+ # @param [Integer] m 月番号(0始まり)
792
+ #
793
+ # @return [Integer] 月のID
794
+ #
795
+ def _month_id(key, m)
796
+ trunk = key.upcase[m]
797
+ branch = trunk == key.upcase[m-1] ? 1 : 0
798
+ trunk = trunk.ord if trunk.kind_of?(String)
799
+ trunk -= 64
800
+ branch == 0 ? trunk : When::Coordinates::Pair.new(trunk, branch)
801
+ end
802
+ end
803
+
804
+ # 表引きにより実現する太陰太陽暦(29,30日以外の月がある場合)
805
+ #
806
+ # Luni-Solar calendar which has months with irregular length
807
+ #
808
+ class PatternTableBasedLuniSolarExtended < PatternTableBasedLuniSolar
809
+
810
+ private
811
+
812
+ # rule の遅延生成
813
+ def _rule_(key)
814
+ key.kind_of?(Hash) ? key : super
815
+ end
816
+
817
+ # オフセットの更新
818
+ def _increment_offsets(offsets, subkey)
819
+ return super unless subkey.kind_of?(Hash)
820
+ offsets[1] += subkey['Months'] # 月のオフセットを月数分進める
821
+ offsets[0] += subkey['Days'] # 日のオフセットを日数分進める
822
+ end
823
+ end
824
+
825
+ # 表引きにより実現する太陰太陽暦(Ephemerisにより朔を決定する)
826
+ #
827
+ # Luni-Solar calendar whose new moon is determined by 'engine' calendar
828
+ #
829
+ class PatternTableBasedLuniSolarWithEphemeris < PatternTableBasedLuniSolar
830
+
831
+ private
832
+
833
+ # subkeyの生成
834
+ def _make_subkey(key)
835
+ pattern = @subkey_table[key]
836
+ subkey = ''
837
+ pattern.length.times do |i|
838
+ @month_no += 1
839
+ next_new_moon = @engine._new_month(@month_no)
840
+ subkey += next_new_moon - @new_moon >= 30 ? pattern[i..i].upcase : pattern[i..i].downcase
841
+ @new_moon = next_new_moon
842
+ end
843
+ subkey
844
+ end
845
+
846
+ # オブジェクトの正規化
847
+ #
848
+ # YearLengthTableBased+オブジェクトの性質定義を初期設定します。
849
+ #
850
+ def _normalize(args=[], options={})
851
+ @engine = When.Calendar(@engine)
852
+ divmod = When::Coordinates::Residue.mod(@origin_of_LSC.to_i+3) {|cn| @engine.formula[-1].cn_to_time(cn)}
853
+ @month_no = divmod[0]
854
+ @new_moon = @origin_of_LSC.to_i+3 - divmod[1]
855
+ super
856
+ end
857
+ end
858
+
859
+ # 表引きにより実現する太陽暦(閏月なし, 5,6,27~34日の月に対応)
860
+ #
861
+ # Solar calendar which uses year / month /day table
862
+ #
863
+ class PatternTableBasedSolar < PatternTableBasedLuniSolar
864
+
865
+ private
866
+
867
+ # 朔閏パターン -> 日数/年
868
+ #
869
+ # @param [String] key 朔閏パターン
870
+ #
871
+ # @return [Integer] 日数/年
872
+ #
873
+ def _year_length(key)
874
+ length = 0
875
+ key.length.times do |m|
876
+ length += _month_length(key, m)
877
+ end
878
+ length
879
+ end
880
+
881
+ # 朔閏パターン -> 日数/年
882
+ #
883
+ # @param [String] key 朔閏パターン
884
+ # @param [Integer] m 月番号(0始まり)
885
+ #
886
+ # @return [Integer] 日数/月
887
+ #
888
+ def _month_length(key, m)
889
+ trunk = key.upcase[m]
890
+ trunk = trunk.ord if trunk.kind_of?(String)
891
+ trunk -= 48
892
+ case trunk
893
+ when 5, 6 ; trunk
894
+ when 7..9 ; trunk + 20
895
+ else ; trunk + 30
896
+ end
897
+ end
898
+
899
+ # 朔閏パターン -> 月のID
900
+ #
901
+ # @param [String] key 朔閏パターン
902
+ # @param [Integer] m 月番号(0始まり)
903
+ #
904
+ # @return [Integer] 月のID
905
+ #
906
+ def _month_id(key, m)
907
+ m + 1
908
+ end
909
+ end
910
+
911
+ # 年の配当パターンが限定されている暦
912
+ #
913
+ # Calendar which has some fixed arrangement rules of year pattern
914
+ #
915
+ class CyclicTableBased < TableBased
916
+
917
+ # 通日 - > 年月日
918
+ #
919
+ # @param [Integer] sdn 通日
920
+ #
921
+ # @return [Array<Integer>] [ y, m, d ]
922
+ # y
923
+ # m 月 (0 始まり)
924
+ # d (0 始まり)
925
+ #
926
+ def _number_to_coordinates(sdn)
927
+ root_rule = @rule_table[@entry_key]
928
+ count, value = (sdn-@origin_of_LSC).divmod(root_rule['Days'])
929
+ y, d, key = _read_period(@entry_key,
930
+ 'Days', value,
931
+ 'Years', count * root_rule['Years'])
932
+ rule = _rule(key)
933
+ (rule['Months']-1).downto(0) do |m|
934
+ if d >=rule['Offset'][m]
935
+ d -= rule['Offset'][m]
936
+ return [y, m, d]
937
+ end
938
+ end
939
+ return nil
940
+ end
941
+
942
+ private
943
+
944
+ #
945
+ # new で指定された月日配当規則をプログラムで利用可能にします。
946
+ #
947
+ # key 年月日配当規則のハッシュキー
948
+ # rule 年月日配当規則
949
+ #
950
+ # インスタンス変数 ハッシュのハッシュ@rule_table の要素
951
+ # Years => the period length / year
952
+ # Months => the period length / month
953
+ # Days => the period length / day
954
+ # Rule => Array of sub rules' key
955
+ #
956
+ def _make_rule(key, rule, unit=nil)
957
+ if rule.key?('Rule') # Table of Many Years
958
+ ['Years', 'Months', 'Days'].each do |u|
959
+ rule[u] ||=
960
+ begin
961
+ s = 0
962
+ rule['Rule'].each do |part|
963
+ subkey, count = part
964
+ subrule = @rule_table[subkey]
965
+ _make_rule(subkey, subrule, unit) unless subrule[u]
966
+ s += (count||1) * subrule[u]
967
+ end
968
+ s
969
+ end
970
+ end
971
+ if !@entry_key ||
972
+ @rule_table[@entry_key]['Days'] < rule['Days']
973
+ @entry_key = key
974
+ end
975
+ else # Table of One Year
976
+ super
977
+ end
978
+ end
979
+
980
+ # 年初の通日によるセットアップ
981
+ def _sdn_setup(c_key, c_date)
982
+ root_rule = @rule_table[@entry_key]
983
+ count, value = c_date[0].divmod(root_rule['Years'])
984
+ sdn, y, key = _read_period(@entry_key,
985
+ 'Years', value,
986
+ 'Days', @origin_of_LSC + count * root_rule['Days'])
987
+ @_m_cash_["_key"] ||= {}
988
+ @_m_cash_["_ids"] ||= {}
989
+ @_m_cash_["_key"][c_key] ||= key
990
+ @_m_cash_["_ids"][c_key] ||= @_m_cash_["_rule"][key]['IDs']
991
+ @_m_cash_["_sdn"][c_key] ||= sdn
992
+ end
993
+
994
+ # 年初の通日
995
+ #
996
+ # @param [Array<Numeric>] date ( y )
997
+ #
998
+ # y
999
+ #
1000
+ # @return [Integer] 年初の通日
1001
+ #
1002
+ def _sdn_(date)
1003
+ root_rule = @rule_table[@entry_key]
1004
+ count, value = date[0].divmod(root_rule['Years'])
1005
+ sdn, y, key = _read_period(@entry_key,
1006
+ 'Years', value,
1007
+ 'Days', @origin_of_LSC + count * root_rule['Days'])
1008
+ return sdn
1009
+ end
1010
+
1011
+ # 暦日表のキー取得
1012
+ #
1013
+ # @param [Array<Numeric>] date ( y )
1014
+ #
1015
+ # y 年
1016
+ #
1017
+ # @return [Integer] 暦日表のキー
1018
+ #
1019
+ def _key_(date)
1020
+ root_rule = @rule_table[@entry_key]
1021
+ count, value = date[0].divmod(root_rule['Years'])
1022
+ sdn, y, key = _read_period(@entry_key,
1023
+ 'Years', value,
1024
+ 'Days', @origin_of_LSC + count * root_rule['Days'])
1025
+ return key
1026
+ end
1027
+
1028
+ # 年の配当規則を読み出します。
1029
+ #
1030
+ # key 年の配当規則のキー
1031
+ # akey 入力が'Days'か'Years'かを指定
1032
+ # avalue 入力の'Days'または'Years'の値
1033
+ # zkey 出力が'Years'か'Days'かを指定
1034
+ # zvalue 出力の'Years'または'Days'の値
1035
+ def _read_period(key, akey, avalue, zkey, zvalue)
1036
+ rule = @rule_table
1037
+ rule[key]['Rule'].each do |part|
1038
+ subkey, count, = [*part] << 1
1039
+ if avalue >= count * rule[subkey][akey]
1040
+ avalue -= count * rule[subkey][akey]
1041
+ zvalue += count * rule[subkey][zkey]
1042
+ else
1043
+ count, avalue = avalue.divmod(rule[subkey][akey])
1044
+ zvalue += count * rule[subkey][zkey]
1045
+ return zvalue, avalue, subkey unless rule[subkey].key?('Rule')
1046
+ return _read_period(subkey, akey, avalue, zkey, zvalue)
1047
+ end
1048
+ end
1049
+ end
1050
+ end
1051
+
1052
+ #
1053
+ # 年初を太陽黄経で決定する暦
1054
+ #
1055
+ class YearLengthTableBased < TableBased
1056
+
1057
+ # 天体暦アルゴリズム
1058
+ #
1059
+ # @return [Array<When::Ephemeris::Formula>]
1060
+ #
1061
+ attr_reader :formula
1062
+
1063
+ #protected
1064
+
1065
+ private
1066
+
1067
+ # 年初の通日
1068
+ #
1069
+ # @param [Array<Numeric>] date ( y )
1070
+ #
1071
+ # y 年
1072
+ #
1073
+ # @return [Integer] 年初の通日
1074
+ #
1075
+ def _sdn_(date)
1076
+ y = +date[0]
1077
+ t = @formula[0].cn_to_time(y.to_f + @cycle_offset)
1078
+ return solar_sdn(t + @day_offset)
1079
+ end
1080
+
1081
+ # オブジェクトの正規化
1082
+ #
1083
+ # YearLengthTableBased+オブジェクトの性質定義を初期設定します。
1084
+ #
1085
+ def _normalize(args=[], options={})
1086
+
1087
+ Rational
1088
+ @cycle_offset = (@cycle_offset||0).to_r
1089
+ @day_offset = (@day_offset||0).to_r
1090
+ @formula = 'Formula?formula=1S'
1091
+
1092
+ super
1093
+ end
1094
+ end
1095
+
1096
+ # 月日の配当が太陽または月の位置によって決定される暦
1097
+ #
1098
+ # Calendar based on the ephemeris of the Sun or the Moon
1099
+ #
1100
+ class EphemerisBased < When::TM::Calendar
1101
+
1102
+ # 天体暦
1103
+ #
1104
+ # @return [When::Ephmeris::Formula]
1105
+ #
1106
+ attr_reader :formula
1107
+
1108
+ #protected
1109
+
1110
+ # 年月日 -> 通日
1111
+ #
1112
+ # @param [Numeric] y 年
1113
+ # @param [Integer] m 月 (0 始まり)
1114
+ # @param [Integer] d 日 (0 始まり)
1115
+ #
1116
+ # @return [Integer] 通日
1117
+ #
1118
+ def _coordinates_to_number(y, m, d)
1119
+ _new_month(@months_in_year * (+y) + m) + d
1120
+ end
1121
+
1122
+ # 通日 - > 年月日
1123
+ #
1124
+ # @param [Integer] sdn 通日
1125
+ #
1126
+ # @return [Array<Integer>] [ y, m, d ]
1127
+ # y 年
1128
+ # m 月 (0 始まり)
1129
+ # d 日 (0 始まり)
1130
+ #
1131
+ def _number_to_coordinates(sdn)
1132
+ m, d = Residue.mod(sdn) {|m| _new_month(m)}
1133
+ y, m = m.divmod(@months_in_year)
1134
+ return y, m, d
1135
+ end
1136
+
1137
+ # 暦要素数
1138
+ #
1139
+ # @overload _length(date)
1140
+ # @param [Array<Integer>] date ( y )
1141
+ #
1142
+ # y 年
1143
+ #
1144
+ # @return [Integer] その年の月数
1145
+ #
1146
+ # @overload _length(date)
1147
+ # @param [Array<Integer>] date ( y, m )
1148
+ #
1149
+ # y 年
1150
+ #
1151
+ # m (0 始まり)
1152
+ #
1153
+ # @return [Integer] その年月の日数
1154
+ #
1155
+ def _length(date)
1156
+ y, m = date
1157
+ if (m)
1158
+ # 指定した月に含まれる日の数を返します。
1159
+ m += @months_in_year * +y
1160
+ return _new_month(m+1) - _new_month(m)
1161
+ else
1162
+ # 指定した年に含まれる月の数を返します。
1163
+ return @months_in_year
1164
+ end
1165
+ end
1166
+
1167
+ # 暦要素数
1168
+ #
1169
+ # @param [Array<Numeric>] date ( y )
1170
+ #
1171
+ # y
1172
+ #
1173
+ # @return [Integer] その年の日数
1174
+ #
1175
+ def _sum_(date)
1176
+ y, = date
1177
+ m = @months_in_year * +y
1178
+ return _new_month(m+@months_in_year) - _new_month(m)
1179
+ end
1180
+
1181
+ private
1182
+
1183
+ # オブジェクトの正規化
1184
+ #
1185
+ # @months_in_year = 1年の月の数
1186
+ #
1187
+ def _normalize(args=[], options={})
1188
+ @months_in_year ||= 12
1189
+ @formula = When::Parts::Resource._instantiate(@formula)
1190
+ super
1191
+ end
1192
+ end
1193
+
1194
+ # 月日の配当が太陽の位置によって決定される太陽暦
1195
+ #
1196
+ # Calendar based on the ephemeris of the Sun
1197
+ #
1198
+ class EphemerisBasedSolar < EphemerisBased
1199
+
1200
+ #protected
1201
+
1202
+ # 月初の通日
1203
+ #
1204
+ # @param [Integer] m 通月
1205
+ #
1206
+ # @return [Integer] 月初の通日
1207
+ #
1208
+ def _new_month_(m)
1209
+ return solar_sdn(@formula[0].cn_to_time(m + @cycle_offset))
1210
+ end
1211
+
1212
+ private
1213
+
1214
+ # オブジェクトの正規化
1215
+ # cycle_offset = 位相のオフセット / 1か月分の角度
1216
+ # formula = 位相の計算に用いる太陽の Formula
1217
+ #
1218
+ def _normalize(args=[], options={})
1219
+ @cycle_offset ||= -1.5
1220
+ @formula ||= "Formula?formula=#{@months_in_year||12}S"
1221
+ super
1222
+ end
1223
+ end
1224
+
1225
+ # 月日の配当が月の位相によって決定される純太陰暦
1226
+ #
1227
+ # Calendar based on the ephemeris of the Moon
1228
+ #
1229
+ class EphemerisBasedLunar < EphemerisBased
1230
+
1231
+ include Lunar
1232
+
1233
+ #protected
1234
+
1235
+ # 月初の通日
1236
+ #
1237
+ # @param [Integer] m 通月
1238
+ #
1239
+ # @return [Integer] 月初の通日
1240
+ #
1241
+ def _new_month_(m)
1242
+ return lunar_sdn(@formula[-1].cn_to_time(m + @cycle_offset))
1243
+ end
1244
+
1245
+ private
1246
+
1247
+ # オブジェクトの正規化
1248
+ # cycle_offset = Goldstein Number に対する暦元の補正
1249
+ #
1250
+ def _normalize(args=[], options={})
1251
+ @cycle_offset ||= 1671 * 12 + 4
1252
+ super
1253
+ end
1254
+ end
1255
+
1256
+ # 月日の配当が太陽および月の位置によって決定される太陰太陽暦
1257
+ #
1258
+ # Calendar based on the ephemeris of the Sun and the Moon
1259
+ #
1260
+ class EphemerisBasedLuniSolar < EphemerisBasedSolar
1261
+
1262
+ include Lunar
1263
+
1264
+ # 計算方法
1265
+ # @return [Array<When::Ephemeris::Formula>]
1266
+ attr_reader :formula
1267
+
1268
+ #protected
1269
+
1270
+ # 年月日 -> 通日
1271
+ #
1272
+ # @param [Numeric] yy
1273
+ # @param [Integer] mm 月 (0 始まり)
1274
+ # @param [Integer] dd 日 (0 始まり)
1275
+ #
1276
+ # @return [Integer] 通日
1277
+ #
1278
+ def _coordinates_to_number(yy, mm, dd)
1279
+ _new_month(_new_year_month(+yy) + mm) + dd
1280
+ end
1281
+
1282
+ # 通日 - > 年月日
1283
+ #
1284
+ # @param [Integer] sdn 通日
1285
+ #
1286
+ # @return [Array<Integer>] ( y, m, d )
1287
+ # [ y ]
1288
+ # [ m 月 (0 始まり) ]
1289
+ # [ d 日 (0 始まり) ]
1290
+ #
1291
+ def _number_to_coordinates(sdn)
1292
+ nn, dd = Residue.mod(sdn) {|m| _new_month(m)}
1293
+ yy, mm = Residue.mod(nn) {|y| _new_year_month(y)}
1294
+ [yy, mm, dd]
1295
+ end
1296
+
1297
+ # 暦要素数
1298
+ #
1299
+ # @overload _length(date)
1300
+ # @param [Array<Integer>] date ( 年 )
1301
+ # @return [Integer] その年の月数
1302
+ #
1303
+ # @overload _length(date)
1304
+ # @param [Array<Integer>] date ( 年, 月 )
1305
+ # @note 月は 0 始まり
1306
+ # @return [Integer] その年月の日数
1307
+ #
1308
+ def _length(date)
1309
+ y, m = date
1310
+ if (m)
1311
+ # 指定した月に含まれる日の数を返します。
1312
+ m += _new_year_month(+y)
1313
+ return _new_month(m+1) - _new_month(m)
1314
+ else
1315
+ # 指定した年に含まれる月の数を返します。
1316
+ return _ids([y]).length
1317
+ end
1318
+ end
1319
+
1320
+ private
1321
+
1322
+ # 暦要素数
1323
+ #
1324
+ # @param [Array<Numeric>] date ( y )
1325
+ #
1326
+ # y
1327
+ #
1328
+ # @return [Integer] その年の日数
1329
+ #
1330
+ def _sum_(date)
1331
+ y = +date[0]
1332
+ return _new_month(_new_year_month(y+1)) - _new_month(_new_year_month(y))
1333
+ end
1334
+
1335
+ # 太陽月初の通日
1336
+ #
1337
+ #
1338
+ alias :_new_epoch_ :_new_month_
1339
+
1340
+ # 太陰月初の通日
1341
+ #
1342
+ # @param [Integer] m 通月
1343
+ #
1344
+ # @return [Integer] 月初の通日
1345
+ #
1346
+ def _new_month_(m)
1347
+ lunar_sdn(@formula[-1].cn_to_time(m))
1348
+ end
1349
+
1350
+ # 年初の通月
1351
+ #
1352
+ # @param [Integer] y 年
1353
+ #
1354
+ # @return [Integer] 年初の通月
1355
+ #
1356
+ def _new_year_month_(y)
1357
+ raise TypeError, 'EphemerisBasedLuniSolar is abstract class'
1358
+ end
1359
+
1360
+ # オブジェクトの正規化
1361
+ #
1362
+ # cycle_offset = 雨水の場合 -1
1363
+ # formula = 位相の計算に用いる太陽と月の Formula
1364
+ # notes = to_a でデフォルトとして用いる暦注
1365
+ #
1366
+ def _normalize(args=[], options={})
1367
+ @formula ||= ['Formula?formula=12S', 'Formula?formula=1L']
1368
+ super
1369
+ end
1370
+ end
1371
+
1372
+ #
1373
+ # 日時要素の境界 - Border
1374
+ #
1375
+ class Border < When::TM::ReferenceSystem
1376
+ #
1377
+ # 境界の振舞
1378
+ #
1379
+ # @return [Numeric]
1380
+ #
1381
+ # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
1382
+ #
1383
+ # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
1384
+ #
1385
+ def behavior
1386
+ @border[0]
1387
+ end
1388
+
1389
+ # 境界の取得
1390
+ #
1391
+ # @param [Array<Numeric>] date 境界を計算する年/日
1392
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1393
+ #
1394
+ # @return [Array<Numeric>] その年/日の境界
1395
+ #
1396
+ def border(date=[], frame=nil)
1397
+ last = date.length-1
1398
+ return @border if last<0
1399
+ bDate = date[0..last] + @border[(last+1)..-1]
1400
+ branch = @border[last] * 0
1401
+ bDate[last] = When::Coordinates::Pair.new(date[last] * 1, branch)
1402
+ return bDate
1403
+ end
1404
+
1405
+ # 境界の正規化
1406
+ #
1407
+ # @param [Array<Numeric>] date 境界を計算する年/日
1408
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1409
+ #
1410
+ # @return [Array<Numeric>] その年/日の境界
1411
+ #
1412
+ def _adjust_epoch(date, frame=nil)
1413
+ s_date = date.dup
1414
+ e_date = border([+date[0]], frame)
1415
+ branch = behavior * 0
1416
+ branch += 1 if (s_date[1..-1] <=> e_date[1..-1]) < 0
1417
+ s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
1418
+ return s_date
1419
+ end
1420
+
1421
+ # 日付の補正
1422
+ # @private
1423
+ def _date_adjust(source)
1424
+ source
1425
+ end
1426
+
1427
+ private
1428
+
1429
+ # 要素の正規化
1430
+ def _normalize(args=[], options={})
1431
+ @border = When::Coordinates::Pair._en_pair_date_time(@border) if (@border.kind_of?(String))
1432
+ end
1433
+ end
1434
+
1435
+ #
1436
+ # 日時要素の境界 - 年/日によって、異なる境界を使用する場合
1437
+ #
1438
+ class MultiBorder < Border
1439
+
1440
+ #
1441
+ # 境界の配列
1442
+ # @return [Array<When::CalendarTypes::Border>]
1443
+ attr_reader :borders
1444
+
1445
+ #
1446
+ # 境界の振舞
1447
+ #
1448
+ # @return [Numeric]
1449
+ #
1450
+ # Pair(-1,+1) - 暦年/暦日が進む(境界が前年/日にあり、境界後が当年/日の扱いになる)
1451
+ #
1452
+ # Pair( 0, 0) - 暦年/暦日が戻る(境界が当年/日にあり、境界前が前年/日の扱いになる)
1453
+ #
1454
+ def behavior
1455
+ @borders[0][:border].behavior
1456
+ end
1457
+
1458
+ # 境界の取得
1459
+ #
1460
+ # @param [Array<Numeric>] date 境界を計算する年/日
1461
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1462
+ #
1463
+ # @return [Array<Numeric>] その年/日の境界
1464
+ #
1465
+ def border(date=[], frame=nil)
1466
+ last = date.length-1
1467
+ return @borders[0][:boder] if (last<0)
1468
+ @borders.each do |border|
1469
+ return border[:border].border(date, frame) if date[0] >= border[:key]
1470
+ end
1471
+ date[0..last]
1472
+ end
1473
+
1474
+ # 境界の正規化
1475
+ #
1476
+ # @param [Array<Numeric>] date 境界を計算する年/日
1477
+ # @param [When::TM::ReferenceSystem] frame 使用する暦法/時法
1478
+ #
1479
+ # @return [Array<Numeric>] その年/日の境界
1480
+ #
1481
+ def _adjust_epoch(date, frame=nil)
1482
+ @borders.each do |border|
1483
+ next unless date[0] >= border[:key]
1484
+ s_date = date.dup
1485
+ e_date = border[:border].border(date[0..0], frame)
1486
+ branch = border[:border].behavior * 0
1487
+ branch += 1 if ((s_date[1..-1] <=> e_date[1..-1]) < 0)
1488
+ s_date[0] = When::Coordinates::Pair.new(+s_date[0]-branch, branch)
1489
+ return s_date
1490
+ end
1491
+ date
1492
+ end
1493
+
1494
+ private
1495
+
1496
+ # 要素の正規化
1497
+ def _normalize(args=[], options={})
1498
+ if @borders.kind_of?(String)
1499
+ list = @borders.split(/(\(.+?\))/)
1500
+ list.shift if list[0]==''
1501
+ list.unshift(-Float::MAX) unless list[0] =~ /\(/
1502
+ list.push('0-1-1') if list[-1] =~ /\(/
1503
+ @borders = []
1504
+ loop do
1505
+ key, border, *list = list
1506
+ break unless key
1507
+ key = $1.to_i if key.kind_of?(String) && /\((.+?)\)/ =~ key
1508
+ border = "_c:Border?border=#{border}" unless border =~ /\A[A-Z_]/i
1509
+ border = When.Calendar(border)
1510
+ @borders << {:key=>key, :border=>border}
1511
+ end
1512
+ end
1513
+ @borders = @borders.sort_by {|border| -border[:key]}
1514
+ end
1515
+ end
1516
+
1517
+ #
1518
+ # 日時要素の境界 - 日の出,日の入り
1519
+ #
1520
+ class DayBorder < Border
1521
+
1522
+ # 境界の取得
1523
+ #
1524
+ # @param [Array<Numeric>] date 境界を計算する日
1525
+ # @param [When::TM::ReferenceSystem] clock 使用する時法
1526
+ #
1527
+ # @return [Array<Numeric>] その日の境界
1528
+ #
1529
+ # @note 属性 @event によって境界を計算する (see {When::Ephemeris::Formula#day_event})
1530
+ #
1531
+ def border(date=[], clock=When::UTC)
1532
+ return @border unless date[0] && clock.formula
1533
+
1534
+ time =
1535
+ clock._number_to_coordinates(clock.second *
1536
+ clock.time_standard.from_dynamical_time(
1537
+ When::TM::JulianDate._d_to_t(
1538
+ clock.formula.first.day_event(
1539
+ clock.time_standard.to_dynamical_date(date[0] + @border[0]*0), @event, When.Resource('_ep:Sun'), @height
1540
+ ))))
1541
+
1542
+ time[0] += When::TM::JulianDate::JD19700101
1543
+ time[0] = When::Coordinates::Pair.new(time[0]-@border[0]*0, @border[0]*0) unless @border[0]*0 == 0
1544
+ clock._encode(time, false)
1545
+ end
1546
+
1547
+ # 日付の補正
1548
+ # @private
1549
+ def _date_adjust(source)
1550
+ source * 1 + @border[0] * 0
1551
+ end
1552
+ end
1553
+
1554
+ #
1555
+ # 日時要素の境界 - 日の出
1556
+ #
1557
+ class Sunrise < DayBorder
1558
+
1559
+ private
1560
+
1561
+ # 要素の正規化
1562
+ def _normalize(args=[], options={})
1563
+ @border = [0,0,0,0]
1564
+ @event = -1
1565
+ @height ||= '0'
1566
+ end
1567
+ end
1568
+
1569
+ #
1570
+ # 日時要素の境界 - 日の入り
1571
+ #
1572
+ class Sunset < DayBorder
1573
+
1574
+ private
1575
+
1576
+ # 要素の正規化
1577
+ def _normalize(args=[], options={})
1578
+ @border = [When::Coordinates::Pair.new(+1,-1),0,0,0]
1579
+ @event = +1
1580
+ @height ||= '0'
1581
+ end
1582
+ end
1583
+ end