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

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)

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

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

.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

144
145
146
147
148
149
150
151
152
153
# File 'lib/tod/time_of_day.rb', line 144

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

.parsable?(tod_string) ⇒ Boolean

Determine if a string is parsable into a TimeOfDay instance

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

Returns:

  • (Boolean)

205
206
207
# File 'lib/tod/time_of_day.rb', line 205

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.


175
176
177
# File 'lib/tod/time_of_day.rb', line 175

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


210
211
212
# File 'lib/tod/time_of_day.rb', line 210

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

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/tod/time_of_day.rb', line 183

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.


121
122
123
# File 'lib/tod/time_of_day.rb', line 121

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.


127
128
129
130
131
132
133
# File 'lib/tod/time_of_day.rb', line 127

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


70
71
72
73
# File 'lib/tod/time_of_day.rb', line 70

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

#inspectObject


115
116
117
# File 'lib/tod/time_of_day.rb', line 115

def inspect
  "#<#{self.class} #{self}>"
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


137
138
139
# File 'lib/tod/time_of_day.rb', line 137

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


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

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


91
92
93
94
95
96
# File 'lib/tod/time_of_day.rb', line 91

def strftime(format_string)
  # Special case 2400 because strftime will load TimeOfDay into Time which
  # will convert 24 to 0
  format_string = 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


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

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

#value_for_databaseObject


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

def value_for_database
  to_s
end