Class: TaskJuggler::TjTime

Inherits:
Object show all
Defined in:
lib/taskjuggler/TjTime.rb

Overview

The TjTime class extends the original Ruby class Time with lots of TaskJuggler specific additional functionality. This is mostly for handling time zones.

Constant Summary collapse

MON_MAX =

The number of days per month. Leap years are taken care of separately.

[ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
@@tz =

Initialize @@tz with the current time zone if it is set.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(t = nil) ⇒ TjTime

call-seq:

TjTime() -> TjTime (now)
TjTime(tjtime) -> TjTime
TjTime(time, timezone) -> TjTime
TjTime(str) -> TjTime
TjTime(secs) -> TjTime

The constructor is overloaded and accepts 4 kinds of arguments. If t is a Time object it’s assumed to be in local time. If it’s a string, it is parsed as a date. Or else it is interpreted as seconds after Epoch.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/taskjuggler/TjTime.rb', line 42

def initialize(t = nil)
  @timeZone = @@tz

  case t
  when nil
    @time = Time.now
  when Time
    @time = t
    @timeZone = nil
  when TjTime
    @time = t.time
    @timeZone = nil
  when String
    parse(t)
  when Array
    @time = Time.mktime(*t)
  else
    @time = Time.at(t)
  end
end

Instance Attribute Details

#timeObject (readonly)

Returns the value of attribute time.



24
25
26
# File 'lib/taskjuggler/TjTime.rb', line 24

def time
  @time
end

#timeZoneObject (readonly)

Returns the value of attribute timeZone.



24
25
26
# File 'lib/taskjuggler/TjTime.rb', line 24

def timeZone
  @timeZone
end

Class Method Details

.checkTimeZone(zone) ⇒ Object

Check if zone is a valid time zone.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/taskjuggler/TjTime.rb', line 64

def TjTime.checkTimeZone(zone)
  return true if zone == 'UTC'

  # Valid time zones must be of the form 'Region/City'
  return false unless zone.include?('/')

  # Save curent value of TZ
  tz = ENV['TZ']
  ENV['TZ'] = zone
  newZone = Time.new.zone
  # If the time zone is valid, the OS can convert a zone like
  # 'America/Denver' into 'MST'. Unknown time zones are either not
  # converted or cause a fallback to UTC.
  # Since glibc 2.10 Time.new.zone only return the region for illegal
  # zones instead of the full zone string like it does on earlier
  # versions.
  region = zone[0..zone.index('/') - 1]
  res = (newZone != zone && newZone != region && newZone != 'UTC')
  # Restore TZ if it was set earlier.
  if tz
    ENV['TZ'] = tz
  else
    ENV.delete('TZ')
  end
  res
end

.setTimeZone(zone) ⇒ Object

Set a new active time zone. zone must be a valid String known to the underlying operating system.



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/taskjuggler/TjTime.rb', line 93

def TjTime.setTimeZone(zone)
  unless zone && TjTime.checkTimeZone(zone)
    raise "Illegal time zone #{zone}"
  end

  oldTimeZone = @@tz

  @@tz = zone
  ENV['TZ'] = zone

  oldTimeZone
end

.timeZoneObject

Return the name of the currently active time zone.



107
108
109
# File 'lib/taskjuggler/TjTime.rb', line 107

def TjTime.timeZone
  @@tz
end

Instance Method Details

#%(val) ⇒ Object

Convert the time to seconds since Epoch and return the module of val.



144
145
146
# File 'lib/taskjuggler/TjTime.rb', line 144

def %(val)
  @time.to_i % val
end

#+(secs) ⇒ Object

Add secs number of seconds to the time.



129
130
131
# File 'lib/taskjuggler/TjTime.rb', line 129

def +(secs)
  TjTime.new(@time.to_i + secs)
end

#-(arg) ⇒ Object

Substract arg number of seconds or return the number of seconds between arg and this time.



135
136
137
138
139
140
141
# File 'lib/taskjuggler/TjTime.rb', line 135

def -(arg)
  if arg.is_a?(TjTime)
    @time - arg.time
  else
    TjTime.new(@time.to_i - arg)
  end
end

#<(t) ⇒ Object

Return true if time is smaller than t.



149
150
151
152
# File 'lib/taskjuggler/TjTime.rb', line 149

def <(t)
  return false unless t
  @time < t.time
end

#<=(t) ⇒ Object

Return true if time is smaller or equal than t.



155
156
157
158
# File 'lib/taskjuggler/TjTime.rb', line 155

def <=(t)
  return false unless t
  @time <= t.time
end

#<=>(t) ⇒ Object

Coparison operator for time with another time t.



179
180
181
182
# File 'lib/taskjuggler/TjTime.rb', line 179

def <=>(t)
  return -1 unless t
  @time <=> t.time
end

#==(t) ⇒ Object

Return true if time and t are identical.



173
174
175
176
# File 'lib/taskjuggler/TjTime.rb', line 173

def ==(t)
  return false unless t
  @time == t.time
end

#>(t) ⇒ Object

Return true if time is larger than t.



161
162
163
164
# File 'lib/taskjuggler/TjTime.rb', line 161

def >(t)
  return true unless t
  @time > t.time
end

#>=(t) ⇒ Object

Return true if time is larger or equal than t.



167
168
169
170
# File 'lib/taskjuggler/TjTime.rb', line 167

def >=(t)
  return true unless t
  @time >= t.time
end

#align(clock) ⇒ Object

Align the date to a time grid. The grid distance is determined by clock.



112
113
114
# File 'lib/taskjuggler/TjTime.rb', line 112

def align(clock)
  TjTime.new((localtime.to_i / clock) * clock)
end

#beginOfHourObject

Normalize time to the beginning of the current hour.



195
196
197
198
199
# File 'lib/taskjuggler/TjTime.rb', line 195

def beginOfHour
  sec, min, hour, day, month, year = localtime.to_a
  sec = min = 0
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#beginOfMonthObject

Normalize time to the beginning of the current month.



224
225
226
227
228
229
# File 'lib/taskjuggler/TjTime.rb', line 224

def beginOfMonth
  sec, min, hour, day, month, year = localtime.to_a
  sec = min = hour = 0
  day = 1
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#beginOfQuarterObject

Normalize time to the beginning of the current quarter.



232
233
234
235
236
237
238
# File 'lib/taskjuggler/TjTime.rb', line 232

def beginOfQuarter
  sec, min, hour, day, month, year = localtime.to_a
  sec = min = hour = 0
  day = 1
  month = ((month - 1) % 3 ) + 1
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#beginOfWeek(startMonday) ⇒ Object

Normalize time to the beginning of the current week. startMonday determines whether the week should start on Monday or Sunday.



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/taskjuggler/TjTime.rb', line 210

def beginOfWeek(startMonday)
  t = localtime.to_a
  # Set time to noon, 12:00:00
  t[0, 3] = [ 0, 0, 12 ]
  weekday = t[6]
  t.slice!(6, 4)
  t.reverse!
  # Substract the number of days determined by the weekday t[6] and set time
  # to midnight of that day.
  (TjTime.new(Time.local(*t)) -
   (weekday - (startMonday ? 1 : 0)) * 60 * 60 * 24).midnight
end

#beginOfYearObject

Normalize time to the beginning of the current year.



241
242
243
244
245
246
# File 'lib/taskjuggler/TjTime.rb', line 241

def beginOfYear
  sec, min, hour, day, month, year = localtime.to_a
  sec = min = hour = 0
  day = month = 1
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#dayObject

Return the day of the month (1..n).



406
407
408
# File 'lib/taskjuggler/TjTime.rb', line 406

def day
  localtime.day
end

#daysTo(date) ⇒ Object

Return the number of days between this time and date. The result is always rounded up.



340
341
342
# File 'lib/taskjuggler/TjTime.rb', line 340

def daysTo(date)
  countIntervals(date, :sameTimeNextDay)
end

#hourObject

Return the hours of the day (0..23)



401
402
403
# File 'lib/taskjuggler/TjTime.rb', line 401

def hour
  localtime.hour
end

#hoursLater(hours) ⇒ Object

Return a new time that is hours later than time.



249
250
251
# File 'lib/taskjuggler/TjTime.rb', line 249

def hoursLater(hours)
  TjTime.new(@time + hours * 3600)
end

#hoursTo(date) ⇒ Object

Return the number of hours between this time and date. The result is always rounded up.



333
334
335
336
# File 'lib/taskjuggler/TjTime.rb', line 333

def hoursTo(date)
  t1, t2 = order(date)
  ((t2 - t1) / 3600).ceil
end

#midnightObject

Normalize time to the beginning of the current day.



202
203
204
205
206
# File 'lib/taskjuggler/TjTime.rb', line 202

def midnight
  sec, min, hour, day, month, year = localtime.to_a
  sec = min = hour = 0
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#monthObject Also known as: mon

Return the month of the year (1..12)



411
412
413
# File 'lib/taskjuggler/TjTime.rb', line 411

def month
  localtime.month
end

#monthsTo(date) ⇒ Object

Return the number of months between this time and date. The result is always rounded up.



352
353
354
# File 'lib/taskjuggler/TjTime.rb', line 352

def monthsTo(date)
  countIntervals(date, :sameTimeNextMonth)
end

#nextDayOfWeek(dow) ⇒ Object

Return the start of the next dow day of week after date. dow must be 0 for Sundays, 1 for Mondays and 6 for Saturdays. If date is a Tuesday and dow is 5 (Friday) the date of next Friday 0:00 will be returned. If date is a Tuesday and dow is 2 (Tuesday) the date of the next Tuesday will be returned.



323
324
325
326
327
328
329
# File 'lib/taskjuggler/TjTime.rb', line 323

def nextDayOfWeek(dow)
  raise "Day of week must be 0 - 6." unless dow >= 0 && dow <= 6
  d = midnight.sameTimeNextDay
  currentDoW = d.strftime('%w').to_i
  1.upto((dow + 7 - currentDoW) % 7) { |i| d = d.sameTimeNextDay }
  d
end

#quartersTo(date) ⇒ Object

Return the number of quarters between this time and date. The result is always rounded up.



358
359
360
# File 'lib/taskjuggler/TjTime.rb', line 358

def quartersTo(date)
  countIntervals(date, :sameTimeNextQuarter)
end

#sameTimeNextDayObject

Return a new time that is 1 day later than time but at the same time of day.



260
261
262
263
264
265
266
267
268
269
270
# File 'lib/taskjuggler/TjTime.rb', line 260

def sameTimeNextDay
  sec, min, hour, day, month, year = localtime.to_a
  if (day += 1) > lastDayOfMonth(month, year)
    day = 1
    if (month += 1) > 12
      month = 1
      year += 1
    end
  end
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#sameTimeNextHourObject

Return a new time that is 1 hour later than time.



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

def sameTimeNextHour
  hoursLater(1)
end

#sameTimeNextMonthObject

Return a new time that is 1 month later than time but at the same time of day.



288
289
290
291
292
293
294
295
296
297
# File 'lib/taskjuggler/TjTime.rb', line 288

def sameTimeNextMonth
  sec, min, hour, day, month, year = localtime.to_a
  monMax = month == 2 && leapYear?(year) ? 29 : MON_MAX[month]
  if (month += 1) > 12
    month = 1
    year += 1
  end
  day = monMax if day >= lastDayOfMonth(month, year)
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#sameTimeNextQuarterObject

Return a new time that is 1 quarter later than time but at the same time of day.



301
302
303
304
305
306
307
308
# File 'lib/taskjuggler/TjTime.rb', line 301

def sameTimeNextQuarter
  sec, min, hour, day, month, year = localtime.to_a
  if (month += 3) > 12
    month -= 12
    year += 1
  end
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#sameTimeNextWeekObject

Return a new time that is 1 week later than time but at the same time of day.



274
275
276
277
278
279
280
281
282
283
284
# File 'lib/taskjuggler/TjTime.rb', line 274

def sameTimeNextWeek
  sec, min, hour, day, month, year = localtime.to_a
  if (day += 7) > lastDayOfMonth(month, year)
    day -= lastDayOfMonth(month, year)
    if (month += 1) > 12
      month = 1
      year += 1
    end
  end
  TjTime.new([ year, month, day, hour, min, sec, 0 ])
end

#sameTimeNextYearObject

Return a new time that is 1 year later than time but at the same time of day.



312
313
314
315
316
# File 'lib/taskjuggler/TjTime.rb', line 312

def sameTimeNextYear
  sec, min, hour, day, month, year = localtime.to_a
  year += 1
  TjTime.new([ year, month, day, hour, min, sec, 0])
end

#secondsOfDay(tz = nil) ⇒ Object

Returns the total number of seconds of the day. The time is assumed to be in the time zone specified by tz.



123
124
125
126
# File 'lib/taskjuggler/TjTime.rb', line 123

def secondsOfDay(tz = nil)
  lt = localtime
  (lt.to_i + lt.gmt_offset) % (60 * 60 * 24)
end

#strftime(format) ⇒ Object



391
392
393
# File 'lib/taskjuggler/TjTime.rb', line 391

def strftime(format)
  localtime.strftime(format)
end

#to_aObject



387
388
389
# File 'lib/taskjuggler/TjTime.rb', line 387

def to_a
  localtime.to_a
end

#to_iObject

Return the seconds since Epoch.



383
384
385
# File 'lib/taskjuggler/TjTime.rb', line 383

def to_i
  localtime.to_i
end

#to_s(format = nil) ⇒ Object

This function is just a wrapper around Time.strftime(). In case @time is nil, it returns ‘unkown’.



370
371
372
373
374
375
376
377
378
379
380
# File 'lib/taskjuggler/TjTime.rb', line 370

def to_s(format = nil)
  return 'unknown' if @time.nil?
  if format.nil?
    fmt = '%Y-%m-%d-%H:%M' + (@time.sec == 0 ? '' : ':%S') + '-%z'
  else
    # Handle TJ specific extensions to the strftime format.
    fmt = format.sub(/%Q/, "#{((localtime.mon - 1) / 3) + 1}")
  end
  # Always report values in local timezone
  localtime.strftime(fmt)
end

#upto(endDate, step = 1) ⇒ Object

Iterator that executes the block until time has reached endDate increasing time by step on each iteration.



186
187
188
189
190
191
192
# File 'lib/taskjuggler/TjTime.rb', line 186

def upto(endDate, step = 1)
  t = @time
  while t < endDate.time
    yield(TjTime.new(t))
    t += step
  end
end

#utcObject

Return the time object in UTC.



117
118
119
# File 'lib/taskjuggler/TjTime.rb', line 117

def utc
  TjTime.new(@time.dup.gmtime)
end

#wdayObject

Return the day of the week. 0 for Sunday, 1 for Monday and so on.



396
397
398
# File 'lib/taskjuggler/TjTime.rb', line 396

def wday
  localtime.wday
end

#weeksTo(date) ⇒ Object

Return the number of weeks between this time and date. The result is always rounded up.



346
347
348
# File 'lib/taskjuggler/TjTime.rb', line 346

def weeksTo(date)
  countIntervals(date, :sameTimeNextWeek)
end

#yearObject

Return the year.



418
419
420
# File 'lib/taskjuggler/TjTime.rb', line 418

def year
  localtime.year
end

#yearsTo(date) ⇒ Object

Return the number of years between this time and date. The result is always rounded up.



364
365
366
# File 'lib/taskjuggler/TjTime.rb', line 364

def yearsTo(date)
  countIntervals(date, :sameTimeNextYear)
end