when_exe 0.3.6 → 0.3.7

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