timesteps 0.9.6 → 0.9.7

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,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
+