when_exe 0.4.0 → 0.4.1

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.
@@ -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