wareki 1.1.0 → 2.1.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.
data/lib/wareki/common.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'wareki/calendar_def'
3
+ require 'wareki/calendar'
4
4
  require 'wareki/era_def'
5
5
  require 'date'
6
6
  # Wareki module common constants definitions
@@ -9,8 +9,9 @@ module Wareki
9
9
  GREGORIAN_START_YEAR = 1873
10
10
  IMPERIAL_START = 1_480_041 # Date.new(-660, 2, 11, Date::GREGORIAN).jd
11
11
  IMPERIAL_START_YEAR = -660
12
+ WESTERN_ERA_NAMES = ['', '西暦', '紀元前'].freeze
13
+ IMPERIAL_ERA_NAMES = %w(皇紀 神武天皇即位紀元).freeze
12
14
  DATE_INFINITY = ::Date.new(280_000_000, 12, 31) # Use 280000000 for jruby limitation...
13
- YEAR_BY_NUM = Hash[*YEAR_DEFS.map { |y| [y.year, y] }.flatten].freeze
14
15
  KANJI_VARIANTS = {
15
16
  '宝' => '寳',
16
17
  '霊' => '靈',
@@ -42,8 +43,8 @@ module Wareki
42
43
  NORMALIZE_KANJI_VARIANTS_HASH = KANJI_VARIANTS.each_with_object({}) { |(n, s), h| s.each_char { |c| h[c] = n } }
43
44
  NORMALIZE_KANJI_VARIANTS = ->(str) { str.gsub(NORMALIZE_KANJI_VARIANTS_REGEX, NORMALIZE_KANJI_VARIANTS_HASH) }
44
45
  ERA_BY_NAME = Hash[*(ERA_NORTH_DEFS + ERA_DEFS).flat_map { |g| [g.name, g] }]
45
- ERA_BY_NAME['皇紀'] = ERA_BY_NAME['神武天皇即位紀元'] = Era.new('皇紀', -660, 1_480_041, DATE_INFINITY.jd)
46
- ERA_BY_NAME['西暦'] = ERA_BY_NAME[''] = Era.new('西暦', 1, 1_721_424, DATE_INFINITY.jd)
46
+ ERA_BY_NAME['皇紀'] = ERA_BY_NAME['神武天皇即位紀元'] = Era.new('皇紀', -660, 1_480_041, DATE_INFINITY.jd).freeze
47
+ ERA_BY_NAME['西暦'] = ERA_BY_NAME[''] = Era.new('西暦', 1, 1_721_424, DATE_INFINITY.jd).freeze
47
48
  ERA_BY_NAME.default_proc = ->(hash, key) { hash.fetch(SQUARE_ERAS[key] || NORMALIZE_KANJI_VARIANTS[key], nil) }
48
49
  ERA_BY_NAME.freeze
49
50
  ERA_REGEX = Regexp.new(
@@ -58,17 +59,35 @@ module Wareki
58
59
  (?:(?<era_name>紀元前|#{ERA_REGEX})?
59
60
  (?:(?<year>[元#{NUM_CHARS}]+)年))?
60
61
  (?:(?<is_leap>閏|潤|うるう)?
61
- (?:(?<month>[正#{NUM_CHARS}]+) |
62
+ (?:(?<month>[正#{NUM_CHARS}]+)(?<is_leap_post>['’])?月 |
62
63
  (?<alt_month>#{ALT_MONTH_NAME.join('|')})))?
63
64
  (?:(?<day>[元朔晦#{NUM_CHARS}]+)日|元旦)?
64
65
  }x.freeze
66
+ # REGEX が空でないマッチを返すには 年/月/日/元旦 か、「月」を含まない
67
+ # 月の別名 (弥生・師走) のいずれかが必要。それ以外は素の Date.parse に
68
+ # 直行させて monkey patch のオーバーヘッドを避ける。
69
+ PARSE_QUICK_FILTER = /[年月日]|元旦|弥生|師走/.freeze
70
+ TIME_REGEX = %r{
71
+ (?<noon>正午) |
72
+ (?:(?<ampm>午前|午後)[[:space:]]*)?
73
+ (?<hour>[#{NUM_CHARS}]+)[[:space:]]*時
74
+ (?:[[:space:]]*
75
+ (?:(?<half>半) |
76
+ (?<min>[#{NUM_CHARS}]+)[[:space:]]*分
77
+ (?:[[:space:]]*(?<sec>[#{NUM_CHARS}]+)[[:space:]]*秒)?))?
78
+ }x.freeze
79
+ # TIME_REGEX が非空マッチしうるのは「時」か「正午」を含む文字列のみ
80
+ TIME_PARSE_QUICK_FILTER = /時|正午/.freeze
65
81
 
66
82
  class UnsupportedDateRange < StandardError; end
83
+ class InvalidDate < ArgumentError; end
67
84
 
68
85
  module_function
69
86
 
70
87
  def parse_to_date(str, start = ::Date::ITALY)
71
88
  Date.parse(str).to_date(start)
89
+ rescue InvalidDate
90
+ raise
72
91
  rescue ArgumentError
73
92
  ::Date.parse(str, true, start)
74
93
  end
data/lib/wareki/date.rb CHANGED
@@ -8,33 +8,25 @@ require 'wareki/kansuji'
8
8
  module Wareki
9
9
  # Wareki date handling class, main implementation.
10
10
  class Date
11
- attr_accessor :year, :month, :day, :era_year, :era_name
11
+ include Comparable
12
+
13
+ attr_reader :year, :month, :day, :era_year, :era_name
12
14
 
13
15
  def self.today
14
16
  jd(::Date.today.jd)
15
17
  end
16
18
 
17
- def self._check_invalid_date(era, year, month, day)
18
- month == 12 or return true
19
- day > 2 or return true
20
- (era == '明治' && year == 5 ||
21
- %w(皇紀 神武天皇即位紀元).member?(era) &&
22
- year == GREGORIAN_START_YEAR - IMPERIAL_START_YEAR - 1) and
23
- return false
24
- true
25
- end
26
-
27
19
  def self._parse(str)
28
20
  str = str.to_s.gsub(/[[:space:]]/, '')
29
21
  match = REGEX.match(str)
30
- match && !match[0].empty? or
22
+ (match && !match[0].empty?) or
31
23
  raise ArgumentError, "Invaild Date: #{str}"
32
24
  era = match[:era_name]
33
25
  if (era.nil? || era == '') && match[:year].nil?
34
26
  year = Date.today.year
35
27
  else
36
- (year = Utils.k2i(match[:year])) > 0 or
37
- raise ArgumentError, "Invalid year: #{str}"
28
+ year = Utils.k2i(match[:year])
29
+ year > 0 or raise ArgumentError, "Invalid year: #{str}"
38
30
  end
39
31
  month = day = 1
40
32
 
@@ -48,20 +40,20 @@ module Wareki
48
40
  end
49
41
 
50
42
  month > 12 || month < 1 and
51
- raise ArgumentError, "Invalid month: #{str}"
43
+ raise InvalidDate, "invalid date (month out of range): #{str}"
44
+
45
+ is_leap = !!(match[:is_leap] || match[:is_leap_post])
52
46
 
53
47
  if match[:day]
54
48
  if match[:day] == '晦'
55
- day = Utils.last_day_of_month(ERA_BY_NAME[era].year + year - 1, month, match[:is_leap])
49
+ civil_year = Utils.era_year_to_civil(era, year)
50
+ day = Utils.last_day_of_era_month(era, civil_year, month, is_leap)
56
51
  else
57
52
  day = Utils.k2i(match[:day])
58
53
  end
59
54
  end
60
55
 
61
- _check_invalid_date(era, year, month, day) or
62
- raise ArgumentError, "Invaild Date: #{str}"
63
-
64
- {era: era, year: year, month: month, day: day, is_leap: !!match[:is_leap]}
56
+ {era: era, year: year, month: month, day: day, is_leap: is_leap}
65
57
  end
66
58
 
67
59
  def self.parse(str)
@@ -87,23 +79,13 @@ module Wareki
87
79
  end
88
80
 
89
81
  def initialize(era_name, era_year, month = 1, day = 1, is_leap_month = false)
90
- raise ArgumentError, "Undefined era '#{era_name}'" if
91
- era_name.to_s != '' && era_name != '紀元前' && !ERA_BY_NAME[era_name]
92
-
82
+ @era_name = era_name.to_s
83
+ @era_year = era_year
93
84
  @month = month
94
85
  @day = day
95
86
  @is_leap_month = is_leap_month
96
- @era_name = era_name
97
- @era_year = era_year
98
- if era_name.to_s == '' || era_name == '西暦'
99
- @year = @era_year
100
- elsif era_name.to_s == '紀元前'
101
- @year = -@era_year
102
- elsif %w(皇紀 神武天皇即位紀元).include? era_name
103
- @year = era_year + IMPERIAL_START_YEAR
104
- else
105
- @year = ERA_BY_NAME[era_name].year + era_year - 1
106
- end
87
+ @year = Utils.era_year_to_civil(@era_name, @era_year)
88
+ _validate_date!
107
89
  end
108
90
 
109
91
  def imperial_year
@@ -111,7 +93,7 @@ module Wareki
111
93
  end
112
94
 
113
95
  def imperial_year=(v)
114
- @year = v - IMPERIAL_START_YEAR
96
+ self.year = v + IMPERIAL_START_YEAR
115
97
  end
116
98
 
117
99
  def leap_month?
@@ -119,36 +101,92 @@ module Wareki
119
101
  end
120
102
 
121
103
  def leap_month=(v)
104
+ @jd = nil
122
105
  @is_leap_month = v
123
106
  end
124
107
 
108
+ def year=(v)
109
+ era_year = Utils.civil_to_era_year(@era_name, v)
110
+ @jd = nil
111
+ @year = v
112
+ @era_year = era_year
113
+ end
114
+
115
+ def month=(v)
116
+ @jd = nil
117
+ @month = v
118
+ end
119
+
120
+ def day=(v)
121
+ @jd = nil
122
+ @day = v
123
+ end
124
+
125
+ def era_year=(v)
126
+ year = Utils.era_year_to_civil(@era_name, v)
127
+ @jd = nil
128
+ @era_year = v
129
+ @year = year
130
+ end
131
+
132
+ def era_name=(v)
133
+ era_name = v.to_s
134
+ year = Utils.era_year_to_civil(era_name, @era_year)
135
+ @jd = nil
136
+ @era_name = era_name
137
+ @year = year
138
+ end
139
+
125
140
  def __set_jd(v)
126
141
  @jd = v
127
142
  end
128
143
 
129
144
  def month_index
130
145
  return month - 1 if
131
- ['', '西暦', '紀元前'].include?(@era_name) || @year >= GREGORIAN_START_YEAR
146
+ WESTERN_ERA_NAMES.include?(@era_name) || @year >= GREGORIAN_START_YEAR
132
147
 
133
- yobj = YEAR_BY_NUM[@year] or
148
+ Calendar.covers_year?(@year) or
134
149
  raise UnsupportedDateRange, "Cannot get year info of #{inspect}"
135
- idx = month - 1
136
- idx += 1 if leap_month? || yobj.leap_month && month > yobj.leap_month
137
- idx
150
+ Calendar.month_index(@year, month, leap_month?)
151
+ end
152
+
153
+ def last_day_of_month
154
+ Utils.last_day_of_era_month(@era_name, @year, month, leap_month?)
155
+ end
156
+
157
+ def _validate_date!
158
+ (month.is_a?(Integer) && month >= 1 && month <= 12) or
159
+ raise InvalidDate, "invalid date (month out of range): #{inspect}"
160
+ (day.is_a?(Integer) && day >= 1) or
161
+ raise InvalidDate, "invalid date (day out of range): #{inspect}"
162
+ if !WESTERN_ERA_NAMES.include?(@era_name) && @year < GREGORIAN_START_YEAR
163
+ # 暦テーブル外の年は従来どおり jd 変換時の UnsupportedDateRange に委ねる
164
+ Calendar.covers_year?(@year) or return
165
+ !leap_month? || Calendar.leap_month(@year) == month or
166
+ raise InvalidDate, "invalid date (no leap month): #{inspect}"
167
+ day <= Calendar.last_day_of_month(@year, month, leap_month?) or
168
+ raise InvalidDate, "invalid date (day out of range): #{inspect}"
169
+ else
170
+ leap_month? and
171
+ raise InvalidDate, "invalid date (no leap month): #{inspect}"
172
+ day <= last_day_of_month or
173
+ raise InvalidDate, "invalid date (day out of range): #{inspect}"
174
+ end
138
175
  end
139
176
 
140
177
  def jd
141
178
  @jd and return @jd
142
179
 
143
- ['', '西暦', '紀元前'].include?(@era_name) and
180
+ _validate_date!
181
+ WESTERN_ERA_NAMES.include?(@era_name) and
144
182
  return @jd = ::Date.new(@year, month, day, ::Date::ITALY).jd
145
183
 
146
184
  @year >= GREGORIAN_START_YEAR and
147
185
  return @jd = ::Date.new(@year, month, day, ::Date::GREGORIAN).jd
148
186
 
149
- yobj = YEAR_BY_NUM[@year] or
187
+ @jd = Calendar.to_jd(@year, month, day, leap_month?) or
150
188
  raise UnsupportedDateRange, "Cannot convert to jd #{inspect}"
151
- @jd = yobj.month_starts[month_index] + day - 1
189
+ @jd
152
190
  end
153
191
 
154
192
  def to_date(start = ::Date::ITALY)
@@ -159,61 +197,72 @@ module Wareki
159
197
  to_date.to_time
160
198
  end
161
199
 
200
+ FORMAT_DIRECTIVE_REGEX = /%J(-|[_0]{0,2}[0-9]*|)([fFyYegGoOiImMsSlLdD][kK]?)/.freeze
201
+ FORMAT_EXPANSION_REGEX = /(?<!%)(?:%%)*\K#{FORMAT_DIRECTIVE_REGEX}/.freeze
202
+
203
+ def expand_wareki_format(format_str)
204
+ format_str.to_str.gsub(FORMAT_EXPANSION_REGEX) { format($2, $1) || $& }
205
+ end
206
+
162
207
  def strftime(format_str = '%JF')
163
- ret = format_str.to_str.gsub(/%J([fFyYegGoOiImMsSlLdD][kK]?)/) { format($1) || $& }
208
+ ret = expand_wareki_format(format_str)
164
209
  ret.index('%') or return ret
165
210
  d = to_date
166
211
  d.respond_to?(:_wareki_strftime_orig) ? d._wareki_strftime_orig(ret) : d.strftime(ret)
167
212
  end
168
213
 
169
- def format(key)
214
+ def _number_format(opt)
215
+ Utils.number_format(opt)
216
+ end
217
+
218
+ def format(key, opt = '')
170
219
  case key.to_sym
171
220
  when :e then era_name
172
- when :g then era_name.to_s == '' ? '' : era_year
221
+ when :g then era_name.to_s == '' ? '' : Kernel.format(_number_format(opt), era_year)
173
222
  when :G then era_name.to_s == '' ? '' : Utils.i2z(era_year)
174
- when :Gk then era_name.to_s == '' ? '' : YaKansuji.to_kan(era_year, :simple)
223
+ when :Gk then era_name.to_s == '' ? '' : Utils.to_simple_kan(era_year)
175
224
  when :GK
176
225
  if era_name.to_s == ''
177
226
  ''
178
227
  elsif era_year == 1
179
228
  '元'
180
229
  else
181
- YaKansuji.to_kan(era_year, :simple)
230
+ Utils.to_simple_kan(era_year)
182
231
  end
183
232
  when :o then year
184
233
  when :O then Utils.i2z(year)
185
- when :Ok then YaKansuji.to_kan(year, :simple)
234
+ when :Ok then Utils.to_simple_kan(year)
186
235
  when :i then imperial_year
187
236
  when :I then Utils.i2z(imperial_year)
188
- when :Ik then YaKansuji.to_kan(imperial_year, :simple)
189
- when :s then month
237
+ when :Ik then Utils.to_simple_kan(imperial_year)
238
+ when :s then Kernel.format(_number_format(opt), month)
190
239
  when :S then Utils.i2z(month)
191
- when :Sk then YaKansuji.to_kan(month, :simple)
240
+ when :Sk then Utils.to_simple_kan(month)
192
241
  when :SK then Utils.alt_month_name(month)
193
242
  when :l then leap_month? ? "'" : ''
194
243
  when :L then leap_month? ? '’' : ''
195
244
  when :Lk then leap_month? ? '閏' : ''
196
- when :d then day
245
+ when :d then Kernel.format(_number_format(opt), day)
197
246
  when :D then Utils.i2z(day)
198
- when :Dk then YaKansuji.to_kan(day, :simple)
247
+ when :Dk then Utils.to_simple_kan(day)
199
248
  when :DK
200
249
  if month == 1 && !leap_month? && day == 1
201
250
  '元'
202
251
  elsif day == 1
203
252
  '朔'
204
- elsif day == Utils.last_day_of_month(year, month, leap_month?)
253
+ elsif day == last_day_of_month
205
254
  '晦'
206
255
  else
207
- YaKansuji.to_kan(day, :simple)
256
+ Utils.to_simple_kan(day)
208
257
  end
209
- when :m then "#{format(:s)}#{format(:l)}"
258
+ when :m then "#{format(:s, opt)}#{format(:l)}"
210
259
  when :M then "#{format(:Lk)}#{format(:S)}"
211
260
  when :Mk then "#{format(:Lk)}#{format(:Sk)}"
212
- when :y then "#{format(:e)}#{format(:g)}"
261
+ when :y then "#{format(:e)}#{format(:g, opt)}"
213
262
  when :Y then "#{format(:e)}#{format(:G)}"
214
263
  when :Yk then "#{format(:e)}#{format(:Gk)}"
215
264
  when :YK then "#{format(:e)}#{format(:GK)}"
216
- when :f then "#{format(:e)}#{format(:g)}年#{format(:s)}#{format(:l)}月#{format(:d)}日"
265
+ when :f then "#{format(:e)}#{format(:g, opt)}年#{format(:s, opt)}#{format(:l)}月#{format(:d, opt)}日"
217
266
  when :F then "#{format(:e)}#{format(:GK)}年#{format(:Lk)}#{format(:Sk)}月#{format(:Dk)}日"
218
267
  end
219
268
  end
@@ -239,20 +288,48 @@ module Wareki
239
288
  true
240
289
  end
241
290
 
291
+ def hash
292
+ [self.class, @era_name, @era_year, @year, @month, @day, leap_month?].hash
293
+ end
294
+
295
+ def <=>(other)
296
+ ojd = _jd_if_date_like(other)
297
+ ojd = other if ojd.nil? && other.is_a?(Numeric)
298
+ ojd.nil? and return nil
299
+ jd <=> ojd
300
+ end
301
+
302
+ def succ
303
+ self + 1
304
+ end
305
+
242
306
  def -(other)
243
- self.class.jd jd - _to_jd_for_calc(other)
307
+ n = _to_days(other)
308
+ n.nil? or return self.class.jd(jd - n)
309
+ ojd = _jd_if_date_like(other)
310
+ ojd and return jd - ojd
311
+ raise TypeError, "Cannot subtract #{other.inspect} from Wareki::Date"
244
312
  end
245
313
 
246
314
  def +(other)
247
- self.class.jd jd + _to_jd_for_calc(other)
315
+ n = _to_days(other)
316
+ n.nil? and raise TypeError, "Cannot add #{other.inspect} to Wareki::Date"
317
+ self.class.jd(jd + n)
248
318
  end
249
319
 
250
- def _to_jd_for_calc(other)
251
- other.class.to_s == 'ActiveSupport::Duration' and
252
- raise NotImplementedError, 'Date calcration with ActiveSupport::Duration currently is not supported. Please use numeric.'
253
- other.respond_to?(:to_date) and other = other.to_date
254
- other.respond_to?(:jd) and other = other.jd
255
- other
320
+ def _to_days(other)
321
+ # rubocop:disable Style/ClassEqualityComparison
322
+ return other.in_days if other.class.name == 'ActiveSupport::Duration'
323
+ # rubocop:enable Style/ClassEqualityComparison
324
+
325
+ other.is_a?(Numeric) ? other : nil
326
+ end
327
+
328
+ def _jd_if_date_like(other)
329
+ return other.jd if other.respond_to?(:jd)
330
+
331
+ other.respond_to?(:to_date) && !other.is_a?(Numeric) and other = other.to_date
332
+ other.respond_to?(:jd) ? other.jd : nil
256
333
  end
257
334
  end
258
335
  end
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
  # coding: utf-8
3
+
4
+ # Generated by build-util/gen-era-def.rb from manakai data-locale era-defs.json.
5
+ # Do not edit this file directly.
3
6
  module Wareki
4
7
  DAY_MAX = 1684383730585466947585
5
8
  Era = Struct.new(:name, :year, :start, :end)
6
9
  ERA_DEFS = [
7
10
  Era.new("大化", 645, 1956842, 1958551),
8
- Era.new("白雉", 650, 1958551, 1960259),
9
- Era.new("朱鳥", 686, 1971845, 1971893),
11
+ Era.new("白雉", 650, 1958551, 1960339),
12
+ Era.new("朱鳥", 686, 1971845, 1972033),
10
13
  Era.new("大宝", 701, 1977221, 1978361),
11
14
  Era.new("慶雲", 704, 1978361, 1979692),
12
15
  Era.new("和銅", 708, 1979692, 1982487),
@@ -163,9 +166,9 @@ module Wareki
163
166
  Era.new("建武", 1334, 2208365, 2209133),
164
167
  Era.new("延元", 1336, 2209133, 2210638),
165
168
  Era.new("興国", 1340, 2210638, 2213069),
166
- Era.new("正平", 1346, 2213069, 2221678),
167
- Era.new("建徳", 1370, 2221678, 2222302),
168
- Era.new("文中", 1372, 2222302, 2223453),
169
+ Era.new("正平", 1346, 2213069, 2221512),
170
+ Era.new("建徳", 1370, 2221512, 2222332),
171
+ Era.new("文中", 1372, 2222332, 2223453),
169
172
  Era.new("天授", 1375, 2223453, 2225533),
170
173
  Era.new("弘和", 1381, 2225533, 2226702),
171
174
  Era.new("元中", 1384, 2226702, 2229809),
@@ -252,11 +255,11 @@ module Wareki
252
255
  Era.new("昭和", 1926, 2424875, 2447534),
253
256
  Era.new("平成", 1989, 2447535, 2458604),
254
257
  Era.new("令和", 2019, 2458605, DAY_MAX),
255
- ].freeze
258
+ ].each(&:freeze).freeze
256
259
  ERA_NORTH_DEFS = [
257
260
  Era.new("大化", 645, 1956842, 1958551),
258
- Era.new("白雉", 650, 1958551, 1960259),
259
- Era.new("朱鳥", 686, 1971845, 1971893),
261
+ Era.new("白雉", 650, 1958551, 1960339),
262
+ Era.new("朱鳥", 686, 1971845, 1972033),
260
263
  Era.new("大宝", 701, 1977221, 1978361),
261
264
  Era.new("慶雲", 704, 1978361, 1979692),
262
265
  Era.new("和銅", 708, 1979692, 1982487),
@@ -407,15 +410,15 @@ module Wareki
407
410
  Era.new("元亨", 1321, 2203634, 2205008),
408
411
  Era.new("正中", 1324, 2205008, 2205527),
409
412
  Era.new("嘉暦", 1326, 2205527, 2206740),
410
- Era.new("元徳", 1329, 2206740, 2207459),
413
+ Era.new("元徳", 1329, 2206740, 2207714),
411
414
  Era.new("元弘", 1331, 2207459, 2208365),
412
415
  Era.new("正慶", 1332, 2207714, 2208124),
413
416
  Era.new("建武", 1334, 2208365, 2210046),
414
417
  Era.new("延元", 1336, 2209133, 2210638),
415
418
  Era.new("興国", 1340, 2210638, 2213069),
416
- Era.new("正平", 1346, 2213069, 2221678),
417
- Era.new("建徳", 1370, 2221678, 2222302),
418
- Era.new("文中", 1372, 2222302, 2223453),
419
+ Era.new("正平", 1346, 2213069, 2221512),
420
+ Era.new("建徳", 1370, 2221512, 2222332),
421
+ Era.new("文中", 1372, 2222332, 2223453),
419
422
  Era.new("天授", 1375, 2223453, 2225533),
420
423
  Era.new("弘和", 1381, 2225533, 2226702),
421
424
  Era.new("元中", 1384, 2226702, 2229809),
@@ -502,5 +505,5 @@ module Wareki
502
505
  Era.new("昭和", 1926, 2424875, 2447534),
503
506
  Era.new("平成", 1989, 2447535, 2458604),
504
507
  Era.new("令和", 2019, 2458605, DAY_MAX),
505
- ].freeze
508
+ ].each(&:freeze).freeze
506
509
  end
@@ -5,8 +5,26 @@ require 'wareki/date'
5
5
  module Wareki
6
6
  # :nodoc:
7
7
  module StdExt
8
+ module_function
9
+
10
+ def wareki_directive?(format)
11
+ !!(format.to_str =~ Wareki::Date::FORMAT_EXPANSION_REGEX)
12
+ end
13
+
14
+ def wareki_time_directive?(format)
15
+ !!(format.to_str =~ Wareki::Utils::TIME_FORMAT_EXPANSION_REGEX)
16
+ end
17
+
18
+ # %JT 時刻トークンと %J 日付トークンの両方を展開した文字列を返す
19
+ def expand_all_wareki_formats(format, datetime)
20
+ ret = format
21
+ ret = Wareki::Utils.expand_time_format(ret, datetime) if wareki_time_directive?(ret)
22
+ ret = datetime.to_wareki_date.expand_wareki_format(ret) if wareki_directive?(ret)
23
+ ret
24
+ end
8
25
  end
9
26
  end
27
+
10
28
  # :nodoc:
11
29
  class Date
12
30
  JAPAN = Wareki::GREGORIAN_START
@@ -17,29 +35,59 @@ class Date
17
35
 
18
36
  alias _wareki_strftime_orig strftime
19
37
  def strftime(format = '%F')
20
- if format.index('%J')
21
- to_wareki_date.strftime(format)
22
- else
23
- _wareki_strftime_orig(format)
24
- end
38
+ return _wareki_strftime_orig(format) unless Wareki::StdExt.wareki_directive?(format)
39
+
40
+ _wareki_strftime_orig(to_wareki_date.expand_wareki_format(format))
25
41
  end
26
42
 
27
43
  class << self
28
44
  alias _wareki_parse_orig parse
29
45
  def parse(str = '-4712-01-01', comp = true, start = ::Date::ITALY)
46
+ return ::Date._wareki_parse_orig(str, comp, start) if str.to_s.ascii_only?
47
+
48
+ str = Wareki::Utils.normalize_time(str)
49
+ str.to_s =~ Wareki::PARSE_QUICK_FILTER or
50
+ return ::Date._wareki_parse_orig(str, comp, start)
30
51
  Wareki::Date.parse(str).to_date(start)
31
- rescue ArgumentError, Wareki::UnsupportedDateRange
52
+ rescue Wareki::InvalidDate
53
+ raise
54
+ rescue ArgumentError
32
55
  ::Date._wareki_parse_orig(str, comp, start)
33
56
  end
34
57
 
35
58
  alias _wareki__parse_orig _parse
36
59
  def _parse(str, comp = true)
60
+ return ::Date._wareki__parse_orig(str, comp) if str.to_s.ascii_only?
61
+
62
+ str = Wareki::Utils.normalize_time(str)
63
+ str.to_s =~ Wareki::PARSE_QUICK_FILTER or
64
+ return ::Date._wareki__parse_orig(str, comp)
37
65
  di = Wareki::Date._parse(str)
38
66
  wdate = Wareki::Date.new(di[:era], di[:year], di[:month], di[:day], di[:is_leap])
39
- rescue ArgumentError, Wareki::UnsupportedDateRange
67
+ rescue ArgumentError
40
68
  ::Date._wareki__parse_orig(str, comp)
41
69
  else
42
70
  ::Date._wareki__parse_orig(str.sub(Wareki::REGEX, wdate.strftime('%F ')), comp)
43
71
  end
44
72
  end
45
73
  end
74
+
75
+ # :nodoc:
76
+ class DateTime
77
+ alias _wareki_strftime_orig strftime
78
+ def strftime(format = '%FT%T%:z')
79
+ _wareki_strftime_orig(Wareki::StdExt.expand_all_wareki_formats(format, self))
80
+ end
81
+ end
82
+
83
+ # :nodoc:
84
+ class Time
85
+ def to_wareki_date
86
+ Wareki::Date.jd(to_date.jd)
87
+ end
88
+
89
+ alias _wareki_strftime_orig strftime
90
+ def strftime(format)
91
+ _wareki_strftime_orig(Wareki::StdExt.expand_all_wareki_formats(format, self))
92
+ end
93
+ end