Class: EtOrbi::EoTime

Inherits:
Object
  • Object
show all
Defined in:
lib/et-orbi/time.rb

Overview

Our EoTime class (which quacks like a ::Time).

An EoTime instance should respond to most of the methods ::Time instances respond to. If a method is missing, feel free to open an issue to ask (politely) for it. If it makes sense, it’ll get added, else a workaround will get suggested. The immediate workaround is to call #to_t on the EoTime instance to get equivalent ::Time instance in the local, current, timezone.

Constant Summary collapse

DAY_S =
24 * 3600
WEEK_S =
7 * DAY_S

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(s, zone) ⇒ EoTime



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/et-orbi/time.rb', line 67

def initialize(s, zone)

  z = zone
  z = nil if zone.is_a?(String) && zone.strip == ''
    #
    # happens with JRuby (and offset tzones like +04:00)
    #
    # $ jruby -r time -e "p Time.parse('2012-1-1 12:00 +04:00').zone"
    # # => ""
    # ruby -r time -e "p Time.parse('2012-1-1 12:00 +04:00').zone"
    # # => nil

  @seconds = s.to_f
  @zone = self.class.get_tzone(z || :local)

  fail ArgumentError.new(
    "Cannot determine timezone from #{zone.inspect}" +
    "\n#{EtOrbi.render_nozone_time(@seconds)}" +
    "\n#{EtOrbi.platform_info.sub(',debian:', ",\ndebian:")}" +
    "\nTry setting `ENV['TZ'] = 'Continent/City'` in your script " +
    "(see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)" +
    (defined?(TZInfo::Data) ? '' : "\nand adding gem 'tzinfo-data'")
  ) unless @zone

  touch
end

Instance Attribute Details

#secondsObject

instance methods



64
65
66
# File 'lib/et-orbi/time.rb', line 64

def seconds
  @seconds
end

#zoneObject

Returns the value of attribute zone.



65
66
67
# File 'lib/et-orbi/time.rb', line 65

def zone
  @zone
end

Class Method Details

.get_tzone(o) ⇒ Object



30
31
32
33
# File 'lib/et-orbi/time.rb', line 30

def get_tzone(o)

  EtOrbi.get_tzone(o)
end

.local(*a) ⇒ Object



55
56
57
58
# File 'lib/et-orbi/time.rb', line 55

def local(*a)

  EtOrbi.send(:make_from_array, a, local_tzone)
end

.local_tzoneObject



35
36
37
38
# File 'lib/et-orbi/time.rb', line 35

def local_tzone

  EtOrbi.determine_local_tzone
end

.make(o) ⇒ Object



45
46
47
48
# File 'lib/et-orbi/time.rb', line 45

def make(o)

  EtOrbi.make_time(o)
end

.now(zone = nil) ⇒ Object



20
21
22
23
# File 'lib/et-orbi/time.rb', line 20

def now(zone=nil)

  EtOrbi.now(zone)
end

.parse(str, opts = {}) ⇒ Object



25
26
27
28
# File 'lib/et-orbi/time.rb', line 25

def parse(str, opts={})

  EtOrbi.parse(str, opts)
end

.platform_infoObject



40
41
42
43
# File 'lib/et-orbi/time.rb', line 40

def platform_info

  EtOrbi.platform_info
end

.utc(*a) ⇒ Object



50
51
52
53
# File 'lib/et-orbi/time.rb', line 50

def utc(*a)

  EtOrbi.send(:make_from_array, a, EtOrbi.get_tzone('UTC'))
end

Instance Method Details

#+(t) ⇒ Object



250
# File 'lib/et-orbi/time.rb', line 250

def +(t); inc(t, 1); end

#-(t) ⇒ Object



251
# File 'lib/et-orbi/time.rb', line 251

def -(t); inc(t, -1); end

#<(o) ⇒ Object



243
# File 'lib/et-orbi/time.rb', line 243

def <(o); @seconds < _to_f(o); end

#<=(o) ⇒ Object



244
# File 'lib/et-orbi/time.rb', line 244

def <=(o); @seconds <= _to_f(o); end

#<=>(o) ⇒ Object



245
# File 'lib/et-orbi/time.rb', line 245

def <=>(o); @seconds <=> _to_f(o); end

#==(o) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/et-orbi/time.rb', line 220

