Class: Tod::TimeOfDay

Inherits:
Object
  • Object
show all
Extended by:
Mongoization::ClassMethods
Includes:
Comparable, Mongoization
Defined in:
lib/tod/time_of_day.rb,
lib/tod/mongoization.rb

Constant Summary collapse

PARSE_24H_REGEX =
/
  \A
  ([01]?\d|2[0-4])
  :?
  ([0-5]\d)?
  :?
  ([0-5]\d)?
  \z
/x
PARSE_12H_REGEX =
/
  \A
  (0?\d|1[0-2])
  :?
  ([0-5]\d)?
  :?
  ([0-5]\d)?
  \s*
  ([ap])
  \.?
  \s*
  m?
  \.?
  \z
/x
WORDS =
{
  "noon" => "12pm",
  "midnight" => "12am"
}
NUM_SECONDS_IN_DAY =
86400
NUM_SECONDS_IN_HOUR =
3600
NUM_SECONDS_IN_MINUTE =
60
FORMATS =
{
  short: "%-l:%M %P",
  medium: "%-l:%M:%S %P",
  time: "%H:%M"
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mongoization::ClassMethods

demongoize, evolve, mongoize

Methods included from Mongoization

#mongoize

Constructor Details

#initialize(h, m = 0, s = 0) ⇒ TimeOfDay

Returns a new instance of TimeOfDay.

Raises:

  • (ArgumentError)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/tod/time_of_day.rb', line 51

def initialize(h, m=0, s=0)
  @hour = Integer(h)
  @minute = Integer(m)
  @second = Integer(s)

  raise ArgumentError, "hour must be between 0 and 24" unless (0..24).include?(@hour)
  if @hour == 24 && (@minute != 0 || @second != 0)
    raise ArgumentError, "hour can only be 24 when minute and second are 0"
  end
  raise ArgumentError, "minute must be between 0 and 59" unless (0..59).include?(@minute)
  raise ArgumentError, "second must be between 0 and 59" unless (0..59).include?(@second)

  @second_of_day = @hour * 60 * 60 + @minute * 60 + @second

  freeze # TimeOfDay instances are value objects
end

Instance Attribute Details

#hourObject (readonly)

Returns the value of attribute hour.



5
6
7
# File 'lib/tod/time_of_day.rb', line 5

def hour
  @hour
end

#minuteObject (readonly) Also known as: min

Returns the value of attribute minute.



5
6
7
# File 'lib/tod/time_of_day.rb', line 5

def minute
  @minute
end

#secondObject (readonly) Also known as: sec

Returns the value of attribute second.



5
6
7
# File 'lib/tod/time_of_day.rb', line 5

def second
  @second
end

#second_of_dayObject (readonly) Also known as: to_i

Returns the value of attribute second_of_day.



5
6
7
# File 'lib/tod/time_of_day.rb', line 5

def second_of_day
  @second_of_day
end

Class Method Details

.dump(time_of_day) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/tod/time_of_day.rb', line 204

def self.dump(time_of_day)
  time_of_day =
    if time_of_day.is_a? Hash
       # rails multiparam attribute
       # get hour, minute and second and construct new TimeOfDay object
      ::Tod::TimeOfDay.new(time_of_day[4], time_of_day[5], time_of_day[6])
    else
      # return nil, if input is not parsable
      Tod::TimeOfDay(time_of_day){}
    end
  time_of_day.to_s if time_of_day
end

.from_second_of_day(second_of_day) ⇒ Object Also known as: from_i

Build a new TimeOfDay instance from second_of_day

TimeOfDay.from_second_of_day(3600) == TimeOfDay.new(1)   # => true


134
135
136
137
138
139
140
141
142
143
# File 'lib/tod/time_of_day.rb', line 134

def self.from_second_of_day(second_of_day)
  second_of_day = Integer(second_of_day)
  return new 24 if second_of_day == NUM_SECONDS_IN_DAY
  remaining_seconds = second_of_day % NUM_SECONDS_IN_DAY
  hour = remaining_seconds / NUM_SECONDS_IN_HOUR
  remaining_seconds -= hour * NUM_SECONDS_IN_HOUR
  minute = remaining_seconds / NUM_SECONDS_IN_MINUTE
  remaining_seconds -= minute * NUM_SECONDS_IN_MINUTE
  new hour, minute, remaining_seconds
end

.load(time) ⇒ Object



217
218
219
220
221
222
# File 'lib/tod/time_of_day.rb', line 217

def self.load(time)
  if time && !time.to_s.empty?
    return ::Tod::TimeOfDay.new(24) if time.respond_to?(:day) && time.day == 2 && time.hour == 0 && time.min == 0 && time.sec == 0
    ::Tod::TimeOfDay(time)
  end
end

.parsable?(tod_string) ⇒ Boolean

Determine if a string is parsable into a TimeOfDay instance

TimeOfDay.parsable? "8am"                      # => true
TimeOfDay.parsable? "abc"                      # => false

Returns:

  • (Boolean)


195
196
197
# File 'lib/tod/time_of_day.rb', line 195

def self.parsable?(tod_string)
  !!try_parse(tod_string)
end

.parse(tod_string) ⇒ Object

Build a TimeOfDay instance from string

Strings only need to contain an hour. Minutes, seconds, AM or PM, and colons are all optional.

TimeOfDay.parse "8"                            # => 08:00:00
TimeOfDay.parse "8am"                          # => 08:00:00
TimeOfDay.parse "8pm"                          # => 20:00:00
TimeOfDay.parse "8p"                           # => 20:00:00
TimeOfDay.parse "9:30"                         # => 09:30:00
TimeOfDay.parse "15:30"                        # => 15:30:00
TimeOfDay.parse "3:30pm"                       # => 15:30:00
TimeOfDay.parse "1230"                         # => 12:30:00
TimeOfDay.parse "3:25:58"                      # => 03:25:58
TimeOfDay.parse "515p"                         # => 17:15:00
TimeOfDay.parse "151253"                       # => 15:12:53

You can give a block, that is called with the input if the string is not parsable. If no block is given an ArgumentError is raised if try_parse returns nil.



165
166
167
# File 'lib/tod/time_of_day.rb', line 165

def self.parse(tod_string)
  try_parse(tod_string) || (block_given? ? yield(tod_string) : raise(ArgumentError, "Invalid time of day string"))
end

.time_zoneObject

If ActiveSupport TimeZone is available and set use current time zone else return Time



200
201
202
# File 'lib/tod/time_of_day.rb', line 200

def self.time_zone
  (Time.respond_to?(:zone) && Time.zone) || Time
end

.try_parse(tod_string) ⇒ Object

Same as parse(), but return nil if not parsable (instead of raising an error)

TimeOfDay.try_parse "8am"                      # => 08:00:00
TimeOfDay.try_parse ""                         # => nil
TimeOfDay.try_parse "abc"                      # => nil


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/tod/time_of_day.rb', line 173

def self.try_parse(tod_string)
  tod_string = tod_string.to_s
  tod_string = tod_string.strip
  tod_string = tod_string.downcase
  tod_string = WORDS[tod_string] || tod_string
  if PARSE_24H_REGEX =~ tod_string || PARSE_12H_REGEX =~ tod_string
    hour, minute, second, a_or_p = $1.to_i, $2.to_i, $3.to_i, $4
    if hour == 12 && a_or_p == "a"
      hour = 0
    elsif hour < 12 && a_or_p == "p"
      hour += 12
    end

    new hour, minute, second
  else
    nil
  end
end

Instance Method Details

#+(num_seconds) ⇒ Object

Return a new TimeOfDay num_seconds greater than self. It will wrap around at midnight.



111
112
113
# File 'lib/tod/time_of_day.rb', line 111

def +(num_seconds)
  TimeOfDay.from_second_of_day @second_of_day + num_seconds
end

#-(other) ⇒ Object

Return a new TimeOfDay num_seconds less than self. It will wrap around at midnight.



117
118
119
120
121
122
123
# File 'lib/tod/time_of_day.rb', line 117

def -(other)
  if other.instance_of?(TimeOfDay)
    TimeOfDay.from_second_of_day @second_of_day - other.second_of_day
  else
    TimeOfDay.from_second_of_day @second_of_day - other
  end
end

#<=>(other) ⇒ Object



68
69
70
71
# File 'lib/tod/time_of_day.rb', line 68

def <=>(other)
  return unless other.respond_to?(:second_of_day)
  @second_of_day <=> other.second_of_day
end

#on(date, time_zone = Tod::TimeOfDay.time_zone) ⇒ Object

Returns a Time instance on date using self as the time of day Optional time_zone will build time in that zone



127
128
129
# File 'lib/tod/time_of_day.rb', line 127

def on(date, time_zone=Tod::TimeOfDay.time_zone)
  time_zone.local date.year, date.month, date.day, @hour, @minute, @second
end

#round(round_sec = 1) ⇒ Object

Rounding to the given nearest number of seconds



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/tod/time_of_day.rb', line 74

def round(round_sec = 1)
  down = self - (self.to_i % round_sec)
  up = down + round_sec

  difference_down = self - down
  difference_up = up - self

  if (difference_down < difference_up)
    return down
  else
    return up
  end
end

#strftime(format_string) ⇒ Object

Formats identically to Time#strftime



89
90
91
92
93
94
# File 'lib/tod/time_of_day.rb', line 89

def strftime(format_string)
  # Special case 2400 because strftime will load TimeOfDay into Time which
  # will convert 24 to 0
  format_string.gsub!(/%H|%k/, '24') if @hour == 24
  Time.local(2000,1,1, @hour, @minute, @second).strftime(format_string)
end

#to_formatted_s(format = :default) ⇒ Object Also known as: to_s



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/tod/time_of_day.rb', line 96

def to_formatted_s(format = :default)
  if formatter = FORMATS[format]
    if formatter.respond_to?(:call)
      formatter.call(self).to_s
    else
      strftime(formatter)
    end
  else
    strftime "%H:%M:%S"
  end
end