when_exe 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/when_exe.rb +89 -3
  3. data/lib/when_exe/basictypes.rb +37 -7
  4. data/lib/when_exe/calendarnote.rb +18 -13
  5. data/lib/when_exe/calendartypes.rb +3 -9
  6. data/lib/when_exe/coordinates.rb +23 -373
  7. data/lib/when_exe/ephemeris.rb +7 -7
  8. data/lib/when_exe/ephemeris/notes.rb +0 -14
  9. data/lib/when_exe/events.rb +1851 -0
  10. data/lib/when_exe/icalendar.rb +121 -4
  11. data/lib/when_exe/linkeddata.rb +29 -18
  12. data/lib/when_exe/locales/akt.rb +63 -0
  13. data/lib/when_exe/locales/locale.rb +60 -11
  14. data/lib/when_exe/mini_application.rb +14 -8
  15. data/lib/when_exe/namespace.rb +42 -0
  16. data/lib/when_exe/parts/enumerator.rb +13 -2
  17. data/lib/when_exe/parts/geometric_complex.rb +51 -1
  18. data/lib/when_exe/parts/method_cash.rb +15 -10
  19. data/lib/when_exe/parts/resource.rb +47 -29
  20. data/lib/when_exe/region/chinese.rb +4 -3
  21. data/lib/when_exe/region/chinese/calendars.rb +4 -4
  22. data/lib/when_exe/region/chinese/epochs.rb +6 -6
  23. data/lib/when_exe/region/chinese/notes.rb +3 -3
  24. data/lib/when_exe/region/chinese/twins.rb +6 -6
  25. data/lib/when_exe/region/islamic.rb +1 -1
  26. data/lib/when_exe/region/japanese.rb +4 -4
  27. data/lib/when_exe/region/japanese/eclipses.rb +2 -2
  28. data/lib/when_exe/region/japanese/location.rb +93 -0
  29. data/lib/when_exe/region/japanese/notes.rb +29 -11
  30. data/lib/when_exe/region/japanese/residues.rb +1 -1
  31. data/lib/when_exe/region/japanese/twins.rb +18 -6
  32. data/lib/when_exe/region/location.rb +40 -0
  33. data/lib/when_exe/region/martian.rb +1 -1
  34. data/lib/when_exe/region/ryukyu.rb +1 -1
  35. data/lib/when_exe/spatial.rb +611 -0
  36. data/lib/when_exe/timestandard.rb +3 -3
  37. data/lib/when_exe/tmobjects.rb +32 -0
  38. data/lib/when_exe/tmposition.rb +211 -1318
  39. data/lib/when_exe/tmptypes.rb +1265 -0
  40. data/lib/when_exe/tmreference.rb +35 -0
  41. data/lib/when_exe/version.rb +3 -3
  42. data/test/events/example-datasets +7 -0
  43. data/test/events/history-dataset.csv +22 -0
  44. data/test/events/japanese-holiday-index.csv +28 -0
  45. data/test/events/japanese-holiday.csv +77 -0
  46. data/test/events/japanese-holiday.ttl +778 -0
  47. data/test/events/make_events_ttl.rb +18 -0
  48. data/test/events/mori_wikichoshi.csv +14 -0
  49. data/test/events/ndl_koyomi.csv +220 -0
  50. data/test/events/ndl_koyomi_index.csv +44 -0
  51. data/test/events/primeminister-dataset.csv +19 -0
  52. data/test/events/shogun-dataset.csv +22 -0
  53. data/test/events/test-history-dataset-edge-sparql.csv +26 -0
  54. data/test/events/test-history-dataset-edge.csv +27 -0
  55. data/test/events/test-history-dataset-sparql.csv +22 -0
  56. data/test/events/test-history-dataset.csv +23 -0
  57. data/test/events/test-history-events-edge.ttl +89 -0
  58. data/test/events/test-history-events.csv +6 -0
  59. data/test/examples/Terms.m17n +1 -1
  60. data/test/test.rb +6 -0
  61. data/test/test/coordinates.rb +2 -2
  62. data/test/test/events.rb +32 -0
  63. data/test/test/region/japanese.rb +20 -0
  64. data/test/test/region/m17n.rb +2 -2
  65. data/test/test/region/mayan.rb +6 -6
  66. data/test/test/tmposition.rb +63 -1
  67. metadata +26 -2
@@ -57,7 +57,7 @@ module When::TimeStandard
57
57
 
58
58
  DeltaT = [ 63.467, # 1999
59
59
  63.827, 64.092, 64.300, 64.473, 64.573, 64.689, 64.846, 65.145, 65.456, 65.779, # 2000-
60
- 66.070, 66.324, 66.603, 66.909, 67.282 # 2010-
60
+ 66.070, 66.324, 66.603, 66.909, 67.282, 67.642 # 2010-
61
61
  ]
62
62
 
63
63
  class << self
@@ -377,7 +377,7 @@ module When::TimeStandard
377
377
  #
378
378
  def _normalize_time_basis
379
379
 
380
- @_time_basis ||= @time_basis || (@location ? @location.long / When::Coordinates::Spatial::DEGREE * 240 : When::UTC)
380
+ @_time_basis ||= @time_basis || (@location ? @location.long / @location.degree * 240 : When::UTC)
381
381
  @_time_basis = When::Locale._split(@_time_basis) if @_time_basis.kind_of?(String)
382
382
  @_time_basis = [@_time_basis] unless @_time_basis.kind_of?(Array)
383
383
  @_time_basis = @_time_basis.map {|clock| When.Clock(clock)}
@@ -404,7 +404,7 @@ module When::TimeStandard
404
404
  # @return [Numeric] difference / day
405
405
  #
406
406
  def localdate_difference
407
- @localdate_difference ||= @location.long / (360.0 * When::Coordinates::Spatial::DEGREE)
407
+ @localdate_difference ||= @location.long / (360.0 * @location.degree)
408
408
  end
409
409
 
410
410
  # local time と universal time の差 / 128秒
@@ -530,6 +530,13 @@ module When::TM
530
530
  #
531
531
  class Duration
532
532
 
533
+ # 繰り返し有無
534
+ #
535
+ # @return [Boolean]
536
+ #
537
+ attr_accessor :repeat
538
+ protected :repeat=
539
+
533
540
  #
534
541
  # When::TM::IntervalLength への変換
535
542
  #
@@ -556,6 +563,31 @@ module When::TM
556
563
  When::TM::PeriodDuration.new(duration / SECOND, When::SECOND)
557
564
  end
558
565
 
566
+ # オブジェクト変換オプションの遅延適用(ダミー)
567
+ #
568
+ # @param [Hash] options 以下の通り
569
+ # @option options [Hash<Integrt=>When::Coordinates::Residue>] :residue
570
+ # @option options [When::TM::Clock] :clock
571
+ # @option options [Array <When::TM::Calendar>] :frame
572
+ #
573
+ # @return [When::TM::Duration]
574
+ #
575
+ def apply_delayed_options(options)
576
+ self
577
+ end
578
+
579
+ # 繰り返し設定
580
+ #
581
+ # @param [Boolean] repeat 設定
582
+ #
583
+ # @return [When::TM::Duration]
584
+ #
585
+ def set_repeat(repeat)
586
+ repeated = dup
587
+ repeated.repeat = repeat
588
+ repeated
589
+ end
590
+
559
591
  #
560
592
  # Duration 用の Enumerator
561
593
  #
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  =begin
3
- Copyright (C) 2011-2015 Takashi SUGA
3
+ Copyright (C) 2011-2016 Takashi SUGA
4
4
 
5
5
  You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
6
  =end
@@ -313,21 +313,32 @@ module When::TM
313
313
  iso8601form = When::Parts::Resource::ContentLine.extract_rfc5545_Property(specification, options).
314
314
  gsub(When::Parts::Resource::IRIDecode) {|c| When::Parts::Resource::IRIDecodeTable[c]}
315
315
 
316
- # suffix - Frame specification
317
- if iso8601form =~ /\A(.*[^\d]|.+=[^:]+)\((([-+*&%@!>=<?\dW.T:]|\{.+?\})+)\)\z/
316
+ # delayed_options
317
+ delayed_options = {}
318
+
319
+ # Frame specifications - 暦法指定の取り出し
320
+ if iso8601form =~ /\A(.*[^\d]|.+=[^:]+)\((([-+*&%@!>=<?\d.A-Z:]|\{.+?\})+)\)\z/
318
321
  frame, iso8601form = $~[1..2]
319
322
  frame.sub!(/_+\z/, '')
320
323
  else
321
324
  iso8601form, frame, *rest = iso8601form.split(/(?:\^|%5E){1,2}/i)
322
- return rest.inject(_instance(iso8601form + '^' + frame, options)) {|p,c| When.Resource(c, '_c:').jul_trans(p)} unless rest.empty?
325
+ delayed_options[:frame] = rest.map{|calendar| When.Resource(calendar, '_c:')} unless rest.empty?
323
326
  end
324
327
 
