timesteps 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,6 @@
1
1
 
2
+ autoload :TZInfo, "tzinfo"
3
+
2
4
  class TimeStep
3
5
 
4
6
  class Calendar
@@ -23,12 +25,11 @@ class TimeStep
23
25
 
24
26
  # Construct the object
25
27
  #
26
- def initialize (calendar = "standard", bc: false)
28
+ def initialize (calendar = "standard", tz: nil)
27
29
  unless calendar.is_a?(String)
28
30
  raise ArgumentError, "argument calendar '#{calendar}' should be string"
29
31
  end
30
32
  @name = calendar
31
- @bc = bc
32
33
  case @name.downcase.intern
33
34
  when :standard, :gregorian
34
35
  @calendar = :standard
@@ -43,22 +44,30 @@ class TimeStep
43
44
  when SYM_360_day
44
45
  @calendar = SYM_360_day
45
46
  end
47
+ if tz
48
+ raise "'standard' calendar needed for tz" unless @calendar == :standard
49
+ case tz
50
+ when nil
51
+ when String
52
+ @tz = TZInfo::Timezone.get(tz)
53
+ when TZInfo::Timezone
54
+ @tz = tz
55
+ else
56
+ raise "invalid tz specification"
57
+ end
58
+ end
46
59
  end
47
60
 
48
- attr_reader :name, :calendar
49
-
61
+ attr_reader :name, :calendar, :tz
62
+
50
63
  def inspect
51
- "#<TimeStep::Calendar calendar='#{@calendar.to_s}' bc='#{@bc}'>"
64
+ "#<TimeStep::Calendar calendar='#{@calendar.to_s}'>"
52
65
  end
53
66
 
54
67
  def == (other)
55
- return @calendar == other.calendar && bc? == other.bc?
56
- end
57
-
58
- def bc?
59
- return @bc ? true : false
68
+ return @calendar == other.calendar
60
69
  end
61
-
70
+
62
71
  def valid_datetime_type? (time)
63
72
  return false unless time.is_a?(DateTimeType[@calendar])
64
73
  case @calendar
@@ -77,10 +86,31 @@ class TimeStep
77
86
  # and creates an date time instance.
78
87
  #
79
88
  # @return [DateTime object]
80
- def parse (timespec, format: nil)
81
- return DateTime.parse_timestamp(timespec, calendar: @calendar.to_s, bc: @bc, format: format)
89
+ def parse (timespec, format: nil, offset: nil)
90
+ return DateTime.parse_timestamp(timespec, calendar: @calendar.to_s, format: format, tz: @tz, offset: offset)
82
91
  end
83
-
92
+
93
+ def time_new (year, month, day, hour=0, min=0, sec=0, offset=0)
94
+ case @calendar
95
+ when :standard
96
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::ITALY)
97
+ when :proleptic_gregorian
98
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::GREGORIAN)
99
+ when :proleptic_julian
100
+ time = DateTime.new(year, month, day, hour, min, sec, offset, Date::JULIAN)
101
+ when :noleap
102
+ time = DateTime::NoLeap.new(year, month, day, hour, min, sec, offset)
103
+ when :allleap
104
+ time = DateTime::AllLeap.new(year, month, day, hour, min, sec, offset)
105
+ when SYM_360_day
106
+ time = DateTime::Fixed360Day.new(year, month, day, hour, min, sec, offset)
107
+ end
108
+ if @tz
109
+ time = @tz.to_local(time)
110
+ end
111
+ return time
112
+ end
113
+
84
114
  def jday2date (jday)
85
115
  case @calendar
86
116
  when :standard
@@ -96,27 +126,17 @@ class TimeStep
96
126
  when SYM_360_day
97
127
  time = DateTime::Fixed360Day.jd(jday, 0, 0, 0, 0)
98
128
  end
129
+ if @tz
130
+ time = @tz.to_local(time)
131
+ end
99
132
  return time
100
133
  end
101
134
 
102
135
  def date2jday (year, month, day)
