Class: Nickel::ZTime

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/nickel/ztime.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hhmmss = nil, am_pm = nil) ⇒ ZTime

\@time is always stored on 24 hour clock, but we could initialize a Time object with ZTime.new("1020", :pm) we will convert this to 24 hour clock and set \@firm = true


12
13
14
15
16
17
18
19
# File 'lib/nickel/ztime.rb', line 12

def initialize(hhmmss = nil, am_pm = nil)
  t = hhmmss ? hhmmss : ::Time.new.strftime('%H%M%S')
  t.gsub!(/:/, '') # remove any hyphens, so a user can initialize with something like "2008-10-23"
  self.time = t
  if am_pm
    adjust_for(am_pm)
  end
end

Instance Attribute Details

#firmObject

\@firm will be used to indicate user provided am/pm


8
9
10
# File 'lib/nickel/ztime.rb', line 8

def firm
  @firm
end

Class Method Details

.am_pm_modifier(*time_array) ⇒ Object

send an array of ZTime objects, this will make a guess at whether they should be am/pm if the user did not specify NOTE ORDER IS IMPORTANT: times[0] is assumed to be BEFORE times[1]


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/nickel/ztime.rb', line 170

def am_pm_modifier(*time_array)
  # find firm time indices
  firm_time_indices = []
  time_array.each_with_index { |t, i| firm_time_indices << i if t.firm }

  if firm_time_indices.empty?
    # pure guess
    # DO WE REALLY WANT TO DO THIS?
    time_array.each_index do |i|
      # user gave us nothing
      next if i == 0
      time_array[i].guess_modify_such_that_is_after(time_array[i - 1])
    end
  else
    # first handle soft times up to first firm time
    min_boundary = 0
    max_boundary = firm_time_indices[0]
    (min_boundary...max_boundary).to_a.reverse.each do |i|      # this says, iterate backwards starting from max_boundary, but not including it, until the min boundary
      time_array[i].modify_such_that_is_before(time_array[i + 1])
    end

    firm_time_indices.each_index do |j|
      # now handle all times after first firm time until the next firm time
      min_boundary = firm_time_indices[j]
      max_boundary = firm_time_indices[j + 1] || time_array.size
      (min_boundary + 1...max_boundary).each do |i|     # any boundary problems here? What if there is only 1 time?  Nope.
        time_array[i].modify_such_that_is_after(time_array[i - 1])
      end
    end
  end
end

.am_to_24hr(h) ⇒ Object


202
203
204
205
# File 'lib/nickel/ztime.rb', line 202

def am_to_24hr(h)
  # note 12am is 00
  h % 12
end

.format_hour(h) ⇒ Object


211
212
213
# File 'lib/nickel/ztime.rb', line 211

def format_hour(h)
  h.to_s.rjust(2, '0')
end

.format_minute(m) ⇒ Object


215
216
217
# File 'lib/nickel/ztime.rb', line 215

def format_minute(m)
  m.to_s.rjust(2, '0')
end

.format_second(s) ⇒ Object


219
220
221
# File 'lib/nickel/ztime.rb', line 219

def format_second(s)
  s.to_s.rjust(2, '0')
end

.format_time(hours, minutes = 0, seconds = 0) ⇒ Object

formats the hours, minutes and seconds into the format expected by the ZTime constructor


224
225
226
# File 'lib/nickel/ztime.rb', line 224

def format_time(hours, minutes = 0, seconds = 0)
  format_hour(hours) + format_minute(minutes) + format_second(seconds)
end

.interpret(str) ⇒ Object

Interpret Time is an important one, set some goals: match all of the following a.) 5, 12, 530, 1230, 2000 b.) 5pm, 12pm, 530am, 1230am, c.) 5:30, 12:30, 20:00 d.) 5:3, 12:3, 20:3 ... that's not needed but we supported it in version 1, this would be 5:30 and 12:30 e.) 5:30am, 12:30am 20:00am, 20:00pm ... ZTime will flag these as invalid, so it is ok if we match them here


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/nickel/ztime.rb', line 236

def interpret(str)
  a_b   = /^(\d{1,4})(am|pm)?$/                     # handles cases (a) and (b)
  c_d_e = /^(\d{1,2}):(\d{1,2})(am|pm)?$/           # handles cases (c), (d), and (e)
  if mdata = str.match(a_b)
    am_pm = mdata[2]
    # this may look a bit confusing, but all we are doing is interpreting
    # what the user meant based on the number of digits they provided
    if mdata[1].length <= 2
      # e.g. "11" means 11:00
      hstr = mdata[1]
      mstr = '0'
    elsif mdata[1].length == 3
      # e.g. "530" means 5:30
      hstr = mdata[1][0..0]
      mstr = mdata[1][1..2]
    elsif mdata[1].length == 4
      # e.g. "1215" means 12:15
      hstr = mdata[1][0..1]
      mstr = mdata[1][2..3]
    end
  elsif mdata = str.match(c_d_e)
    am_pm = mdata[3]
    hstr = mdata[1]
    mstr = mdata[2]
  else
    return nil
  end
  # in this case we do not care if time fails validation, if it does, it just means we haven't found a valid time, return nil
  begin ZTime.new(ZTime.format_time(hstr, mstr), am_pm) rescue return nil end