325
328
  # add frame to options
326
329
  options = options.merge({:frame=>When.Resource(frame, '_c:')}) if frame
327
330
 
331
+ return _instance_element(iso8601form, options, delayed_options) unless iso8601form =~ /\A(.+?)(\.{2,3})(.+)\z/
332
+ first, separater, last = $1, $2, $3
333
+ When::Events::Range.new(_instance_element(first, options.dup, delayed_options.dup),
334
+ _instance_element(last, options.dup, delayed_options.dup), separater == '...')
335
+ end
336
+
337
+ def _instance_element(iso8601form, options, delayed_options)
338
+
328
339
  # indeterminateValue
329
340
  if (iso8601form.sub!(/\/After\z|\ABefore\/|\ANow\z|\AUnknown\z|\A[-+]Infinity\z/i, ''))
330
- options[:indeterminated_position] = When::TimeValue::S[$&.sub(/\//,'')]
341
+ options[:indeterminated_position] = When::TimeValue::S[$&.sub('/','')]
331
342
  case options[:indeterminated_position]
332
343
  when When::TimeValue::Now,
333
344
  When::TimeValue::Unknown,
@@ -337,10 +348,11 @@ module When::TM
337
348
  end
338
349
  end
339
350
 
340
- # each specification
341
- splitted = iso8601form.split(/\//)
342
- if (splitted[0] =~ /\AR(\d+)?\z/)
351
+ # each specification - '/' で区切られた個々の要素の取り出し
352
+ splitted = iso8601form.gsub(/\{.+?\}/) {|m| m.gsub('/', When::Locale::NUL)}.split('/').map {|m| m.gsub(When::Locale::NUL, '/')}
353
+ if (splitted[0] =~ /\AR(\d+)?(P.+)?\z/)
343
354
  repeat = $1 ? $1.to_i : true
355
+ delayed_options[:duration] = When.Duration($2) if $2
344
356
  splitted.shift
345
357
  end
346
358
  case splitted.length
@@ -353,77 +365,59 @@ module When::TM
353
365
  raise ArgumentError, "Irregal ISO8601 Format: #{iso8601form}"
354
366
  end
355
367
  options = self._options(options)
356
- element = splitted.map { |v| _date_time_or_duration(v, options.dup) }
368
+ element = splitted.map { |v|
369
+ d, r, z2 = _date_time_or_duration(v, options.dup)
370
+ delayed_options[:residue] = r if r
371
+ delayed_options[:clock ] = z2 if z2
372
+ d
373
+ }
357
374
 
358
- # total result
375
+ # 意味のある繰り返しのない場合
376
+ has_residue_options = delayed_options[:residue] && (delayed_options[:residue][0].kind_of?(String) ||
377
+ delayed_options[:residue][2].kind_of?(String))
359
378
  case repeat
360
- when nil
361
- case element[1]
362
- when nil
363
- return element[0]
364
- when Duration
365
- case element[0]
366
- when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
367
- when self ; return When::Parts::GeometricComplex.new(*element)
368
- else ; return When::Parts::GeometricComplex.new(element[0].first, element[1])
369
- end
370
- when self
371
- case element[0]
372
- when Duration ; return When::Parts::GeometricComplex.new([[element[1]-element[0],false], [element[1],true ]])
373
- when self ; return When::Parts::GeometricComplex.new(element[0]..element[1])
374
- else ; return When::Parts::GeometricComplex.new(element[0].first..element[1])
375
- end
376
- else
377
- case element[0]
378
- when Duration ; return When::Parts::GeometricComplex.new([[element[1].first-element[0],false],
379
- [element[1].last-element[0]-1,true ]])
380
- when self ; return When::Parts::GeometricComplex.new(element[0]...element[1].last)
381
- else ; return When::Parts::GeometricComplex.new(element[0].first...element[1].last)
382
- end
383
- end
384
- when 0 ; return []
385
- when Integer ; return [element[0]] * repeat unless element[1]
379
+ when nil ; return _instance_without_repeat(element).apply_delayed_options(delayed_options) unless has_residue_options
380
+ when 0 ; return []
381
+ when Integer
382
+ return [element[0]] * repeat if element.length == 1 && element[0].kind_of?(When::TM::Duration) # JIS X0301 5.6.3
383
+ raise ArgumentError, "Duration or TemporalPosition missing" unless element[1] || has_residue_options
386
384
  end
387
385
 
388
- case element[1]
389
- when Duration
390
- case element[0]
391
- when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
392
- else ; duration = element[1]
393
- end
394
- when self
395
- case element[0]
396
- when Duration ; duration = -element[0]
397
- when self ; duration = element[1] - element[0]
398
- else ; duration = element[1] - element[0].first
399
- end
400
- else
401
- case element[0]
402
- when Duration ; duration = -element[0]
403
- when self ; duration = element[1].first - element[0]
404
- else ; duration = element[1].first - element[0].first
386
+ # 繰り返しの起点と間隔
387
+ base = element[0].kind_of?(Duration) ? element[1] : element[0]
388
+ duration = _duration_for_repeat(element)
389
+
390
+ # 繰り返しのある場合
391
+ if repeat.kind_of?(Integer) && !has_residue_options
392
+
393
+ # iCalendar の機能を使用しない
394
+ result = []
395
+ seed = base
396
+ guard = (base - When::V::Event.default_until)...(base + When::V::Event.default_until)
397
+ while result.length < repeat && seed >= guard.first && seed < guard.last
398
+ applied = seed.apply_delayed_options(delayed_options)
399
+ applied = When::Parts::GeometricComplex.new(applied, delayed_options[:duration]) if delayed_options[:duration]
400
+ result << applied if applied
401
+ seed += duration
405
402
  end
406
- end
407
- base = element[0].kind_of?(Duration) ? element[1] : element[0]
408
-
409
- if repeat.kind_of?(Integer)
410
- result = case base
411
- when self ; (1..repeat-1).inject([base]) {|a,i| a << (a[-1] + duration) }
412
- else ; (1..repeat-1).inject([base]) {|a,i| a << When::Parts::GeometricComplex.new(
413
- a[-1].first+duration...a[-1].last+duration)}
414
- end
415
403
  result.reverse! if duration.sign < 0
416
404
  return result
417
405
 
418
406
  else
419
- duration = -duration if duration.sign < 0
420
- return case base
421
- when self ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base})
422
- else ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base.first,
423
- 'dtend' =>base.last})
424
- end
407
+
408
+ return duration.set_repeat(true) unless base
409
+
410
+ # iCalendar の機能を使用する
411
+ should_limit = base.precision == When::MONTH && !(delayed_options[:residue] && delayed_options[:residue][0])
412
+ iterator = When::V::Event.iterator_for_ISO8601(base, duration, delayed_options)
413
+ case repeat
414
+ when nil ; (result=iterator.succ).precision > base.precision && should_limit && base != result ? nil : result
415
+ when Integer ; (0...repeat).to_a.map {iterator.succ}
416
+ else ; iterator
417
+ end
425
418
  end
426
419
  end
420
+ private :_instance_element
427
421
 
428
422
  # When::TM::TemporalPosition の生成
429
423
  #
@@ -463,6 +457,10 @@ module When::TM
463
457
  else ; nil
464
458
  end
465
459
  }
460
+ parse = options.delete(:parse)
461
+ if parse && parse[:residue] && parse[:residue][2]
462
+ res << parse[:residue][2]
463
+ end
466
464
  if args.length > 0
467
465
  options[:clock] ||= Clock.local_time
468
466
  options[:clock] = When.Clock(options[:clock])
@@ -503,7 +501,7 @@ module When::TM
503
501
  main = {}
504
502
  clock = Clock.get_clock_option(query)
505
503
  main[:clock] = clock if clock