103
- case @calendar
104
- when :standard
105
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::ITALY)
106
- when :proleptic_gregorian
107
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::GREGORIAN)
108
- when :proleptic_julian
109
- time = DateTime.new(year, month, day, 0, 0, 0, 0, Date::JULIAN)
110
- when :noleap
111
- time = DateTime::NoLeap.new(year, month, day, 0, 0, 0, 0)
112
- when :allleap
113
- time = DateTime::AllLeap.new(year, month, day, 0, 0, 0, 0)
114
- when SYM_360_day
115
- time = DateTime::Fixed360Day.new(year, month, day, 0, 0, 0, 0)
116
- end
117
- return time.jd
136
+ return time_new(year, month, day).jd
118
137
  end
119
138
 
120
139
  end
121
140
 
122
141
  end
142
+
@@ -2,12 +2,11 @@ require "timesteps"
2
2
 
3
3
  class TimeStep::Converter
4
4
 
5
- def initialize (reference, name: "reference", calendar: "standard", bc: false)
5
+ def initialize (reference, name: "reference", calendar: "standard")
6
6
  @calendar = calendar
7
- @bc = bc
8
7
  case reference
9
8
  when String
10
- @reference = TimeStep.new(reference, calendar: calendar, bc: bc)
9
+ @reference = TimeStep.new(reference, calendar: calendar)
11
10
  when TimeStep
12
11
  @reference = reference
13
12
  else
@@ -21,9 +20,9 @@ class TimeStep::Converter
21
20
  # Append or reset new target time step for the given name.
22
21
  #
23
22
  def add_timestep (spec, name:)
24
- @pair[name] = TimeStep::Pair.new(@reference, spec, calendar: @calendar, bc: @bc)
23
+ @pair[name] = TimeStep::Pair.new(@reference, spec, calendar: @calendar)
25
24
  @target[name] = @pair[name].to
26
- return @target[name]
25
+ return @pair[name]
27
26
  end
28
27
 
29
28
  # Append or reset new target time step for the given name.
@@ -35,14 +34,14 @@ class TimeStep::Converter
35
34
  # Return target time step of the given name.
36
35
  #
37
36
  def [] (name)
38
- return @target[name]
37
+ return @pair[name]
39
38
  end
40
39
 
41
40
  # Relete target of the given name.
42
41
  #
43
42
  def delete (name)
44
- @pair.delete(name)
45
43
  @target.delete(name)
44
+ @pair.delete(name)
46
45
  end
47
46
 
48
47
  # Returns the time represented by the given index as DateTime object
@@ -56,11 +55,18 @@ class TimeStep::Converter
56
55
 
57
56
  # Converts index refering `from` timestep to index refering `to`timestep.
58
57
  #
59
- def forward (*indices, with_time: false)
58
+ def forward (*indices, with_time: nil)
60
59
  if with_time
61
- hash = {
62
- "time" => time_at(*indices)
63
- }
60
+ case with_time
61
+ when String
62
+ hash = {
63
+ "time" => time_at(*indices).map{|t| t.strftime(with_time) }
64
+ }
65
+ else
66
+ hash = {
67
+ "time" => time_at(*indices)
68
+ }
69
+ end
64
70
  else
65
71
  hash = {}
66
72
  end
@@ -70,8 +76,8 @@ class TimeStep::Converter
70
76
  return hash
71
77
  end
72
78
 
73
- def range (ge: nil, gt: nil, lt: nil, le: nil, with_time: false)
74
- indices = @reference.range(ge: ge, gt: gt, lt: lt, le: le)
79
+ def range (other, with_time: false)
80
+ indices = @reference.range(other)
75
81
  return forward(*indices, with_time: with_time)
76
82
  end
77
83
 
@@ -80,14 +86,5 @@ class TimeStep::Converter
80
86
  return forward(*indices, with_time: with_time)
81
87
  end
82
88
 
