zakuro 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.gitignore +69 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +14 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +19 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +44 -0
  10. data/Rakefile +20 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/345/205/245/345/256/232/346/260/227.png +0 -0
  14. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/346/234/210/343/201/256/351/200/262/351/200/200.png +0 -0
  15. data/images/source/description.numbers +0 -0
  16. data/images//345/205/245/345/256/232/346/260/227/343/201/256/350/265/267/347/256/227.png +0 -0
  17. data/images//345/220/204/346/234/210/343/201/256/346/261/202/343/202/201/346/226/271.png +0 -0
  18. data/images//345/244/252/351/231/275/343/201/250/346/234/210.png +0 -0
  19. data/lib/zakuro.rb +9 -0
  20. data/lib/zakuro/condition.rb +239 -0
  21. data/lib/zakuro/cycle/abstract_remainder.rb +457 -0
  22. data/lib/zakuro/cycle/zodiac.rb +103 -0
  23. data/lib/zakuro/era/gengou/set-001-until-south.yaml +375 -0
  24. data/lib/zakuro/era/gengou/set-002-from-north.yaml +166 -0
  25. data/lib/zakuro/era/gengou/set-003-modern.yaml +12 -0
  26. data/lib/zakuro/era/japan.rb +630 -0
  27. data/lib/zakuro/era/western.rb +412 -0
  28. data/lib/zakuro/merchant.rb +57 -0
  29. data/lib/zakuro/output/error.rb +10 -0
  30. data/lib/zakuro/output/logger.rb +64 -0
  31. data/lib/zakuro/output/response.rb +170 -0
  32. data/lib/zakuro/output/result.rb +219 -0
  33. data/lib/zakuro/output/stringifier.rb +62 -0
  34. data/lib/zakuro/version.rb +7 -0
  35. data/lib/zakuro/version/abstract_version.rb +29 -0
  36. data/lib/zakuro/version/genka/genka.rb +19 -0
  37. data/lib/zakuro/version/gihou/gihou.rb +19 -0
  38. data/lib/zakuro/version/gregorio/gregorio.rb +19 -0
  39. data/lib/zakuro/version/houryaku/houryaku.rb +19 -0
  40. data/lib/zakuro/version/joukyou/joukyou.rb +19 -0
  41. data/lib/zakuro/version/kansei/kansei.rb +19 -0
  42. data/lib/zakuro/version/senmyou/README.md +586 -0
  43. data/lib/zakuro/version/senmyou/base/era.rb +81 -0
  44. data/lib/zakuro/version/senmyou/base/gengou.rb +210 -0
  45. data/lib/zakuro/version/senmyou/base/remainder.rb +60 -0
  46. data/lib/zakuro/version/senmyou/base/solar_term.rb +66 -0
  47. data/lib/zakuro/version/senmyou/base/year.rb +58 -0
  48. data/lib/zakuro/version/senmyou/monthly/lunar_phase.rb +220 -0
  49. data/lib/zakuro/version/senmyou/monthly/month.rb +112 -0
  50. data/lib/zakuro/version/senmyou/senmyou.rb +34 -0
  51. data/lib/zakuro/version/senmyou/stella/lunar_orbit.rb +332 -0
  52. data/lib/zakuro/version/senmyou/stella/solar_average.rb +192 -0
  53. data/lib/zakuro/version/senmyou/stella/solar_orbit.rb +398 -0
  54. data/lib/zakuro/version/senmyou/stella/winter_solstice.rb +106 -0
  55. data/lib/zakuro/version/senmyou/summary/annual_data.rb +186 -0
  56. data/lib/zakuro/version/senmyou/summary/gengou_data.rb +294 -0
  57. data/lib/zakuro/version/taien/taien.rb +19 -0
  58. data/lib/zakuro/version/tenpou/tenpou.rb +19 -0
  59. data/lib/zakuro/version_factory.rb +59 -0
  60. data/zakuro.gemspec +31 -0
  61. metadata +106 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../output/logger'