def ==(o)

  if o.is_a?(EoTime)
    o.seconds == @seconds &&
    (o.zone == @zone || o.zone.current_period == @zone.current_period)
  elsif o.is_a?(::Time)
    (to_f * 1000).to_i == (o.to_f * 1000).to_i
  else
    false
  end
end

#>(o) ⇒ Object

Nota Bene:

Unlike ==, the equal? method should never be overridden by subclasses as it is used to determine object identity (that is, a.equal?(b) if and only if a is the same object as b)

The eql? method returns true if obj and other refer to the same hash key. This is used by Hash to test members for equality.



241
# File 'lib/et-orbi/time.rb', line 241

def >(o); @seconds > _to_f(o); end

#>=(o) ⇒ Object



242
# File 'lib/et-orbi/time.rb', line 242

def >=(o); @seconds >= _to_f(o); end

#add(t) ⇒ Object



247
# File 'lib/et-orbi/time.rb', line 247

def add(t); @seconds += t.to_f; touch; self; end

#ambiguous?Boolean

Returns true if this EoTime instance corresponds to 2 different UTC times. It happens when transitioning from DST to winter time.

www.timeanddate.com/time/change/usa/new-york?year=2018



127
128
129
130
131
132
133
134
135
136
# File 'lib/et-orbi/time.rb', line 127

def ambiguous?

  @zone.local_to_utc(@zone.utc_to_local(utc))

  false

rescue TZInfo::AmbiguousTime

  true
end

#cloneObject



413
414
415
416
# File 'lib/et-orbi/time.rb', line 413

def clone

  EtOrbi::EoTime.new(@seconds, @zone)
end

#inc(t, dir = 1) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/et-orbi/time.rb', line 316

def inc(t, dir=1)

  case t
  when ::Time, ::EtOrbi::EoTime

    fail(
      ArgumentError.new("Cannot add #{t.class} to EoTime instance")
        ) if dir > 0

    @seconds + dir * t.to_f

  when String

    false

  else

    if t.respond_to?(:to_f)
      nt = self.dup; nt.seconds += dir * t.to_f; nt
    elsif t.respond_to?(:to_i)
      nt = self.dup; nt.seconds += dir * t.to_i; nt
    else
      false
    end

  end || fail(
    ArgumentError.new(
      "Cannot call add or subtract #{t.class} on EoTime instance"))
end

#is_dst?Boolean Also known as: isdst



188
189
190
191
# File 'lib/et-orbi/time.rb', line 188

def is_dst?

  @zone.period_for_utc(utc).std_offset != 0
end

#iso8601(fraction_digits = 0) ⇒ Object



291
292
293
294
295
# File 'lib/et-orbi/time.rb', line 291

def iso8601(fraction_digits=0)

  s = (fraction_digits || 0) > 0 ? ".%#{fraction_digits}N" : ''
  strftime("%Y-%m-%dT%H:%M:%S#{s}%:z")
end

#localtime(zone = nil) ⇒ Object Also known as: translate, in_time_zone



346
347
348
349
# File 'lib/et-orbi/time.rb', line 346

def localtime(zone=nil)

  EoTime.new(self.to_f, zone)
end

#monthdaysObject



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/et-orbi/time.rb', line 256

def monthdays

  date = to_time

  pos = 1
  d = self.dup

  loop do
    d.add(-WEEK_S)
    break if d.month != date.month
    pos = pos + 1
  end

  neg = -1
  d = self.dup

  loop do
    d.add(WEEK_S)
    break if d.month != date.month
    neg = neg - 1
  end

  [ "#{date.wday}##{pos}", "#{date.wday}##{neg}" ]
end

#rdayObject

“reference day”, used in fugit for cron modulo notation



362
363
364
365
366
367
# File 'lib/et-orbi/time.rb', line 362

def rday

  @rday ||= (
    (EtOrbi.make_time(strftime('%F 12:00:00'), @zone) - rref) / DAY_S
      ).floor
end

#reach(points) ⇒ Object



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/et-orbi/time.rb', line 381

def reach(points)

  t = EoTime.new(self.to_f, @zone)
  step = 1

  s = points[:second] || points[:sec] || points[:s]
  m = points[:minute] || points[:min] || points[:m]
  h = points[:hour] || points[:hou] || points[:h]

  fail ArgumentError.new("missing :second, :minute, and :hour") \
    unless s || m || h

  if !s && !m
    step = 60 * 60
    t -= t.sec
    t -= t.min * 60
  elsif !s
    step = 60
    t -= t.sec
  end

  loop do
    t += step
    next if s && t.sec != s
    next if m && t.min != m
    next if h && t.hour != h
    break
  end

  t