83
- def valid? (*indices)
84
- hash = {}
85
- @pair.each do |name, conv|
86
- hash[name] = conv.to.valid?(*conv.forward(*indices))
87
- end
88
- return hash
89
- end
90
-
91
-
92
89
  end
93
90
 
@@ -46,28 +46,18 @@ class TimeStep
46
46
  #
47
47
  # @return [Integer]
48
48
  def difference_in_years (other)
49
- extra = 0
50
- if self.year > other.year && self.compare_md(other) < 0
51
- extra = -1
52
- end
53
- if self.year < other.year && self.compare_md(other) > 0
54
- extra = 1
55
- end
56
- return self.year - other.year + extra
49
+ my = self
50
+ other = other.new_offset(self.offset)
51
+ return my.year - other.year + my.compare_md(other).quo(2)
57
52
  end
58
53
 
59
54
  # Calculate difference between the object and other object in months.
60
55
  #
61
56
  # @return [Integer]
62
57
  def difference_in_months (other)
63
- extra = 0
64
- if self.month > other.month && self.compare_d(other) < 0
65
- extra = -1
66
- end
67
- if self.month < other.month && self.compare_d(other) > 0
68
- extra = 1
69
- end
70
- return 12*(self.year - other.year) + self.month - other.month + extra
58
+ my = self
59
+ other = other.new_offset(self.offset)
60
+ return 12*(my.year - other.year) + my.month - other.month + my.compare_d(other).quo(2)
71
61
  end
72
62
 
73
63
  end
@@ -8,21 +8,19 @@ class TimeStep::Pair
8
8
  # The `from` and `to` arguments are either TimeStep or a string representing
9
9
  # the time step definition ("<INTERVAL> since <TIME>").
10
10
  # If a string representing the time step definition is given as an argument
11
- # `from` or `to`, the options `calendar` and `bc` are used to construct
11
+ # `from` or `to`, the options `calendar` are used to construct
12
12
  # TimeStep. The option `calendar` specifies the calendar for datetime
13
- # calculation. The option `bc` is a flag whether AD/BC or EC when parsing
14
- # timestamp for negative year.
13
+ # calculation.
15
14
  #
16
15
  # @param from [TimeStep, String]
17
16
  # @param to [TimeStep, String]
18
17
  # @option calendar [String, TimeStep::Calendar]
19
- # @option bc [Boolean]
20
18
  #
21
- def initialize (from, to, calendar: "standard", bc: false)
19
+ def initialize (from, to, calendar: "standard")
22
20
 
23
21
  case from
24
22
  when String
25
- @from = TimeStep.new(from, calendar: calendar, bc: bc)
23
+ @from = TimeStep.new(from, calendar: calendar)
26
24
  when TimeStep
27
25
  @from = from
28
26
  else
@@ -31,7 +29,7 @@ class TimeStep::Pair
31
29
 
32
30
  case to
33
31
  when String
34
- @to = TimeStep.new(to, calendar: calendar, bc: bc)
32
+ @to = TimeStep.new(to, calendar: calendar)
35
33
  when TimeStep
36
34
  @to = to
37
35
  else
@@ -66,13 +64,17 @@ class TimeStep::Pair
66
64
  "#<TimeStep::Pair from='#{from.inspect}' to='#{to.inspect}'>"
67
65
  end
68
66
 
67
+ def invert
68
+ return TimeStep::Pair.new(@to, @from)
69
+ end
70
+
69
71
  # Returns true if other has same contents of `from` and `to` as self has.
70
72
  #
71
73
  # @return [Boolean]
72
74
  def == (other)
73
75
  return @from == other.from && @to == other.to
74
76
  end
75
-
77
+
76
78
  # Returns the time represented by the given index as DateTime object
77
79
  #
78
80
  # @param indices [Numeric,Array<Numeric>]
@@ -85,10 +87,16 @@ class TimeStep::Pair
85
87
  # Converts index refering `from` timestep to index refering `to`timestep.
86
88
  #
87
89
  def forward (*indices, &block)
