class Nickel::ZTime
Attributes
firm will be used to indicate user provided am/pm
Public Class Methods
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 is assumed to be BEFORE times
# 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
# File lib/nickel/ztime.rb, line 202 def am_to_24hr(h) # note 12am is 00 h % 12 end
# File lib/nickel/ztime.rb, line 211 def format_hour(h) h.to_s.rjust(2, '0') end
# File lib/nickel/ztime.rb, line 215 def format_minute(m) m.to_s.rjust(2, '0') end
# File lib/nickel/ztime.rb, line 219 def format_second(s) s.to_s.rjust(2, '0') end
formats the hours, minutes and seconds into the format expected by the ZTime
constructor
# 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
use this if we don’t have a firm time to modify off
# 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
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
# 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 end
# 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
this can very easily be cleaned up
# 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
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
# 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
# File lib/nickel/ztime.rb, line 207 def pm_to_24hr(h) h == 12 ? 12 : h + 12 end
Private Class Methods
# File lib/nickel/ztime.rb, line 356 def adjust_for(am_pm) # how does validation work? Well, we already know that @time is valid, and once we modify we call time= which will # perform validation on the new time. That won't catch something like this though: ZTime.new("2215", :am) # so we will check for that here. # If user is providing :am or :pm, the hour must be between 1 and 12 fail 'ZTime#adjust_for says: you specified am or pm with 1 > hour > 12' unless hour >= 1 && hour <= 12 if am_pm == :am || am_pm == 'am' change_hour_to(ZTime.am_to_24hr(hour)) elsif am_pm == :pm || am_pm == 'pm' change_hour_to(ZTime.pm_to_24hr(hour)) else fail 'ZTime#adjust_for says: you passed an invalid value for am_pm, use :am or :pm' end @firm = true end
# File lib/nickel/ztime.rb, line 352 def after?(other) (hour > other.hour) || (hour == other.hour && (min > other.min || (min == other.min && sec > other.sec))) end
# File lib/nickel/ztime.rb, line 348 def before?(other) (hour < other.hour) || (hour == other.hour && (min < other.min || (min == other.min && sec < other.sec))) end
# File lib/nickel/ztime.rb, line 392 def lazy(s) # someone isn't following directions, but we will let it slide s.length == 1 && s = "0#{s}0000" # only provided h s.length == 2 && s << '0000' # only provided hh s.length == 4 && s << '00' # only provided hhmm s end
# File lib/nickel/ztime.rb, line 376 def valid @time.length == 6 && @time !~ /\D/ && valid_hour && valid_minute && valid_second end
# File lib/nickel/ztime.rb, line 380 def valid_hour hour >= 0 && hour < 24 end
# File lib/nickel/ztime.rb, line 384 def valid_minute min >= 0 && min < 60 end
# File lib/nickel/ztime.rb, line 388 def valid_second sec >= 0 && sec < 60 end
# File lib/nickel/ztime.rb, line 372 def validate fail 'ZTime#validate says: invalid time' unless valid end
Public Instance Methods
# 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
# 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_ 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)}
# 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
# File lib/nickel/ztime.rb, line 139 def am? hour < 12 # 0 through 11 on 24hr clock end
# File lib/nickel/ztime.rb, line 143 def am_pm am? ? 'am' : 'pm' end
NOTE: change_ methods modify self.
# File lib/nickel/ztime.rb, line 105 def change_hour_to(h) self.time = ZTime.format_time(h, min_str, sec_str) self end
# File lib/nickel/ztime.rb, line 110 def change_minute_to(m) self.time = ZTime.format_time(hour_str, m, sec_str) self end
# File lib/nickel/ztime.rb, line 115 def change_second_to(s) self.time = ZTime.format_time(hour_str, min_str, s) self end
# File lib/nickel/ztime.rb, line 55 def hour hour_str.to_i end
# File lib/nickel/ztime.rb, line 128 def hour_on_12hr_clock h = hour % 12 h += 12 if h == 0 h end
# File lib/nickel/ztime.rb, line 31 def hour_str @time[0..1] end
# File lib/nickel/ztime.rb, line 134 def is_am? warn '[DEPRECATION] `is_am?` is deprecated. Please use `am?` instead.' am? end
# File lib/nickel/ztime.rb, line 65 def min min_str.to_i end
# File lib/nickel/ztime.rb, line 41 def min_str @time[2..3] end
@deprecated Please use {#min} instead
# File lib/nickel/ztime.rb, line 60 def minute warn '[DEPRECATION] `minute` is deprecated. Please use `min` instead.' min end
@deprecated Please use {#min_str} instead
# File lib/nickel/ztime.rb, line 36 def minute_str warn '[DEPRECATION] `minute_str` is deprecated. Please use `min_str` instead.' min_str end
# File lib/nickel/ztime.rb, line 120 def readable @time[0..1] + ':' + @time[2..3] + ':' + @time[4..5] end
# File lib/nickel/ztime.rb, line 124 def readable_12hr hour_on_12hr_clock + ':' + @time[2..3] + " #{am_pm}" end
# File lib/nickel/ztime.rb, line 75 def sec sec_str.to_i end
# File lib/nickel/ztime.rb, line 51 def sec_str @time[4..5] end
@deprecated Please use {#sec} instead
# File lib/nickel/ztime.rb, line 70 def second warn '[DEPRECATION] `second` is deprecated. Please use `sec` instead.' sec end
@deprecated Please use {#sec_str} instead
# File lib/nickel/ztime.rb, line 46 def second_str warn '[DEPRECATION] `second_str` is deprecated. Please use `sec_str` instead.' sec_str end
# File lib/nickel/ztime.rb, line 21 def time @time end
# File lib/nickel/ztime.rb, line 25 def time=(hhmmss) @time = lazy(hhmmss) @firm = false validate end
# File lib/nickel/ztime.rb, line 159 def to_s time end
# File lib/nickel/ztime.rb, line 163 def to_time Time.parse("#{hour}:#{min}:#{sec}") end