end

#rrObject



379
# File 'lib/et-orbi/time.rb', line 379

def rr; [ strftime('%F %a'), rweek, rday ]; end

#rweekObject

“reference week”, used in fugit for cron modulo notation



371
372
373
374
# File 'lib/et-orbi/time.rb', line 371

def rweek

  rday / 7
end

#strftime(format) ⇒ Object



169
170
171
172
173
174
# File 'lib/et-orbi/time.rb', line 169

def strftime(format)

  format = format.gsub(/%(\/?Z|:{0,2}z)/) { |f| strfz(f) }

  to_time.strftime(format)
end

#subtract(t) ⇒ Object



248
# File 'lib/et-orbi/time.rb', line 248

def subtract(t); @seconds -= t.to_f; touch; self; end

#to_debug_sObject



194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/et-orbi/time.rb', line 194

def to_debug_s

  uo = self.utc_offset
  uos = uo < 0 ? '-' : '+'
  uo = uo.abs
  uoh, uom = [ uo / 3600, uo % 3600 ]

  [
    'ot',
    self.strftime('%Y-%m-%d %H:%M:%S'),
    "%s%02d:%02d" % [ uos, uoh, uom ],
    "dst:#{self.isdst}"
  ].join(' ')
end

#to_fObject



159
160
161
162
# File 'lib/et-orbi/time.rb', line 159

def to_f

  @seconds
end

#to_iObject



164
165
166
167
# File 'lib/et-orbi/time.rb', line 164

def to_i

  @seconds.to_i
end

#to_local_timeObject Also known as: to_t

Returns this ::EtOrbi::EoTime as a ::Time instance in the current timezone.

Has a #to_t alias.



181
182
183
184
# File 'lib/et-orbi/time.rb', line 181

def to_local_time

  Time.at(@seconds)
end

#to_sObject



281
282
283
284
# File 'lib/et-orbi/time.rb', line 281

def to_s

  strftime('%Y-%m-%d %H:%M:%S %z')
end

#to_time_sObject



311
312
313
314
# File 'lib/et-orbi/time.rb', line 311

def to_time_s

  strftime('%H:%M:%S.%6N')
end

#to_utc_comparison_sObject

Debug current time by showing local time / delta / utc time for example: “0120-7(0820)”



300
301
302
303
304
305
306
307
308
309
# File 'lib/et-orbi/time.rb', line 300

def to_utc_comparison_s

  per = @zone.period_for_utc(utc)
  off = per.utc_total_offset

  off = off / 3600
  off = off >= 0 ? "+#{off}" : off.to_s

  strftime('%H%M') + off + utc.strftime('(%H%M)')
end

#to_zsObject



286
287
288
289
# File 'lib/et-orbi/time.rb', line 286

def to_zs

  strftime('%Y-%m-%d %H:%M:%S %/Z')
end

#touchObject

Nullify the “caches” used by #to_time, #rday, and others



96
97
98
99
100
# File 'lib/et-orbi/time.rb', line 96

def touch

  @time = nil
  @rday = nil
end

#utcObject Also known as: getutc, getgm, to_utc_time

Returns this ::EtOrbi::EoTime as a ::Time instance in the current UTC timezone.



141
142
143
144
# File 'lib/et-orbi/time.rb', line 141

def utc

  Time.utc(1970) + @seconds
end

#utc?Boolean

Returns true if this ::EtOrbi::EoTime instance timezone is UTC. Returns false else.



149
150
151
152
153
# File 'lib/et-orbi/time.rb', line 149

def utc?

  %w[ gmt utc zulu etc/gmt etc/utc ].include?(
    @zone.canonical_identifier.downcase)
end

#utc_offsetObject



209
210
211
212
# File 'lib/et-orbi/time.rb', line 209

def utc_offset

  @zone.period_for_utc(utc).utc_total_offset
end

#wday_in_monthObject



354
355
356
357
358
# File 'lib/et-orbi/time.rb', line 354

def wday_in_month

  [ count_weeks(EtOrbi.make_time(strftime('%F 12:00:00 %/Z')), -1),
    - count_weeks(EtOrbi.make_time(strftime('%F 12:00:00 %/Z')) , 1) ]
end