88
- if indices.size == 1
90
+ if indices.empty? || indices.size == 1 && indices.first.is_a?(Range)
91
+ return forward(*@from.range(*indices), &block)
92
+ elsif indices.size == 1
89
93
  index = indices.first.to_r
90
94
  if @numeric_conversion
91
- value = (index*@from_interval + @difference).quo(@to_interval)
95
+ if @to_interval == 0
96
+ value = 0
97
+ else
98
+ value = (index*@from_interval + @difference).quo(@to_interval)
99
+ end
92
100
  else
93
101
  case @from_symbol
94
102
  when :years, :months
@@ -97,12 +105,14 @@ class TimeStep::Pair
97
105
  target = @from_origin + index*(@from_interval.quo(86400))
98
106
  end
99
107
  case @to_symbol
100
- when :years
101
- value = target.difference_in_years(@to_origin).quo(@to.numeric)
108
+ when :years
109
+ diff = target.difference_in_years(@to_origin)
110
+ value = diff.quo(@to.numeric)
102
111
  when :months
103
- value = target.difference_in_months(@to_origin).quo(@to.numeric)
112
+ diff = target.difference_in_months(@to_origin)
113
+ value = diff.quo(@to.numeric)
104
114
  else
105
- value = (target.ajd - @to_origin.ajd).quo(@to_interval)
115
+ value = (target.ajd - @to_origin.ajd).quo(@to_interval)*86400
106
116
  end
107
117
  end
108
118
  value = value.to_i if value.denominator == 1
@@ -126,18 +136,19 @@ class TimeStep::Pair
126
136
  return forward(*indices) {|index| @to.time_at(index) }
127
137
  end
128
138
 
129
- def range (ge: nil, gt: nil, lt: nil, le: nil)
130
- indices = @from.range(ge: ge, gt: gt, lt: lt, le: le)
131
- return forward(*indices)
132
- end
133
-
134
139
  # Converts index refering `to` timestep to index refering `from` timestep.
135
140
  #
136
141
  def inverse (*indices, &block)
137
- if indices.size == 1
142
+ if indices.empty? || indices.size == 1 && indices.first.is_a?(Range)
143
+ return inverse(*@to.range(*indices), &block)
144
+ elsif indices.size == 1
138
145
  index = indices.first.to_r
139
146
  if @numeric_conversion
140
- value = (index*@to_interval - @difference).quo(@from_interval)
147
+ if @from_interval == 0
148
+ value = 0
149
+ else
150
+ value = (index*@to_interval - @difference).quo(@from_interval)
151
+ end
141
152
  else
142
153
  case @to_symbol
143
154
  when :years, :months
@@ -147,11 +158,13 @@ class TimeStep::Pair
147
158
  end
148
159
  case @from_symbol
149
160
  when :years
150
- value = target.difference_in_years(@from_origin).quo(@from.numeric)
161
+ diff = target.difference_in_years(@from_origin)
162
+ value = diff.quo(@from.numeric)
151
163
  when :months
152
- value = target.difference_in_months(@from_origin).quo(@from.numeric)
164
+ diff = target.difference_in_months(@from_origin)
165
+ value = diff.quo(@from.numeric)
153
166
  else
154
- value = (target.ajd - @from_origin.ajd).quo(@from_interval)
167
+ value = (target.ajd - @from_origin.ajd).quo(@from_interval)*86400
155
168
  end
156
169
  end
157
170
  value = value.to_i if value.denominator == 1
