Class: TimeOfDay

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/emery/tod.rb

Overview

source: github.com/jackc/tod

Copyright © 2010-2015 Jack Christensen

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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".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

Constructor Details

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

Returns a new instance of TimeOfDay.

Raises:

  • (ArgumentError)


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/emery/tod.rb', line 84

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.



38
39
40
# File 'lib/emery/tod.rb', line 38

def hour
  @hour
end

#minuteObject (readonly) Also known as: min

Returns the value of attribute minute.



38
39
40
# File 'lib/emery/tod.rb', line 38

def minute
  @minute
end

#secondObject (readonly) Also known as: sec

Returns the value of attribute second.



38
39
40
# File 'lib/emery/tod.rb', line 38

def second
  @second
end

#second_of_dayObject (readonly) Also known as: to_i

Returns the value of attribute second_of_day.



38
39
40
# File 'lib/emery/tod.rb', line 38

def second_of_day
  @second_of_day
end

Class Method Details

.dump(time_of_day) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/emery/tod.rb', line 241

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


171
172
173
174
175
176
177
178
179
180
# File 'lib/emery/tod.rb', line 171

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

.jsoner_deserialize(json_value) ⇒ Object



31
32
33
# File 'lib/emery/tod.rb', line 31

def self.jsoner_deserialize(json_value)
  TimeOfDay.parse(T.check(String, json_value))
end

.jsoner_serialize(value) ⇒ Object



34
35
36
# File 'lib/emery/tod.rb', line 34

def self.jsoner_serialize(value)
  T.check(TimeOfDay, value).to_s
end

.load(time) ⇒ Object



254
255
256
257
258
259
# File 'lib/emery/tod.rb', line 254

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:



232
233
234
# File 'lib/emery/tod.rb', line 232

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.



202
203
204
# File 'lib/emery/tod.rb', line 202

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



237
238
239
# File 'lib/emery/tod.rb', line 237

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


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/emery/tod.rb', line 210

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.



148
149
150
# File 'lib/emery/tod.rb', line 148

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.



154
155
156
157
158
159
160
# File 'lib/emery/tod.rb', line 154

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



101
102
103
104
# File 'lib/emery/tod.rb', line 101

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



164
165
166
# File 'lib/emery/tod.rb', line 164

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



107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/emery/tod.rb', line 107

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



122
123
124
125
126
127
# File 'lib/emery/tod.rb', line 122

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



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/emery/tod.rb', line 129

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



142
143
144
# File 'lib/emery/tod.rb', line 142

def value_for_database
  to_s
end