class TimeWithZone

Handle time with zone withtout ActiveSupport or ENV

require 'time_with_zone'

TimeWithZone.strptime_with_zone('2016-10-10', '%Y-%m-%d', 'Asia/Taipei')
# => '2016-10-10 00:00:00 +0800'

TimeWithZone.parse_with_zone('2016-10-10', 'Asia/Taipei')
# => '2016-10-10 00:00:00 +0800'

time = Time.parse('2016-10-10 00:00:00 +00:00')
TimeWithZone.set_zone(time, 'Asia/Taipei')
# => '2016-10-10 00:00:00 +0800'

time = Time.parse('2016-10-10 00:00:00 +00:00')
TimeWithZone.localtime_with_zone(time, 'Asia/Taipei')
# => '2016-10-10 08:00:00 +0800'

Constants

NAME_PATTERN

Region/Zone, Region/Zone/Zone

NUMERIC_PATTERN

[+-]HH:MM, [+-]HHMM, [+-]HH

VERSION
ZoneOffset

Short-abbreviation timezone which Ruby's Time class supports

@see github.com/ruby/ruby/blob/6ce158ba870eb815ba9775ac8380b32fd81be040/lib/time.rb#L97-L113

Public Class Methods

localtime_with_zone(time, timezone) click to toggle source

Time#localtime with timezone (non-destructive)

ENV['TZ'] = '+09:00' # Assume your local timezone is +09:00

require 'time'

time = Time.parse("2016-10-20 00:00:00 +00:00") 
time.dup.localtime("+08:00")
#=> 2010-10-20 08:00:00 +0800
time.dup.localtime("CDT")
#=> error
time.dup.localtime("Asia/Taipei")
#=> error

require 'time_with_zone'

time = Time.parse("2016-10-20 00:00:00 +00:00") 
TimeWithZone.localtime_with_zone(time, "+08:00")
#=> 2010-10-20 08:00:00 +0800
TimeWithZone.localtime_with_zone(time, "CDT")
#=> 2016-10-19 19:00:00 -0500
TimeWithZone.localtime_with_zone(time, "Asia/Taipei")
#=> 2010-10-20 08:00:00 +0800

This method is actually a short-hand of

require 'time_with_zone'
time = Time.parse("2016-10-20 00:00:00 +00:00")

time.dup.localtime(TimeWithZone.zone_offset("Asia/Taipei", time))
#=> 2010-10-20 08:00:00 +0800

@param [Time] time object @param [String] timezone {NUMERIC_PATTERN} or {NAME_PATTERN} or {ZoneOffset} @return [Time]

# File lib/time_with_zone.rb, line 69
def self.localtime_with_zone(time, timezone)
  localtime_with_zone!(time.dup, timezone)
end
localtime_with_zone!(time, timezone) click to toggle source

Time#localtime with timezone (destructive)

@param [Time] time object @param [String] timezone {NUMERIC_PATTERN} or {NAME_PATTERN} or {ZoneOffset} @return [Time] @see localtime_with_zone

# File lib/time_with_zone.rb, line 79
def self.localtime_with_zone!(time, timezone)
  _zone_offset = zone_offset(timezone, time)
  time.localtime(_zone_offset)
end
parse_with_zone(date, timezone) click to toggle source

Time.parse with timezone

ENV['TZ'] = '+09:00' # Assume your local timezone is +09:00

require 'time'

Time.parse("2016-10-20")
#=> 2016-10-20 00:00:00 +0900
Time.parse("2016-10-20 00:00:00 +08:00")
#=> 2016-10-20 00:00:00 +0800
Time.parse("2016-10-20 00:00:00 CDT")
#=> 2016-10-20 00:00:00 -0500
Time.parse("2016-10-20 00:00:00 Asia/Taipei")
#=> 2016-10-20 00:00:00 +0900 (does not work)

require 'time_with_zone'

TimeWithZone.parse_with_zone("2016-10-20", "+08:00")
#=> 2016-10-20 00:00:00 +0800
TimeWithZone.parse_with_zone("2016-10-20", "CDT")
#=> 2016-10-20 00:00:00 -0500
TimeWithZone.parse_with_zone("2016-10-20", "Asia/Taipei")
#=> 2016-10-20 00:00:00 +0800

@param [String] date string to be parsed @param [String] timezone {NUMERIC_PATTERN} or {NAME_PATTERN} or {ZoneOffset} @return [Time]

# File lib/time_with_zone.rb, line 111
def self.parse_with_zone(date, timezone)
  time = Time.parse(date)
  set_zone!(time, timezone)
end
parse_with_zone_offset(date, zone_offset) click to toggle source

Time.parse with zone_offset

require 'time_with_zone'

TimeWithZone.parse_with_zone_offset("2016-10-20", 28800)
#=> 2016-10-20 00:00:00 +0800

@param [String] date string to be parsed @param [Integer] zone_offset @return [Time]

# File lib/time_with_zone.rb, line 126
def self.parse_with_zone_offset(date, zone_offset)
  time = Time.parse(date)
  set_zone_offset!(time, zone_offset)
end
set_zone(time, timezone) click to toggle source

This method changes only the zone field of Time object

require 'time_with_zone'

time = Time.parse("2016-02-02 00:00:00 +00:00")
TimeWithZone.set_zone(time, "+08:00")
#=> "2016-02-02 00:00:00 +0800"
# Note that it is not "2016-02-02 08:00:00 +0800" like Time#localtime(timezone)