506
- [:indeterminated_position, :frame, :events, :precision,
504
+ [:indeterminated_position, :frame, :events, :precision, :parse,
507
505
  :era_name, :era, :abbr, :extra_year_digits, :ordinal_date_digits, :wkst, :time_standard, :location].each do |key|
508
506
  main[key] = query.delete(key) if query.key?(key)
509
507
  end
@@ -573,20 +571,94 @@ module When::TM
573
571
  when :week
574
572
  position = ((position + PeriodDuration.new(4, DAY)) & (Residue.day_of_week(options[:wkst]) << 1)) +
575
573
  PeriodDuration.new((w[0]-1)*7 + (w[1]||1)-1, DAY)
576
- position = When::Parts::GeometricComplex.new(position...(position+P1W)) unless w[1]
574
+ position = When::Parts::GeometricComplex.new(position, P1W) unless w[1]
577
575
  end
578
- if r
579
- r.keys.sort.each do |count|
580
- residue = When.Residue(r[count])
581
- if count == 0
582
- residue = residue.to('year')
583
- else
584
- position = position.succ if residue.carry < 0
585
- end
586
- position &= residue
576
+ [position, r, z2]
577
+ end
578
+
579
+ # オブジェクト変換オプションの遅延適用対象オブジェクト
580
+ #
581
+ # @param [Array<When::TM::TemporalPosition or When::TM::Duration>] element
582
+ #
583
+ # @return [When::TM::TemporalPosition] ISO8601 time point
584
+ # @return [When::Parts::GeometricComplex] ISO8601 遅延適用対象オブジェクト
585
+ #
586
+ def _instance_without_repeat(element)
587
+ case element[1]
588
+ when nil
589
+ return element[0]
590
+ when Duration
591
+ case element[0]
592
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
593
+ when self ; return When::Parts::GeometricComplex.new(*element)
594
+ else ; return When::Parts::GeometricComplex.new(element[0].first, element[1])
595
+ end
596
+ when self
597
+ case element[0]
598
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1]-element[0],false], [element[1],true ]])
599
+ when self ; return When::Parts::GeometricComplex.new(element[0]..element[1])
600
+ else ; return When::Parts::GeometricComplex.new(element[0].first..element[1])
601
+ end
602
+ else
603
+ case element[0]
604
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1].first-element[0],false],
605
+ [element[1].last-element[0]-1,true ]])
606
+ when self ; return When::Parts::GeometricComplex.new(element[0]...element[1].last)
607
+ else ; return When::Parts::GeometricComplex.new(element[0].first...element[1].last)
608
+ end
609
+ end
610
+ end
611
+
612
+ # 繰り返し用の Duration 取得
613
+ #
614
+ # @param [Array<When::TM::TemporalPosition or When::TM::Duration>] element
615
+ #
616
+ # @return [When::TM::Duration] repeating interval
617
+ #
618
+ def _duration_for_repeat(element)
619
+ case element[1]
620
+ when Duration
621
+ case element[0]
622
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
623
+ else ; return element[1]
624
+ end
625
+ when self
626
+ case element[0]
627
+ when Duration ; return -element[0]
628
+ when self ; return element[1] - element[0]
629
+ else ; return element[1] - element[0].first
630
+ end
631
+ when nil
632
+ case element[0]
633
+ when Duration ; return element[0]
634
+ else ; nil
635
+ end
636
+ else
637
+ case element[0]
638
+ when Duration ; return -element[0]
639
+ when self ; return element[1].first - element[0]
640
+ else ; return element[1].first - element[0].first
587
641
  end
588
642
  end
589
- z2 ? z2 ^ position : position
643
+ end
644
+
645
+ # オブジェクト変換オプションの遅延適用
646
+ #
647
+ # @param [Object] object 遅延適用するオブジェクト
648
+ #
649
+ # @param [Hash] options 以下の通り
650
+ # @option options [Hash<Integrt=>When::Coordinates::Residue>] :residue
651
+ # @option options [When::TM::Clock] :clock
652
+ # @option options [Array <When::TM::Calendar>] :frame
653
+ #
654
+ # @return [Object] 遅延適用されたオブジェクト
655
+ #
656
+ def _apply_delayed_options(object, options)
657
+ case object
658
+ when Array ; object.map {|obj| _apply_delayed_options(obj, options)}
659
+ when self, When::Parts::GeometricComplex ; object.apply_delayed_options(options)
660
+ else ; object
661
+ end
590
662
  end
591
663
  end
592
664
 
@@ -759,13 +831,15 @@ module When::TM
759
831
  #
760
832
  def +(other)
761
833
  case other
762
- when Integer ; self + PeriodDuration.new(other, When::DAY)
763
- when Numeric ; self + IntervalLength.new(other, 'day')
764
- when PeriodDuration ; _plus(other)
765
- when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time + other.duration), self._attr) :
766
- JulianDate.dynamical_time(dynamical_time + other.duration, self._attr)
767
- else ; raise TypeError, "The right operand should be Numeric or Duration"
834
+ when Integer ; return self + PeriodDuration.new(other, When::DAY)
835
+ when Numeric ; return self + IntervalLength.new(other, 'day')
836
+ when Duration ; begin other = other.set_repeat(false); return other.enum_for(self+other, :forward) end if other.repeat
837
+ when Array ; return other.map {|o| self + o}
838
+ else ; raise TypeError, "The right operand should be Numeric or Duration"
768
839
  end
840
+ return _plus(other) if other.kind_of?(PeriodDuration)
841
+ @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time + other.duration), self._attr) :
842
+ JulianDate.dynamical_time(dynamical_time + other.duration, self._attr)
769
843
  rescue RangeError
770
844
  (@frame ^ self) + other
771
845
  end
@@ -779,16 +853,18 @@ module When::TM
779
853
  #
780
854
  def -(other)
781
855
  case other
782
- when TimeValue ; self.time_standard.rate_of_clock == other.time_standard.rate_of_clock && [@precision, other.precision].min <= When::DAY ?
783
- PeriodDuration.new(self.to_i - other.to_i, When::DAY) :
784
- IntervalLength.new((self.dynamical_time - other.dynamical_time) / Duration::SECOND, 'second')
785
- when Integer ; self - PeriodDuration.new(other, When::DAY)
786
- when Numeric ; self - IntervalLength.new(other, 'day')
787
- when PeriodDuration ; _plus(-other)
788
- when Duration ; @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time - other.duration), self._attr) :
789
- JulianDate.dynamical_time(dynamical_time - other.duration, self._attr)
790
- else ; raise TypeError, "The right operand should be Numeric, Duration or TemporalPosition"
856
+ when TimeValue; return self.time_standard.rate_of_clock == other.time_standard.rate_of_clock && [@precision, other.precision].min <= When::DAY ?
857
+ PeriodDuration.new(self.to_i - other.to_i, When::DAY) :
858
+ IntervalLength.new((self.dynamical_time - other.dynamical_time) / Duration::SECOND, 'second')
859
+ when Integer ; return self - PeriodDuration.new(other, When::DAY)
860
+ when Numeric ; return self - IntervalLength.new(other, 'day')
861
+ when Duration ; begin other = other.set_repeat(false); return other.enum_for(self-other, :reverse) end if other.repeat
862
+ when Array ; return other.map {|o| self - o}
863
+ else ; raise TypeError, "The right operand should be Numeric, Duration or TemporalPosition"
791
864
  end
865
+ return _plus(-other) if other.kind_of?(PeriodDuration)
866
+ @frame.kind_of?(Calendar) ? @frame.jul_trans(JulianDate.dynamical_time(dynamical_time - other.duration), self._attr) :
867
+ JulianDate.dynamical_time(dynamical_time - other.duration, self._attr)
792
868
  rescue RangeError
793
869
  (@frame ^ self) - other
794
870
  end
@@ -804,31 +880,6 @@ module When::TM
804
880
  @period = When.Duration(period_name)
805
881
  end
806
882
 
807
- # 前の日時
808
- #
809
- # @return [When::TM::TemporalPosition]
810
- #
811
- # 分解能に対応する Duration だけ,日時を戻す
812
- #
813
- def prev
814
- @precision==When::DAY ? _force_euqal_day(-1) : self-period
815
- rescue RangeError
816
- (When::Gregorian ^ self) - period
817
- end
818
-
819
- # 次の日時
820
- #
821
- # @return [When::TM::TemporalPosition]
822
- #
823
- # 分解能に対応する Duration だけ,日時を進める
824
- #
825
- def succ
826
- @precision==When::DAY ? _force_euqal_day(+1) : self+period
827
- rescue RangeError
828
- (When::Gregorian ^ self) + period
829
- end
830
- alias :next :succ
831
-
832
883
  #
833
884
  # 前後の日時を取得可能か?
834
885
  #
@@ -857,7 +908,7 @@ module When::TM
857
908
  # @return [Boolean]
858
909
  #
859
910
  def has_time?
860
- (@precision > 0)
911
+ (@precision > DAY)
861
912
  end
862
913
 
863
914
  # 指定の日時を含むか?
@@ -927,6 +978,10 @@ module When::TM
927
978
  return result unless result == 0
928
979
  end
929
980
  @cal_date[prec - 1] <=> other.cal_date[prec - 1]
981
+ elsif prec < DAY && precision < other.precision
982
+ return +1 if other.to_i < self.to_i
983
+ return -1 if other.to_i >= succ.to_i
984
+ return 0
930
985
  else
931
986
  source = (prec >= self.precision ) ? self : self.floor(prec)
932
987
  target = (prec >= other.precision) ? other : other.floor(prec)
@@ -1000,6 +1055,41 @@ module When::TM
1000
1055
  scan(other, leaf, &block)
1001
1056
  end
1002
1057
 
