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

Returns a new instance of 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



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

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

#-(t) ⇒ Object



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

def -(t); inc(t, -1); 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



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

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

#==(o) ⇒ Object



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

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.



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

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

#>=(o) ⇒ Object



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

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

#add(t) ⇒ Object



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

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

Returns:

  • (Boolean)


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

def ambiguous?

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

  false

rescue TZInfo::AmbiguousTime

  true
end

#cloneObject



409
410
411
412
# File 'lib/et-orbi/time.rb', line 409

def clone

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

#inc(t, dir = 1) ⇒ Object



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

def inc(t, dir=1)

  r =
    case t
    when Numeric
      nt = self.dup
      nt.seconds += dir * t.to_f
      nt
    when ::Time, ::EtOrbi::EoTime
      fail ArgumentError.new(
        "Cannot add #{t.class} to EoTime") if dir > 0
      @seconds + dir * t.to_f
    else
      fail ArgumentError.new(
        "Cannot call add or subtract #{t.class} to EoTime instance")
    end

  touch

  r
end

#is_dst?Boolean Also known as: isdst

Returns:

  • (Boolean)


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

def is_dst?

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

#iso8601(fraction_digits = 0) ⇒ Object



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

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



339
340
341
342
# File 'lib/et-orbi/time.rb', line 339

def localtime(zone=nil)

  EoTime.new(self.to_f, zone)
end

#monthdaysObject



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

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 week”, used in fugit for cron modulo notation



367
368
369
370
371
372
373
374
375
# File 'lib/et-orbi/time.rb', line 367

def rday

  @rday ||=
    begin
      ref = EtOrbi.make_time('2019-01-01 12:00:00', @zone)
      noon = EtOrbi.make_time(strftime('%F 12:00:00'), @zone)
      ((noon - ref) / DAY_S).floor + 1
    end
end

#reach(points) ⇒ Object



377
378
379
380
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
# File 'lib/et-orbi/time.rb', line 377

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

#rweekObject

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



355
356
357
358
359
360
361
362
363
# File 'lib/et-orbi/time.rb', line 355

def rweek

  @rweek ||=
    begin
      ref = EtOrbi.make_time('2019-01-01 12:00:00', @zone)
      noon = EtOrbi.make_time(strftime('%F 12:00:00'), @zone)
      ((noon - ref) / WEEK_S).floor + 1
    end
end

#strftime(format) ⇒ Object



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

def strftime(format)

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

  to_time.strftime(format)
end

#subtract(t) ⇒ Object



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

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

#to_debug_sObject



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

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



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

def to_f

  @seconds
end

#to_iObject



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

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.



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

def to_local_time

  Time.at(@seconds)
end

#to_sObject



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

def to_s

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

#to_time_sObject



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

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)”



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

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



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

def to_zs

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

#touchObject

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



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

def touch

  @time = nil
  @rday = nil
  @rweek = nil
end

#utcObject Also known as: getutc, getgm, to_utc_time

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



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

def utc

  Time.utc(1970) + @seconds
end

#utc?Boolean

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

Returns:

  • (Boolean)


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

def utc?

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

#utc_offsetObject



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

def utc_offset

  @zone.period_for_utc(utc).utc_total_offset
end

#wday_in_monthObject



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

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