Class: EasyTime

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/easy_time.rb,
lib/easy_time/convert.rb,
lib/easy_time/version.rb

Overview

These class methods are for converting most date or time formats to a Time

Constant Summary collapse

DEFAULT_TIME_COMPARISON_TOLERANCE =

we define a default tolerance below. This causes time value differences less than this to be considered "equal". This allows for time comparisons between values from different systems where the clock sync might not be very accurate.

If this default tolerance is not desired, it can be overridden with an explicit tolerance setting in the singleton class instance:

EasyTime.comparison_tolerance = 0

1.second
ISO_DATE_RE =

A regexp pattern to match the date part of an ISO8601 time string

/(?: \d{4}-\d\d-\d\d  # yyyy-mm-dd
   | \d{4}-\d\d       # yyyy-mm
   | \d{4}-\d{3}      # yyyy-ddd
   | \d{7,8}          # yyyymmdd  or yyyyddd
   | --\d\d-?\d\d     # --mm-dd or --mmdd
 )
/x.freeze
ISO_TIME_RE =

A regexp pattern to match the time part of an ISO8601 time string

/(?:
   (?:
     (?: \d\d:\d\d:\d\d  # hh:mm:ss
       | \d{6}           # hhmmss
     )
     (?: \.\d+ )?        # optional .sss
   )
   | \d\d:?\d\d          # hh:mm or hhmm
   | \d{2}               # hh
 )
/x.freeze
ISO_ZONE_RE =

A regexp pattern to match the timezone part of an ISO8601 time string

/(?: Z                # Z for zulu (GMT = 0)
   | [+-] \d\d:?\d\d  # +-HH:MM or +-HHMM
 )
/x.freeze
ISO8601_RE =

A regexp pattern to match an ISO8601 time string.

/ #{ISO_DATE_RE} T #{ISO_TIME_RE} #{ISO_ZONE_RE} /x.freeze
RFC2822_RE =

A regexp pattern to match an RFC2822 time string (used in Email messages and systems)

/\w{3},         \s  # Wed,
 \d{1,2}        \s  # 01 or 1
 \w{3}          \s  # Oct
 \d{4}          \s  # 2020
 \d\d:\d\d:\d\d \s  # HH:MM:SS
 [+-]\d\d:?\d\d     # +-HH:MM or +-HHHMM (zone)
/x.freeze
HTTPDATE_RE =

A regexp pattern to match an HTTPDate time string (used in web server transactions and logs)

/\w{3},         \s  # Wed,
 \d{1,2}        \s  # 01 or 1
 \w{3}          \s  # Oct
 \d{4}          \s  # 2020
 \d\d:\d\d:\d\d \s  # HH:MM:SS
 \w+                # GMT
/x.freeze
XMLSCHEMA_RE =

A regexp pattern to match an XMLSchema time string (used in XML documents)

/\d{4}-\d\d-\d\d   # yyyy-mm-dd
 T
 \d\d:\d\d:\d\d    # HH:MM:SS
 [+-]              # +-
 \d\d:\d\d         # HH:MM
/x.freeze
VERSION =
"0.2.2"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*time, tolerance: nil) ⇒ EasyTime

Returns a new instance of EasyTime.



248
249
250
251
# File 'lib/easy_time.rb', line 248

def initialize(*time, tolerance: nil)
  @time = time.size.nonzero? && convert(time.size == 1 ? time.first : time)
  @comparison_tolerance = tolerance
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol, *args, &block) ⇒ Object (private)

intercept any time methods so they can wrap the time-like result in a new EasyTime object.



384
385
386
387
388
389
390
391
# File 'lib/easy_time.rb', line 384

def method_missing(symbol, *args, &block)
  if time.respond_to?(symbol)
    value = time.send(symbol, *args, &block)
    is_a_time?(value) ? dup.tap { |eztime| eztime.time = value } : value
  else
    super(symbol, *args, &block)
  end
end

Class Attribute Details

.comparison_toleranceInteger

Returns the number of seconds of tolerance to use for "equality" tests.

Returns:

  • (Integer)

    the number of seconds of tolerance to use for "equality" tests



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

def comparison_tolerance
  @tolerance || DEFAULT_TIME_COMPARISON_TOLERANCE
end

Instance Attribute Details

#comparison_toleranceObject

if there is no instance value, default to the class value



254
255
256
# File 'lib/easy_time.rb', line 254

def comparison_tolerance
  @comparison_tolerance || self.class.comparison_tolerance
end

#other_timeObject (readonly)

Returns the value of attribute other_time.



243
244
245
# File 'lib/easy_time.rb', line 243

def other_time
  @other_time
end

#timeObject

Returns the value of attribute time.



242
243
244
# File 'lib/easy_time.rb', line 242

