File: timestamp.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (160 lines) | stat: -rw-r--r-- 4,756 bytes parent folder | download | duplicates (3)
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