end

.pm_to_24hr(h) ⇒ Object


207
208
209
# File 'lib/nickel/ztime.rb', line 207

def pm_to_24hr(h)
  h == 12 ? 12 : h + 12
end

Instance Method Details

#<=>(other) ⇒ Object


147
148
149
150
151
152
153
154
155
156
157
# File 'lib/nickel/ztime.rb', line 147

def <=>(other)
  return nil unless [:hour, :min, :sec].all? { |m| other.respond_to?(m) }

  if before?(other)
    -1
  elsif after?(other)
    1
  else
    0
  end
end

#add_hours(number, &block) ⇒ Object


96
97
98
99
100
101
102
# File 'lib/nickel/ztime.rb', line 96

def add_hours(number, &block)
  o = dup
  if block_given?
    yield((o.hour + number) / 24)
  end
  o.change_hour_to((o.hour + number) % 24)
end

#add_minutes(number, &block) ⇒ Object

add_ methods return new ZTime object add_ methods take an optional block, the block will be passed the number of days that have passed; i.e. adding 48 hours will pass a 2 to the block, this is handy for something like this: time.add_hours(15) {|x| date.add_days(x)}


83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/nickel/ztime.rb', line 83

def add_minutes(number, &block)
  # new minute is going to be (current minute + number) % 60
  # number of hours to add is (current minute + number) / 60
  hours_to_add = (min + number) / 60
  # note add_hours returns a new time object
  if block_given?
    o = add_hours(hours_to_add, &block)
  else
    o = add_hours(hours_to_add)
  end
  o.change_minute_to((o.min + number) % 60)  # modifies self
end

#am?Boolean


139
140
141
# File 'lib/nickel/ztime.rb', line 139

def am?
  hour < 12   # 0 through 11 on 24hr clock
end

#am_pmObject


143
144
145
# File 'lib/nickel/ztime.rb', line 143

def am_pm
  am? ? 'am' : 'pm'
end

#change_hour_to(h) ⇒ Object

NOTE: change_ methods modify self.


105
106
107
108
# File 'lib/nickel/ztime.rb', line 105

def change_hour_to(h)
  self.time = ZTime.format_time(h, min_str, sec_str)
  self
end

#change_minute_to(m) ⇒ Object


110
111
112
113
# File 'lib/nickel/ztime.rb', line 110

def change_minute_to(m)
  self.time = ZTime.format_time(hour_str, m, sec_str)
  self
end

#change_second_to(s) ⇒ Object


115
116
117
118
# File 'lib/nickel/ztime.rb', line 115

def change_second_to(s)
  self.time = ZTime.format_time(hour_str, min_str, s)
  self
end

#guess_modify_such_that_is_after(time1) ⇒ Object

use this if we don't have a firm time to modify off


334
335
336
337
338
339
340
341
342
343
344
# File 'lib/nickel/ztime.rb', line 334

def guess_modify_such_that_is_after(time1)
  # time1 to self    time1 to self
  # 9    to    5 --> 0900 to 0500
  # 9   to     9 --> 0900 to 0900
  # 12   to   12 --> 1200 to 1200
  # 12 to 6   --->   1200 to 0600
  if time1 >= self
    # crossed boundary at noon
    hour == 12 ? change_hour_to(0) : change_hour_to(hour + 12)
  end
end

#hourObject


55
56
57
# File 'lib/nickel/ztime.rb', line 55

def hour
  hour_str.to_i
end

#hour_on_12hr_clockObject


128
129
130
131
132
# File 'lib/nickel/ztime.rb', line 128

def hour_on_12hr_clock
  h = hour % 12
  h += 12 if h == 0
  h
end

#hour_strObject


31
32
33
# File 'lib/nickel/ztime.rb', line 31

def hour_str
  @time[0..1]
end

#is_am?Boolean


134
135
136
137
# File 'lib/nickel/ztime.rb', line 134

def is_am?
  warn '[DEPRECATION] `is_am?` is deprecated.  Please use `am?` instead.'
  am?
end

#minObject


65
66
67
# File 'lib/nickel/ztime.rb', line 65

def min
  min_str.to_i
end

#min_strObject


41
42
43
# File 'lib/nickel/ztime.rb', line 41

def min_str
  @time[2..3]
end

#minuteObject

Deprecated.

Please use #min instead


60
61
62
63
# File 'lib/nickel/ztime.rb', line 60

def minute
  warn '[DEPRECATION] `minute` is deprecated.  Please use `min` instead.'
  min
end

#minute_strObject

Deprecated.

Please use #min_str instead


36
37
38
39
# File 'lib/nickel/ztime.rb', line 36