1058
+ # オブジェクト変換オプションの遅延適用
1059
+ #
1060
+ # @param [Hash] options 以下の通り
1061
+ # @option options [Hash<Integrt=>When::Coordinates::Residue>] :residue
1062
+ # @option options [When::TM::Clock] :clock
1063
+ # @option options [Array <When::TM::Calendar>] :frame
1064
+ #
1065
+ # @return [When::TM::TemporalPosition]
1066
+ #
1067
+ def apply_delayed_options(options)
1068
+ position = self
1069
+ if options[:residue]
1070
+ options[:residue].keys.sort.each do |count|
1071
+ options[:residue][count] = When.Residue(options[:residue][count]) unless options[:residue][count].kind_of?(When::Coordinates::Residue)
1072
+ residue = options[:residue][count]
1073
+ if count == 0
1074
+ residue = residue.to('year')
1075
+ position &= residue
1076
+ else
1077
+ upper = position
1078
+ position = position.succ if residue.carry < 0
1079
+ position &= residue
1080
+ return nil if self.precision == When::MONTH && !(upper == position)
1081
+ end
1082
+ end
1083
+ end
1084
+ if options[:clock]
1085
+ position = options[:clock] ^ position
1086
+ end
1087
+ if options[:frame]
1088
+ position = options[:frame].inject(position) {|p,c| c.jul_trans(p)}
1089
+ end
1090
+ position
1091
+ end
1092
+
1003
1093
  # 属性を変更したコピーを作る
1004
1094
  #
1005
1095
  # @param [Hash] options { 属性=>属性値 }
@@ -1142,1201 +1232,4 @@ module When::TM
1142
1232
  end
1143
1233
  end
1144
1234
  end
