lib/tzinfo/timezone.rb in tzinfo-0.0.2 vs lib/tzinfo/timezone.rb in tzinfo-0.0.3

- old
+ new

@@ -14,10 +14,12 @@ # puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s # # The timezone information all comes from the tz database # (see http://www.twinsun.com/tz/tz-link.htm) class Timezone + include Comparable + # Returns a timezone by its identifier (e.g. "Europe/London", # "America/Chicago" or "UTC"). # # Raises an exception of the timezone couldn't be found. def self.get(identifier) @@ -33,10 +35,11 @@ end # If identifier is nil calls super(), else calls get(identifier). def self.new(identifier = nil) if identifier + puts 'getting' get(identifier) else super() end end @@ -56,15 +59,13 @@ end # Returns all the Timezones defined for all Countries. This is not the # complete set of Timezones as some are not country specific (e.g. # 'Etc/GMT'). - # - # This method will take a substantial time to return the first time it is - # called as all the Timezone classes will have to be loaded by Ruby. If - # you just want the zone identifiers use all_country_zone_identifiers - # instead. + # + # Returns TimezoneProxy objects to avoid the overhead of loading Timezone + # definitions until a conversion is actually required. def self.all_country_zones Country.all_codes.inject([]) {|zones,country| zones += Country.get(country).zones } end @@ -78,12 +79,14 @@ zones += Country.get(country).zone_identifiers } end # Returns all US Timezone instances. A shortcut for - # TZInfo::Country.get('US').zones. If you only need the zone identifiers, - # use us_zone_identifiers instead. + # TZInfo::Country.get('US').zones. + # + # Returns TimezoneProxy objects to avoid the overhead of loading Timezone + # definitions until a conversion is actually required. def self.us_zones Country.get('US').zones end # Returns all US zone identifiers. A shortcut for @@ -95,10 +98,58 @@ # The identifier of the timezone, e.g. "Europe/Paris". def identifier 'Unknown' end + # An alias for identifier. + def name + # Don't use alias, as identifier gets overridden. + identifier + end + + # Returns a friendlier version of the idenfitifer. + def to_s + friendly_identifier + end + + # Returns a friendlier version of the idenfitifer. Set skip_first_part to + # omit the first part of the identifier (typically a region name) where + # there is more than one part. + def friendly_identifier(skip_first_part = false) + parts = identifier.split('/') + if parts.empty? + # shouldn't happen + identifier + elsif parts.length == 1 + parts[0] + else + if skip_first_part + result = '' + else + result = parts[0] + ' - ' + end + + parts[1, parts.length - 1].reverse_each {|part| + part.gsub!(/_/, ' ') + + # Missing a space if a lower case followed by an upper case and the + # name isn't McXxxx. + part.gsub!(/[^M]([a-z])([A-Z])/, '\1 \2') + part.gsub!(/[M]([a-bd-z])([A-Z])/, '\1 \2') + + # Missing an apostrophe if two consecutive upper case characters. + part.gsub!(/([A-Z])([A-Z])/, '\1\'\2') + + result << part + result << ', ' + } + + result.slice!(result.length - 2, 2) + result + end + end + # Returns the TimezonePeriod for the given UTC time. utc can either be # a DateTime or a Time. Any timezone information in utc is ignored (it is # treated as a UTC time). # # If no TimezonePeriod could be found, PeriodNotFound is raised. @@ -205,35 +256,47 @@ def current_period_and_time utc = Time.now.utc [utc_to_local(utc), period_for_utc(utc)] end + # Two Timezones are considered to be equal if their identifiers are the same. + def ==(tz) + identifier == tz.identifier + end + + # Compare two Timezones based on their identifier. Returns -1 if tz is less + # than self, 0 if tz is equal to self and +1 if tz is greater than self. + def <=>(tz) + identifier <=> tz.identifier + end + protected def self.setup class_eval <<CODE @@periods = [] - def self.add_period(period) - @@periods << period - end + @@instance = new - def self.set_identifier(identifier) - @@identifier = identifier - end - - def periods - @@periods - end - protected :periods - def identifier @@identifier - end + end - @@instance = new def self.instance @@instance - end + end + + def periods + @@periods.freeze + end + + protected + def self.add_period(period) + @@periods << period + end + + def self.set_identifier(identifier) + @@identifier = identifier + end CODE end private # Executes a block with a DateTime. If datetime is a Time it will be @@ -249,9 +312,43 @@ else yield datetime end end end + + # A proxy class representing a timezone with a given identifier. It can be + # constructed with an identifier and behaves almost identically to a Timezone + # loaded through Timezone.get. The first time an attempt is made to perform + # a conversion on the proxy, the real Timezone class is loaded. If the + # proxy's identifier was not valid, then an exception will be thrown at this + # point. + class TimezoneProxy < Timezone + # Construct a new TimezoneProxy for the given identifier. The identifier + # is not checked when constructing the proxy. It will be validated on the + # first conversion. + def self.new(identifier) + # Need to override new to undo the behaviour introduced in Timezone#new. + tzp = super() + tzp.instance_eval <<CODE + @identifier = identifier + @real_tz = nil +CODE + tzp + end + + # The identifier of the timezone, e.g. "Europe/Paris". + def identifier + @real_tz.nil? ? @identifier : @real_tz.identifier + end + + def periods #:nodoc: + if @real_tz.nil? + # We now need the actual data. Load in the real timezone. + @real_tz = Timezone.get(@identifier) + end + @real_tz.periods + end + end # A period of time in a timezone where the same offset from UTC applies. class TimezonePeriod # Start time of the period (UTC). May be nil if unbounded. attr_reader :utc_start