def minute_str
  warn '[DEPRECATION] `minute_str` is deprecated.  Please use `min_str` instead.'
  min_str
end

#modify_such_that_is_after(time1) ⇒ Object


302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/nickel/ztime.rb', line 302

def modify_such_that_is_after(time1)
  fail 'ZTime#modify_such_that_is_after says: trying to modify time that has @firm set' if @firm
  fail 'ZTime#modify_such_that_is_after says: time1 does not have @firm set' unless time1.firm
  # time1 to self --> time1 to self
  # 8pm   to 835  --> 2000 to 835
  # 835pm to 835  --> 2035 to 835
  # 10pm  to 11   --> 2200 to 1100
  # 1021pm to 1223--> 2221 to 1223
  # 930am  to 5 --->  0930 to 0500
  # 930pm  to 5 --->  2130 to 0500
  if self < time1
    unless time1.hour >= 12 && ZTime.new(ZTime.format_time(time1.hour - 12, time1.min_str, time1.sec_str)) >= self
      hour == 12 ? change_hour_to(0) : change_hour_to(hour + 12)
    end
  elsif self > time1
    # # time1 to self --> time1 to self
    # # 10am  to 11   --> 1000  to 1100
    # #
    # if time1.hour >= 12 && ZTime.new(ZTime.format_time(time1.hour - 12, time1.min_str, time1.sec_str)) > self
    #   change_hour_to(self.hour + 12)
    # else
    #   # do nothing
    # end
  else
    # the times are equal, and self can only be between 0100 and 1200, so move self forward 12 hours, unless hour is 12
    hour == 12 ? change_hour_to(0) : change_hour_to(hour + 12)
  end
  self.firm = true
  self
end

#modify_such_that_is_before(time2) ⇒ Object

this can very easily be cleaned up


269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/nickel/ztime.rb', line 269

def modify_such_that_is_before(time2)
  fail 'ZTime#modify_such_that_is_before says: trying to modify time that has @firm set' if @firm
  fail 'ZTime#modify_such_that_is_before says: time2 does not have @firm set' unless time2.firm
  # self cannot have @firm set, so all hours will be between 1 and 12
  # time2 is an end time, self could be its current setting, or off by 12 hours

  # self to time2 --> self to time2
  # 12   to 2am   --> 1200 to 0200
  # 12   to 12am  --> 1200 to 0000
  # 1220 to 12am  --> 1220 to 0000
  # 11 to 2am  or 1100 to 0200
  if self > time2
    if hour == 12 && time2.hour == 0
      # do nothing
    else
      hour == 12 ? change_hour_to(0) : change_hour_to(hour + 12)
    end
  elsif self < time2
    if time2.hour >= 12 && ZTime.new(ZTime.format_time(time2.hour - 12, time2.min_str, time2.sec_str)) > self
      # 4 to 5pm  or 0400 to 1700
      change_hour_to(hour + 12)
    else
      # 4 to 1pm  or 0400 to 1300
      # do nothing
    end
  else
    # the times are equal, and self can only be between 0100 and 1200, so move self forward 12 hours, unless hour is 12
    hour == 12 ? change_hour_to(0) : change_hour_to(hour + 12)
  end
  self.firm = true
  self
end

#readableObject


120
121
122
# File 'lib/nickel/ztime.rb', line 120

def readable
  @time[0..1] + ':' + @time[2..3] + ':' + @time[4..5]
end

#readable_12hrObject


124
125
126
# File 'lib/nickel/ztime.rb', line 124

def readable_12hr
  hour_on_12hr_clock + ':' + @time[2..3] + " #{am_pm}"
end

#secObject


75
76
77
# File 'lib/nickel/ztime.rb', line 75

def sec
  sec_str.to_i
end

#sec_strObject


51
52
53
# File 'lib/nickel/ztime.rb', line 51

def sec_str
  @time[4..5]
end

#secondObject

Deprecated.

Please use #sec instead


70
71
72
73
# File 'lib/nickel/ztime.rb', line 70

def second
  warn '[DEPRECATION] `second` is deprecated.  Please use `sec` instead.'
  sec
end

#second_strObject

Deprecated.

Please use #sec_str instead


46
47
48
49
# File 'lib/nickel/ztime.rb', line 46

def second_str
  warn '[DEPRECATION] `second_str` is deprecated.  Please use `sec_str` instead.'
  sec_str
end

#timeObject


21
22
23
# File 'lib/nickel/ztime.rb', line 21

def time
  @time
end

#time=(hhmmss) ⇒ Object


25
26
27
28
29
# File 'lib/nickel/ztime.rb', line 25

def time=(hhmmss)
  @time = lazy(hhmmss)
  @firm = false
  validate
end

#to_sObject


159
160
161
# File 'lib/nickel/ztime.rb', line 159

def to_s
  time
end

#to_timeObject


163
164
165
# File 'lib/nickel/ztime.rb', line 163

def to_time
  Time.parse("#{hour}:#{min}:#{sec}")
end