1145
-
1146
- #
1147
- # 時間座標 - 単一の時間間隔によって定義する連続な間隔尺度を基礎とする
1148
- #
1149
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeCoordinateType gml schema}
1150
- #
1151
- class Coordinate < TemporalPosition
1152
-
1153
- class << self
1154
- # 内部時間によるオブジェクトの生成
1155
- alias :universal_time :new
1156
-
1157
- # 外部時間によるオブジェクトの生成
1158
- #
1159
- # @param [Numeric] dynamical_time 外部時間による時間座標値
1160
- #
1161
- # @param [Hash] options 下記の通り
1162
- # @option options [When::TimeStandard] :time_standard
1163
- #
1164
- # @return [When::TM::Coordinate]
1165
- #
1166
- def dynamical_time(dynamical_time, options={})
1167
- universal_time(When.Resource(options[:time_standard] || 'UniversalTime', '_t:').from_dynamical_time(dynamical_time), options)
1168
- end
1169
-
1170
- # 他種の時間位置によるオブジェクトの生成
1171
- #
1172
- # @param [Numeric, When::TM::ClockTime, ::Time, ::Date, ::DateTime] time 他種の時間位置によるオブジェクト
1173
- #
1174
- # @param [Hash] options 下記の通り
1175
- # @option options [When::TM::Clock] :clock
1176
- # @option options [When::Parts::Timezone] :tz
1177
- #
1178
- # @return [When::TM::Coordinate]
1179
- #
1180
- def new(time, options={})
1181
- options = options.dup
1182
- options[:frame] = Clock.get_clock_option(options)
1183
- case time
1184
- when Numeric
1185
- options[:frame] ||= When::UTC unless time.kind_of?(Integer)
1186
- universal_time = (2*time - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1187
- when ClockTime
1188
- options[:frame] ||= time.clock
1189
- universal_time = time.clk_time[0] + time.universal_time
1190
- when ::Time
1191
- options[:frame] ||= When.Clock(time.gmtoff)
1192
- universal_time = options[:frame].time_standard.from_time_object(time)
1193
- when TimeValue
1194
- options[:frame] ||= time.clock
1195
- universal_time = time.universal_time
1196
- else
1197
- if ::Object.const_defined?(:Date) && ::Date.method_defined?(:+) && time.respond_to?(:ajd)
1198
- case time
1199
- when ::DateTime
1200
- options[:frame] ||= When.Clock((time.offset * 86400).to_i)
1201
- universal_time = (2*time.ajd - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1202
- when ::Date
1203
- universal_time = JulianDate._d_to_t(time.jd)
1204
- if options[:frame] && options[:frame].rate_of_clock != 1.0
1205
- universal_time = options[:frame].time_standard.from_dynamical_time(
1206
- When::TimeStandard.to_dynamical_time(universal_time))
1207
- end
1208
- end
1209
- end
1210
- end
1211
- raise TypeError, "Can't create #{self} from #{time.class}" unless universal_time
1212
- universal_time(universal_time, options)
1213
- end
1214
- end
1215
-
1216
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1217
- #
1218
- # The time reference system associated with the temporal position being described
1219
- #
1220
- # @return [When::TM::ReferenceSystem]
1221
- #
1222
- alias :clock :frame
1223
-
1224
- # 内部時間による時間座標値
1225
- #
1226
- # @return [Numeric]
1227
- #
1228
- attr_accessor :universal_time
1229
- alias :coordinateValue :universal_time
1230
- protected :universal_time=
1231
-
1232
- # 内部時間(ローカル)
1233
- #
1234
- # @return [Numeric]
1235
- #
1236
- # 1970-01-01T00:00:00(ローカル) からの Universal Coordinated Time の経過時間 / 128秒
1237
- #
1238
- def local_time
1239
- @universal_time
1240
- end
1241
-
1242
- # CoordinateSystem による時間座標値
1243
- #
1244
- # @return [Numeric]
1245
- #
1246
- def coordinateValue
1247
- (universal_time - frame.origin.universal_time) / frame.interval.to_f
1248
- end
1249
-
1250
- # 加算
1251
- #
1252
- # @param [Numeric, When::TM::IntervalLength] other
1253
- #
1254
- # @return [When::TM::TemporalPosition]
1255
- #
1256
- def +(other)
1257
- other = other.to_interval_length if other.kind_of?(PeriodDuration)
1258
- super(other)
1259
- end
1260
-
1261
- # 減算
1262
- #
1263
- # @param [When::TM::TemporalPosition, Numeric, When::TM::IntervalLength] other
1264
- #
1265
- # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::IntervalLength
1266
- # @return [When::TM::IntervalLength] if other is a When::TM::TemporalPosition
1267
- #
1268
- def -(other)
1269
- other = other.to_interval_length if other.kind_of?(PeriodDuration)
1270
- super(other)
1271
- end
1272
-
1273
- # オブジェクトの生成
1274
- #
1275
- # @param [Numeric] universal_time 内部時間による時間座標値
1276
- #
1277
- # @param [Hash] options 下記の通り
1278
- # @option options [When::TM::CoordinateSystem] :frame
1279
- #
1280
- def initialize(universal_time, options={})
1281
- super(options)
1282
- @universal_time = universal_time
1283
- end
1284
- end
1285
-
1286
- #
1287
- # ユリウス日
1288
- #
1289
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#JulianDateType CALENdeRsign}
1290
- #
1291
- class JulianDate < Coordinate
1292
-
1293
- # Julian Day Number
1294
- # 19700101T120000Z
1295
- JD19700101 = 2440588
1296
-
1297
- # Modified Julian Date
1298
- #
1299
- # see {https://en.wikipedia.org/wiki/Julian_day#Variants MJD}
1300
- JDN_of_MJD = 2400000.5
1301
-
1302
- # Countdown to Equinoctial Planetconjunction
1303
- #
1304
- # see {http://www.calendersign.com/en/cs_cep-pec.php CEP}
1305
- JDN_of_CEP = 2698162
1306
-
1307
- class << self
1308
-
1309
- JD19700100_5 = JD19700101 - 0.5
1310
-
1311
- #
1312
- # 日時の内部表現をユリウス日に変換
1313
- #
1314
- # @param [Numeric] t
1315
- #
1316
- # @return [Numeric]
1317
- #
1318
- def _t_to_d(t)
1319
- t / Duration::DAY + JD19700100_5
1320
- end
1321
-
1322
- #
1323
- # ユリウス日をに日時の内部表現変換
1324
- #
1325
- # @param [Numeric] d
1326
- #
1327
- # @return [Numeric]
1328
- #
1329
- def _d_to_t(d)
1330
- (d - JD19700100_5) * Duration::DAY
1331
- end
1332
-
1333
- # Generation of Temporal Objetct
1334
- #
1335
- # @param [String] specification ユリウス通日を表す文字列
1336
- # @param [Hash] options 暦法や時法などの指定 (see {When::TM::TemporalPosition._instance})
1337
- #
1338
- # @return [When::TM::TemporalPosition, When::TM::Duration, When::Parts::GeometricComplex or Array<them>]
1339
- #
1340
- def parse(specification, options={})
1341
- num, *calendars = specification.split(/\^{1,2}/)
1342
- jdn = num.sub!(/[.@]/, '.') ? num.to_f : num.to_i
1343
- case num
1344
- when/MJD/i ; jdn += JDN_of_MJD
1345
- when/CEP/i ; jdn += JDN_of_CEP
1346
- end
1347
- frame = calendars.shift || options[:frame]
1348
- return self.new(jdn, options) unless frame
1349
- calendars.unshift(frame).inject(jdn) {|date, calendar| When.Calendar(calendar).jul_trans(date, options)}
1350
- end
1351
- end
1352
-
1353
- # 加算
1354
- #
1355
- # @param [Numeric, When::TM::IntervalLength] other
1356
- #
1357
- # @return [When::TM::TemporalPosition]
1358
- #
1359
- def +(other)
1360
- new_date = super
1361
- new_date.frame = new_date.frame._daylight(new_date.universal_time) if new_date.frame && new_date.frame._need_validate
1362
- return new_date
1363
- end
1364
-
1365
- # ユリウス日が指定の剰余となる日
1366
- #
1367
- # @param [When::Coordinates::Residue] other
1368
- #
1369
- # @return [When::TM::TemporalPosition]
1370
- #
1371
- def &(other)
1372
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1373
- raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1374
- jdn = to_i
1375
- new_date = self.dup
1376
- new_date.universal_time += ((other & jdn) - jdn) * Duration::DAY
1377
- return new_date
1378
- end
1379
-
1380
- # ユリウス日の剰余
1381
- #
1382
- # @param [When::Coordinates::Residue] other
1383
- #
1384
- # @return [Numeric]
1385
- #
1386
- def %(other)
1387
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1388
- raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1389
- other % to_i
1390
- end
1391
-
1392
- private
1393
-
1394
- # オブジェクトの生成
1395
- #
1396
- # @param [Numeric] universal_time 内部時間による時間座標値
1397
- #
1398
- # @param [Hash] options 以下の通り
1399
- # @option options [When::TM::Clock] :frame
1400
- # @option options [Integer] :precision
1401
- #
1402
- def initialize(universal_time, options={})
1403
- @frame = options.delete(:frame)
1404
- @frame = When.Clock(@frame) if @frame.kind_of?(String)
1405
- @frame = @frame._daylight(universal_time) if @frame && @frame._need_validate
1406
- precision = options.delete(:precision)
1407
- precision ||= DAY unless @frame.kind_of?(Clock)
1408
- @precision = Index.precision(precision)
1409
- super
1410
- end
1411
- end
1412
-
1413
- #
1414
- # 順序時間参照系で参照する位置
1415
- #
1416
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeOrdinalPositionType gml schema}
1417
- #
1418
- class OrdinalPosition < TemporalPosition
1419
-
1420
- # この順序位置と関連付けられた順序年代 (relation - Reference)
1421
- #
1422
- # The ordinal era associated with the ordinal position being described
1423
- #
1424
- # @return [When::TM::OrdinalEra]
1425
- #
1426
- attr_reader :ordinal_position
1427
- alias :ordinalPosition :ordinal_position
1428
-
1429
- # オブジェクトの生成
1430
- #
1431
- # @param [When::TM::OrdinalEra] ordinal_position この順序位置と関連付けられた順序年代
1432
- # @param [Hash] options see {When::TM::TemporalPosition._instance}
1433
- #
1434
- def initialize(ordinal_position, options={})
1435
- super(options)
1436
- @ordinal_position = ordinal_position
1437
- end
1438
- end
1439
-
1440
- #
1441
- # 時刻
1442
- #
1443
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#ClockTimeType gml schema}
1444
- #
1445
- class ClockTime < TemporalPosition
1446
-
1447
- # 時刻要素
1448
- #
1449
- # @return [Array<Numeric>]
1450
- #
1451
- # @note ISO19108 では sequence<Integer> だが、閏時・閏秒などが表現可能なよう Numeric としている。
1452
- #
1453
- attr_reader :clk_time
1454
- alias :clkTime :clk_time
1455
-
1456
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1457
- #
1458
- # The time reference system associated with the temporal position being described
1459
- #
1460
- # @return [When::TM::ReferenceSystem]
1461
- #
1462
- alias :clock :frame
1463
-
1464
- # 内部時間
1465
- #
1466
- # @param [Integer] sdn 参照事象の通し番号
1467
- #
1468
- # @return [Numeric]
1469
- #
1470
- # T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1471
- #
1472
- # 時法によっては、異なる意味を持つことがある
1473
- #
1474
- def universal_time(sdn=nil)
1475
- raise NameError, "Temporal Reference System is not defined" unless @frame
1476
- @universal_time ||= @frame.to_universal_time(@clk_time, sdn)
1477
- end
1478
-
1479
- # 内部時間(ローカル)
1480
- #
1481
- # @param [Integer] sdn 参照事象の通し番号
1482
- #
1483
- # @return [Numeric]
1484
- #
1485
- # T00:00:00(ローカル) からの Universal Coordinated Time の経過時間 / 128秒
1486
- #
1487
- # 時法によっては、異なる意味を持つことがある
1488
- #
1489
- def local_time(sdn=nil)
1490
- raise NameError, "Temporal Reference System is not defined" unless @frame
1491
- @local_time ||= @frame.to_local_time(@clk_time, sdn)
1492
- end
1493
-
1494
- # 繰り上がり
1495
- #
1496
- # @return [Numeric]
1497
- #
1498
- # 日付の境界が午前0時でない場合、clk_time の最上位桁に 0 以外が入ることがある
1499
- #
1500
- def carry
1501
- return @clk_time[0]
1502
- end
1503
-
1504
- # 要素の参照
1505
- #
1506
- # @param [Integer, String] index 参照する要素の指定
1507
- #
1508
- # @return [Numeric]
1509
- #
1510
- def value(index)
1511
- @clk_time[_digit(index) {|digit| digit >= DAY}]
1512
- end
1513
-
1514
- #protected
1515
- # 属性のコピー
1516
- # @private
1517
- def _copy(options={})
1518
- @clk_time = options[:time] if options.key?(:time)
1519
- if options.key?(:clock)
1520
- options.delete(:frame)
1521
- @frame = options[:clock]
1522
- end
1523
- if options.key?(:tz_prop)
1524
- @frame = @frame.dup
1525
- @frame.tz_prop = options[:tz_prop]
1526
- end
1527
- return super
1528
- end
1529
-
1530
- # オブジェクトの生成
1531
- #
1532
- # @param [String] time ISO8601形式の時刻表現
1533
- # @param [Array<Numeric>] time (日, 時, 分, 秒)
1534
- #
1535
- #
1536
- # @param [Hash] options 以下の通り
1537
- # @option options [When::TM::Clock] :frame
1538
- # @option options [Integer] :precision
1539
- #
1540
- def initialize(time, options={})
1541
- # 参照系の取得
1542
- @frame = options[:frame] || Clock.local_time
1543
- @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1544
- options.delete(:frame)
1545
-
1546
- # 時刻表現の解読 ( Time Zone の解釈 )
1547
- if (time.kind_of?(String))
1548
- case time
1549
- when /\A([-+])?(\d{2,}?):?(\d{2})?:?(\d{2}(\.\d+)?)?\z/
1550
- sign, hh, mm, ss = $~[1..4]
1551
- time = @frame._validate([0,0,0,0],
1552
- [0,
1553
- -(sign.to_s + "0" + hh.to_s).to_i,
1554
- -(sign.to_s + "0" + mm.to_s).to_i,
1555
- Pair._en_number(-(sign.to_s + "0" + ss.to_s).to_f)])
1556
- time[0] = Pair.new(0, time[0].to_i) if (time[0] != 0)
1557
- when /\AZ\z/
1558
- time = [0,0,0,0]
1559
- else
1560
- raise ArgumentError, "Invalid Time Format"
1561
- end
1562
- end
1563
- @clk_time = time
1564
-
1565
- # 分解能
1566
- @precision = @frame._precision(time, options.delete(:precision))
1567
-
1568
- super(options)
1569
- end
1570
-
1571
- private
1572
-
1573
- # オブジェクトの正規化
1574
- def _normalize(options={})
1575
- # strftime で使用する locale
1576
- @keys |= @frame.keys
1577
-
1578
- # 時刻の正規化
1579
- @clk_time = @frame._validate(@clk_time) unless options[:validate]
1580
- end
1581
-
1582
- # 加減算共通処理
1583
- def _plus(period)
1584
- self.dup._copy({:time=>@frame._validate(@clk_time, @frame._arrange_length(period.time)),
1585
- :events=>nil, :query=>nil, :validate=>:done})
1586
- end
1587
- end
1588
-
1589
- #
1590
- # 暦日
1591
- #
1592
- # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#CalDateType]
1593
- #
1594
- class CalDate < TemporalPosition
1595
-
1596
- # 検索オプション
1597
- # @private
1598
- SearchOption = {After=>[0, -2, Before], Before=>[-2, 0, After]}
1599
-
1600
- # 日付要素
1601
- #
1602
- # @return [Array<Numeric>]
1603
- #
1604
- # @note ISO19108 では sequence<Integer> だが、閏月などが表現可能なよう Numeric としている。
1605
- #
1606
- attr_reader :cal_date
1607
- alias :calDate :cal_date
1608
-
1609
- # この時間位置と関連付けられた時間参照系 (relation - Reference)
1610
- #
1611
- # The time reference system associated with the temporal position being described
1612
- #
1613
- # @return [When::TM::ReferenceSystem]
1614
- #
1615
- alias :calendar :frame
1616
-
1617
- # 暦年代
1618
- #
1619
- # @return [When::TM::CalendarEra]
1620
- #
1621
- attr_accessor :calendar_era
1622
- private :calendar_era=
1623
- alias :calendarEra :calendar_era
1624
-
1625
- # 暦年代属性
1626
- #
1627
- # @return [Array] ( name, epoch, reverse, go back )
1628
- # - name [String] 暦年代名
1629
- # - epoch [Integer] 使用する When::TM::Calendar で暦元に対応する年
1630
- # - reverse [Boolean] 年数が昇順(false,nil)か降順(true)か
1631
- # - go back [Boolean] 参照イベントより前の暦日か(true)、否か(false,nil)
1632
- #
1633
- attr_accessor :calendar_era_props
1634
- private :calendar_era_props=
1635
-
1636
- # 暦年代名
1637
- #
1638
- # @return [String] 暦年代名
1639
- #
1640
- def calendar_era_name
1641
- @calendar_era_props ? @calendar_era_props[0] : nil
1642
- end
1643
- alias :calendarEraName :calendar_era_name
1644
-
1645
- # 暦年代元期
1646
- #
1647
- # @return [Integer] 使用する When::TM::Calendar で暦元に対応する年
1648
- #
1649
- def calendar_era_epoch
1650
- @calendar_era_props ? @calendar_era_props[1] : 0
1651
- end
1652
-
1653
- # 暦年代正逆
1654
- #
1655
- # @return [Boolean] 年数が昇順(false,nil)か降順(true)か
1656
- #
1657
- def calendar_era_reverse
1658
- @calendar_era_props ? @calendar_era_props[2] : false
1659
- end
1660
-
1661
- # 暦年代遡及
1662
- #
1663
- # @return [Boolean] 参照イベントより前の暦日か(true)、否か(false,nil)
1664
- #
1665
- def calendar_era_go_back
1666
- @calendar_era_props ? @calendar_era_props[3] : false
1667
- end
1668
-
1669
- # 時法の取得 - ダミー
1670
- def clock
1671
- nil
1672
- end
1673
-
1674
- # 内部時間
1675
- #
1676
- # @return [Numeric]
1677
- #
1678
- # 当日正午の 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1679
- #
1680
- def universal_time
1681
- return super if [Now, Max, Min].include?(@indeterminated_position)
1682
- @universal_time ||= JulianDate._d_to_t(to_i)
1683
- end
1684
- alias :local_time :universal_time
1685
-
1686
- # ユリウス日
1687
- #
1688
- # @return [Integer]
1689
- #
1690
- # -4712-01-01からの経過日数に対応する通番
1691
- #
1692
- def to_i
1693
- @sdn ||= _to_i
1694
- end
1695
-
1696
- #
1697
- # 暦法上の通日
1698
- #
1699
- def _to_i
1700
- void, epoch = @calendar_era_props
1701
- if epoch
1702
- date = @cal_date.dup
1703
- date[0] += epoch
1704
- else
1705
- date = @cal_date
1706
- end
1707
- @frame.to_julian_date(date)
1708
- end
1709
- private :_to_i
1710
-
1711
- # 剰余類化
1712
- #
1713
- # @param [Numeric] remainder 剰余
1714
- # @param [Integer] divisor 法(>0)
1715
- #
1716
- # @return [When::Coordinates::Residue] 当日、当年を基準とする剰余類
1717
- #
1718
- def to_residue(remainder, divisor)
1719
- When::Coordinates::Residue.new(remainder, divisor, {'day' => least_significant_coordinate,
1720
- 'year' => most_significant_coordinate})
1721
- end
1722
-
1723
- # 暦年代の削除
1724
- #
1725
- # @return [When::TM::CalDate] 暦年代を削除した When::TM::CalDate
1726
- #
1727
- def without_era
1728
- target = dup
1729
- return target unless calendar_era
1730
- options = _attr
1731
- options[:era] = nil
1732
- options[:era_name] = nil
1733
- options[:date] = cal_date.dup
1734
- options[:date][0] += calendar_era_epoch
1735
- target._copy(options)
1736
- end
1737
-
1738
- # 要素の参照
1739
- #
1740
- # @param [Integer, String] index 参照する要素の指定
1741
- #
1742
- # @return [Numeric]
1743
- #
1744
- def value(index)
1745
- @cal_date[(_digit(index) {|digit| digit <= DAY})-1]
1746
- end
1747
-
1748
- #
1749
- # 最上位の要素
1750
- #
1751
- # @return [Numeric] 暦年代の epoch に関わらず暦法に従った年の通し番号を返す
1752
- #
1753
- def most_significant_coordinate
1754
- coordinate = @cal_date[0]
1755
- coordinate += calendar_era_epoch if @calendar_era_props
1756
- @frame.index_of_MSC.times do |i|
1757
- coordinate = +coordinate * @frame.indices[i].unit + @cal_date[i+1] - @frame.indices[i].base
1758
- end
1759
- coordinate
1760
- end
1761
-
1762
- #
1763
- # 最下位の要素
1764
- #
1765
- # @return [Numeric] 剰余類の演算に用いる日の通し番号を返す
1766
- #
1767
- def least_significant_coordinate
1768
- return to_i + @frame.indices[-1].shift
1769
- end
1770
-
1771
- # ユリウス日または通年が指定の剰余となる日
1772
- #
1773
- # @param [When::Coordinates::Residue] other
1774
- #
1775
- # @return [When::TM::CalDate]
1776
- #
1777
- def &(other)
1778
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1779
- case other.event
1780
- when 'day'
1781
- # 指定の剰余となる日
1782
- sdn = other & to_i
1783
- options = {:date=>_date_with_era(@frame.to_cal_date(sdn)), :events=>nil, :query=>@query, :validate=>:done}
1784
- options[:precision] = When::DAY if precision < When::DAY
1785
- result = self.dup._copy(options)
1786
- result.send(:_force_euqal_day, sdn-result.to_i)
1787
-
1788
- when 'year'
1789
- # 指定の剰余となる年
1790
- date = @frame.send(:_decode, _date_without_era)
1791
- date[0] = (other & (date[0] + @frame.diff_to_CE)) - @frame.diff_to_CE
1792
- options = {:date=>_date_with_era(@frame.send(:_encode, date)), :events=>nil, :query=>@query}
1793
- options[:precision] = When::YEAR if precision < When::YEAR
1794
- return self.dup._copy(options)
1795
-
1796
- else
1797
- raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1798
- end
1799
- end
1800
-
1801
- # ユリウス日または通年の剰余
1802
- #
1803
- # @param [When::Coordinates::Residue] other
1804
- #
1805
- # @return [Numeric]
1806
- #
1807
- def %(other)
1808
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1809
- if precision <= When::YEAR && other.units['year'] && other.event != 'year'
1810
- other.to('year') % (most_significant_coordinate + @frame.epoch_in_CE)
1811
- else
1812
- case other.event
1813
- when 'day' ; other % least_significant_coordinate
1814
- when 'year' ; other % (most_significant_coordinate + @frame.epoch_in_CE)
1815
- else ; raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1816
- end
1817
- end
1818
- end
1819
-
1820
- # 下位桁の切り捨て
1821
- #
1822
- # @param [Integer] digit 切り捨てずに残す、最下位の桁
1823
- #
1824
- # @param [Integer] precision 切り捨て結果の分解能
1825
- #
1826
- # @return [When::TM::CalDate]
1827
- #
1828
- def floor(digit=DAY, precision=digit)
1829
- options = {:date=>@cal_date[0..(digit-1)], :events=>nil, :query=>nil}
1830
- options[:precision] = precision if precision
1831
- self.dup._copy(options)
1832
- end
1833
-
1834
- # 下位桁の切り上げ
1835
- #
1836
- # @param [Integer] digit 切り上げずに残す、最下位の桁
1837
- #
1838
- # @param [Integer] precision 切り上げ結果の分解能
1839
- #
1840
- # @return [When::TM::CalDate]
1841
- #
1842
- def ceil(digit=DAY, precision=digit)
1843
- (self + PeriodDuration.new(1, digit, (-@frame.indices.length)..0)).floor(digit, precision)
1844
- end
1845
-
1846
- # 要素数 ― 上位要素に含まれる下位要素の数
1847
- #
1848
- # @param [Integer] upper 上位要素のインデックス
1849
- # @param [Integer] lower 下位要素のインデックス(DAY または MONTH)
1850
- #
1851
- # @return [Integer]
1852
- #
1853
- def length(upper, lower=DAY)
1854
- range = [floor(upper).to_i, ceil(upper).to_i]
1855
- range = range.map {|d| (Residue.mod(d) {|m| frame._new_month(m)})[0]} if lower == MONTH
1856
- range[1] - range[0]
1857
- end
1858
-
1859
- # 時刻情報のない When::TM::CalDate を返す
1860
- #
1861
- # @return [When::TM::CalDate]
1862
- #
1863
- alias :to_cal_date :dup
1864
- alias :to_CalDate :to_cal_date
1865
-
1866
- # 暦年代が末端の参照であるか?
1867
- #
1868
- # @return [Boolean]
1869
- #
1870
- def leaf?
1871
- ! @calendar_era.respond_to?(:_pool) || @calendar_era.leaf?
1872
- end
1873
-
1874
- # 属性の Hash
1875
- # @private
1876
- def _attr
1877
- super.merge({:era_name=>@calendar_era_props, :era=>@calendar_era})
1878
- end
1879
- protected
1880
-
1881
- # 属性のコピー
1882
- # @private
1883
- def _copy(options={})
1884
- @cal_date = options[:date] if options.key?(:date)
1885
- @calendar_era = options[:era] if options.key?(:era)
1886
- @calendar_era_props = options[:era_name] if options.key?(:era_name)
1887
- return super
1888
- end
1889
-
1890
- # オブジェクトの生成
1891
- #
1892
- # @param [Array<Numeric>] date 日付表現
1893
- #
1894
- # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1895
- # @option options [When::TM::Calendar] :frame
1896
- # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1897
- # (Integer は 当該年号の 0 年に相当する通年)
1898
- # @option options [Integer] :precision
1899
- #
1900
- def initialize(date, options={})
1901
- # 年号 & 日付
1902
- @calendar_era_props = options[:era_name]
1903
- @calendar_era = options[:era]
1904
- @cal_date = date
1905
-
1906
- super(options)
1907
- end
1908
-
1909
- private
1910
-
1911
- # オブジェクトの正規化
1912
- def _normalize(options={})
1913
-
1914
- # 日付配列の長さ
1915
- cal_date_index = @cal_date.compact.length
1916
-
1917
- # 日付の正規化
1918
- if @calendar_era_props
1919
- # Calendar Era がある場合
1920
- trans_options = @trans || {} # TODO? 消す
1921
- count = trans_options[:count] || 1
1922
- query_options = (@query || {}).dup
1923
- query_options[:label] = @calendar_era_props
1924
- query_options[:count] = count
1925
- era = date = nil
1926
- if @calendar_era
1927
- era, date = _search_era(@calendar_era, trans_options)
1928
- else
1929
- eras = CalendarEra._instance(query_options)
1930
- raise ArgumentError, "CalendarEraName doesn't exist: #{query_options[:label]}" if eras.empty?
1931
- eras.each do |e|
1932
- era, date = _search_era(e, trans_options)
1933
- next unless era
1934
- count -= 1
1935
- break unless count > 0
1936
- end
1937
- end
1938
- raise RangeError, "Out of CalendarEra Range" unless era
1939
- @calendar_era_props = date.calendar_era_props
1940
- @calendar_era = era
1941
- @cal_date = date.cal_date
1942
- @frame = date.frame
1943
- @query = (@query||{}).merge(date.query)
1944
- @trans = (@trans||{}).merge(date.trans)
1945
- @keys |= calendar_era_name.keys | @frame.keys
1946
- else
1947
- # Calendar Era がない場合
1948
- @frame = When.Resource(options[:frame] || @frame || 'Gregorian', '_c:')
1949
- @cal_date = @frame._validate(@cal_date) unless options[:validate] == :done
1950
- @keys |= @frame.keys
1951
- end
1952
-
1953
- # 分解能
1954
- precision = options.delete(:precision) || @precision
1955
- cal_date_index =
1956
- case options.delete(:_format)
1957
- when :century ; CENTURY
1958
- when :week, :day ; DAY
1959
- else ; cal_date_index - (@frame.indices.length + 1)
1960
- end
1961
- precision ||= cal_date_index if cal_date_index < DAY
1962
- precision ||= @clk_time.precision if @clk_time
1963
- @precision = Index.precision(precision || DAY)
1964
- end
1965
-
1966
- # 暦年代を探す
1967
- def _search_era(era, trans_options)
1968
- cal_date = @cal_date.dup
1969
- cal_date[0] = -cal_date[0] if era.reverse?
1970
- cal_date[0] += era.epoch_year
1971
- date = era.trans(cal_date, trans_options)
1972
- loop do
1973
- case date
1974
- when Before, After
1975
- i0, i1, reverse = SearchOption[date]
1976
- new_era = (date == Before) ? era.prev : era.succ
1977
- break unless new_era
1978
- date = new_era.trans(cal_date, trans_options)
1979
- if date == reverse
1980
- cal_date = new_era.epoch[i0].frame.to_cal_date(era.epoch[i1].frame.to_julian_date(cal_date))
1981
- date = new_era.trans(cal_date, trans_options)
1982
- break if date == reverse
1983
- end
1984
- era = new_era
1985
- when TimeValue
1986
- return era, date
1987
- else
1988
- break
1989
- end
1990
- end
1991
- return nil
1992
- end
1993
-
1994
- # 加減算共通処理
1995
- def _plus(period)
1996
- _frame_adjust(period, self.dup._copy({:date=>_date_with_era(@frame._validate(_date_without_era,
1997
- @frame._arrange_length(period.date))),
1998
- :events=>nil, :query=>nil, :validate=>:done}))
1999
- end
2000
-
2001
- # 年号を除外した @frame の暦法に対応する日付
2002
- def _date_without_era
2003
- date = @cal_date.dup
2004
- date[0] += calendar_era_epoch if @calendar_era_props
2005
- date
2006
- end
2007
-
2008
- # 年号に続く日付
2009
- def _date_with_era(date)
2010
- date[0] -= calendar_era_epoch if @calendar_era_props
2011
- date
2012
- end
2013
-
2014
- # 暦法が変わった場合の補正
2015
- def _frame_adjust(period, date)
2016
- return date if @frame.equal?(date.frame) || (period.to_day||0) == 0
2017
- diff = period.to_day - (date.to_i - to_i)
2018
- return date if diff == 0
2019
- date.send(:_force_euqal_day, diff)
2020
- end
2021
- end
2022
-
2023
- #
2024
- # 時刻を伴った日付
2025
- #
2026
- # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#DateAndTimeType gml schema}
2027
- #
2028
- class DateAndTime < CalDate
2029
-
2030
- # 時刻要素
2031
- #
2032
- # @return [When::TM::ClockTime]
2033
- #
2034
- attr_reader :clk_time
2035
- alias :clkTime :clk_time
2036
-
2037
- # 時法の取得
2038
- #
2039
- # @return [When::TM::Clock]
2040
- #
2041
- def clock
2042
- @clk_time.frame
2043
- end
2044
-
2045
- # 内部時間
2046
- #
2047
- # @return [Numeric]
2048
- #
2049
- # 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
2050
- #
2051
- # 暦法によっては、異なる意味を持つことがある
2052
- #
2053
- def universal_time
2054
- return super if [Now, Max, Min].include?(@indeterminated_position)
2055
- raise NameError, "Temporal Reference System is not defined" unless (@frame && clock)
2056
- @universal_time ||= (to_i - JulianDate::JD19700101) * Duration::DAY + @clk_time.universal_time(to_i)
2057
- end
2058
-
2059
- # 内部時間(ローカル)
2060
- #
2061
- # @return [Numeric]
2062
- #
2063
- # 1970-01-01T00:00:00(ローカル) からの Universal Coordinated Time の経過時間 / 128秒
2064
- #
2065
- # 暦法によっては、異なる意味を持つことがある
2066
- #
2067
- def local_time
2068
- return super if [Now, Max, Min].include?(@indeterminated_position)
2069
- raise NameError, "Temporal Reference System is not defined" unless (@frame && clock)
2070
- @local_time ||= (to_i - JulianDate::JD19700101) * Duration::DAY + @clk_time.local_time(to_i)
2071
- end
2072
-
2073
- # 要素の参照
2074
- #
2075
- # @param [Integer] index 参照する要素の指定
2076
- #
2077
- # @return [Numeric]
2078
- #
2079
- def value(index)
2080
- digit = _digit(index)
2081
- return (digit <= DAY) ? @cal_date[digit-1] : @clk_time.clk_time[digit]
2082
- end
2083
-
2084
- # ユリウス日または通年が指定の剰余となる日
2085
- #
2086
- # @param [When::Coordinates::Residue] other
2087
- #
2088
- # @return [When::TM::DateAndTime]
2089
- #
2090
- def &(other)
2091
- raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
2092
- case other.event
2093
- when 'day'
2094
- # 指定の剰余となる日
2095
- sdn = other & to_i
2096
- options = {:date=>_date_with_era(@frame.to_cal_date(sdn)), :time=>@clk_time.clk_time.dup,
2097
- :events=>nil, :query=>@query, :validate=>:done}
2098
- options[:precision] = When::DAY if precision < When::DAY
2099
- result = self.dup._copy(options)
2100
- result.send(:_force_euqal_day, sdn-result.to_i)
2101
-
2102
- when 'year'
2103
- # 指定の剰余となる年
2104
- date = @frame.send(:_decode, _date_without_era)
2105
- date[0] = (other & (date[0] + @frame.diff_to_CE)) - @frame.diff_to_CE
2106
- options = {:date=>_date_with_era(@frame.send(:_encode, date)), :time=>@clk_time.clk_time.dup,
2107
- :events=>nil, :query=>@query}
2108
- options[:precision] = When::YEAR if precision < When::YEAR
2109
- return self.dup._copy(options)
2110
-
2111
- else
2112
- raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
2113
- end
2114
- end
2115
-
2116
- # 下位桁の切り捨て
2117
- #
2118
- # @param [Integer] digit 切り捨てずに残す、最下位の桁
2119
- #
2120
- # @param [Integer] precision 切り捨て結果の分解能
2121
- #
2122
- # @return [When::TM::DateAndTime]
2123
- #
2124
- def floor(digit=DAY, precision=digit)
2125
- count = digit - clock.indices.length
2126
-
2127
- if digit>=DAY
2128
- date = @cal_date.dup
2129
- elsif @calendar_era_props
2130
- date = @cal_date.dup
2131
- date[0] += calendar_era_epoch
2132
- date = @frame._validate(date[0..(digit-1)])
2133
- date[0] -= calendar_era_epoch
2134
- else
2135
- date = @frame._validate(@cal_date[0..(digit-1)])
2136
- end
2137
-
2138
- time = @clk_time.clk_time[0..((digit<=DAY) ? 0 : ((count>=0) ? -1 : digit))]
2139
- time[0] += to_i
2140
- time = clock._validate(time)
2141
- time[0] -= to_i
2142
-
2143
- if (count >= 0)
2144
- factor = 10**count
2145
- time[-1] = (time[-1] * factor).floor.to_f / factor
2146
- end
2147
-
2148
- # オブジェクトの生成
2149
- options = {:date=>date, :validate=>:done, :events=>nil, :query=>nil,
2150
- :time=>(digit<=DAY) ? time : @clk_time.dup._copy({:time=>time})}
2151
- options[:precision] = precision if precision
2152
- return self.dup._copy(options)
2153
- end
2154
-
2155
- # 下位桁の切り上げ
2156
- #
2157
- # @param [Integer] digit 切り上げずに残す、最下位の桁
2158
- #
2159
- # @param [Integer] precision 切り上げ結果の分解能
2160
- #
2161
- # @return [When::TM::DateAndTime]
2162
- #
2163
- def ceil(digit=DAY, precision=digit)
2164
- length = clock.indices.length
2165
- count = digit - length
2166
- period = PeriodDuration.new((count<=0) ? 1 : 0.1**count, digit, (-@frame.indices.length)..length)
2167
- result = floor(digit, precision) + period
2168
- result += clock.tz_difference if (result.universal_time <= self.universal_time)
2169
- return result
2170
- end
2171
-
2172
- # 位置情報
2173
- #
2174
- # @return [When::Coordinates::Spatial]
2175
- #
2176
- def location
2177
- @location ||= @clk_time.frame.location
2178
- end
2179
-
2180
- # 時刻情報のない When::TM::CalDate を返す
2181
- #
2182
- # @return [When::TM::CalDate]
2183
- #
2184
- def to_cal_date
2185
- options = _attr
2186
- options.delete(:clock)
2187
- options[:precision] = [When::DAY, options[:precision]].min
2188
- CalDate.new(@cal_date, options)
2189
- end
2190
- alias :to_CalDate :to_cal_date
2191
-
2192
- # 標準ライブラリの DateTime オブジェクトへの変換
2193
- #
2194
- alias :to_date_or_datetime :to_datetime
2195
-
2196
- #protected
2197
-
2198
- # 属性の Hash
2199
- # @private
2200
- def _attr
2201
- super.merge({:clock=>clock})
2202
- end
2203
-
2204
- # 属性のコピー
2205
- # @private
2206
- def _copy(options={})
2207
- # 夏時間の調整
2208
- case options[:time]
2209
- when Array
2210
- if clock._need_validate
2211
- if @calendar_era_props
2212
- date = options[:date].dup
2213
- date[0] += calendar_era_epoch
2214
- else
2215
- date = options[:date]
2216
- end
2217
- new_clock = clock._daylight([@frame, date, options[:time]])
2218
- options[:time] = options[:time].map {|t| t * 1}
2219
- else
2220
- new_clock = clock
2221
- end
2222
- options[:time] = @clk_time.dup._copy(options.merge({:clock=>new_clock}))
2223
- when nil
2224
- options[:time] = @clk_time.dup._copy(options)
2225
- end
2226
-
2227
- return super(options)
2228
- end
2229
-
2230
- # オブジェクトの生成
2231
- #
2232
- # @param [Array<Numeric>] date 日付表現
2233
- # @param [Array<Numeric>] time 時刻表現
2234
- # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
2235
- # @option options [When::TM::Calendar] :frame
2236
- # @option options [When::TM::Clock] :clock
2237
- # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
2238
- # (Integer は 当該年号の 0 年に相当する通年)
2239
- # @option options [Integer] :precision
2240
- #
2241
- def initialize(date, time, options={})
2242
- options[:time] = time
2243
- super(date, options)
2244
- end
2245
-
2246
- private
2247
-
2248
- # オブジェクトの正規化
2249
- def _normalize(options={})
2250
-
2251
- # Clock
2252
- unless options[:validate]
2253
- clock = Clock.get_clock_option(options)
2254
- clock ||= options[:time].frame if options[:time].kind_of?(ClockTime)
2255
- clock ||= Clock.local_time
2256
- end
2257
- clock = When.Clock(clock) if clock.kind_of?(String)
2258
- clock_is_timezone = clock && !clock.kind_of?(When::TM::Clock)
2259
- clock = clock.daylight if clock_is_timezone
2260
-
2261
- # ClockTime
2262
- @clk_time =
2263
- case options[:time]
2264
- when ClockTime ; options[:time]
2265
- when Array ; ClockTime.new(options[:time], {:frame=>clock, :precision=>options[:precision], :validate=>:done})
2266
- else ; clock.to_clk_time(options[:time], {:precision=>options[:precision]})
2267
- end
2268
-
2269
- super
2270
-
2271
- # 日付と時刻の正規化
2272
- unless options[:validate]
2273
- time = @clk_time.clk_time
2274
- precision = @clk_time.precision
2275
- second = time[clock.base.length-1]
2276
- second -= clock.base[-1] if second
2277
- if second && second != 0 && time_standard.has_leap?
2278
- zero = DateAndTime.new(@cal_date, time[0..-2],
2279
- {:frame=>@frame, :clock=>clock, :precision=>precision,
2280
- :era_name=>@calendar_era_props, :era=>options[:era],
2281
- :time_standard=>time_standard, :location=>@location})
2282
- end
2283
-
2284
- # 日付と時刻の関係の調整
2285
- @cal_date = _date_with_era(@frame._validate(_date_without_era) {|jdn|
2286
- time[0] += jdn
2287
- time[0..-1] = clock._validate(time)
2288
- jdn = time[0] * 1
2289
- time[0] -= jdn
2290
- jdn
2291
- })
2292
-
2293
- # 夏時間の調整
2294
- if clock._need_validate
2295
- if @calendar_era_props
2296
- cal_date = @cal_date.dup
2297
- cal_date[0] += calendar_era_epoch
2298
- else
2299
- cal_date = @cal_date
2300
- end
2301
- clock = clock._daylight([@frame, cal_date, time])
2302
- end
2303
- time = [time[0]] + time[1..-1].map {|t| t * 1}
2304
- @clk_time = ClockTime.new(time, {:frame=>clock, :precision=>precision, :validate=>:done}) if clock_is_timezone
2305
-
2306
- # 閏秒
2307
- if zero
2308
- leap = ((dynamical_time - zero.dynamical_time) * clock.second - second).to_i
2309
- if leap != 0 && leap.abs < clock.second
2310
- @cal_date = zero.cal_date
2311
- @clk_time = zero.clk_time
2312
- @clk_time.clk_time[-1] += second
2313
- leap /= clock.second
2314
- @universal_time = @local_time = When::Coordinates::LeapSeconds.new(@local_time-leap, leap, 1/clock.second)
2315
- @dynamical_time -= leap
2316
- end
2317
- end
2318
- end
2319
-
2320
- # 後処理
2321
- @keys |= @clk_time.keys
2322
- end
2323
-
2324
- # 加減算共通処理
2325
- def _plus(period)
2326
- # 日時の加算
2327
- time = @clk_time.clk_time.dup
2328
- pdate = @frame._arrange_length(period.date)
2329
- ptime = clock._arrange_length(period.time)
2330
- date = _date_with_era(@frame._validate(_date_without_era, pdate) {|jdn|
2331
- time[0] += jdn
2332
- time = clock._validate(time, ptime)
2333
- jdn = time[0] * 1
2334
- time[0] -= jdn
2335
- jdn
2336
- })
2337
-
2338
- # オブジェクトの生成
2339
- _frame_adjust(period, self.dup._copy({:date=>date, :time=>time, :validate=>:done, :events=>nil, :query=>nil}))
2340
- end
2341
- end
2342
1235
  end