@param [Time] time @param [String] timezone @return [Time]

# File lib/time_with_zone.rb, line 193
def self.set_zone(time, timezone)
  set_zone!(time.dup, timezone)
end
set_zone_offset(time, zone_offset) click to toggle source

This method changes only the zone offset field of Time object

require 'time_with_zone'
time = Time.parse("2016-02-02 00:00:00 +00:00")

TimeWithZone.set_zone_offset(time, 28800)
#=> "2016-02-02 00:00:00 +0800"
# Note that it is not "2016-02-02 08:00:00 +0800" like Time#localtime(zone_offset)

@param [Time] time @param [Integer] zone_offset @return [Time]

# File lib/time_with_zone.rb, line 209
def self.set_zone_offset(time, zone_offset)
  set_zone_offset!(time.dup, zone_offset)
end
strptime_with_zone(date, format, timezone) click to toggle source

Time.strptime with timezone

ENV['TZ'] = '+09:00' # Assume your local timezone is +09:00

require 'time'

Time.strptime("2016-10-20", "%Y-%m-%d")
#=> 2016-10-20 00:00:00 +0900
Time.strptime("2016-10-20 +08:00", "%Y-%m-%d %z")
#=> 2016-10-20 00:00:00 +0800
Time.strptime("2016-10-20 CDT", "%Y-%m-%d %Z")
#=> 2016-10-20 00:00:00 -0500
Time.strptime("2016-10-20 Asia/Taipei", "%Y-%m-%d %Z")
#=> 2016-10-20 00:00:00 +0900 (does not work)

require 'time_with_zone'

TimeWithZone.strptime_with_zone("2016-10-20", "%Y-%m-%d", "+08:00")
#=> 2016-10-20 00:00:00 +0800
TimeWithZone.strptime_with_zone("2016-10-20", "%Y-%m-%d", "CDT")
#=> 2016-10-20 00:00:00 -0500
TimeWithZone.strptime_with_zone("2016-10-20", "%Y-%m-%d", "Asia/Taipei")
#=> 2016-10-20 00:00:00 +0800

@param [String] date string to be parsed @param [String] format @param [String] timezone {NUMERIC_PATTERN} or {NAME_PATTERN} or {ZoneOffset} @return [Time]

# File lib/time_with_zone.rb, line 160
def self.strptime_with_zone(date, format, timezone)
  time = Time.strptime(date, format)
  set_zone!(time, timezone)
end
strptime_with_zone_offset(date, format, zone_offset) click to toggle source

Time.strptime with zone_offset

require 'time_with_zone'

TimeWithZone.strptime_with_zone_offset("2016-10-20", "%Y-%m-%d", 28800)
#=> 2016-10-20 00:00:00 +0800

@param [String] date string to be parsed @param [String] format @param [Integer] zone_offset @return [Time]

# File lib/time_with_zone.rb, line 176
def self.strptime_with_zone_offset(date, format, zone_offset)
  time = Time.strptime(date, format)
  set_zone_offset!(time, zone_offset)
end
zone_offset(timezone, time = nil) click to toggle source

Returns zone offset for given timezone string

require 'time_with_zone'

TimeWithZone.zone_offset("+08:00")
#=> 28800
TimeWithZone.zone_offset("Asia/Taipei")
#=> 28800
TimeWithZone.zone_offset("PST")
#=> -28800
TimeWithZone.zone_offset("America/Los_Angeles")
#=> -25200
TimeWithZone.zone_offset("America/Los_Angeles", Time.parse("2016-07-07 00:00:00 +00:00"))
#=> -25200 # DST (Daylight Saving Time)
TimeWithZone.zone_offset("America/Los_Angeles", Time.parse("2016-01-01 00:00:00 +00:00"))
#=> -28800 # non DST

@param [String] timezone {NUMERIC_PATTERN} or {NAME_PATTERN} or {ZoneOffset} @param [Time] time if you need to consider Daylight Saving Time @return [Integer] zone offset

# File lib/time_with_zone.rb, line 233
def self.zone_offset(timezone, time = nil)
  if NUMERIC_PATTERN === timezone
    Time.zone_offset(timezone)
  elsif NAME_PATTERN === timezone
    tz = TZInfo::Timezone.get(timezone)
    if time
      tz.period_for_utc(time).utc_total_offset
    else
      tz.current_period.utc_total_offset
    end
  elsif ZoneOffset.include?(timezone)
    ZoneOffset[timezone] * 3600
  else
    raise ArgumentError, "timezone format is invalid: #{timezone}"
  end
end

Private Class Methods

set_zone!(time, timezone) click to toggle source

This method changes only the zone field of Time object

@param [Time] time @param [String] timezone @return [Time] @see set_zone @todo how to modify time object destructively?

# File lib/time_with_zone.rb, line 259
def self.set_zone!(time, timezone)
  set_zone_offset!(time, zone_offset(timezone, time))
end
set_zone_offset!(time, zone_offset) click to toggle source

This method changes only the zone offset field of Time object

@param [Time] time @param [Integer] zone_offset @return [Time] @see set_zone_offset @todo how to modify time object destructively?

# File lib/time_with_zone.rb, line 270
def self.set_zone_offset!(time, zone_offset)
  utc_offset = time.utc_offset
  time.localtime(zone_offset) + utc_offset - zone_offset
end