when_exe 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1408 +1,1480 @@
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
- module When
9
- module Parts::Resource
10
-
11
- # option key for _m17n_form
12
- # @private
13
- FormOptions = [:precision, :camel, :method, :locale, :prefix]
14
-
15
- # When::Parts::Resource オブジェクトを分かりやすい文字列にして返します
16
- #
17
- # @return [String] 先頭部分を簡約表現にした IRI
18
- #
19
- def inspect
20
- return super unless @_pool
21
- expression = iri(true)
22
- expression == '' ? super : When::EncodingConversion.to_external_encoding(expression)
23
- end
24
-
25
- #
26
- # オブジェクトの内容を Hash 化
27
- #
28
- # @param [String, Integer] options {When::TM::TemporalPosition#_to_h}に渡す
29
- # @param [Hash] options 下記のとおり
30
- # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
31
- # @option options [Boolean] :camel true ならシンボルを camel case にする
32
- # @option options [Symbol] :method _to_hash_value で変換に用いるメソッド(:to_m17n, :iri など, 指定なしなら変換しない)
33
- # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
34
- # @option options [Boolean] :prefix true ならIRI の先頭部分を簡約表現にする
35
- # @option options [Object] :その他 各クラス#_to_h を参照
36
- #
37
- # @return [Hash] (Whenモジュール内のクラスは文字列 or M17n化)
38
- #
39
- def to_h(options={})
40
- _m17n_form(_to_h(options), options.kind_of?(Hash) ? options : {})
41
- end
42
-
43
- #
44
- # オブジェクトの内容を JSON 化
45
- #
46
- # @param [Object] options #to_h を参照
47
- #
48
- # @return [String] to_h 結果を JSON文字列化したもの
49
- #
50
- def to_json(options={})
51
- options[:method] = :to_m17n unless options.key?(:method)
52
- JSON.dump(to_h(options))
53
- end
54
-
55
- #
56
- # _m17n_form のための要素生成
57
- # @private
58
- def _to_hash_value(options={})
59
- method = options[:method]
60
- if method.kind_of?(Symbol)
61
- if respond_to?(method, true) && method != :iri
62
- return send(method)
63
- elsif registered?
64
- return iri(options[:prefix])
65
- end
66
- end
67
- self
68
- end
69
-
70
- private
71
- #
72
- # 時間位置オブジェクトの内容を Hash 化
73
- #
74
- # @param [Object] options #to_h を参照
75
- #
76
- # @return [Hash]
77
- # 各クラスの HashProperty に列挙した属性のうち値が false/nil でないものを
78
- # 属性 => 値
79
- # とする Hash
80
- #
81
- def _to_h(options={})
82
- hash = {}
83
- self.class::HashProperty.each do |property|
84
- method, skip = property
85
- value = respond_to?(method, true) ? send(method) : skip
86
- hash[method] = value unless value == skip || value.class == skip
87
- end
88
- hash
89
- end
90
-
91
- #
92
- # element を 文字列(M17n), Numeric あるいはそれらの Hash や Array に変換したもの
93
- #
94
- # @param [Object] element 変換元
95
- # @param [Hash] options 下記の通り
96
- # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
97
- # @option options [Boolean] :camel true ならシンボルを camel case にする
98
- # @option options [Symbol] :method _to_hash_value で変換に用いるメソッド(:to_m17n, :iri など, 指定なしなら変換しない)
99
- # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
100
- # @option options [Boolean] :prefix true ならIRI の先頭部分を簡約表現にする
101
- #
102
- # @return [Hash, Array] 変換結果
103
- #
104
- # @note element.events のある日付は _event_form で変換する
105
- #
106
- def _m17n_form(element, options={})
107
- result = element.respond_to?(:_event_form) ? element._event_form(self, options[:precision]) :
108
- element.respond_to?(:_to_hash_value) ? element._to_hash_value(options) :
109
- element.respond_to?(:label) && element.label ? element.label :
110
- case element
111
- when Hash ; Hash[*(element.keys.inject([]) { |s, k|
112
- s + [_m17n_form(k, options), _m17n_form(element[k], options)]
113
- })]
114
- when Array ; element.map {|e| _m17n_form(e, options)}
115
- when Class ; When::Parts::Resource._path_with_prefix(element, options[:prefix])
116
- when Symbol ; options[:camel] ? element.to_s.split(/_/).map {|e| e.capitalize}.join('').to_sym : element
117
- when Numeric, FalseClass, TrueClass ; element
118
- else ; element.to_s
119
- end
120
- result = When::Locale.translate(result,options[:locale]) if options[:locale] && result.kind_of?(String)
121
- result
122
- end
123
- end
124
-
125
- class Parts::Timezone
126
- # When::Parts::Timezone オブジェクトを分かりやすい文字列にして返します
127
- #
128
- # @return [String] identifier と同様
129
- #
130
- alias :inspect :identifier
131
- end
132
-
133
- module Coordinates
134
-
135
- class Pair
136
- #
137
- # to_h のための要素生成
138
- #
139
- # @param [Hash] options 下記のとおり
140
- # @option options [Symbol] :method :to_m17n なら to_s を返す、その他は self を返す
141
- #
142
- # @return [String, When::Coordinates::Pair]
143
- #
144
- # @private
145
- def _to_hash_value(options={})
146
- options[:method] == :to_m17n ? to_s : self
147
- end
148
-
149
- # When::Coordinates::Pair オブジェクトを分かりやすい文字列にして返します
150
- #
151
- # @return [String] to_s と同様
152
- #
153
- def inspect
154
- When::EncodingConversion.to_external_encoding(to_s)
155
- end
156
- end
157
-
158
- class Residue
159
- # 多言語対応文字列化
160
- #
161
- # @return [When::BasicTypes::M17n]
162
- #
163
- def to_m17n
164
- return m17n(@remainder.to_s) unless label
165
- return label + "(#{difference})" unless @format
166
- (label[0...0] + @format) % [label, difference, difference+1]
167
- end
168
-
169
- # 文字列化
170
- #
171
- # @return [String]
172
- #
173
- def to_s
174
- return @remainder.to_s unless label
175
- return label.to_s + "(#{difference})" unless @format
176
- @format.to_s % [label.to_s, difference, difference+1]
177
- end
178
-
179
- #
180
- # week_included のための range の決定
181
- #
182
- # @return [Array<Range>]
183
- #
184
- # @private
185
- def _range_for_week_included(date, first, length, block_given=false)
186
- today = date.floor
187
- begun = today.succ & self >> first-1
188
- unless date.frame.equal?(begun.frame)
189
- begun = (date.frame ^ today).succ & self >> first-1
190
- middle = today
191
- end
192
- ended = begun.succ & self >> length-1
193
- middle && block_given ? [begun...middle, middle...ended] : [begun...ended]
194
- end
195
-
196
- #
197
- # week_included のためのコラムの生成
198
- #
199
- # @return [Array<Object>]
200
- #
201
- # @private
202
- def _column_for_week_included(base, range, opt, &block)
203
- range.inject([]) {|s,r| s + r.map { |date|
204
- yield(date, !opt.key?(:Range) || opt[:Range].include?(date.to_i) ? DAY : nil)
205
- }}
206
- end
207
- end
208
- end
209
-
210
- class CalendarNote::Week
211
-
212
- #
213
- # week_included のための range の決定
214
- #
215
- # @return [Array<Range>]
216
- #
217
- # @private
218
- def _range_for_week_included(date, first, length, block_given=false)
219
- begun = ended = nil
220
- if first <= 0
221
- it = enum_for(date.floor, :reverse)
222
- (1-first).times do
223
- begun = it.next
224
- end
225
- else
226
- it = enum_for(date.floor, :forward)
227
- first.times do
228
- begun = it.next
229
- end
230
- end
231
- it = enum_for(begun, :forward)
232
- (length+1).times do
233
- ended = it.next
234
- end
235
- [begun...ended]
236
- end
237
-
238
- #
239
- # week_included のためのコラムの生成
240
- #
241
- # @return [Array<Object>]
242
- #
243
- # @private
244
- def _column_for_week_included(base, range, opt, &block)
245
- count = 0
246
- limit = week(base)[:position][1]
247
- range.inject([]) {|s,r| s + r.map { |date|
248
- position = week(date, base)[:position][0]
249
- (position - count).times do
250
- yield(nil,nil)
251
- end
252
- count = position + 1
253
- yield(date, !opt.key?(:Range) || opt[:Range].include?(date.to_i) ? DAY : nil) if position < limit
254
- }}
255
- end
256
- end
257
-
258
- module TM
259
-
260
- class Calendar
261
-
262
- # TemporalPosition#strftime のためのデフォルト書式
263
- #
264
- # @return [String]
265
- #
266
- def strftime
267
- @strftime ||= '%Y-%m-%d'
268
- end
269
- end
270
-
271
- class TemporalPosition
272
-
273
- AMPM = ['AM', 'PM'].map {|half| When::Parts::Resource._instance('_m:CalendarFormats::' + half)}
274
- Format = {
275
- 'a' => ['%s', 'a'], 'A' => ['%s', 'A'], 'b' => ['%s', 'b'], 'B' => ['%s', 'B'],
276
- 'c' => When::Parts::Resource._instance('_m:CalendarFormats::DateTime'),
277
- 'C' => ['%02d', 'C'], 'd' => ['%02d', 'd'], 'D' => '%m/%d/%y', 'e' => ['%2d', 'd'],
278
- 'E' => ['%s', 'E'], 'F' => ['%s', 'F'], 'G' => ['%d', 'G'], 'g' => ['%02d', 'g'],
279
- 'h' => '%b', 'H' => ['%02d', 'H'], 'I' => ['%02d', 'I'], 'j' => ['%03d', 'j'],
280
- 'k' => ['%2d', 'H'], 'l' => ['%2d', 'I'], 'm' => ['%02d', 'm'], 'M' => ['%02d', 'M'],
281
- 'n' => '\n', 'p' => ['%s', 'p'], 'P' => ['%s', 'P'], 'q' => ['%01d', 'd'],
282
- 'r' => '%I:%M:%S %p', 'R' => '%H:%M', 's' => ['%d', 's'], 'S' => ['%02d', 'S'],
283
- 't' => '\t', 'T' => '%H:%M:%S', 'u' => ['%d', 'u'], 'U' => ['%02d', 'U'],
284
- 'V' => ['%02d', 'V'], 'w' => ['%d', 'w'], 'W' => ['%02d', 'W'],
285
- 'x' => When::Parts::Resource._instance('_m:CalendarFormats::Date'),
286
- 'X' => When::Parts::Resource._instance('_m:CalendarFormats::Time'),
287
- 'y' => ['%02d', 'y'], 'Y' => ['%4d', 'Y'], 'z' => ['%s', 'z'], 'Z' => ['%s', 'Z'],
288
- '+' => '%a %b %d %T %z %Y'
289
- }
290
-
291
- class << self
292
- # When::TM::TemporalPosition Class のグローバルな設定を行う
293
- #
294
- # @param [Hash] format strftime で用いる記号の定義
295
- #
296
- # @return [void]
297
- #
298
- # @note format の指定がない場合、format Format(モジュール定数)と解釈する
299
- #
300
- def _setup_(format=nil)
301
- @format = format ? Format.merge(format) : Format
302
- end
303
-
304
- # 設定情報を取得する
305
- #
306
- # @return [Hash] 設定情報
307
- #
308
- def _setup_info
309
- {:format => format}
310
- end
311
-
312
- # strftime で用いる書式のハッシュ
313
- def format
314
- @format ||= Format
315
- end
316
- end
317
-
318
- # When::TM::TemporalPosition オブジェクトを分かりやすい文字列にして返します
319
- #
320
- # @return [String] to_s と同様
321
- #
322
- def inspect
323
- When::EncodingConversion.to_external_encoding(to_s)
324
- end
325
-
326
- #
327
- # 暦法名
328
- #
329
- # @return [Array<Class>] Class 暦法のクラスオブジェクト
330
- #
331
- def calendar_name
332
- [self.class]
333
- end
334
-
335
- #
336
- # 時法名
337
- # @param [Boolean] prefix true ならIRI の先頭部分を簡約表現にする
338
- #
339
- # @return [Array<String>] String 時法の IRI
340
- #
341
- def clock_name(prefix=true)
342
- [clock.iri(prefix)]
343
- end
344
-
345
- #
346
- # 参照ラベル
347
- #
348
- # @return [When::BasicTypes::M17n]
349
- #
350
- def reference_label
351
- [When::BasicTypes::M17n.new(self.class.to_s.split(/::/)[-1])]
352
- end
353
-
354
- # 含まれる週
355
- #
356
- # @overload week_included(ord, wkst, opt, &block)
357
- # @param [Numeric, Range] ord 週の番号(default: 今週)
358
- # 今週を 0 とする週番号(Integer) または週番号の範囲(Range)
359
- # -1 - 先週
360
- # 0 - 今週
361
- # +1 - 来週
362
- # @param [String] wkst 週の開始曜日(defaultは 月曜)
363
- # @param [When::CalendarNote] wkst 暦注オブジェクト
364
- # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
365
- # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
366
- # @param [Hash] opt 下記の通り
367
- # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
368
- # @param [Block] block
369
- #
370
- # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
371
- #
372
- # @return [Range] 含まれる週を範囲として表現する Range (block 指定なし)
373
- # @return [Array] 含まれる週の各日をブロックに渡した結果の Array (block 指定あり)
374
- #
375
- def week_included(*args, &block)
376
- begin
377
- first, length, wkst, opt = _range(args, 'MO')
378
- range = wkst._range_for_week_included(self, first, length, block_given?)
379
- rescue RangeError
380
- range = wkst._range_for_week_included(@frame ^ self, first, length)
381
- end
382
-
383
- return range.first unless block_given?
384
-
385
- wkst._column_for_week_included(self, range, opt, &block).unshift(yield(range[0].first, WEEK)).compact
386
- end
387
-
388
- # 含まれる月
389
- #
390
- # @overload month_included(ord, wkst, opt, block)
391
- # @param [Numeric, Range] ord 月の番号(default: 今月)
392
- # 今月を 0 とする月番号(Integer) または月番号の範囲(Range)
393
- # -1 - 先月
394
- # 0 - 今月
395
- # +1 - 来月
396
- # @param [String] wkst 週の開始曜日(defaultはなし)
397
- # @param [When::CalendarNote] wkst 暦注オブジェクト
398
- # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
399
- # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
400
- # @param [Hash] opt 下記の通り
401
- # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
402
- # @param [Block] block
403
- #
404
- # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
405
- #
406
- # @return [Range] 含まれる月を範囲として表現する Range (block 指定なし)
407
- # @return [Array] 含まれる月の各日をブロックに渡した結果の Array (block 指定あり, wkst なし)
408
- # @return [Array<Array>] 含まれる月の各日をブロックに渡した結果の 七曜表(block 指定あり, wkst あり)
409
- #
410
- def month_included(*args, &block)
411
- first, length, wkst, opt = _range(args)
412
- if wkst
413
- (first...(first+length)).map {|i|
414
- begun = self.floor(MONTH,DAY) + When::TM::PeriodDuration.new([0,i,0])
415
- ended = begun + P1M
416
- ended = ended.prev until begun.cal_date[MONTH-1] == ended.cal_date[MONTH-1]
417
- if ended.to_i <= begun.to_i
418
- ended = begun
419
- loop do
420
- succ = ended.succ
421
- break unless succ.frame.equal?(begun.frame)
422
- ended = succ
423
- end
424
- end
425
- dates = [begun]
426
- loop do
427
- current = dates[-1].week_included(wkst)
428
- if current.last.to_i > ended.to_i
429
- dates[-1] = ended
430
- break (dates.map {|date| date.week_included(wkst, {:Range=>begun.to_i..ended.to_i}, &block)}).
431
- unshift(yield(begun, MONTH)).compact
432
- elsif wkst.kind_of?(When::Coordinates::Residue)
433
- dates << dates[-1] + wkst.duration
434
- else
435
- it = wkst.enum_for(dates[-1], :forward)
436
- begin
437
- date = it.next
438
- end while date.to_i == dates[-1].to_i
439
- date = date.to_cal_date unless date.instance_of?(TM::CalDate)
440
- dates << date
441
- end
442
- end
443
- }
444
- else
445
- begun = self.floor(MONTH,DAY) + When::TM::PeriodDuration.new([0, first, 0])
446
- ended = begun + When::TM::PeriodDuration.new([0, length, 0])
447
- loop do
448
- last = ended.prev
449
- break unless last.cal_date[MONTH-1] == ended.cal_date[MONTH-1]
450
- ended = last
451
- end
452
- if block_given?
453
- (begun...ended).map do |date|
454
- yield(date)
455
- end
456
- else
457
- begun...ended
458
- end
459
- end
460
- end
461
-
462
- # 含まれる年
463
- #
464
- # @overload month_included(ord, wkst, opt, block)
465
- # @param [Numeric, Range] ord 年の番号(default: 今年)
466
- # 今年を 0 とする年番号(Integer) または年番号の範囲(Range)
467
- # -1 - 先年
468
- # 0 - 今年
469
- # +1 - 来年
470
- # @param [String] wkst 週の開始曜日(defaultはなし)
471
- # @param [When::CalendarNote] wkst 暦注オブジェクト
472
- # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
473
- # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
474
- # @param [Hash] opt 下記の通り
475
- # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
476
- # @param [Block] block
477
- #
478
- # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
479
- #
480
- # @return [Range] 含まれる年を範囲として表現する Range (block 指定なし)
481
- # @return [Array] 含まれる年の各日をブロックに渡した結果の Array (block 指定あり, wkst なし)
482
- # @return [Array<Array>] 含まれる年の各日をブロックに渡した結果の 七曜表(block 指定あり, wkst あり)
483
- #
484
- def year_included(*args, &block)
485
- first, length, wkst, opt = _range(args)
486
- if wkst
487
- (first...(first+length)).map {|i|
488
- begun = _force_euqal_year(i)
489
- ended = _force_euqal_year(i+1)
490
- current = begun
491
- result = [yield(begun, YEAR)]
492
- ended += P1M if ended.floor(MONTH).most_significant_coordinate * 1 == begun.most_significant_coordinate * 1
493
- while current < ended do
494
- result << current.month_included(wkst, &block)
495
- current += P1M
496
- end
497
- result.compact
498
- }
499
- else
500
- begun = _force_euqal_year(first)
501
- ended = _force_euqal_year(first+length)
502
- if block_given?
503
- (begun...ended).map do |date|
504
- yield(date)
505
- end
506
- else
507
- begun...ended
508
- end
509
- end
510
- end
511
-
512
- # 指定の年初を探す
513
- def _force_euqal_year(diff)
514
- year = most_significant_coordinate * 1 + diff
515
- date = (self + When::TM::PeriodDuration.new([diff,0,0])).floor(YEAR,DAY)
516
- done = {}
517
- loop do
518
- case
519
- when date.most_significant_coordinate * 1 == year
520
- return date
521
- when date.most_significant_coordinate * 1 > year
522
- next_date = (date-When::P1Y).floor(YEAR,DAY)
523
- date = (date.to_i == next_date.to_i) ?
524
- (date-When::P1Y*2).floor(YEAR,DAY) :
525
- next_date
526
- else
527
- next_date = (date+When::P1Y).floor(YEAR,DAY)
528
- date = (date.to_i == next_date.to_i) ?
529
- (date+When::P1Y*2).floor(YEAR,DAY) :
530
- next_date
531
- end
532
- raise RangeError, "can't find target date: #{self} -> #{year}" if done.key?(date.to_i)
533
- done[date.to_i] = true
534
- end
535
- end
536
- private :_force_euqal_year
537
-
538
- # 範囲の取得
539
- def _range(args, wkst=nil)
540
- ord = 0
541
- opt = {}
542
- args.each do |arg|
543
- case arg
544
- when Integer, Range ; ord = arg
545
- when Hash ; opt = arg
546
- else ; wkst = arg
547
- end
548
- end
549
- wkst, method = wkst
550
- wkst = When::Coordinates::Residue.day_of_week(wkst) || When.CalendarNote(wkst) if wkst
551
- wkst = wkst[method] if method
552
- return ord, 1, wkst, opt if ord.kind_of?(Integer)
553
- length = ord.last - ord.first
554
- length += 1 unless ord.exclude_end?
555
- return ord.first, length, wkst, opt
556
- end
557
- private :_range
558
-
559
- #
560
- # 時間位置オブジェクトの暦注を取得し value を暦注の値 (String, When::BasicTypes::M17n or When::Coordinates::Residue)とする Hash で表現
561
- #
562
- # @param [String] options { :notes => String } という Hash の指定と等価
563
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
564
- # @param [Hash] options 下記のとおり
565
- # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
566
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
567
- #
568
- # @return [Array<Array<Hash{:note=>note, :value=>value}>>]
569
- # [ note [String, When::BasicTypes::M17n] 暦注名 ]
570
- # [ value [String, When::BasicTypes::M17n, When::Coordinates::Residue] 暦注の値 ]
571
- #
572
- def notes(options={})
573
- form_options = options.kind_of?(Hash) ? options : {}
574
- form_options[:method] = :to_m17n unless form_options.key?(:method)
575
- persistence = options.delete(:persistence) if options.kind_of?(Hash)
576
- retrieved = When::CalendarNote::NotesContainer.retrieve(persistence, self.to_i)
577
- return retrieved unless retrieved == false
578
- When::CalendarNote::NotesContainer.register(_m17n_form(_notes(options), form_options), persistence, self.to_i)
579
- end
580
-
581
- #
582
- # 時間位置オブジェクトの暦注を取得
583
- #
584
- # @param [String] options { :notes => String } という Hash の指定と等価
585
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
586
- # @param [Hash] options 下記のとおり
587
- # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
588
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
589
- #
590
- # @return [Array<Array<Hash{:note=>note, :value=>value}>>]
591
- # [ note [String, When::BasicTypes::M17n, When::Coordinates::Residue] 暦注名 ]
592
- # [ value [String, When::BasicTypes::M17n, When::Coordinates::Residue, When::TM::TemporalPosition] 暦注の値 ]
593
- #
594
- # @note
595
- # When::TM::TemporalPosition の場合、events[0] に暦注名の入ったその暦注に該当する日付である。
596
- # (例) Christian クラス easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
597
- # 暦注サブクラスの場合、要素が増えたり、:note の暦注要素の型が変わったりすることがある。
598
- #
599
- def _notes(options={})
600
- _calendar_note(options).notes(self, options)
601
- end
602
-
603
- #
604
- # 暦注の一致 or 不一致
605
- #
606
- # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
607
- # (指定の notes が存在する場合は前者、しない場合は後者)
608
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
609
- # @param [Hash] options 下記のとおり
610
- # @option options [Object] :value 確認する暦注の値
611
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
612
- #
613
- # @return [Boolean]
614
- # [ true - 暦注が一致 ]
615
- # [ false - 暦注が不一致 ]
616
- #
617
- def note?(options={})
618
- _calendar_note(options).note?(self, options)
619
- end
620
-
621
- # 指定の日時が指定イベントに該当するか?
622
- #
623
- # @overload is?(event=nil, options={})
624
- # @param [String] event options={:notes=>String} または {:notes=>String} または {:value=>String} という指定と等価
625
- # (指定の event が存在する場合は前者、指定の notes が存在する場合は中央、しない場合は後者)
626
- # @param [Integer] event options={ :indices=> Integer } という指定と等価
627
- # @param [Hash] options 下記のとおり
628
- # @option options [When::CalendarNote or String] :calendar_note 該当判断に用いる CalendarNoteオブジェクトまたはその IRI
629
- # @option options [String] :event 確認するイベント名
630
- # @option options [Object] :value 確認する暦注の値
631
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
632
- #
633
- # @return [Boolean]
634
- # [ true - 該当する ]
635
- # [ false - 該当しない ]
636
- #
637
- def is?(*args)
638
- options = args.last.kind_of?(Hash) ? args.pop.dup : {}
639
- note = _calendar_note(options)
640
- event = args.first || options.delete(:event) || note.event
641
- return note.note?(self, options) unless options.empty?
642
- return note.note?(self, event) unless event.to_s =~ /\A([^\d]+)/ && note.respond_to?($1.downcase)
643
- return note.include?(self, event)
644
- end
645
-
646
- #
647
- # 時間位置オブジェクトの内容を Hash 化
648
- #
649
- # @param [String] options { :notes => String } という Hash の指定と等価
650
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
651
- # @param [Hash] options 下記のとおり
652
- # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
653
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
654
- #
655
- # @return [Hash]
656
- # - :sdn 日の通し番号 - ユリウス通日(Integer)
657
- # - :calendar calendar_name の結果 - Array<暦法または暦年代(, 付属情報..)>
658
- # - :notes Hash (の Array (の Array)) - _notes(options)
659
- # clock が定義されている場合、さらに下記も出力する
660
- # - :clock 時計(When::Parts::Timezone::Base)
661
- # - :clk_time to_clock_time の結果 - ( 日, 時, 分, 秒 )
662
- # - :dynamical dynamical_time /
663
- # - :universal universal_time /
664
- #
665
- def _to_h(options={})
666
- hash = super.update({
667
- :sdn => to_i,
668
- :calendar => calendar_name,
669
- :notes => _notes(options)
670
- })
671
-
672
- hash.update({
673
- :clock => clock,
674
- :clk_time => to_clock_time,
675
- :dynamical => dynamical_time * clock.second,
676
- :universal => universal_time * clock.second
677
- }) if clock
678
- hash
679
- end
680
-
681
- # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
682
- #
683
- # @overload to_m17n()
684
- #
685
- # @return [When::BasicTypes::M17n]
686
- #
687
- def to_m17n(*args)
688
- return m17n(I[@indeterminated_position]) if [Unknown, Max, Min].include?(@indeterminated_position)
689
- return m17n(_to_s)
690
- end
691
- # @private
692
- alias :_to_s :to_s
693
-
694
- # 文字列化 - When.exe Standard Representation により文字列化する
695
- #
696
- # @overload to_s()
697
- #
698
- # @return [String]
699
- #
700
- def to_s(*args)
701
- to_m17n(*args).to_s
702
- end
703
-
704
- # caret 付きの frame 名
705
- #
706
- # @return [String]
707
- #
708
- # @note 暦年代付きかまたは frame がグレゴリオ暦の場合は空文字列を返す
709
- #
710
- def caret_frame
711
- prefix = When::Parts::Resource.base_uri + 'CalendarTypes/'
712
- path = frame.iri
713
- return '' if @calendar_era_name || path == prefix + 'Gregorian'
714
- path = path[prefix.length..-1] if path.index(prefix) == 0
715
- '^^' + path
716
- end
717
-
718
- # 指定の書式による多言語対応文字列化 - pattern で指定した書式で多言語対応文字列化する
719
- #
720
- # @param [When::BasicTypes::M17n] pattern 書式
721
- # @param [String, Array<String>] locale 文字列化を行う locale の指定(デフォルト : オブジェクト生成時に保持している locale すべて)
722
- #
723
- # @return [When::BasicTypes::M17n]
724
- #
725
- def strftime(pattern=@frame.strftime, locale=nil)
726
- pattern = m17n([pattern]*self.keys.length, nil, nil, {:locale=>self.keys}) if pattern.instance_of?(String)
727
- pattern._printf([], locale) do |k, *t|
728
- _strftime(k, pattern, [''])
729
- end
730
- end
731
-
732
- # strftime で扱う項の値を取得する
733
- #
734
- # @param [String] designator 項目名
735
- # @param [String] locale 文字列化を行う場合の locale の指定(デフォルト to_s(代表値))
736
- # @param [Integer] d 日付が'年月日'、時刻が'時分秒'でない表現のための桁位置変更指示
737
- # [ 年月に付く場合 - 大きいほうに位置をずらす ]
738
- # [ 分秒に付く場合 - 小さいほうに位置をずらす ]
739
- # @param [Integer] e 月の省略名の文字数
740
- #
741
- # @return [designator に依存]
742
- #
743
- def _term(designator, locale=nil, d=0, e=3)
744
- designator = When::Locale.translate(designator,locale)
745
- case designator
746
- # 現在のロケールにおける曜日の省略名
747
- when 'a' ; When.Resource('_co:Common::Abbr_Day')[to_i % 7].translate(locale)
748
- # 現在のロケールにおける曜日の完全な名前
749
- when 'A' ; When.Resource('_co:Common::Week')[to_i % 7].label.translate(locale)
750
- when 'b' ; (name(MONTH-d).translate(locale))[/\A.{1,#{e}}/] # 現在のロケールにおける月の省略名
751
- when 'B' ; (name(MONTH-d).translate(locale)) # 現在のロケールにおける月の完全な名前
752
- when 'C' ; year(d).div(100) # 世紀 (西暦年の上 2 桁)
753
- when 'd' ; day(d) # 月内通算日
754
- when 'E' ; Array(calendar_era_name)[0].translate(locale)# 年号
755
- when 'F' ; floor(DAY).to_m17n.translate(locale) # ISO 8601 形式の日付フォーマット
756
- when 'G' ; cwyear(d) # ISO 8601 週単位表記の年
757
- when 'g' ; cwyear(d) % 100 # ISO 8601 週単位表記の年の下2桁
758
- when 'H' ; hour(d) # 24 時間表記での時
759
- when 'I' ; (hour(d)-1) % 12 + 1 # 12 時間表記での時
760
- when 'j' ; yday(d) # 年の初めから通算の日数
761
- when 'm' ; month(d) #
762
- when 'M' ; minute(d) #
763
- when 'p' ; (AMPM[hour(d).to_i.div(12)].translate(locale)).upcase # 現在のロケールにおける「午前」「午後」に相当する文字列
764
- when 'P' ; (AMPM[hour(d).to_i.div(12)].translate(locale)).downcase # 前項を小文字で表記
765
- when 's' ; universal_time / Duration::SECOND # 紀元 (1970-01-01T00:00:00Z) からの秒数
766
- when 'S' ; second(d) # 秒 (10 進数表記)
767
- when 'u' ; cwday # 週の何番目の日か(月曜日を 1 とする)
768
- when 'U' ; yweek(6, 7, d) # 年の初めからの通算の週番号(日曜日始まり)
769
- when 'V' ; cweek(d) # ISO 8601 形式での年の始めからの週番号
770
- when 'w' ; wday # 週の何番目の日 か(日曜日を 0 とする)
771
- when 'W' ; yweek(0, 7, d) # 年の初めからの通算の週番号(月曜日始まり)
772
- when 'y' ; year(d) % 100 # 西暦の下2桁 (世紀部分を含まない年)
773
- when 'Y' ; year(d) # 世紀部分を含めた ( 4 桁の) 西暦年
774
- when 'z' ; clock.to_basic # +hhmm や -hhmm の形式のタイムゾーン
775
- when 'Z' ; When::Locale.translate(clock.tzname[0],locale) # タイムゾーンまたはゾーン名または省略名
776
- else ; designator
777
- end
778
- end
779
-
780
- private
781
-
782
- # 指定の書式による多言語対応文字列化
783
- #
784
- # @param [String] locale 文字列化を行う locale
785
- # @param [When::BasicTypes::M17n] pattern 書式
786
- #
787
- # @return [Array] 書式と文字列化項目からなる配列
788
- #
789
- def _strftime(locale, pattern, t)
790
- format, *terms = t
791
- pattern = pattern.translate(locale) if pattern.kind_of?(When::BasicTypes::M17n)
792
- pattern.scan(/(%[O\d]*(?:\.(\d+))?.)|(.)/) do |c,e,s|
793
- case c
794
- when /\A%%/
795
- format += '%%'
796
- when /\A%/
797
- action = TemporalPosition.format[c[-1..-1]]
798
- case action
799
- when Array
800
- format += action[0]
801
- terms << _term(action[1], locale, c[1..-2].to_i, e||3)
802
- when String
803
- action = action.translate(locale) if action.kind_of?(When::BasicTypes::M17n)
804
- if (action =~ /%/)
805
- format, *terms = _strftime(locale, action, [format] + terms)
806
- else
807
- format += action
808
- end
809
- end
810
- else
811
- format += s
812
- end
813
- end
814
- [format] + terms
815
- end
816
-
817
- #
818
- # 使用する When::CalendarNote を決定する
819
- #
820
- # options に副作用があることに注意
821
- #
822
- def _calendar_note(options)
823
- calendar_note = options.delete(:calendar_note) if options.kind_of?(Hash)
824
- calendar_note ||= @frame ? @frame.note : 'JulianDay'
825
- When.CalendarNote(calendar_note)
826
- end
827
- end
828
-
829
- class JulianDate
830
-
831
- # 多言語対応文字列化 - ユリウス日を多言語対応文字列化する
832
- #
833
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
834
- #
835
- # @return [When::BasicTypes::M17n]
836
- #
837
- def to_m17n(precision=@precision)
838
- return m17n(to_s(precision))
839
- end
840
-
841
- # 文字列化 - ユリウス日を文字列化する
842
- #
843
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
844
- #
845
- # @return [String]
846
- #
847
- def to_s(precision=@precision)
848
- coordinate = (precision <= When::DAY) ? to_i : to_f
849
- coordinate.to_s
850
- end
851
- end
852
-
853
- class ClockTime
854
-
855
- # 要素の多言語対応文字列化
856
- #
857
- # @param [Integer] index 多言語対応文字列化する要素の指定
858
- # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
859
- #
860
- # @return [When::BasicTypes::M17n]
861
- #
862
- def name(index, format=nil)
863
- digit = _digit(index) {|digit| digit > DAY}
864
- coordinate = @clk_time[digit]
865
- return m17n(format % coordinate) if format
866
-
867
- indices = @frame.indices[digit-1]
868
- if indices
869
- trunk = indices.trunk
870
- branch = indices.branch
871
- end
872
- format = branch ? m17n("%02d:") : "%02d"
873
- return m17n(format % coordinate) unless trunk
874
- trunk = trunk[coordinate * 1]
875
- return m17n(trunk) unless branch
876
- return trunk.prefix(branch[coordinate * 0])
877
- end
878
-
879
- # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
880
- #
881
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
882
- #
883
- # @return [When::BasicTypes::M17n]
884
- #
885
- def to_m17n(precision=@precision)
886
- time = m17n('T' + _time_to_s(precision))
887
- if @frame
888
- time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
889
- end
890
- return time
891
- end
892
-
893
- # 文字列化 - When.exe Standard Representation により文字列化する
894
- #
895
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
896
- #
897
- # @return [String]
898
- #
899
- def to_s(precision=@precision)
900
- time = 'T' + _time_to_s(precision)
901
- if @frame
902
- time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
903
- end
904
- return time
905
- end
906
-
907
- # 時
908
- #
909
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
910
- #
911
- # @return [Numeric] 自身の「時」
912
- #
913
- def hour(d=0)
914
- @clk_time[HOUR+d]
915
- end
916
-
917
- #
918
- #
919
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
920
- #
921
- #
922
- # @return [Numeric] 自身の「分」
923
- #
924
- def minute(d=0)
925
- @clk_time[MINUTE+d]
926
- end
927
- alias :min :minute
928
-
929
- #
930
- #
931
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
932
- #
933
- #
934
- # @return [Numeric] 自身の「秒」
935
- #
936
- def second(d=0)
937
- @clk_time[SECOND+d]
938
- end
939
- alias :sec :second
940
-
941
- #protected
942
- #
943
- # 時間帯以外の部分の文字列化
944
- #
945
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
946
- #
947
- # @return [String]
948
- #
949
- # @private
950
- def _time_to_s(precision=@precision)
951
- terms = []
952
- format = ''
953
- format += Pair::DL2[@clk_time[0] * 0] || ':' if @frame.pair[0] || @clk_time[0].kind_of?(Pair)
954
-
955
- # 時分
956
- digits = [@clk_time.length-2, precision].min
957
- if digits > 0
958
- terms += @clk_time[1..-1]
959
- format += "%02d:" * digits
960
- format = format[0..-2] if precision == digits
961
- end
962
-
963
- #
964
- digits = [precision - @clk_time.length + 1, STRING-SECOND].min
965
- if digits == 0
966
- format += "%02d"
967
- elsif digits > 0
968
- factor = 10**digits
969
- terms[-1] = ((@clk_time[-1] + 1E-6) * factor).floor.to_f / factor # 切り捨て(10で割る丸めガードあり)
970
- format += "%02.#{digits}f"
971
- end
972
-
973
- # 結果
974
- time = Pair._format([format] + terms)
975
- time.sub(/([^\d])(\d)\./, '\10\2.')
976
- end
977
-
978
- #
979
- # to_h のための要素生成
980
- # @private
981
- def _to_hash_value(options={})
982
- clk_time.map {|e| _m17n_form(e, options) }
983
- end
984
- end
985
-
986
- class CalDate
987
-
988
- #
989
- # 暦法名
990
- #
991
- # @return [Array] ( name, epoch, reverse, go back )
992
- # - name 暦法または暦年代 ({When::TM::Calendar}, {When::TM::CalendarEra})
993
- # - epoch 暦元 (Integer)
994
- # - reverse 暦年の順序 (Boolean)
995
- # [ false, nil 昇順 ]
996
- # [ true 降順 ]
997
- # - go back 参照イベントより前の暦日か(Boolean)
998
- # [ false, nil 否 ]
999
- # [ true 然り ]
1000
- #
1001
- def calendar_name
1002
- void, epoch, reverse, back = @calendar_era_name
1003
- name = [@calendar_era || @frame, epoch, reverse, back]
1004
- name.pop until name[-1]
1005
- return name
1006
- end
1007
-
1008
- #
1009
- # 参照ラベル
1010
- #
1011
- # @return [When::BasicTypes::M17n]
1012
- #
1013
- def reference_label
1014
- return @calendar_era.hierarchy.map {|e| e.label} if @calendar_era
1015
- return [@frame.label] if @frame.label
1016
- [When::BasicTypes::M17n.new(@frame.class.to_s.split(/::/)[-1])]
1017
- end
1018
-
1019
- #
1020
- # Hash
1021
- #
1022
- # @param [String] options { :notes => String } という Hash の指定と等価
1023
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
1024
- # @param [Hash] options 下記のとおり
1025
- # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
1026
- # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
1027
- #
1028
- # @return [Hash]
1029
- # - :calendar calendar_name の結果 ( name, epoch, reverse, go back )
1030
- # - name 暦法または暦年代 ({When::TM::Calendar}, {When::TM::CalendarEra})
1031
- # - epoch 暦元 (Integer)
1032
- # - reverse 暦年の順序 (Boolean)
1033
- # [ false, nil 昇順 ]
1034
- # [ true 降順 ]
1035
- # - go back 参照イベントより前の暦日か(Boolean)
1036
- # [ false, nil ]
1037
- # [ true 然り ]
1038
- # - :cal_date cal_date の内容 (year, month, day)
1039
- # [ year - ({Numeric}) ]
1040
- # [ month - 月 ({Numeric}) ]
1041
- # [ day - 日 ({Numeric}) ]
1042
- # - :clk_time to_clock_time の結果 ( 日, 時, 分, 秒 )
1043
- # - :notes Hash (の Array (の Array)) - _notes(options)
1044
- #
1045
- def _to_h(options={})
1046
- super.update({:cal_date=>@cal_date})
1047
- end
1048
-
1049
- # 要素の多言語対応文字列化
1050
- #
1051
- # @param [Integer] index 多言語対応文字列化する要素の指定
1052
- # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
1053
- #
1054
- # @return [When::BasicTypes::M17n]
1055
- #
1056
- def name(index, format=nil)
1057
- digit = _digit(index) {|digit| digit <= DAY}
1058
- coordinate = @cal_date[digit-1]
1059
- return m17n(format % coordinate) if format
1060
-
1061
- indices = @frame.indices[digit-1]
1062
- if indices
1063
- trunk = indices.trunk
1064
- branch = indices.branch
1065
- end
1066
- format = branch ? m17n("%02d-") : "%02d"
1067
- return m17n(format % coordinate) unless trunk
1068
- trunk = trunk[coordinate * 1]
1069
- return m17n(trunk) unless branch
1070
- return trunk.prefix(branch[coordinate * 0||0])
1071
- end
1072
-
1073
- #
1074
- #
1075
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1076
- #
1077
- # @return [Numeric] 自身の「日」
1078
- #
1079
- def day(d=0)
1080
- @cal_date[DAY-1-d]
1081
- end
1082
-
1083
- # 月内通日
1084
- #
1085
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1086
- #
1087
- # @return [Numeric] 自身の「月内通日」(1始まり)
1088
- #
1089
- def mday(d=0)
1090
- to_i - floor(MONTH-d).to_i + 1
1091
- end
1092
-
1093
- # 年内通日
1094
- #
1095
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1096
- #
1097
- # @return [Numeric] 自身の「年内通日」(1始まり)
1098
- #
1099
- def yday(d=0)
1100
- to_i - floor(YEAR-d).to_i + 1
1101
- end
1102
-
1103
- # 七曜
1104
- #
1105
- # @return [Numeric] 自身の「七曜」(日曜 0 始まり)
1106
- #
1107
- def wday
1108
- (to_i + 1) % 7
1109
- end
1110
-
1111
- # 七曜(暦週)
1112
- #
1113
- # @return [Numeric] 自身の「七曜」(月曜 1 始まり)
1114
- #
1115
- def cwday
1116
- (to_i % 7) + 1
1117
- end
1118
-
1119
- # 暦週
1120
- #
1121
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1122
- #
1123
- # @return [Numeric] 自身の「暦週」
1124
- #
1125
- def cweek(d=0)
1126
- [1,0,-1].each do |i|
1127
- start = ((self + PeriodDuration.new(i, YEAR-d)).floor(YEAR-d,DAY) + PeriodDuration.new(4, DAY)) & Residue.new(0,7,-1)
1128
- return ((to_i - start.to_i).div 7) + 1 if self >= start
1129
- end
1130
- raise IndexError, 'Cannot decide year number'
1131
- end
1132
-
1133
- # 月内通週
1134
- #
1135
- # @param [Integer] w 週の最初の曜日(0:月,.., 6:日)
1136
- # @param [Integer] m 一週間の日数
1137
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1138
- #
1139
- # @return [Numeric] 自身の「月内通週」(その月に完全に含まれる最初の週を1とする)
1140
- #
1141
- def mweek(w=6, m=7, d=0)
1142
- 1 + (to_i - (floor(MONTH-d,DAY) & Residue.new(w,m)).to_i).div(7)
1143
- end
1144
-
1145
- # 年内通週
1146
- #
1147
- # @param [Integer] w 週の最初の曜日(0:月,.., 6:日)
1148
- # @param [Integer] m 一週間の日数
1149
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1150
- #
1151
- # @return [Numeric]
1152
- # 自身の「年内通週」(その年に完全に含まれる最初の週を1とする)
1153
- #
1154
- def yweek(w=6, m=7, d=0)
1155
- 1 + (to_i - (floor(YEAR-d,DAY) & Residue.new(w,m)).to_i).div(7)
1156
- end
1157
-
1158
- #
1159
- #
1160
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1161
- #
1162
- # @return [Numeric] 自身の「月」
1163
- #
1164
- def month(d=0)
1165
- @cal_date[MONTH-1-d]
1166
- end
1167
- alias :mon :month
1168
-
1169
- # 年内通月
1170
- #
1171
- # @param [Integer] d1 日付が'年月日'でない表現のための桁位置変更指示-年用(大きいほうに位置をずらす)
1172
- # @param [Integer] d2 日付が'年月日'でない表現のための桁位置変更指示-月用(大きいほうに位置をずらす)
1173
- #
1174
- # @return [Numeric] 自身の「年内通月」(1始まり)
1175
- #
1176
- def ymon(d1=0, d2=0)
1177
- current = floor(YEAR-d1, MONTH-d2)
1178
- @frame._length(@cal_date[(YEAR-1-d1)...(MONTH-1-d2)]).times do |i|
1179
- return i+1 if current == self
1180
- current = current.succ
1181
- end
1182
- raise IndexError, 'Cannot decide month number'
1183
- end
1184
-
1185
- #
1186
- #
1187
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1188
- #
1189
- # @return [Numeric] 自身の「年」
1190
- #
1191
- def year(d=0)
1192
- @cal_date[YEAR-1-d]
1193
- end
1194
-
1195
- # 暦週の年
1196
- #
1197
- # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1198
- #
1199
- # @return [Numeric] 自身の「暦週の年」
1200
- #
1201
- def cwyear(d=0)
1202
- [1,0,-1].each do |i|
1203
- start = ((self + PeriodDuration.new(i, YEAR-d)).floor(YEAR-d,DAY) + PeriodDuration.new(4, DAY)) & Residue.new(0,7,-1)
1204
- return year(d)+i if self >= start
1205
- end
1206
- raise IndexError, 'Cannot decide year number'
1207
- end
1208
-
1209
- # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1210
- #
1211
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1212
- # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1213
- #
1214
- # @return [When::BasicTypes::M17n]
1215
- #
1216
- def to_m17n(precision=@precision, round=false)
1217
- date = m17n(_date_to_s(precision))
1218
- return date unless @calendar_era
1219
- return _parent_labels.inject(m17n(@calendar_era_name[0])) {|era_name, parent|
1220
- era_name.prefix(m17n(parent) + '::')
1221
- } + date
1222
- end
1223
-
1224
- # 文字列化 - When.exe Standard Representation により文字列化する
1225
- #
1226
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1227
- # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1228
- #
1229
- # @return [String]
1230
- #
1231
- def to_s(precision=@precision, round=false)
1232
- date = _date_to_s(precision)
1233
- return date unless @calendar_era
1234
- return _parent_labels.inject(@calendar_era_name[0].to_s) {|era_name, parent|
1235
- parent.to_s + '::' + era_name
1236
- } + date
1237
- end
1238
-
1239
- # event を 文字列化 - 日時で与えられた event を文字列化する
1240
- #
1241
- # @param [When::TM::TemporalPosition] other 時系の歩度を比較する基準(nilは比較しない)
1242
- # @param [Numeric] round_precision イベント名(イベント)出力の場合の時刻の丸め位置(nilなら丸めない)
1243
- #
1244
- # @return [String]
1245
- #
1246
- # @note
1247
- # events 配列なし - 日時をそのまま文字列化
1248
- # 日時の精度が日より細かい - イベント名(イベント時刻)
1249
- # 日時の精度が日 - イベント名(当日までの経過日数)
1250
- #
1251
- def _event_form(other=nil, round_precision=nil)
1252
- return to_m17n unless events
1253
- return events[0] + '(' + _clk_time_for_inspect(round_precision).to_s(round_precision || precision)[/[:*=0-9]+/] + ')' if precision > When::DAY
1254
- return events[0] unless other
1255
- other = JulianDate.dynamical_time(other.dynamical_time,
1256
- {:time_standard=>time_standard}) unless time_standard.rate_of_clock == other.time_standard.rate_of_clock
1257
- events[0] + '(' + (other.to_i - to_i).to_s + ')'
1258
- end
1259
-
1260
- private
1261
-
1262
- # 日付の年号に曖昧性がある場合の親年号の label の Array
1263
- #
1264
- # @return [Array<String>]
1265
- #
1266
- def _parent_labels
1267
- return [] unless (area = When::TM::CalendarEra[nil]) &&
1268
- (period = area[nil])
1269
- list = []
1270
- era = @calendar_era
1271
- while (labels = period[era.label.to_s]) &&
1272
- (epoch = labels[era.epoch_year]) &&
1273
- (epoch.size > 1) &&
1274
- (parent = era.parent).respond_to?(:epoch_year)
1275
- list << parent.label
1276
- era = parent
1277
- end
1278
- list
1279
- end
1280
-
1281
- # 日付の年号以外の部分を文字列化する
1282
- #
1283
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1284
- #
1285
- # @return [String]
1286
- #
1287
- def _date_to_s(precision)
1288
- # 準備
1289
- precision = [precision, 1 - @cal_date.length].max
1290
- precision = [precision, DAY].min
1291
- terms = []
1292
- ext_dg = [(@extra_year_digits||1).to_i, 0].max
1293
- year_dg = @frame.indices.length <= 2 ? 4 : 2
1294
-
1295
- #
1296
- year_by_epoch = @cal_date[0]
1297
- if @calendar_era_name
1298
- era, epoch, reverse = @calendar_era_name
1299
- year_in_term = reverse ? -year_by_epoch : year_by_epoch
1300
- year_by_calendar = epoch + year_by_epoch if epoch
1301
- terms << year_in_term
1302
- format = (0..99) === (year_in_term * 1) ? "%02d." : "%0#{year_dg}d."
1303
- if year_by_calendar && year_by_calendar != year_in_term
1304
- terms << (year_by_calendar * 1)
1305
- format += "(%0#{year_dg}d)"
1306
- end
1307
- else
1308
- terms << year_by_epoch
1309
- format = (0..(10**year_dg-1)) === (year_by_epoch * 1) ? "%0#{year_dg}d." : "%+0#{5+ext_dg}d."
1310
- end
1311
-
1312
- # 月日
1313
- ((1-@cal_date.length)..-1).each do |i|
1314
- break if (i >= precision)
1315
- terms << @cal_date[i]
1316
- format += "%02d."
1317
- end
1318
-
1319
- # 結果
1320
- date = Pair._format([format] + terms)
1321
- date.sub!(/([^\d])\(([-+\d]+)\)/, '(\2)\1') if era
1322
- date = date[0..-2] unless @frame.pair[precision-1] || date[-1..-1] != '.'
1323
- date.gsub!(/\./, '-') if (@frame.indices.length <= 3) && !era
1324
- return date
1325
- end
1326
- end
1327
-
1328
- class DateAndTime < CalDate
1329
-
1330
- # 要素の多言語対応文字列化
1331
- #
1332
- # @param [Integer] index 多言語対応文字列化する要素の指定
1333
- # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
1334
- #
1335
- # @return [When::BasicTypes::M17n]
1336
- #
1337
- def name(index, format=nil)
1338
- digit = _digit(index)
1339
- (digit <= DAY) ? super : @clk_time.name(digit, format)
1340
- end
1341
-
1342
- # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1343
- #
1344
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1345
- # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1346
- # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1347
- #
1348
- # @return [When::BasicTypes::M17n]
1349
- #
1350
- def to_m17n(precision=@precision, round=false)
1351
- super + _clk_time_for_inspect(round ? precision : nil).to_m17n(precision)
1352
- end
1353
-
1354
- # 文字列化 -When.exe Standard Representation により文字列化する
1355
- #
1356
- # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1357
- # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1358
- # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1359
- #
1360
- # @return [String]
1361
- #
1362
- def to_s(precision=@precision, round=false)
1363
- super + _clk_time_for_inspect(round ? precision : nil).to_s(precision)
1364
- end
1365
-
1366
- # 出力に使用する clk_time の作成
1367
- def _clk_time_for_inspect(precision)
1368
- return @clk_time unless precision && precision > When::DAY
1369
- base = self + When::TM::Duration.new(@clk_time.frame._round_value(precision))
1370
- base.clk_time.clk_time[When::HOUR] = @clk_time.clk_time[When::HOUR] + 1 unless self.to_i == base.to_i
1371
- return base.clk_time
1372
- end
1373
- private :_clk_time_for_inspect
1374
-
1375
- #
1376
- #
1377
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1378
- #
1379
- # @return [Numeric] 自身の「時」
1380
- #
1381
- def hour(d=0)
1382
- @clk_time.hour(d)
1383
- end
1384
-
1385
- #
1386
- #
1387
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1388
- #
1389
- # @return [Numeric] 自身の「分」
1390
- #
1391
- def minute(d=0)
1392
- @clk_time.minute(d)
1393
- end
1394
- alias :min :minute
1395
-
1396
- #
1397
- #
1398
- # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1399
- #
1400
- # @return [Numeric] 自身の「秒」
1401
- #
1402
- def second(d=0)
1403
- @clk_time.second(d)
1404
- end
1405
- alias :sec :second
1406
- end
1407
- end
1408
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2015 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ module When
9
+ module Parts::Resource
10
+
11
+ # option key for _m17n_form
12
+ # @private
13
+ FormOptions = [:precision, :camel, :method, :locale, :prefix]
14
+
15
+ # When::Parts::Resource オブジェクトを分かりやすい文字列にして返します
16
+ #
17
+ # @return [String] 先頭部分を簡約表現にした IRI
18
+ #
19
+ def inspect
20
+ return super unless @_pool
21
+ expression = iri(true)
22
+ expression == '' ? super : When::EncodingConversion.to_external_encoding(expression)
23
+ end
24
+
25
+ # 多言語対応文字列化
26
+ #
27
+ # @return [When::BasicTypes::M17n]
28
+ #
29
+ def to_m17n
30
+ label
31
+ end
32
+
33
+ #
34
+ # オブジェクトの内容を Hash
35
+ #
36
+ # @param [String, Integer] options {When::TM::TemporalPosition#_to_h}に渡す
37
+ # @param [Hash] options 下記のとおり
38
+ # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
39
+ # @option options [Boolean] :camel true ならシンボルを camel case にする
40
+ # @option options [Symbol] :method _to_hash_value で変換に用いるメソッド(:to_m17n, :iri など, 指定なしなら変換しない)
41
+ # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
42
+ # @option options [Boolean] :prefix true ならIRI の先頭部分を簡約表現にする
43
+ # @option options [Object] :その他 各クラス#_to_h を参照
44
+ #
45
+ # @return [Hash] (Whenモジュール内のクラスは文字列 or M17n化)
46
+ #
47
+ def to_h(options={})
48
+ _m17n_form(_to_h(options), options.kind_of?(Hash) ? options : {})
49
+ end
50
+
51
+ #
52
+ # オブジェクトの内容を JSON
53
+ #
54
+ # @param [Object] options #to_h を参照
55
+ #
56
+ # @return [String] to_h 結果を JSON文字列化したもの
57
+ #
58
+ def to_json(options={})
59
+ options[:method] = :to_m17n unless options.key?(:method)
60
+ JSON.dump(to_h(options))
61
+ end
62
+
63
+ #
64
+ # _m17n_form のための要素生成
65
+ # @private
66
+ def _to_hash_value(options={})
67
+ method = options[:method]
68
+ if method.kind_of?(Symbol)
69
+ if respond_to?(method, true) && method != :iri
70
+ return send(method)
71
+ elsif registered?
72
+ return iri(options[:prefix])
73
+ end
74
+ end
75
+ self
76
+ end
77
+
78
+ private
79
+ #
80
+ # 時間位置オブジェクトの内容を Hash 化
81
+ #
82
+ # @param [Object] options #to_h を参照
83
+ #
84
+ # @return [Hash]
85
+ # 各クラスの HashProperty に列挙した属性のうち値が false/nil でないものを
86
+ # 属性 =>
87
+ # とする Hash
88
+ #
89
+ def _to_h(options={})
90
+ hash = {}
91
+ self.class::HashProperty.each do |property|
92
+ method, skip = property
93
+ value = respond_to?(method, true) ? send(method) : skip
94
+ hash[method] = value unless value == skip || value.class == skip
95
+ end
96
+ hash
97
+ rescue
98
+ {}
99
+ end
100
+
101
+ #
102
+ # element を 文字列(M17n), Numeric あるいはそれらの Hash や Array に変換したもの
103
+ #
104
+ # @param [Object] element 変換元
105
+ # @param [Hash] options 下記の通り
106
+ # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
107
+ # @option options [Boolean] :camel true ならシンボルを camel case にする
108
+ # @option options [Symbol] :method _to_hash_value で変換に用いるメソッド(:to_m17n, :iri など, 指定なしなら変換しない)
109
+ # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
110
+ # @option options [Boolean] :prefix true ならIRI の先頭部分を簡約表現にする
111
+ #
112
+ # @return [Hash, Array] 変換結果
113
+ #
114
+ # @note element.events のある日付は _event_form で変換する
115
+ #
116
+ def _m17n_form(element, options={})
117
+ result = element.respond_to?(:_event_form) ? element._event_form(self, options) :
118
+ element.respond_to?(:_to_hash_value) ? element._to_hash_value(options) :
119
+ element.respond_to?(:label) && element.label ? element.label :
120
+ case element
121
+ when Hash ; Hash[*(element.keys.inject([]) { |s, k|
122
+ s + [_m17n_form(k, options), _m17n_form(element[k], options)]
123
+ })]
124
+ when Array ; element.map {|e| _m17n_form(e, options)}
125
+ when Class ; When::Parts::Resource._path_with_prefix(element, options[:prefix])
126
+ when Symbol ; options[:camel] ? element.to_s.split(/_/).map {|e| e.capitalize}.join('').to_sym : element
127
+ when Numeric, FalseClass, TrueClass ; element
128
+ else ; element.to_s
129
+ end
130
+ result = When::Locale.translate(result,options[:locale]) if options[:locale] && result.kind_of?(String)
131
+ result
132
+ end
133
+ end
134
+
135
+ class Parts::Timezone
136
+ # When::Parts::Timezone オブジェクトを分かりやすい文字列にして返します
137
+ #
138
+ # @return [String] identifier と同様
139
+ #
140
+ alias :inspect :identifier
141
+ end
142
+
143
+ module Coordinates
144
+
145
+ class Pair
146
+ #
147
+ # to_h のための要素生成
148
+ #
149
+ # @param [Hash] options 下記のとおり
150
+ # @option options [Symbol] :method :to_m17n なら to_s を返す、その他は self を返す
151
+ #
152
+ # @return [String, When::Coordinates::Pair]
153
+ #
154
+ # @private
155
+ def _to_hash_value(options={})
156
+ options[:method] == :to_m17n ? to_s : self
157
+ end
158
+
159
+ # When::Coordinates::Pair オブジェクトを分かりやすい文字列にして返します
160
+ #
161
+ # @return [String] to_s と同様
162
+ #
163
+ def inspect
164
+ When::EncodingConversion.to_external_encoding(to_s)
165
+ end
166
+ end
167
+
168
+ class Residue
169
+ # 多言語対応文字列化
170
+ #
171
+ # @return [When::BasicTypes::M17n]
172
+ #
173
+ def to_m17n
174
+ return m17n(@remainder.to_s) unless label
175
+ return label + "(#{difference})" unless @format
176
+ (label[0...0] + @format) % [label, difference, difference+1]
177
+ end
178
+
179
+ # 文字列化
180
+ #
181
+ # @return [String]
182
+ #
183
+ def to_s
184
+ return @remainder.to_s unless label
185
+ return label.to_s + "(#{difference})" unless @format
186
+ @format.to_s % [label.to_s, difference, difference+1]
187
+ end
188
+
189
+ #
190
+ # week_included のための range の決定
191
+ #
192
+ # @return [Array<Range>]
193
+ #
194
+ # @private
195
+ def _range_for_week_included(date, first, length, block_given=false)
196
+ today = date.floor
197
+ begun = today.succ & self >> first-1
198
+ unless date.frame.equal?(begun.frame)
199
+ begun = (date.frame ^ today).succ & self >> first-1
200
+ middle = today
201
+ end
202
+ ended = begun.succ & self >> length-1
203
+ middle && block_given ? [begun...middle, middle...ended] : [begun...ended]
204
+ end
205
+
206
+ #
207
+ # week_included のためのコラムの生成
208
+ #
209
+ # @return [Array<Object>]
210
+ #
211
+ # @private
212
+ def _column_for_week_included(base, range, opt, &block)
213
+ range.inject([]) {|s,r| s + r.map { |date|
214
+ yield(date, !opt.key?(:Range) || opt[:Range].include?(date.to_i) ? DAY : nil)
215
+ }}
216
+ end
217
+ end
218
+ end
219
+
220
+ class CalendarNote::Week
221
+
222
+ #
223
+ # week_included のための range の決定
224
+ #
225
+ # @return [Array<Range>]
226
+ #
227
+ # @private
228
+ def _range_for_week_included(date, first, length, block_given=false)
229
+ begun = ended = nil
230
+ if first <= 0
231
+ it = enum_for(date.floor, :reverse)
232
+ (1-first).times do
233
+ begun = it.next
234
+ end
235
+ else
236
+ it = enum_for(date.floor, :forward)
237
+ first.times do
238
+ begun = it.next
239
+ end
240
+ end
241
+ it = enum_for(begun, :forward)
242
+ (length+1).times do
243
+ ended = it.next
244
+ end
245
+ [begun...ended]
246
+ end
247
+
248
+ #
249
+ # week_included のためのコラムの生成
250
+ #
251
+ # @return [Array<Object>]
252
+ #
253
+ # @private
254
+ def _column_for_week_included(base, range, opt, &block)
255
+ count = 0
256
+ limit = week(base)[:position][1]
257
+ range.inject([]) {|s,r| s + r.map { |date|
258
+ position = week(date, base)[:position][0]
259
+ (position - count).times do
260
+ yield(nil,nil)
261
+ end
262
+ count = position + 1
263
+ yield(date, !opt.key?(:Range) || opt[:Range].include?(date.to_i) ? DAY : nil) if position < limit
264
+ }}
265
+ end
266
+ end
267
+
268
+ module TM
269
+
270
+ class Calendar
271
+
272
+ # TemporalPosition#strftime のためのデフォルト書式
273
+ #
274
+ # @return [String]
275
+ #
276
+ def strftime
277
+ @strftime ||= '%Y-%m-%d'
278
+ end
279
+ end
280
+
281
+ class TemporalPosition
282
+
283
+ AMPM = ['AM', 'PM'].map {|half| When::Parts::Resource._instance('_m:CalendarFormats::' + half)}
284
+ Format = {
285
+ 'a' => ['%s', 'a'], 'A' => ['%s', 'A'], 'b' => ['%s', 'b'], 'B' => ['%s', 'B'],
286
+ 'c' => When::Parts::Resource._instance('_m:CalendarFormats::DateTime'),
287
+ 'C' => ['%02d', 'C'], 'd' => ['%02d', 'd'], 'D' => '%m/%d/%y', 'e' => ['%2d', 'd'],
288
+ 'E' => ['%s', 'E'], 'F' => ['%s', 'F'], 'G' => ['%d', 'G'], 'g' => ['%02d', 'g'],
289
+ 'h' => '%b', 'H' => ['%02d', 'H'], 'I' => ['%02d', 'I'], 'j' => ['%03d', 'j'],
290
+ 'k' => ['%2d', 'H'], 'l' => ['%2d', 'I'], 'm' => ['%02d', 'm'], 'M' => ['%02d', 'M'],
291
+ 'n' => '\n', 'p' => ['%s', 'p'], 'P' => ['%s', 'P'], 'q' => ['%01d', 'd'],
292
+ 'r' => '%I:%M:%S %p', 'R' => '%H:%M', 's' => ['%d', 's'], 'S' => ['%02d', 'S'],
293
+ 't' => '\t', 'T' => '%H:%M:%S', 'u' => ['%d', 'u'], 'U' => ['%02d', 'U'],
294
+ 'V' => ['%02d', 'V'], 'w' => ['%d', 'w'], 'W' => ['%02d', 'W'],
295
+ 'x' => When::Parts::Resource._instance('_m:CalendarFormats::Date'),
296
+ 'X' => When::Parts::Resource._instance('_m:CalendarFormats::Time'),
297
+ 'y' => ['%02d', 'y'], 'Y' => ['%4d', 'Y'], 'z' => ['%s', 'z'], 'Z' => ['%s', 'Z'],
298
+ '+' => '%a %b %d %T %z %Y'
299
+ }
300
+
301
+ class << self
302
+ # When::TM::TemporalPosition Class のグローバルな設定を行う
303
+ #
304
+ # @param [Hash] format strftime で用いる記号の定義
305
+ #
306
+ # @return [void]
307
+ #
308
+ # @note format の指定がない場合、format は Format(モジュール定数)と解釈する
309
+ #
310
+ def _setup_(format=nil)
311
+ @format = format ? Format.merge(format) : Format
312
+ end
313
+
314
+ # 設定情報を取得する
315
+ #
316
+ # @return [Hash] 設定情報
317
+ #
318
+ def _setup_info
319
+ {:format => format}
320
+ end
321
+
322
+ # strftime で用いる書式のハッシュ
323
+ def format
324
+ @format ||= Format
325
+ end
326
+ end
327
+
328
+ # When::TM::TemporalPosition オブジェクトを分かりやすい文字列にして返します
329
+ #
330
+ # @return [String] to_s と同様
331
+ #
332
+ def inspect
333
+ When::EncodingConversion.to_external_encoding(to_s)
334
+ end
335
+
336
+ #
337
+ # 暦法名
338
+ #
339
+ # @return [Array<Class>] Class 暦法のクラスオブジェクト
340
+ #
341
+ def calendar_name
342
+ [self.class]
343
+ end
344
+
345
+ #
346
+ # 時法名
347
+ # @param [Boolean] prefix true ならIRI の先頭部分を簡約表現にする
348
+ #
349
+ # @return [Array<String>] String 時法の IRI
350
+ #
351
+ def clock_name(prefix=true)
352
+ [clock.iri(prefix)]
353
+ end
354
+
355
+ #
356
+ # 参照ラベル
357
+ #
358
+ # @return [When::BasicTypes::M17n]
359
+ #
360
+ def reference_label
361
+ [When::BasicTypes::M17n.new(self.class.to_s.split(/::/)[-1])]
362
+ end
363
+
364
+ # 含まれる週
365
+ #
366
+ # @overload week_included(ord, wkst, opt, &block)
367
+ # @param [Numeric, Range] ord 週の番号(default: 今週)
368
+ # 今週を 0 とする週番号(Integer) または週番号の範囲(Range)
369
+ # -1 - 先週
370
+ # 0 - 今週
371
+ # +1 - 来週
372
+ # @param [String] wkst 週の開始曜日(defaultは 月曜)
373
+ # @param [When::CalendarNote] wkst 暦注オブジェクト
374
+ # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
375
+ # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
376
+ # @param [Hash] opt 下記の通り
377
+ # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
378
+ # @param [Block] block
379
+ #
380
+ # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
381
+ #
382
+ # @return [Range] 含まれる週を範囲として表現する Range (block 指定なし)
383
+ # @return [Array] 含まれる週の各日をブロックに渡した結果の Array (block 指定あり)
384
+ #
385
+ def week_included(*args, &block)
386
+ begin
387
+ first, length, wkst, opt = _range(args, 'MO')
388
+ range = wkst._range_for_week_included(self, first, length, block_given?)
389
+ rescue RangeError
390
+ range = wkst._range_for_week_included(@frame ^ self, first, length)
391
+ end
392
+
393
+ return range.first unless block_given?
394
+
395
+ wkst._column_for_week_included(self, range, opt, &block).unshift(yield(range[0].first, WEEK)).compact
396
+ end
397
+
398
+ # 含まれる月
399
+ #
400
+ # @overload month_included(ord, wkst, opt, block)
401
+ # @param [Numeric, Range] ord 月の番号(default: 今月)
402
+ # 今月を 0 とする月番号(Integer) または月番号の範囲(Range)
403
+ # -1 - 先月
404
+ # 0 - 今月
405
+ # +1 - 来月
406
+ # @param [String] wkst 週の開始曜日(defaultはなし)
407
+ # @param [When::CalendarNote] wkst 暦注オブジェクト
408
+ # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
409
+ # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
410
+ # @param [Hash] opt 下記の通り
411
+ # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
412
+ # @param [Block] block
413
+ #
414
+ # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
415
+ #
416
+ # @return [Range] 含まれる月を範囲として表現する Range (block 指定なし)
417
+ # @return [Array] 含まれる月の各日をブロックに渡した結果の Array (block 指定あり, wkst なし)
418
+ # @return [Array<Array>] 含まれる月の各日をブロックに渡した結果の 七曜表(block 指定あり, wkst あり)
419
+ #
420
+ def month_included(*args, &block)
421
+ first, length, wkst, opt = _range(args)
422
+ if wkst
423
+ (first...(first+length)).map {|i|
424
+ begun = self.floor(MONTH,DAY) + When::TM::PeriodDuration.new([0,i,0])
425
+ ended = begun + P1M
426
+ ended = ended.prev until begun.cal_date[MONTH-1] == ended.cal_date[MONTH-1]
427
+ if ended.to_i <= begun.to_i
428
+ ended = begun
429
+ loop do
430
+ succ = ended.succ
431
+ break unless succ.frame.equal?(begun.frame)
432
+ ended = succ
433
+ end
434
+ end
435
+ dates = [begun]
436
+ loop do
437
+ current = dates[-1].week_included(wkst)
438
+ if current.last.to_i > ended.to_i
439
+ dates[-1] = ended
440
+ break (dates.map {|date| date.week_included(wkst, {:Range=>begun.to_i..ended.to_i}, &block)}).
441
+ unshift(yield(begun, MONTH)).compact
442
+ elsif wkst.kind_of?(When::Coordinates::Residue)
443
+ dates << dates[-1] + wkst.duration
444
+ else
445
+ it = wkst.enum_for(dates[-1], :forward)
446
+ begin
447
+ date = it.next
448
+ end while date.to_i == dates[-1].to_i
449
+ date = date.to_cal_date unless date.instance_of?(TM::CalDate)
450
+ dates << date
451
+ end
452
+ end
453
+ }
454
+ else
455
+ begun = self.floor(MONTH,DAY) + When::TM::PeriodDuration.new([0, first, 0])
456
+ ended = begun + When::TM::PeriodDuration.new([0, length, 0])
457
+ loop do
458
+ last = ended.prev
459
+ break unless last.cal_date[MONTH-1] == ended.cal_date[MONTH-1]
460
+ ended = last
461
+ end
462
+ if block_given?
463
+ (begun...ended).map do |date|
464
+ yield(date)
465
+ end
466
+ else
467
+ begun...ended
468
+ end
469
+ end
470
+ end
471
+
472
+ # 含まれる年
473
+ #
474
+ # @overload month_included(ord, wkst, opt, block)
475
+ # @param [Numeric, Range] ord 年の番号(default: 今年)
476
+ # 今年を 0 とする年番号(Integer) または年番号の範囲(Range)
477
+ # -1 - 先年
478
+ # 0 - 今年
479
+ # +1 - 来年
480
+ # @param [String] wkst 週の開始曜日(defaultはなし)
481
+ # @param [When::CalendarNote] wkst 暦注オブジェクト
482
+ # @param [Array<When::CalendarNote, String>] wkst 暦注オブジェクトとそのイベントメソッド名
483
+ # (暦注オブジェクトは、そのIRI文字列を指定しても良い)
484
+ # @param [Hash] opt 下記の通り
485
+ # @option opt [Range] :Range 上位繰り返し範囲(ユリウス通日...ユリウス通日)
486
+ # @param [Block] block
487
+ #
488
+ # @note 引数 ord, wkst, opt はそのクラスで位置づけを判断するため、引数の順序は任意(省略も可)
489
+ #
490
+ # @return [Range] 含まれる年を範囲として表現する Range (block 指定なし)
491
+ # @return [Array] 含まれる年の各日をブロックに渡した結果の Array (block 指定あり, wkst なし)
492
+ # @return [Array<Array>] 含まれる年の各日をブロックに渡した結果の 七曜表(block 指定あり, wkst あり)
493
+ #
494
+ def year_included(*args, &block)
495
+ first, length, wkst, opt = _range(args)
496
+ if wkst
497
+ (first...(first+length)).map {|i|
498
+ begun = _force_euqal_year(i)
499
+ ended = _force_euqal_year(i+1)
500
+ current = begun
501
+ result = [yield(begun, YEAR)]
502
+ ended += P1M if ended.floor(MONTH).most_significant_coordinate * 1 == begun.most_significant_coordinate * 1
503
+ while current < ended do
504
+ result << current.month_included(wkst, &block)
505
+ current += P1M
506
+ end
507
+ result.compact
508
+ }
509
+ else
510
+ begun = _force_euqal_year(first)
511
+ ended = _force_euqal_year(first+length)
512
+ if block_given?
513
+ (begun...ended).map do |date|
514
+ yield(date)
515
+ end
516
+ else
517
+ begun...ended
518
+ end
519
+ end
520
+ end
521
+
522
+ # 指定の年初を探す
523
+ def _force_euqal_year(diff)
524
+ year = most_significant_coordinate * 1 + diff
525
+ date = (self + When::TM::PeriodDuration.new([diff,0,0])).floor(YEAR,DAY)
526
+ done = {}
527
+ loop do
528
+ case
529
+ when date.most_significant_coordinate * 1 == year
530
+ return date
531
+ when date.most_significant_coordinate * 1 > year
532
+ next_date = (date-When::P1Y).floor(YEAR,DAY)
533
+ date = (date.to_i == next_date.to_i) ?
534
+ (date-When::P1Y*2).floor(YEAR,DAY) :
535
+ next_date
536
+ else
537
+ next_date = (date+When::P1Y).floor(YEAR,DAY)
538
+ date = (date.to_i == next_date.to_i) ?
539
+ (date+When::P1Y*2).floor(YEAR,DAY) :
540
+ next_date
541
+ end
542
+ raise RangeError, "can't find target date: #{self} -> #{year}" if done.key?(date.to_i)
543
+ done[date.to_i] = true
544
+ end
545
+ end
546
+ private :_force_euqal_year
547
+
548
+ # 範囲の取得
549
+ def _range(args, wkst=nil)
550
+ ord = 0
551
+ opt = {}
552
+ args.each do |arg|
553
+ case arg
554
+ when Integer, Range ; ord = arg
555
+ when Hash ; opt = arg
556
+ else ; wkst = arg
557
+ end
558
+ end
559
+ wkst, method = wkst
560
+ wkst = When::Coordinates::Residue.day_of_week(wkst) || When.CalendarNote(wkst) if wkst
561
+ wkst = wkst[method] if method
562
+ return ord, 1, wkst, opt if ord.kind_of?(Integer)
563
+ length = ord.last - ord.first
564
+ length += 1 unless ord.exclude_end?
565
+ return ord.first, length, wkst, opt
566
+ end
567
+ private :_range
568
+
569
+ #
570
+ # 時間位置オブジェクトの暦注を取得し value を暦注の値 (String, When::BasicTypes::M17n or When::Coordinates::Residue)とする Hash で表現
571
+ #
572
+ # @param [String] options { :notes => String } という Hash の指定と等価
573
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
574
+ # @param [Hash] options 下記のとおり
575
+ # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
576
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
577
+ #
578
+ # @return [Array<Array<Hash{:note=>note, :value=>value}>>]
579
+ # [ note [String, When::BasicTypes::M17n] 暦注名 ]
580
+ # [ value [String, When::BasicTypes::M17n, When::Coordinates::Residue] 暦注の値 ]
581
+ #
582
+ def notes(options={})
583
+ form_options = options.kind_of?(Hash) ? options : {}
584
+ form_options[:method] = :to_m17n unless form_options.key?(:method)
585
+ persistence = options.delete(:persistence) if options.kind_of?(Hash)
586
+ retrieved = When::CalendarNote::NotesContainer.retrieve(persistence, self.to_i)
587
+ return retrieved unless retrieved == false
588
+ When::CalendarNote::NotesContainer.register(_m17n_form(_notes(options), form_options), persistence, self.to_i)
589
+ end
590
+
591
+ #
592
+ # 時間位置オブジェクトの暦注を取得
593
+ #
594
+ # @param [String] options { :notes => String } という Hash の指定と等価
595
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
596
+ # @param [Hash] options 下記のとおり
597
+ # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
598
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
599
+ #
600
+ # @return [Array<Array<Hash{:note=>note, :value=>value}>>]
601
+ # [ note [String, When::BasicTypes::M17n, When::Coordinates::Residue] 暦注名 ]
602
+ # [ value [String, When::BasicTypes::M17n, When::Coordinates::Residue, When::TM::TemporalPosition] 暦注の値 ]
603
+ #
604
+ # @note
605
+ # When::TM::TemporalPosition の場合、events[0] に暦注名の入ったその暦注に該当する日付である。
606
+ # (例) Christian クラス easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
607
+ # 暦注サブクラスの場合、要素が増えたり、:note の暦注要素の型が変わったりすることがある。
608
+ #
609
+ def _notes(options={})
610
+ _calendar_note(options).notes(self, options)
611
+ end
612
+
613
+ #
614
+ # 暦注の一致 or 不一致
615
+ #
616
+ # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
617
+ # (指定の notes が存在する場合は前者、しない場合は後者)
618
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
619
+ # @param [Hash] options 下記のとおり
620
+ # @option options [Object] :value 確認する暦注の値
621
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
622
+ #
623
+ # @return [Boolean]
624
+ # [ true - 暦注が一致 ]
625
+ # [ false - 暦注が不一致 ]
626
+ #
627
+ def note?(options={})
628
+ _calendar_note(options).note?(self, options)
629
+ end
630
+
631
+ # 指定の日時が指定イベントに該当するか?
632
+ #
633
+ # @overload is?(event=nil, options={})
634
+ # @param [String] event options={:notes=>String} または {:notes=>String} または {:value=>String} という指定と等価
635
+ # (指定の event が存在する場合は前者、指定の notes が存在する場合は中央、しない場合は後者)
636
+ # @param [Integer] event options={ :indices=> Integer } という指定と等価
637
+ # @param [Hash] options 下記のとおり
638
+ # @option options [When::CalendarNote or String] :calendar_note 該当判断に用いる CalendarNoteオブジェクトまたはその IRI
639
+ # @option options [String] :event 確認するイベント名
640
+ # @option options [Object] :value 確認する暦注の値
641
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
642
+ #
643
+ # @return [Boolean]
644
+ # [ true - 該当する ]
645
+ # [ false - 該当しない ]
646
+ #
647
+ def is?(*args)
648
+ options = args.last.kind_of?(Hash) ? args.pop.dup : {}
649
+ note = _calendar_note(options)
650
+ event = args.first || options.delete(:event) || note.event
651
+ return note.note?(self, options) unless options.empty?
652
+ return note.note?(self, event) unless event.to_s =~ /\A([^\d]+)/ && note.respond_to?($1.downcase)
653
+ return note.include?(self, event)
654
+ end
655
+
656
+ #
657
+ # 時間位置オブジェクトの内容を Hash
658
+ #
659
+ # @param [String] options { :notes => String } という Hash の指定と等価
660
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
661
+ # @param [Hash] options 下記のとおり
662
+ # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
663
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
664
+ #
665
+ # @return [Hash]
666
+ # - :sdn 日の通し番号 - ユリウス通日(Integer)
667
+ # - :calendar calendar_name の結果 - Array<暦法または暦年代(, 付属情報..)>
668
+ # - :notes Hash (の Array (の Array)) - _notes(options)
669
+ # clock が定義されている場合、さらに下記も出力する
670
+ # - :clock 時計(When::Parts::Timezone::Base)
671
+ # - :clk_time to_clock_time の結果 - ( 日, 時, 分, 秒 )
672
+ # - :dynamical dynamical_time / 秒
673
+ # - :universal universal_time / 秒
674
+ #
675
+ def _to_h(options={})
676
+ hash = super.update({
677
+ :sdn => to_i,
678
+ :calendar => calendar_name,
679
+ :notes => _notes(options)
680
+ })
681
+
682
+ hash.update({
683
+ :clock => clock,
684
+ :clk_time => to_clock_time,
685
+ :dynamical => dynamical_time * clock.second,
686
+ :universal => universal_time * clock.second
687
+ }) if clock
688
+ hash
689
+ end
690
+
691
+ # 指定の書式による多言語対応文字列化 - pattern で指定した書式で多言語対応文字列化する
692
+ #
693
+ # @param [When::BasicTypes::M17n] pattern 書式
694
+ # @param [String, Array<String>] locale 文字列化を行う locale の指定(デフォルト : オブジェクト生成時に保持している locale すべて)
695
+ #
696
+ # @return [When::BasicTypes::M17n]
697
+ #
698
+ def strftime(pattern=@frame.strftime, locale=nil)
699
+ pattern = m17n([pattern]*self.keys.length, nil, nil, {:locale=>self.keys}) if pattern.instance_of?(String)
700
+ pattern._printf([], locale) do |k, *t|
701
+ _strftime(k, pattern, [''])
702
+ end
703
+ end
704
+
705
+ # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
706
+ #
707
+ # @overload to_m17n()
708
+ #
709
+ # @return [When::BasicTypes::M17n]
710
+ #
711
+ def to_m17n(*args)
712
+ return m17n(I[@indeterminated_position]) if [Unknown, Max, Min].include?(@indeterminated_position)
713
+ return m17n(_to_s)
714
+ end
715
+ # @private
716
+ alias :_to_s :to_s
717
+
718
+ # 文字列化 - When.exe Standard Representation により文字列化する
719
+ #
720
+ # @overload to_s()
721
+ #
722
+ # @return [String]
723
+ #
724
+ def to_s(*args)
725
+ to_m17n(*args).to_s
726
+ end
727
+
728
+ # URI要素化 - URI表現の要素として用いる形式に変換
729
+ #
730
+ # @overload to_uri()
731
+ #
732
+ # @return [String]
733
+ #
734
+ def to_uri(*args)
735
+ _to_uri(to_s(*args))
736
+ end
737
+
738
+ # URI要素化 - URI表現の要素として用いる形式に変換(“^”はエスケープ)
739
+ #
740
+ # @overload to_uri_escape()
741
+ #
742
+ # @return [String]
743
+ #
744
+ def to_uri_escape(*args)
745
+ _to_uri(to_s(*args)).gsub('^', '%5E')
746
+ end
747
+
748
+ private
749
+
750
+ # URI要素化 - URI表現用のEscapeと frame の追加
751
+ #
752
+ # @param [String] date 日付の表現
753
+ #
754
+ # @return [String]
755
+ #
756
+ def _to_uri(date)
757
+ (date.gsub(/\./, '-').gsub(/%/, '@') + caret_frame).
758
+ gsub(When::Parts::Resource::IRIEncode) {|c| When::Parts::Resource::IRIEncodeTable[c]}
759
+ end
760
+
761
+ # caret 付きの frame
762
+ #
763
+ # @return [String]
764
+ #
765
+ # @note 暦年代付きかまたは frame がグレゴリオ暦の場合は空文字列を返す
766
+ #
767
+ def caret_frame
768
+ prefix = When::Parts::Resource.base_uri + 'CalendarTypes/'
769
+ path = frame.iri
770
+ return '' if @calendar_era_props || path == prefix + 'Gregorian'
771
+ path = path[prefix.length..-1] if path.index(prefix) == 0
772
+ '^^' + path
773
+ end
774
+
775
+ # strftime で扱う項の値を取得する
776
+ #
777
+ # @param [String] designator 項目名
778
+ # @param [String] locale 文字列化を行う場合の locale の指定(デフォルト to_s(代表値))
779
+ # @param [Integer] d 日付が'年月日'、時刻が'時分秒'でない表現のための桁位置変更指示
780
+ # [ 年月に付く場合 - 大きいほうに位置をずらす ]
781
+ # [ 分秒に付く場合 - 小さいほうに位置をずらす ]
782
+ # @param [Integer] e 月の省略名の文字数
783
+ #
784
+ # @return [designator に依存]
785
+ #
786
+ def _term(designator, locale=nil, d=0, e=3)
787
+ designator = When::Locale.translate(designator,locale)
788
+ case designator
789
+ # 現在のロケールにおける曜日の省略名
790
+ when 'a' ; When.Resource('_co:Common::Abbr_Day')[to_i % 7].translate(locale)
791
+ # 現在のロケールにおける曜日の完全な名前
792
+ when 'A' ; When.Resource('_co:Common::Week')[to_i % 7].label.translate(locale)
793
+ when 'b' ; (name(MONTH-d).translate(locale))[/\A.{1,#{e}}/] # 現在のロケールにおける月の省略名
794
+ when 'B' ; (name(MONTH-d).translate(locale)) # 現在のロケールにおける月の完全な名前
795
+ when 'C' ; year(d).div(100) # 世紀 (西暦年の上 2 桁)
796
+ when 'd' ; day(d) # 月内通算日
797
+ when 'E' ; Array(@calendar_era_props)[0].translate(locale)# 年号
798
+ when 'F' ; floor(DAY).to_m17n.translate(locale) # ISO 8601 形式の日付フォーマット
799
+ when 'G' ; cwyear(d) # ISO 8601 週単位表記の年
800
+ when 'g' ; cwyear(d) % 100 # ISO 8601 週単位表記の年の下2桁
801
+ when 'H' ; hour(d) # 24 時間表記での時
802
+ when 'I' ; (hour(d)-1) % 12 + 1 # 12 時間表記での時
803
+ when 'j' ; yday(d) # 年の初めから通算の日数
804
+ when 'm' ; month(d) # 月
805
+ when 'M' ; minute(d) #
806
+ when 'p' ; (AMPM[hour(d).to_i.div(12)].translate(locale)).upcase # 現在のロケールにおける「午前」「午後」に相当する文字列
807
+ when 'P' ; (AMPM[hour(d).to_i.div(12)].translate(locale)).downcase # 前項を小文字で表記
808
+ when 's' ; universal_time / Duration::SECOND # 紀元 (1970-01-01T00:00:00Z) からの秒数
809
+ when 'S' ; second(d) # 秒 (10 進数表記)
810
+ when 'u' ; cwday # 週の何番目の日か(月曜日を 1 とする)
811
+ when 'U' ; yweek(6, 7, d) # 年の初めからの通算の週番号(日曜日始まり)
812
+ when 'V' ; cweek(d) # ISO 8601 形式での年の始めからの週番号
813
+ when 'w' ; wday # 週の何番目の日 か(日曜日を 0 とする)
814
+ when 'W' ; yweek(0, 7, d) # 年の初めからの通算の週番号(月曜日始まり)
815
+ when 'y' ; year(d) % 100 # 西暦の下2桁 (世紀部分を含まない年)
816
+ when 'Y' ; year(d) # 世紀部分を含めた ( 4 桁の) 西暦年
817
+ when 'z' ; clock.to_basic # +hhmm や -hhmm の形式のタイムゾーン
818
+ when 'Z' ; When::Locale.translate(clock.tzname[0],locale) # タイムゾーンまたはゾーン名または省略名
819
+ else ; designator
820
+ end
821
+ end
822
+
823
+ # 指定の書式による多言語対応文字列化
824
+ #
825
+ # @param [String] locale 文字列化を行う locale
826
+ # @param [When::BasicTypes::M17n] pattern 書式
827
+ #
828
+ # @return [Array] 書式と文字列化項目からなる配列
829
+ #
830
+ def _strftime(locale, pattern, t)
831
+ format, *terms = t
832
+ pattern = pattern.translate(locale) if pattern.kind_of?(When::BasicTypes::M17n)
833
+ pattern.scan(/(%[O\d]*(?:\.(\d+))?.)|(.)/) do |c,e,s|
834
+ case c
835
+ when /\A%%/
836
+ format += '%%'
837
+ when /\A%/
838
+ action = TemporalPosition.format[c[-1..-1]]
839
+ case action
840
+ when Array
841
+ format += action[0]
842
+ terms << _term(action[1], locale, c[1..-2].to_i, e||3)
843
+ when String
844
+ action = action.translate(locale) if action.kind_of?(When::BasicTypes::M17n)
845
+ if (action =~ /%/)
846
+ format, *terms = _strftime(locale, action, [format] + terms)
847
+ else
848
+ format += action
849
+ end
850
+ end
851
+ else
852
+ format += s
853
+ end
854
+ end
855
+ [format] + terms
856
+ end
857
+
858
+ #
859
+ # 使用する When::CalendarNote を決定する
860
+ #
861
+ # options に副作用があることに注意
862
+ #
863
+ def _calendar_note(options)
864
+ calendar_note = options.delete(:calendar_note) if options.kind_of?(Hash)
865
+ calendar_note ||= @frame ? @frame.note : 'JulianDay'
866
+ When.CalendarNote(calendar_note)
867
+ end
868
+ end
869
+
870
+ class JulianDate
871
+
872
+ # 多言語対応文字列化 - ユリウス日を多言語対応文字列化する
873
+ #
874
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
875
+ #
876
+ # @return [When::BasicTypes::M17n]
877
+ #
878
+ def to_m17n(precision=@precision)
879
+ return m17n(to_s(precision))
880
+ end
881
+
882
+ # 文字列化 - ユリウス日を文字列化する
883
+ #
884
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
885
+ #
886
+ # @return [String]
887
+ #
888
+ def to_s(precision=@precision)
889
+ coordinate = (precision <= When::DAY) ? to_i : to_f
890
+ coordinate.to_s
891
+ end
892
+ end
893
+
894
+ class ClockTime
895
+
896
+ # 要素の多言語対応文字列化
897
+ #
898
+ # @param [Integer] index 多言語対応文字列化する要素の指定
899
+ # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
900
+ #
901
+ # @return [When::BasicTypes::M17n]
902
+ #
903
+ def name(index, format=nil)
904
+ digit = _digit(index) {|digit| digit > DAY}
905
+ coordinate = @clk_time[digit]
906
+ return m17n(format % coordinate) if format
907
+
908
+ indices = @frame.indices[digit-1]
909
+ if indices
910
+ trunk = indices.trunk
911
+ branch = indices.branch
912
+ end
913
+ format = branch ? m17n("%02d:") : "%02d"
914
+ return m17n(format % coordinate) unless trunk
915
+ trunk = trunk[coordinate * 1]
916
+ return m17n(trunk) unless branch
917
+ return trunk.prefix(branch[coordinate * 0])
918
+ end
919
+
920
+ # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
921
+ #
922
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
923
+ #
924
+ # @return [When::BasicTypes::M17n]
925
+ #
926
+ def to_m17n(precision=@precision)
927
+ time = m17n('T' + _time_to_s(precision))
928
+ if @frame
929
+ time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
930
+ end
931
+ return time
932
+ end
933
+
934
+ # 文字列化 - When.exe Standard Representation により文字列化する
935
+ #
936
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
937
+ #
938
+ # @return [String]
939
+ #
940
+ def to_s(precision=@precision)
941
+ time = 'T' + _time_to_s(precision)
942
+ if @frame
943
+ time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
944
+ end
945
+ return time
946
+ end
947
+
948
+ #
949
+ #
950
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
951
+ #
952
+ # @return [Numeric] 自身の「時」
953
+ #
954
+ def hour(d=0)
955
+ @clk_time[HOUR+d]
956
+ end
957
+
958
+ #
959
+ #
960
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
961
+ #
962
+ #
963
+ # @return [Numeric] 自身の「分」
964
+ #
965
+ def minute(d=0)
966
+ @clk_time[MINUTE+d]
967
+ end
968
+ alias :min :minute
969
+
970
+ #
971
+ #
972
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
973
+ #
974
+ #
975
+ # @return [Numeric] 自身の「秒」
976
+ #
977
+ def second(d=0)
978
+ @clk_time[SECOND+d]
979
+ end
980
+ alias :sec :second
981
+
982
+ #protected
983
+ #
984
+ # 時間帯以外の部分の文字列化
985
+ #
986
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
987
+ #
988
+ # @return [String]
989
+ #
990
+ # @private
991
+ def _time_to_s(precision=@precision)
992
+ terms = []
993
+ format = ''
994
+ format += Pair::DL2[@clk_time[0] * 0] || ':' if @frame.pair[0] || @clk_time[0].kind_of?(Pair)
995
+
996
+ # 時分
997
+ digits = [@clk_time.length-2, precision].min
998
+ if digits > 0
999
+ terms += @clk_time[1..-1]
1000
+ format += "%02d:" * digits
1001
+ format = format[0..-2] if precision == digits
1002
+ end
1003
+
1004
+ #
1005
+ digits = [precision - @clk_time.length + 1, STRING-SECOND].min
1006
+ if digits == 0
1007
+ format += "%02d"
1008
+ elsif digits > 0
1009
+ factor = 10**digits
1010
+ terms[-1] = ((@clk_time[-1] + 1E-6) * factor).floor.to_f / factor # 切り捨て(10で割る丸めガードあり)
1011
+ format += "%02.#{digits}f"
1012
+ end
1013
+
1014
+ # 結果
1015
+ time = Pair._format([format] + terms)
1016
+ time.sub(/([^\d])(\d)\./, '\10\2.')
1017
+ end
1018
+
1019
+ #
1020
+ # to_h のための要素生成
1021
+ # @private
1022
+ def _to_hash_value(options={})
1023
+ clk_time.map {|e| _m17n_form(e, options) }
1024
+ end
1025
+ end
1026
+
1027
+ class CalDate
1028
+
1029
+ #
1030
+ # 暦法名
1031
+ #
1032
+ # @return [Array] ( name, epoch, reverse, go back )
1033
+ # - name 暦法または暦年代 ({When::TM::Calendar}, {When::TM::CalendarEra})
1034
+ # - epoch 暦元 (Integer)
1035
+ # - reverse 暦年の順序 (Boolean)
1036
+ # [ false, nil 昇順 ]
1037
+ # [ true 降順 ]
1038
+ # - go back 参照イベントより前の暦日か(Boolean)
1039
+ # [ false, nil ]
1040
+ # [ true 然り ]
1041
+ #
1042
+ def calendar_name
1043
+ void, epoch, reverse, back = @calendar_era_props
1044
+ name = [@calendar_era || @frame, epoch, reverse, back]
1045
+ name.pop until name[-1]
1046
+ return name
1047
+ end
1048
+
1049
+ #
1050
+ # 参照ラベル
1051
+ #
1052
+ # @return [When::BasicTypes::M17n]
1053
+ #
1054
+ def reference_label
1055
+ return @calendar_era.hierarchy.map {|e| e.label} if @calendar_era
1056
+ return [@frame.label] if @frame.label
1057
+ [When::BasicTypes::M17n.new(@frame.class.to_s.split(/::/)[-1])]
1058
+ end
1059
+
1060
+ #
1061
+ # 時間座標値
1062
+ #
1063
+ # @return [Numeric] オブジェクトの precision に対応する時間座標の値
1064
+ #
1065
+ def coordinate
1066
+ raise ArgumentError, "Presicion not defined" unless When::Coordinates::PERIOD_NAME[@precision]
1067
+ self[@precision]
1068
+ end
1069
+
1070
+ #
1071
+ # 自身に所属する When::TM::CalDate オブジェクト
1072
+ #
1073
+ # @return [Array<When::TM::CalDate>] 自身に所属する precision+1 の分解能のオブジェクトの Array
1074
+ #
1075
+ # @note precision が 0(When::DAY) の場合、空 Array を返す
1076
+ #
1077
+ def member
1078
+ raise ArgumentError, "Presicion not defined" unless When::Coordinates::PERIOD_NAME[@precision]
1079
+ child = floor(@precision+1)
1080
+ list = []
1081
+ while self == child
1082
+ list << child
1083
+ child = child.succ
1084
+ end
1085
+ list
1086
+ end
1087
+
1088
+ #
1089
+ # Hash 化
1090
+ #
1091
+ # @param [String] options { :notes => String } という Hash の指定と等価
1092
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
1093
+ # @param [Hash] options 下記のとおり
1094
+ # @option options [When::CalendarNote, String] :calendar_note 暦注を計算する暦注オブジェクトまたはそのIRI
1095
+ # @option options [Object] その他のキー {When::CalendarNote#notes} を参照
1096
+ #
1097
+ # @return [Hash]
1098
+ # - :calendar calendar_name の結果 ( name, epoch, reverse, go back )
1099
+ # - name 暦法または暦年代 ({When::TM::Calendar}, {When::TM::CalendarEra})
1100
+ # - epoch 暦元 (Integer)
1101
+ # - reverse 暦年の順序 (Boolean)
1102
+ # [ false, nil 昇順 ]
1103
+ # [ true 降順 ]
1104
+ # - go back 参照イベントより前の暦日か(Boolean)
1105
+ # [ false, nil 否 ]
1106
+ # [ true 然り ]
1107
+ # - :cal_date cal_date の内容 (year, month, day)
1108
+ # [ year - 年 ({Numeric}) ]
1109
+ # [ month - 月 ({Numeric}) ]
1110
+ # [ day - 日 ({Numeric}) ]
1111
+ # - :clk_time to_clock_time の結果 ( 日, 時, 分, 秒 )
1112
+ # - :notes Hash (の Array (の Array)) - _notes(options)
1113
+ #
1114
+ def _to_h(options={})
1115
+ super.update({:cal_date=>@cal_date})
1116
+ end
1117
+
1118
+ # 要素の多言語対応文字列化
1119
+ #
1120
+ # @param [Integer] index 多言語対応文字列化する要素の指定
1121
+ # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
1122
+ #
1123
+ # @return [When::BasicTypes::M17n]
1124
+ #
1125
+ def name(index, format=nil)
1126
+ digit = _digit(index) {|digit| digit <= DAY}
1127
+ coordinate = @cal_date[digit-1]
1128
+ return m17n(format % coordinate) if format
1129
+
1130
+ indices = @frame.indices[digit-1]
1131
+ if indices
1132
+ trunk = indices.trunk
1133
+ branch = indices.branch
1134
+ end
1135
+ format = branch ? m17n("%02d-") : "%02d"
1136
+ return m17n(format % coordinate) unless trunk
1137
+ trunk = trunk[coordinate * 1]
1138
+ return m17n(trunk) unless branch
1139
+ return trunk.prefix(branch[coordinate * 0||0])
1140
+ end
1141
+
1142
+ #
1143
+ #
1144
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1145
+ #
1146
+ # @return [Numeric] 自身の「日」
1147
+ #
1148
+ def day(d=0)
1149
+ @cal_date[DAY-1-d]
1150
+ end
1151
+
1152
+ # 月内通日
1153
+ #
1154
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1155
+ #
1156
+ # @return [Numeric] 自身の「月内通日」(1始まり)
1157
+ #
1158
+ def mday(d=0)
1159
+ to_i - floor(MONTH-d).to_i + 1
1160
+ end
1161
+
1162
+ # 年内通日
1163
+ #
1164
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1165
+ #
1166
+ # @return [Numeric] 自身の「年内通日」(1始まり)
1167
+ #
1168
+ def yday(d=0)
1169
+ to_i - floor(YEAR-d).to_i + 1
1170
+ end
1171
+
1172
+ # 七曜
1173
+ #
1174
+ # @return [Numeric] 自身の「七曜」(日曜 0 始まり)
1175
+ #
1176
+ def wday
1177
+ (to_i + 1) % 7
1178
+ end
1179
+
1180
+ # 七曜(暦週)
1181
+ #
1182
+ # @return [Numeric] 自身の「七曜」(月曜 1 始まり)
1183
+ #
1184
+ def cwday
1185
+ (to_i % 7) + 1
1186
+ end
1187
+
1188
+ # 暦週
1189
+ #
1190
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1191
+ #
1192
+ # @return [Numeric] 自身の「暦週」
1193
+ #
1194
+ def cweek(d=0)
1195
+ [1,0,-1].each do |i|
1196
+ start = ((self + PeriodDuration.new(i, YEAR-d)).floor(YEAR-d,DAY) + PeriodDuration.new(4, DAY)) & Residue.new(0,7,-1)
1197
+ return ((to_i - start.to_i).div 7) + 1 if self >= start
1198
+ end
1199
+ raise IndexError, 'Cannot decide year number'
1200
+ end
1201
+
1202
+ # 月内通週
1203
+ #
1204
+ # @param [Integer] w 週の最初の曜日(0:月,.., 6:日)
1205
+ # @param [Integer] m 一週間の日数
1206
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1207
+ #
1208
+ # @return [Numeric] 自身の「月内通週」(その月に完全に含まれる最初の週を1とする)
1209
+ #
1210
+ def mweek(w=6, m=7, d=0)
1211
+ 1 + (to_i - (floor(MONTH-d,DAY) & Residue.new(w,m)).to_i).div(7)
1212
+ end
1213
+
1214
+ # 年内通週
1215
+ #
1216
+ # @param [Integer] w 週の最初の曜日(0:月,.., 6:日)
1217
+ # @param [Integer] m 一週間の日数
1218
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1219
+ #
1220
+ # @return [Numeric]
1221
+ # 自身の「年内通週」(その年に完全に含まれる最初の週を1とする)
1222
+ #
1223
+ def yweek(w=6, m=7, d=0)
1224
+ 1 + (to_i - (floor(YEAR-d,DAY) & Residue.new(w,m)).to_i).div(7)
1225
+ end
1226
+
1227
+ #
1228
+ #
1229
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1230
+ #
1231
+ # @return [Numeric] 自身の「月」
1232
+ #
1233
+ def month(d=0)
1234
+ @cal_date[MONTH-1-d]
1235
+ end
1236
+ alias :mon :month
1237
+
1238
+ # 年内通月
1239
+ #
1240
+ # @param [Integer] d1 日付が'年月日'でない表現のための桁位置変更指示-年用(大きいほうに位置をずらす)
1241
+ # @param [Integer] d2 日付が'年月日'でない表現のための桁位置変更指示-月用(大きいほうに位置をずらす)
1242
+ #
1243
+ # @return [Numeric] 自身の「年内通月」(1始まり)
1244
+ #
1245
+ def ymon(d1=0, d2=0)
1246
+ current = floor(YEAR-d1, MONTH-d2)
1247
+ @frame._length(@cal_date[(YEAR-1-d1)...(MONTH-1-d2)]).times do |i|
1248
+ return i+1 if current == self
1249
+ current = current.succ
1250
+ end
1251
+ raise IndexError, 'Cannot decide month number'
1252
+ end
1253
+
1254
+ #
1255
+ #
1256
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1257
+ #
1258
+ # @return [Numeric] 自身の「年」
1259
+ #
1260
+ def year(d=0)
1261
+ @cal_date[YEAR-1-d]
1262
+ end
1263
+
1264
+ # 暦週の年
1265
+ #
1266
+ # @param [Integer] d 日付が'年月日'でない表現のための桁位置変更指示(大きいほうに位置をずらす)
1267
+ #
1268
+ # @return [Numeric] 自身の「暦週の年」
1269
+ #
1270
+ def cwyear(d=0)
1271
+ [1,0,-1].each do |i|
1272
+ start = ((self + PeriodDuration.new(i, YEAR-d)).floor(YEAR-d,DAY) + PeriodDuration.new(4, DAY)) & Residue.new(0,7,-1)
1273
+ return year(d)+i if self >= start
1274
+ end
1275
+ raise IndexError, 'Cannot decide year number'
1276
+ end
1277
+
1278
+ # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1279
+ #
1280
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1281
+ # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1282
+ #
1283
+ # @return [When::BasicTypes::M17n]
1284
+ #
1285
+ def to_m17n(precision=@precision, round=false)
1286
+ date = m17n(_date_to_s(precision))
1287
+ return date unless @calendar_era
1288
+ return _parent_labels.inject(m17n(calendar_era_name)) {|era_name, parent|
1289
+ era_name.prefix(m17n(parent) + '::')
1290
+ } + date
1291
+ end
1292
+
1293
+ # 文字列化 - When.exe Standard Representation により文字列化する
1294
+ #
1295
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1296
+ # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1297
+ #
1298
+ # @return [String]
1299
+ #
1300
+ def to_s(precision=@precision, round=false)
1301
+ date = _date_to_s(precision)
1302
+ return date unless @calendar_era
1303
+ return _parent_labels.inject(calendar_era_name.to_s) {|era_name, parent|
1304
+ parent.to_s + '::' + era_name
1305
+ } + date
1306
+ end
1307
+
1308
+ # event を 文字列化 - 日時で与えられた event を文字列化する
1309
+ #
1310
+ # @param [When::TM::TemporalPosition] other 時系の歩度を比較する基準(nilは比較しない)
1311
+ # @param [Hash] options 下記の通り
1312
+ # @option options [Numeric] :precision イベント名(イベント)出力の場合の時刻の丸め位置(nilなら丸めない)
1313
+ # @option options [Symbol] :method 変換に用いるメソッド(:to_m17n のとき多言語文字列化)
1314
+ #
1315
+ # @return [String]
1316
+ #
1317
+ # @note
1318
+ # events 配列なし - 日時をそのまま文字列化
1319
+ # 日時の精度が日より細かい - イベント名(イベント時刻)
1320
+ # 日時の精度が日 - イベント名(当日までの経過日数)
1321
+ #
1322
+ def _event_form(other=nil, options={})
1323
+ return options[:method]==:to_m17n ? to_m17n : self unless events
1324
+ return events[0] + '(' + _clk_time_for_inspect(options[:precision]).
1325
+ to_s(options[:precision] || precision)[/[:*=0-9]+/] + ')' if precision > When::DAY
1326
+ return events[0] unless other
1327
+ other = JulianDate.dynamical_time(other.dynamical_time,
1328
+ {:time_standard=>time_standard}) unless time_standard.rate_of_clock == other.time_standard.rate_of_clock
1329
+ events[0] + '(' + (other.to_i - to_i).to_s + ')'
1330
+ end
1331
+
1332
+ private
1333
+
1334
+ # 日付の年号に曖昧性がある場合の親年号の label の Array
1335
+ #
1336
+ # @return [Array<String>]
1337
+ #
1338
+ def _parent_labels
1339
+ return [] unless (area = When::TM::CalendarEra[nil]) &&
1340
+ (period = area[nil])
1341
+ list = []
1342
+ era = @calendar_era
1343
+ while (labels = period[era.label.to_s]) &&
1344
+ (epoch = labels[era.epoch_year]) &&
1345
+ (epoch.size > 1) &&
1346
+ (parent = era.parent).respond_to?(:epoch_year)
1347
+ list << parent.label
1348
+ era = parent
1349
+ end
1350
+ list
1351
+ end
1352
+
1353
+ # 日付の年号以外の部分を文字列化する
1354
+ #
1355
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1356
+ #
1357
+ # @return [String]
1358
+ #
1359
+ def _date_to_s(precision)
1360
+ # 準備
1361
+ precision = [precision, 1 - @cal_date.length].max
1362
+ precision = [precision, DAY].min
1363
+ terms = []
1364
+ ext_dg = [(@extra_year_digits||1).to_i, 0].max
1365
+ year_dg = @frame.indices.length <= 2 ? 4 : 2
1366
+
1367
+ #
1368
+ year_by_epoch = @cal_date[0]
1369
+ if @calendar_era_props
1370
+ era, epoch, reverse = @calendar_era_props
1371
+ year_in_term = reverse ? -year_by_epoch : year_by_epoch
1372
+ year_by_calendar = epoch + year_by_epoch if epoch
1373
+ terms << year_in_term
1374
+ format = (0..99) === (year_in_term * 1) ? "%02d." : "%0#{year_dg}d."
1375
+ if year_by_calendar && year_by_calendar != year_in_term
1376
+ terms << (year_by_calendar * 1)
1377
+ format += "(%0#{year_dg}d)"
1378
+ end
1379
+ else
1380
+ terms << year_by_epoch
1381
+ format = (0..(10**year_dg-1)) === (year_by_epoch * 1) ? "%0#{year_dg}d." : "%+0#{5+ext_dg}d."
1382
+ end
1383
+
1384
+ # 月日
1385
+ ((1-@cal_date.length)..-1).each do |i|
1386
+ break if (i >= precision)
1387
+ terms << @cal_date[i]
1388
+ format += "%02d."
1389
+ end
1390
+
1391
+ # 結果
1392
+ date = Pair._format([format] + terms)
1393
+ date.sub!(/([^\d])\(([-+\d]+)\)/, '(\2)\1') if era
1394
+ date = date[0..-2] unless @frame.pair[precision-1] || date[-1..-1] != '.'
1395
+ date.gsub!(/\./, '-') if (@frame.indices.length <= 3) && !era
1396
+ return date
1397
+ end
1398
+ end
1399
+
1400
+ class DateAndTime < CalDate
1401
+
1402
+ # 要素の多言語対応文字列化
1403
+ #
1404
+ # @param [Integer] index 多言語対応文字列化する要素の指定
1405
+ # @param [When::BasicTypes::M17n] format 多言語対応文字列化の書式
1406
+ #
1407
+ # @return [When::BasicTypes::M17n]
1408
+ #
1409
+ def name(index, format=nil)
1410
+ digit = _digit(index)
1411
+ (digit <= DAY) ? super : @clk_time.name(digit, format)
1412
+ end
1413
+
1414
+ # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1415
+ #
1416
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1417
+ # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1418
+ # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1419
+ #
1420
+ # @return [When::BasicTypes::M17n]
1421
+ #
1422
+ def to_m17n(precision=@precision, round=false)
1423
+ super + _clk_time_for_inspect(round ? precision : nil).to_m17n(precision)
1424
+ end
1425
+
1426
+ # 文字列化 -When.exe Standard Representation により文字列化する
1427
+ #
1428
+ # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1429
+ # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1430
+ # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1431
+ #
1432
+ # @return [String]
1433
+ #
1434
+ def to_s(precision=@precision, round=false)
1435
+ super + _clk_time_for_inspect(round ? precision : nil).to_s(precision)
1436
+ end
1437
+
1438
+ # 出力に使用する clk_time の作成
1439
+ def _clk_time_for_inspect(precision)
1440
+ return @clk_time unless precision && precision > When::DAY
1441
+ base = self + When::TM::Duration.new(@clk_time.frame._round_value(precision))
1442
+ base.clk_time.clk_time[When::HOUR] = @clk_time.clk_time[When::HOUR] + 1 unless self.to_i == base.to_i
1443
+ return base.clk_time
1444
+ end
1445
+ private :_clk_time_for_inspect
1446
+
1447
+ # 時
1448
+ #
1449
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1450
+ #
1451
+ # @return [Numeric] 自身の「時」
1452
+ #
1453
+ def hour(d=0)
1454
+ @clk_time.hour(d)
1455
+ end
1456
+
1457
+ # 分
1458
+ #
1459
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1460
+ #
1461
+ # @return [Numeric] 自身の「分」
1462
+ #
1463
+ def minute(d=0)
1464
+ @clk_time.minute(d)
1465
+ end
1466
+ alias :min :minute
1467
+
1468
+ # 秒
1469
+ #
1470
+ # @param [Integer] d 時刻が'時分秒'でない表現のための桁位置変更指示(小さいほうに位置をずらす)
1471
+ #
1472
+ # @return [Numeric] 自身の「秒」
1473
+ #
1474
+ def second(d=0)
1475
+ @clk_time.second(d)
1476
+ end
1477
+ alias :sec :second
1478
+ end
1479
+ end
1480
+ end