@@ -0,0 +1,150 @@
1
+ require "forwardable"
2
+
3
+ class TimeStep::Range
4
+
5
+ extend Forwardable
6
+
7
+ def initialize (timestep, start = nil, last = nil, count: nil, ends: "[]")
8
+
9
+ raise "'ends' option should be one of '[]', '()', '(]', '[)" unless ends =~ /\A[\[\(][\]\)]\z/
10
+ include_start = ends[0] == "["
11
+ include_last = ends[1] == "]"
12
+
13
+ if last
14
+ case start
15
+ when Numeric
16
+ raise "start and last types doesn't match" unless last.is_a?(Numeric)
17
+ when DateTime, DateTimeLike, String
18
+ if last.is_a?(Integer)
19
+ start = timestep.index_at(start)
20
+ last = start + last - 1
21
+ else
22
+ raise "start and last types doesn't match" unless last.is_a?(start.class)
23
+ start = timestep.index_at(start)
24
+ last = timestep.index_at(last)
25
+ end
26
+ else
27
+ raise "unknown argument"
28
+ end
29
+ else
30
+ case start
31
+ when Integer
32
+ count = start
33
+ start = 0
34
+ last = count - 1
35
+ when String
36
+ raise "count should be set" unless count.is_a?(Integer)
37
+ start = timestep.index_at(start)
38
+ last = start + count - 1
39
+ when TimePeriod
40
+ period = range
41
+ return initialize(timestep, period.origin..period.last, ends: period.ends)
42
+ else
43
+ raise "unknown argument"
44
+ end
45
+ end
46
+
47
+ if include_start
48
+ @start = start.ceil
49
+ else
50
+ @start = start.floor + 1
51
+ end
52
+
53
+ if include_last
54
+ @last = last.floor
55
+ else
56
+ @last = last.ceil - 1
57
+ end
58
+
59
+ @timestep = timestep.new_origin(timestep.time_at(@start))
60
+ @count = @last - @start + 1
61
+
62
+ @start_time = @timestep.time_at(@start)
63
+ @last_time = @timestep.time_at(@last)
64
+ end
65
+
66
+ attr_reader :timestep, :count, :start, :last, :start_time, :last_time
67
+
68
+ def_delegators :@timestep, :numeric, :symbol, :interval,
69
+ :origin, :offset, :calendar, :definition,
70
+ :parse, :time_at, :index_at, :duration_at, :[]
71
+
72
+
73
+ # FIXME
74
+ def valid? (*indices)
75
+ if @count
76
+ if indices.size == 1
77
+ index = indices.first
78
+ if index >= 0 and index < @count
79
+ return true
80
+ else
81
+ return false
82
+ end
83
+ else
84
+ return indices.map{|index| valid?(index) }
85
+ end
86
+ else
87
+ if indices.size == 1
88
+ return true
89
+ else
90
+ return indices.map{|index| true }
91
+ end
92
+ end
93
+ end
94
+
95
+ # Returns TimeStep object which has origin time determined
96
+ # by the given `index`.
97
+ #
98
+ # @param index [Numeric]
99
+ #
100
+ # @return [TimeStep]
101
+ def shift_origin (index)
102
+ case with
103
+ when :index, "index"
104
+ timestep = @timestep.shift_origin(index)
105
+ return TimeStep::Range.new(timestep, count: @count)
106
+ when :duration, "duration", :days, "days"
107
+ timestep = @timestep.shift_origin(index, with: "duration")
108
+ return TimeStep::Range.new(timestep, count: @count)
109
+ end
110
+ end
111
+
112
+ # Returns TimeStep object which has origin time specified
113
+ # by the given `time`.
114
+ #
115
+ # @param time [DateTime]
116
+ #
117
+ # @return [TimeStep]
118
+ def new_origin (time)
119
+ timestep = @timestep.new_origin(time)
120
+ return TimeStep::Range.new(timestep, count: @count)
121
+ end
122
+
123
+ include Enumerable
124
+
125
+ def each_time (&block)
126
+ if block
127
+ @start.upto(@last) do |k|
128
+ block.call(@timestep.time_at(k))
129
+ end
130
+ else
131
+ return Enumerator.new {|y|
132
+ @start.upto(@last) do |k|
133
+ y << @timestep.time_at(k)
134
+ end
135
+ }
136
+ end
137
+ end
138
+
139
+ alias each each_time
140
+
141
+ def each_index (&block)
142
+ (@start..@last).each(&block)
143
+ end
144
+
145
+ def map_index (&block)
146
+ (@start..@last).map(&block)
147
+ end
148
+
149
+ end
150
+