when_exe 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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