1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
module Puppet::Pops
module Time
class Timestamp < TimeData
DEFAULT_FORMATS_WO_TZ = ['%FT%T.%N', '%FT%T', '%F %T.%N', '%F %T', '%F']
DEFAULT_FORMATS = ['%FT%T.%N %Z', '%FT%T %Z', '%F %T.%N %Z', '%F %T %Z', '%F %Z'] + DEFAULT_FORMATS_WO_TZ
CURRENT_TIMEZONE = 'current'.freeze
KEY_TIMEZONE = 'timezone'.freeze
# Converts a timezone that strptime can parse using '%z' into '-HH:MM' or '+HH:MM'
# @param [String] tz the timezone to convert
# @return [String] the converted timezone
#
# @api private
def self.convert_timezone(tz)
if tz =~ /\A[+-]\d\d:\d\d\z/
tz
else
offset = utc_offset(tz) / 60
if offset < 0
offset = offset.abs
sprintf('-%2.2d:%2.2d', offset / 60, offset % 60)
else
sprintf('+%2.2d:%2.2d', offset / 60, offset % 60)
end
end
end
# Returns the zone offset from utc for the given `timezone`
# @param [String] timezone the timezone to get the offset for
# @return [Integer] the timezone offset, in seconds
#
# @api private
def self.utc_offset(timezone)
if CURRENT_TIMEZONE.casecmp(timezone) == 0
::Time.now.utc_offset
else
hash = DateTime._strptime(timezone, '%z')
offset = hash.nil? ? nil : hash[:offset]
raise ArgumentError, _("Illegal timezone '%{timezone}'") % { timezone: timezone } if offset.nil?
offset
end
end
# Formats a ruby Time object using the given timezone
def self.format_time(format, time, timezone)
unless timezone.nil? || timezone.empty?
time = time.localtime(convert_timezone(timezone))
end
time.strftime(format)
end
def self.now
from_time(::Time.now)
end
def self.from_time(t)
new(t.tv_sec * NSECS_PER_SEC + t.tv_nsec)
end
def self.from_hash(args_hash)
parse(args_hash[KEY_STRING], args_hash[KEY_FORMAT], args_hash[KEY_TIMEZONE])
end
def self.parse(str, format = :default, timezone = nil)
has_timezone = !(timezone.nil? || timezone.empty? || timezone == :default)
if format.nil? || format == :default
format = has_timezone ? DEFAULT_FORMATS_WO_TZ : DEFAULT_FORMATS
end
parsed = nil
if format.is_a?(Array)
format.each do |fmt|
parsed = DateTime._strptime(str, fmt)
next if parsed.nil?
if parsed.include?(:leftover) || (has_timezone && parsed.include?(:zone))
parsed = nil
next
end
break
end
if parsed.nil?
raise ArgumentError, _(
"Unable to parse '%{str}' using any of the formats %{formats}") % { str: str, formats: format.join(', ') }
end
else
parsed = DateTime._strptime(str, format)
if parsed.nil? || parsed.include?(:leftover)
raise ArgumentError, _("Unable to parse '%{str}' using format '%{format}'") % { str: str, format: format }
end
if has_timezone && parsed.include?(:zone)
raise ArgumentError, _(
'Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument')
end
end
unless has_timezone
timezone = parsed[:zone]
has_timezone = !timezone.nil?
end
fraction = parsed[:sec_fraction]
# Convert msec rational found in _strptime hash to usec
fraction = fraction * 1000000 unless fraction.nil?
# Create the Time instance and adjust for timezone
parsed_time = ::Time.utc(parsed[:year], parsed[:mon], parsed[:mday], parsed[:hour], parsed[:min], parsed[:sec], fraction)
parsed_time -= utc_offset(timezone) if has_timezone
# Convert to Timestamp
from_time(parsed_time)
end
undef_method :-@, :+@, :div, :fdiv, :abs, :abs2, :magnitude # does not make sense on a Timestamp
if method_defined?(:negative?)
undef_method :negative?, :positive?
end
if method_defined?(:%)
undef_method :%, :modulo, :divmod
end
def +(o)
case o
when Timespan
Timestamp.new(@nsecs + o.nsecs)
when Integer, Float
Timestamp.new(@nsecs + (o * NSECS_PER_SEC).to_i)
else
raise ArgumentError, _("%{klass} cannot be added to a Timestamp") % { klass: a_an_uc(o) }
end
end
def -(o)
case o
when Timestamp
# Diff between two timestamps is a timespan
Timespan.new(@nsecs - o.nsecs)
when Timespan
Timestamp.new(@nsecs - o.nsecs)
when Integer, Float
# Subtract seconds
Timestamp.new(@nsecs - (o * NSECS_PER_SEC).to_i)
else
raise ArgumentError, _("%{klass} cannot be subtracted from a Timestamp") % { klass: a_an_uc(o) }
end
end
def format(format, timezone = nil)
self.class.format_time(format, to_time, timezone)
end
def to_s
format(DEFAULT_FORMATS[0])
end
def to_time
::Time.at(to_r).utc
end
end
end
end
|