4
+
5
+ # :nodoc:
6
+ module Zakuro
7
+ # :nodoc:
8
+ module Senmyou
9
+ # :reek:TooManyConstants
10
+
11
+ #
12
+ # WinterSolstice 冬至
13
+ #
14
+ module WinterSolstice
15
+ # @return [Integer] 統法(1日=8400分)
16
+ DAY = 8400
17
+ # @return [Integer] 朔望月
18
+ SYNODIC_MONTH = 248_057
19
+ # @return [Integer] 一年
20
+ YEAR = 3_068_055
21
+ # @return [Integer] 通余: (YEAR - DAY * 12 * 30) = 44055
22
+ REMAINDER_ALL_YEAR = 44_055
23
+ # @return [Integer] 旬周(60日) 8400 * 60
24
+ SIXTY_DAYS = 504_000
25
+ # @return [Integer] 積年(甲子夜半朔旦冬至〜暦の開始前)
26
+ TOTAL_YEAR = 7_070_138
27
+ # @return [Integer] 暦の開始年(長慶2年)
28
+ BEGIN_YEAR = 822
29
+
30
+ # @return [Logger] ロガー
31
+ LOGGER = Logger.new(location: 'winter_solstice')
32
+
33
+ # :reek:TooManyStatements { max_statements: 6 }
34
+
35
+ #
36
+ # 対象年の前年の冬至を求める
37
+ #
38
+ # @param [Integer] western_year 西暦年
39
+ #
40
+ # @return [Remainder] 前年の冬至
41
+ #
42
+ def self.calc(western_year:)
43
+ # 積年の開始から対象年までの年数
44
+ total = TOTAL_YEAR + western_year - BEGIN_YEAR
45
+ remainder_year = total % SIXTY_DAYS
46
+
47
+ LOGGER.debug("[a01]:#{remainder_year}")
48
+
49
+ # 通余を使う
50
+ winter_solstice_minute = (remainder_year * REMAINDER_ALL_YEAR) % SIXTY_DAYS
51
+
52
+ LOGGER.debug("[a02]:#{winter_solstice_minute}")
53
+
54
+ Remainder.new(total: winter_solstice_minute)
55
+ end
56
+
57
+ # :reek:TooManyStatements { max_statements: 7 }
58
+
59
+ #
60
+ # 対象年の天正閏余(冬至より前にある11月経朔との差 = 月齢)を算出する
61
+ # 太陽と月の運動による補正値を算出し、その補正結果を返す
62
+ #
63
+ # @param [Integer] western_year 西暦年
64
+ #
65
+ # @return [Remainder] 天正閏余
66
+ #
67
+ def self.calc_moon_age(western_year:)
68
+ # 積年の開始から対象年までの年数
69
+ total = TOTAL_YEAR + western_year - BEGIN_YEAR
70
+
71
+ # 12朔望月に対する1年の余り(単位:分)
72
+ remainder_minute = YEAR - (SYNODIC_MONTH * 12)
73
+
74
+ # 朔望月に含まれなかった余り(単位:年)
75
+ remainder_year = total % SYNODIC_MONTH
76
+
77
+ LOGGER.debug("[b01]: #{remainder_year}")
78
+
79
+ # 天正閏余
80
+ winter_solstice_age = remainder_minute * remainder_year % SYNODIC_MONTH
81
+
82
+ LOGGER.debug("[b02]: #{winter_solstice_age}")
83
+
84
+ # 大余・小余に変換する
85
+ Remainder.new(total: winter_solstice_age)
86
+ end
87
+
88
+ #
89
+ # 11月経朔(冬至が含まれる月の1日)を求める
90
+ #
91
+ # @param [Integer] western_year 西暦年
92
+ #
93
+ # @return [Remainder] 11月経朔
94
+ #
95
+ def self.calc_averaged_last_november_1st(western_year:)
96
+ # 冬至
97
+ winter_solstice = calc(western_year: western_year)
98
+ # 天正閏余
99
+ winter_solstice_age = calc_moon_age(western_year: western_year)
100
+
101
+ # 11月経朔
102
+ winter_solstice.sub(winter_solstice_age)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../output/logger'
4
+ require_relative '../base/remainder'
5
+ require_relative '../monthly/month'
6
+ require_relative '../base/solar_term'
7
+ require_relative '../monthly/lunar_phase'
8
+ require_relative '../stella/solar_orbit'
9
+ require_relative '../stella/solar_average'
10
+ require_relative '../stella/lunar_orbit'
11
+
12
+ # :nodoc:
13
+ module Zakuro
14
+ # :nodoc:
15
+ module Senmyou
16
+ # AnnualData 年間データ
17
+ module AnnualData
18
+ # @return [Logger] ロガー
19
+ LOGGER = Logger.new(location: 'annual_data')
20
+
21
+ # :reek:TooManyStatements { max_statements: 6 }
22
+
23
+ #
24
+ # 11月定朔(冬至が含まれる月の1日:補正済)を求める
25
+ #
26
+ # @param [Integer] western_year 西暦年
27
+ #
28
+ # @return [Remainder] 11月定朔
29
+ #
30
+ def self.calc_last_november_1st(western_year:)
31
+ # 天正閏余
32
+ winter_solstice_age = \
33
+ WinterSolstice.calc_moon_age(western_year: western_year)
34
+ # 11月経朔
35
+ november_1st = \
36
+ WinterSolstice.calc_averaged_last_november_1st(western_year: western_year)
37
+ # 11月定朔
38
+
39
+ # 補正
40
+ correction_value = correction_value_on_last_november_1st(
41
+ winter_solstice_age: winter_solstice_age,
42
+ western_year: western_year
43
+ )
44
+
45
+ result = november_1st.add(Remainder.new(day: 0, minute: correction_value,
46
+ second: 0))
47
+ # 進朔
48
+ result.up_on_new_moon!
49
+ result
50
+ end
51
+
52
+ # :reek:TooManyStatements { max_statements: 6 }
53
+
54
+ #
55
+ # 一覧取得する
56
+ #
57
+ # * 対象年に対して、前年11月-当年11月までを出力する
58
+ # * 対象年(西暦)と計算年(元号x年)の紐付けは行わない
59
+ #
60
+ # @param [Integer] western_year 西暦年
61
+ #
62
+ # @return [Array<Month>] 1年データ
63
+ #
64
+ def self.collect_annual_data_after_last_november_1st(western_year:)
65
+ annual_data = initialized_annual_data(western_year: western_year)
66
+
67
+ apply_big_and_small_of_the_month(annual_data: annual_data)
68
+
69
+ SolarAverage.set_solar_terms_into_annual_data(western_year: western_year,
70
+ annual_data: annual_data)
71
+
72
+ # 月間隔を取得するためだけの末尾要素を削除
73
+ annual_data.pop
74
+
75
+ adjust_leap_month(annual_data: annual_data)
76
+
77
+ annual_data
78
+ end
79
+
80
+ #
81
+ # 11月定朔の補正値を求める
82
+ #
83
+ # @param [Remainder] winter_solstice_age 天正閏余
84
+ # @param [Integer] western_year 西暦年
85
+ #
86
+ # @return [Integer] 補正値
87
+ #
88
+ def self.correction_value_on_last_november_1st(winter_solstice_age:, western_year:)
89
+ # 補正
90
+ solar_term = SolarTerm.new(
91
+ remainder: winter_solstice_age
92
+ )
93
+ solar_term = \
94
+ SolarOrbit.calc_solar_term_by_remainder(
95
+ solar_term: solar_term
96
+ )
97
+
98
+ moon_remainder, is_forward = LunarOrbit.calc_moon_point(
99
+ remainder: winter_solstice_age, western_year: western_year
100
+ )
101
+
102
+ SolarOrbit.calc_sun_orbit_value(solar_term: solar_term) +
103
+ LunarOrbit.calc_moon_orbit_value(remainder_month: moon_remainder,
104
+ is_forward: is_forward)
105
+ end
106
+ private_class_method :correction_value_on_last_november_1st
107
+
108
+ #
109
+ # 1年データを取得する
110
+ #
111
+ # @param [Integer] western_year 西暦年
112
+ #
113
+ # @return [Array<Month>] 1年データ
114
+ #
115
+ def self.initialized_annual_data(western_year:)
116
+ result = []
117
+ lunar_phase = LunarPhase.new(western_year: western_year)
118
+
119
+ is_last_year = true
120
+
121
+ monthes = [11, 12] + [*1..12]
122
+
123
+ monthes.each do |month|
124
+ LOGGER.debug('---', "month: #{month}", "is_last_year: #{is_last_year}")
125
+
126
+ adjusted = lunar_phase.next_month
127
+
128
+ result.push(
129
+ Month.new(is_last_year: is_last_year, number: month,
130
+ remainder: adjusted, phase_index: 0)
131
+ )
132
+ is_last_year = false if month == 12
133
+ end
134
+ result
135
+ end
136
+ private_class_method :initialized_annual_data
137
+
138
+ #
139
+ # 1年データの各月に月の大小を設定する
140
+ #
141
+ # @param [Array<Month>] annual_data 1年データ
142
+ #
143
+ def self.apply_big_and_small_of_the_month(annual_data:)
144
+ size = annual_data.size - 1
145
+ (0...size).each do |idx|
146
+ current_month = annual_data[idx]
147
+ next_month = annual_data[idx + 1]
148
+ current_month.is_many_days = \
149
+ current_month.remainder.same_remainder_divided_by_ten?(
150
+ other: next_month.remainder.day
151
+ )
152
+ end
153
+ end
154
+ private_class_method :apply_big_and_small_of_the_month
155
+
156
+ # :reek:TooManyStatements { max_statements: 10 }
157
+
158
+ #
159
+ # 閏月が存在した場合に以降の月を1つずつ減らす
160
+ # @example 7,8,9 と続く月の8月が閏の場合、7, 閏7, 8 となる
161
+ #
162
+ # @param [Array<Month>] annual_data 1年データ
163
+ #
164
+ def self.adjust_leap_month(annual_data:)
165
+ # 閏による月の再調整を行う
166
+ leaped = false
167
+ annual_data.each do |month|
168
+ if month.even_term.invalid?
169
+ month.leaped = true
170
+ leaped = true
171
+ end
172
+ next unless leaped
173
+
174
+ # NOTE: 常気法では閏月は2-3年に一度のため、1年に二度発生しない前提
175
+ number = month.number - 1
176
+ if number <= 0
177
+ month.is_last_year = true
178
+ number = 12
179
+ end
180
+ month.number = number
181
+ end
182
+ end
183
+ private_class_method :adjust_leap_month
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,294 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../era/western'
4
+ require_relative 'annual_data'
5
+ require_relative '../../../output/response'
6
+ require_relative '../base/era'
7
+ require_relative '../base/gengou'
8
+ require_relative '../base/year'
9
+
10
+ # :nodoc:
11
+ module Zakuro
12
+ # :nodoc:
13
+ module Senmyou
14
+ #
15
+ # GengouData 元号の年情報
16
+ #
17
+ module GengouData
18
+ def self.get_ancient_date(date: Western::Calendar.new)
19
+ SingleDay.get_ancient_date(date: date)
20
+ end
21
+
22
+ #
23
+ # SingleDay 1日データ
24
+ #
25
+ module SingleDay
26
+ #
27
+ # 1日データを取得する
28
+ #
29
+ # @param [Western::Calendar] date 年月日情報(西暦)
30
+ #
31
+ # @return [Response::SingleDay] 1日データ
32
+ #
33
+ def self.get_ancient_date(date: Western::Calendar.new)
34
+ raise ArgumentError, 'invalid senmyou date' unless Era.include?(date: date)
35
+
36
+ target = get_target(year_list: get_year_list(date: date), date: date)
37
+
38
+ Response::SingleDay.save_single_day(
39
+ param: Response::SingleDay::Param.new(
40
+ year: target.year, month: target.month,
41
+ date: target.date, days: target.days
42
+ )
43
+ )
44
+ end
45
+
46
+ #
47
+ # Target 対象
48
+ #
49
+ class Target
50
+ # @return [Year] 年
51
+ attr_reader :year
52
+ # @return [Month] 月
53
+ attr_reader :month
54
+ # @return [Western::Calendar] 日
55
+ attr_reader :date
56
+ # @return [Integer] 月初からの日数
57
+ attr_reader :days
58
+
59
+ #
60
+ # 初期化
61
+ #
62
+ # @param [Year] year 年
63
+ # @param [Month] month 月
64
+ # @param [Western::Calendar] date 日
65
+ # @param [Integer] days 月初からの日数
66
+ #
67
+ def initialize(year:, month:, date:, days:)
68
+ @year = year
69
+ @month = month
70
+ @date = date
71
+ @days = days
72
+ end
73
+ end
74
+
75
+ #
76
+ # MonthSearchResult 1年内に該当する月を検索した結果
77
+ #
78
+ class MonthSearchResult
79
+ # @return [Month] 月
80
+ attr_reader :target_month
81
+ # @return [Western::Calendar] 月初日
82
+ attr_reader :first_date
83
+ # @return [True] 該当データ引き当て済
84
+ # @return [False] 該当データ引き当てなし
85
+ attr_reader :breaked
86
+
87
+ #
88
+ # 初期化
89
+ #
90
+ # @param [Month] target_month 月
91
+ # @param [Western::Calendar] first_date 月初日
92
+ # @param [True, False] breaked 該当データ引き当て
93
+ #
94
+ def initialize(target_month: Month.new,
95
+ first_date: Western::Calendar.new, breaked:)
96
+ @target_month = target_month
97
+ @first_date = first_date
98
+ @breaked = breaked
99
+ end
100
+ end
101
+
102
+ #
103
+ # 指定日に関連する暦の範囲だけ年データを生成する
104
+ #
105
+ # @param [Western::Calendar] date 西暦日
106
+ #
107
+ # @return [Array<Year>] 年データ
108
+ #
109
+ def self.get_year_list(date:)
110
+ # 元号の最初の年月日を取る
111
+ gengou = Gengou.new(date: date.clone)
112
+
113
+ # 南北朝には元号が2つ並び、必ずしも範囲が重ならないケースもある
114
+ # 対象日付に重なる元号から論理和の範囲を取り、処理漏れがないようにする
115
+ start_date = gengou.first_date.clone
116
+ # 最後の年は翌年から今年の冬至を求めるため範囲を1年広げる
117
+ newest_date = gengou.choise_newest_gengou_date.next_year
118
+
119
+ Series.get_year_list(
120
+ start_gengou: gengou, start_date: start_date, end_date: newest_date
121
+ )
122
+ end
123
+
124
+ #
125
+ # 対象を引き当てる
126
+ #
127
+ # @param [Array<Year>] year_list 年データ
128
+ # @param [Western::Calendar] date 西暦日
129
+ #
130
+ # @return [Target] 対象
131
+ #
132
+ def self.get_target(year_list:, date:)
133
+ # 対象年/対象月を引き当てる
134
+ year_list.each do |year|
135
+ result = get_target_month(date: date, year: year)
136
+
137
+ next unless result.breaked
138
+
139
+ return Target.new(
140
+ year: year, month: result.target_month, date: date,
141
+ # 対象日に調整する
142
+ days: date - result.first_date
143
+ )
144
+ end
145
+
146
+ raise ArgumentError, "invalid range: #{date.format}"
147
+ end
148
+
149
+ # :reek:TooManyStatements { max_statements: 6 }
150
+
151
+ #
152
+ # 対象の月を引き当てる
153
+ #
154
+ # @param [Western::Calendar] date 西暦日
155
+ # @param [Year] year 年
156
+ #
157
+ # @return [MonthSearchResult] 1年内に該当する月を検索した結果
158
+ #
159
+ def self.get_target_month(date:, year:)
160
+ first_date = year.gengou.first_date.clone
161
+ year.months.each do |month|
162
+ # 月末の日付を超える場合
163
+ next_first_date = first_date.clone + month.days
164
+ if date < next_first_date
165
+ return MonthSearchResult.new(target_month: month,
166
+ first_date: first_date, breaked: true)
167
+ end
168
+ first_date = next_first_date
169
+ end
170
+ MonthSearchResult.new(breaked: false)
171
+ end
172
+ end
173
+
174
+ #
175
+ # Series 和暦時系列データ
176
+ #
177
+ module Series
178
+ # :reek:TooManyInstanceVariables { max_instance_variables: 6 }
179
+
180
+ #
181
+ # TimeAdvance 時間進行
182
+ #
183
+ class TimeAdvance
184
+ #
185
+ # 初期化
186
+ #
187
+ # @param [Gengou] start_gengou 開始元号
188
+ # @param [Western::Calendar] start_date 開始日
189
+ # @param [Western::Calendar] end_date 終了日
190
+ #
191
+ def initialize(start_gengou:, start_date:, end_date:)
192
+ @current_date = start_date.clone
193
+ @end_date = end_date
194
+ @year_list = []
195
+ @western_year = start_date.year
196
+ @year = Year.new(gengou: start_gengou)
197
+ end
198
+
199
+ #
200
+ # 年データを収集する
201
+ #
202
+ # @return [Array<Year>] 年データ
203
+ #
204
+ def collect_year_list
205
+ push_current_year
206
+
207
+ # 2年目以降
208
+ while @current_date <= @end_date
209
+ compensate_last_year
210
+
211
+ push_current_year
212
+ end
213
+
214
+ @year_list
215
+ end
216
+
217
+ private
218
+
219
+ #
220
+ # 1年データを収集する
221
+ #
222
+ # @return [Array<Month>] 1年データ
223
+ #
224
+ def collect_annual_data
225
+ AnnualData.collect_annual_data_after_last_november_1st(
226
+ western_year: @western_year
227
+ )
228
+ end
229
+
230
+ #
231
+ # 昨年データの不足分を補完する
232
+ # 昨年11月1日から今年1月1日の前日のデータを1年前データとする
233
+ #
234
+ # @return [<Type>] <description>
235
+ #
236
+ def compensate_last_year
237
+ last_year = @year_list[@year_list.size - 1]
238
+ collect_annual_data.each do |month|
239
+ next unless month.is_last_year
240
+
241
+ # 昨年の月データのみ
242
+ last_year.push(month: month)
243
+ @current_date += month.days
244
+ end
245
+ end
246
+
247
+ # :reek:TooManyStatements { max_statements: 7 }
248
+
249
+ #
250
+ # 当年データを生成する
251
+ #
252
+ def push_current_year
253
+ next_year
254
+ collect_annual_data.each do |month|
255
+ next if month.is_last_year
256
+
257
+ @year.push(month: month)
258
+ @current_date += month.days
259
+ end
260
+ @year_list.push(@year)
261
+
262
+ @western_year += 1
263
+ end
264
+
265
+ #
266
+ # 次の元号年を取得する
267
+ #
268
+ def next_year
269
+ return if @year_list.empty?
270
+
271
+ @year = Year.new(gengou: @year.gengou.next_year)
272
+ end
273
+ end
274
+
275
+ #
276
+ # 期間内の年データ全てを返す
277
+ #
278
+ # @param [Gengou] start_gengou 開始元号
279
+ # @param [Western::Calendar] start_date 開始日
280
+ # @param [Western::Calendar] end_date 終了日
281
+ #
282
+ # @return [Array<Year>] 年データ
283
+ #
284
+ def self.get_year_list(start_gengou:, start_date:, end_date:)
285
+ advance = TimeAdvance.new(
286
+ start_gengou: start_gengou, start_date: start_date, end_date: end_date
287
+ )
288
+
289
+ advance.collect_year_list
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end