when_exe 0.4.1 → 0.4.2

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