def time
  @time
end

Class Method Details

.add(time, duration) ⇒ EasyTime

Returns the time with the duration added.

Parameters:

  • time (Time, DateTime, ActiveSupport::TimeWithZone, Date, String, Integer, Array<Integer>)

    a time value

  • duration (ActiveSupport::Duration, Integer, Float)

    a duration value

Returns:

  • (EasyTime)

    the time with the duration added



182
183
184
# File 'lib/easy_time.rb', line 182

def add(time, duration)
  EasyTime.new(time) + duration
end

.between?(time1, t_min, t_max, tolerance: nil) ⇒ Boolean .between?(time1, time_range, tolerance: nil) ⇒ Boolean

Overloads:

  • .between?(time1, t_min, t_max, tolerance: nil) ⇒ Boolean

    Returns true if t_min <= time1 <= t_max, using tolerant comparisons.

    Parameters:

    • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      a time value

    • t_min (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      the minimum time

    • t_max (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      the maximum time

    Returns:

    • (Boolean)

      true if t_min <= time1 <= t_max, using tolerant comparisons

  • .between?(time1, time_range, tolerance: nil) ⇒ Boolean

    Returns true if time_range.min <= time1 <= time_range.max, using tolerant comparisons.

    Parameters:

    • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      a time value

    • time_range (Range)

      a range (t_min..t_max) of time values

    Returns:

    • (Boolean)

      true if time_range.min <= time1 <= time_range.max, using tolerant comparisons



153
154
155
156
157
158
159
160
161
162
# File 'lib/easy_time.rb', line 153

def between?(time1, t_arg, t_max=nil, tolerance: nil)
  if t_arg.is_a?(Range)
    t_min = t_arg.min
    t_max = t_arg.max
  else
    t_min = t_arg
  end
  compare(time1, t_min, tolerance: tolerance) >= 0 &&
    compare(time1, t_max, tolerance: tolerance) <= 0
end

.compare(time1, time2, tolerance: nil) ⇒ Integer

Returns one of [-1, 0, 1] if time1 <, ==, or > than time2, or nil if time2 cannot be converted to a Time value.

Parameters:

  • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    a time value

  • time2 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    another time value

  • tolerance (Integer) (defaults to: nil)

    seconds of tolerance (optional)

Returns:

  • (Integer)

    one of [-1, 0, 1] if time1 <, ==, or > than time2, or nil if time2 cannot be converted to a Time value.



170
171
172
# File 'lib/easy_time.rb', line 170

def compare(time1, time2, tolerance: nil)
  new(time1, tolerance: tolerance) <=> time2
end

.convert(arg, coerce = true) ⇒ Time

Parameters:

  • arg (String, EasyTime, Time, Date, DateTime, Array<Integer>, Duration)

    various kinds of date and time values

  • coerce (Boolean) (defaults to: true)

    if true, coerce the arg into a Time object (default: true)

Returns:

  • (Time)


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/easy_time/convert.rb', line 44

def convert(arg, coerce = true)
  case arg
  when String
    parse_string(arg)              # parse the string value into an EasyTime object
  when Array
    ::Time.new(*arg)               # convert Time arguments: [yyyy, mm, dd, hh, MM, SS]
  when ::EasyTime
    arg.time                       # extract the EasyTime value
  when ActiveSupport::TimeWithZone
    arg.to_time                    # convert the TimeWithZone value to a Time object
  when ActiveSupport::Duration
    coerce ? Time.now + arg : arg  # coerced duration objects are relative to "now"
  when ::Time
    arg                            # accept Time objects as-as
  when ::Date, ::DateTime
    ::Time.iso8601(arg.iso8601)    # convert Date and DateTime objects via ISO8601 formatting
  when NilClass
    ::Time.now                     # a nil object means "now"
  when Numeric
    coerce ? Time.at(arg) : arg   # if coerced, treat as seconds-since-Epoch
  else
    raise ArgumentError, "EasyTime: unknown value: '#{arg.inspect}'"
  end
end

.is_a_time?(value) ⇒ Boolean

Returns true if value is one the known Time classes, or responds to :acts_like_time?.

Parameters:

  • value (Anything)

    value to test as a time-like object

Returns:

  • (Boolean)

    true if value is one the known Time classes, or responds to :acts_like_time?



230
231
232
233
234
235
236
237
238
239
# File 'lib/easy_time.rb', line 230

def is_a_time?(value)
  case value
  when Integer, ActiveSupport::Duration
    false
  when Date, Time, DateTime, ActiveSupport::TimeWithZone, EasyTime
    true
  else
    value.respond_to?(:acts_like_time?) && value.acts_like_time?
  end
end

.method_missing(symbol, *args, &block) ⇒ Object



215
216
217
218
219
220
221
222
# File 'lib/easy_time.rb', line 215

def method_missing(symbol, *args, &block)
  if Time.respond_to?(symbol)
    value = Time.send(symbol, *args, &block)
    is_a_time?(value) ? new(value) : value
  else
    super(symbol, *args, &block)
  end
end

.newer?(time1, time2, tolerance: nil) ⇒ Boolean

Returns true if time1 > time2, using a tolerant comparison.

Parameters:

  • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    a time value

  • time2 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    another time value

  • tolerance (Integer) (defaults to: nil)

    seconds of tolerance (optional)

Returns:

  • (Boolean)

    true if time1 > time2, using a tolerant comparison



119
120
121
# File 'lib/easy_time.rb', line 119

def newer?(time1, time2, tolerance: nil)
  compare(time1, time2, tolerance: tolerance).positive?
end

.older?(time1, time2, tolerance: nil) ⇒ Boolean

Returns true if time1 > time2, using a tolerant comparison.

Parameters:

  • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    a time value

  • time2 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    another time value

  • tolerance (Integer) (defaults to: nil)

    seconds of tolerance (optional)

Returns:

  • (Boolean)

    true if time1 > time2, using a tolerant comparison



128
129
130
# File 'lib/easy_time.rb', line 128

def older?(time1, time2, tolerance: nil)
  compare(time1, time2, tolerance: tolerance).negative?
end

.parse(time_string) ⇒ EasyTime

Parameters:

  • time_string (String)

    a time string in one of the many known Time string formats

Returns:



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

def parse(time_string)
  new(parse_string(time_string))
end

.respond_to_missing?(symbol, include_all = false) ⇒ Boolean

Returns:

  • (Boolean)


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

def respond_to_missing?(symbol, include_all=false)
  Time.respond_to?(symbol, include_all)
end

.same?(time1, time2, tolerance: nil) ⇒ Boolean

Returns true if time1 > time2, using a tolerant comparison.

Parameters:

  • time1 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    a time value

  • time2 (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

    another time value

  • tolerance (Integer) (defaults to: nil)

    seconds of tolerance (optional)

Returns:

  • (Boolean)

    true if time1 > time2, using a tolerant comparison



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

def same?(time1, time2, tolerance: nil)
  compare(time1, time2, tolerance: tolerance).zero?
end

.subtract(time, time_or_duration) ⇒ EasyTime, Duration

Returns an EasyTime value, when a duration is subtracted from a time, or a duration (Integer) value, when one time is subtracted from another time.

Parameters:

  • time (Time, DateTime, ActiveSupport::TimeWithZone, Date, String, Integer, Array<Integer>)

    a time value

  • time_or_duration (Time, DateTime, ActiveSupport::Duration, Integer, Float)

    a time or duration value

Returns:

  • (EasyTime, Duration)

    an EasyTime value, when a duration is subtracted from a time, or a duration (Integer) value, when one time is subtracted from another time.



191
192
193
# File 'lib/easy_time.rb', line 191

def subtract(time, time_or_duration)
  EasyTime.new(time) - time_or_duration
end

.time_format_style(str) ⇒ Object

this method returns parser class methods in the Time class for corresponding time format patterns



139
140
141
142
143
144
145
146
# File 'lib/easy_time/convert.rb', line 139

def time_format_style(str)
  case str
  when RFC2822_RE   then :rfc2822
  when HTTPDATE_RE  then :httpdate
  when XMLSCHEMA_RE then :xmlschema
  when ISO8601_RE   then :iso8601
  end
end

Instance Method Details

#+(duration) ⇒ EasyTime

Returns updated date and time value.

Parameters:

  • duration (Integer)

    seconds to add to the EasyTime value

Returns:

  • (EasyTime)

    updated date and time value



353
354
355
# File 'lib/easy_time.rb', line 353

def +(duration)
  dup.tap { |eztime| eztime.time += duration }
end

#-(other) ⇒ EasyTime, Integer

Subtract a value from an EasyTime. If the value is an integer, it is treated as seconds. If the value is any of the Date, DateTime, Time, EasyTime, or a String- formatted date/time, it is subtracted from the EasyTime value resulting in an integer duration.

Parameters:

  • other (Date, Time, DateTime, EasyTime, Duration, String, Integer)

    a date/time value, a duration, or an Integer

Returns:

  • (EasyTime, Integer)

    updated time (time - duration) or duration (time - time)



364
365
366
367
368
369
370
371
# File 'lib/easy_time.rb', line 364

def -(other)
  @other_time = convert(other, false)
  if is_a_time?(other_time)
    time - other_time
  elsif other_time
    dup.tap { |eztime| eztime.time -= other_time }
  end
end

#<=>(other) ⇒ Integer

compare with automatic type-conversion and tolerance

Returns:

  • (Integer)

    one of [-1, 0, 1] or nil



320
321
322
323
324
325
326
327
# File 'lib/easy_time.rb', line 320

def <=>(other)
  diff = self - other  # note: this has a side-effect of setting @other_time
  if diff && diff.to_i.abs <= comparison_tolerance.to_i
    0
  elsif diff
    time <=> other_time
  end
end

#acts_like_time?Boolean

Returns:

  • (Boolean)


373
374
375
# File 'lib/easy_time.rb', line 373

def acts_like_time?
  true
end

#between?(t_min, t_max, tolerance: nil) ⇒ Boolean #between?(time_range, tolerance: nil) ⇒ Boolean

compare a time against a min and max date pair, or against a time Range value.

Overloads:

  • #between?(t_min, t_max, tolerance: nil) ⇒ Boolean

    Returns true if t_min <= self.time <= t_max, using tolerant comparisons.

    Parameters:

    • t_min (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      the minimum time

    • t_max (Date, Time, DateTime, EasyTime, Duration, String, Array<Integer>)

      the maximum time

    • tolerance (Integer) (defaults to: nil)

      the optional amount of seconds of tolerance to use in the comparison

    Returns:

    • (Boolean)

      true if t_min <= self.time <= t_max, using tolerant comparisons

  • #between?(time_range, tolerance: nil) ⇒ Boolean

    Returns true if time_range.min <= self.time <= time_range.max, using tolerant comparisons.

    Parameters:

    • time_range (Range)

      a range (t_min..t_max) of time values

    • tolerance (Integer) (defaults to: nil)

      the optional amount of seconds of tolerance to use in the comparison

    Returns:

    • (Boolean)

      true if time_range.min <= self.time <= time_range.max, using tolerant comparisons



341
342
343
344
345
346
347
348
349
# File 'lib/easy_time.rb', line 341

def between?(t_arg, t_max = nil)
  if t_arg.is_a?(Range)
    t_min = t_arg.min
    t_max = t_arg.max
  else
    t_min = t_arg
  end
  compare(t_min) >= 0 && compare(t_max) <= 0
end

#compare(time2, tolerance: nil) ⇒ Integer

Returns one of the values: [-1, 0, 1] if self [<, ==, >] time2, or nil if time2 cannot be converted to a Time value.

Parameters:

  • time2 (String, Date, Time, DateTime, Duration, Array<Integer>)

    another time value

Returns:

  • (Integer)

    one of the values: [-1, 0, 1] if self [<, ==, >] time2, or nil if time2 cannot be converted to a Time value



312
313
314
315
# File 'lib/easy_time.rb', line 312

def compare(time2, tolerance: nil)
  self.comparison_tolerance = tolerance if tolerance
  self <=> time2
end

#different?(time2) ⇒ Boolean

Returns true if self != time2.

Parameters:

  • time2 (String, Date, Time, DateTime, Duration, Array<Integer>)

    another time value

Returns:

  • (Boolean)

    true if self != time2



304
305
306
# File 'lib/easy_time.rb', line 304

def different?(time2)
  self != time2
end

#newer?(time2) ⇒ Boolean

Returns true if self > time2.

Parameters:

  • time2 (String, Date, Time, DateTime, Duration, Array<Integer>)

    another time value

Returns:

  • (Boolean)

    true if self > time2



282
283
284
# File 'lib/easy_time.rb', line 282

def newer?(time2)
  self > time2
end

#older?(time2) ⇒ Boolean

Returns true if self < time2.

Parameters:

  • time2 (String, Date, Time, DateTime, Duration, Array<Integer>)

    another time value

Returns:

  • (Boolean)

    true if self < time2



289
290
291
# File 'lib/easy_time.rb', line 289

def older?(time2)
  self < time2
end

#same?(time2) ⇒ Boolean Also known as: eql?

Returns true if self == time2.

Parameters:

  • time2 (String, Date, Time, DateTime, Duration, Array<Integer>)

    another time value

Returns:

  • (Boolean)

    true if self == time2



296
297
298
# File 'lib/easy_time.rb', line 296

def same?(time2)
  self == time2
end

#with_tolerance(value) ⇒ EasyTime

returns a new EasyTime value with the tolerance set to value

Examples:

Example:


t1 = EasyTime.new(some_time)
t1.with_tolerance(2.seconds) <= some_other_time

Parameters:

  • value (Integer)

    a number of seconds to use as the comparison tolerance

Returns:

  • (EasyTime)

    a new EasyTime value with the given tolerance



268
269
270
# File 'lib/easy_time.rb', line 268

def with_tolerance(value)
  dup.tap { |time| time.comparison_tolerance = value }
end