123456789_123456789_123456789_123456789_123456789_

Class: TZInfo::DataSource Abstract

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Inherits: Object
Defined in: lib/tzinfo/data_source.rb

Overview

This class is abstract.

To create a custom data source, create a subclass of DataSource and implement the #load_timezone_info, #data_timezone_identifiers, #linked_timezone_identifiers, #load_country_info and #country_codes methods.

::TZInfo can be used with different data sources for time zone and country data. Each source of data is implemented as a subclass of DataSource.

To choose a data source and override the default selection, use the .set method.

Class Method Summary

Instance Method Summary

Constructor Details

.newDataSource

Initializes a new DataSource instance. Typically only called via subclasses of DataSource.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 166

def initialize
  @timezones = Concurrent::Map.new
end

Class Method Details

.create_default_data_sourceDataSource (private)

Creates a DataSource instance for use as the default. Used if no preference has been specified manually.

Returns:

  • (DataSource)

    the newly created default DataSource instance.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 145

def create_default_data_source
  has_tzinfo_data = false

  begin
    require 'tzinfo/data'
    has_tzinfo_data = true
  rescue LoadError
  end

  return DataSources::RubyDataSource.new if has_tzinfo_data

  begin
    return DataSources::ZoneinfoDataSource.new
  rescue DataSources::ZoneinfoDirectoryNotFound
    raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to https://tzinfo.github.io/datasourcenotfound for help resolving this error."
  end
end

.getDataSource

Returns:

  • (DataSource)

    the currently selected source of data.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 42

def get
  # If a DataSource hasn't been manually set when the first request is
  # made to obtain a DataSource, then a default data source is created.
  #
  # This is done at the first request rather than when TZInfo is loaded to
  # avoid unnecessary attempts to find a suitable DataSource.
  #
  # A `Mutex` is used to ensure that only a single default instance is
  # created (this avoiding the possibility of retaining two copies of the
  # same data in memory).

  unless @@instance
    @@default_mutex.synchronize do
      set(create_default_data_source) unless @@instance
    end
  end

  @@instance
end

.set(data_source_or_type, *args)

Sets the currently selected data source for time zone and country data.

This should usually be set to one of the two standard data source types:

  • :ruby - read data from the Ruby modules included in the TZInfo::Data library (tzinfo-data gem).
  • :zoneinfo - read data from the zoneinfo files included with most Unix-like operating systems (e.g. in /usr/share/zoneinfo).

To set ::TZInfo to use one of the standard data source types, call TZInfo::DataSource.set` in one of the following ways:

TZInfo::DataSource.set(:ruby)
TZInfo::DataSource.set(:zoneinfo)
TZInfo::DataSource.set(:zoneinfo, zoneinfo_dir)
TZInfo::DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_tab_file)

DataSource.set(:zoneinfo) will automatically search for the zoneinfo directory by checking the paths specified in DataSources::ZoneinfoDataSource.search_path. DataSources::ZoneinfoDirectoryNotFound will be raised if no valid zoneinfo directory could be found.

DataSource.set(:zoneinfo, zoneinfo_dir) uses the specified zoneinfo_dir directory as the data source. If the directory is not a valid zoneinfo directory, a DataSources::InvalidZoneinfoDirectory exception will be raised.

DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_tab_file) uses the specified zoneinfo_dir directory as the data source, but loads the iso3166.tab file from the path given by iso3166_tab_file. If the directory is not a valid zoneinfo directory, a DataSources::InvalidZoneinfoDirectory exception will be raised.

Custom data sources can be created by subclassing DataSource and implementing the following methods:

To have ::TZInfo use the custom data source, call .set, passing an instance of the custom data source implementation as follows:

TZInfo::DataSource.set(CustomDataSource.new)

Calling .set will only affect instances of Timezone and Country obtained with Timezone.get and Country.get subsequent to the .set call. Existing Timezone and Country instances will be unaffected.

If .set is not called, ::TZInfo will by default attempt to use TZInfo::Data as the data source. If TZInfo::Data is not available (i.e. if require 'tzinfo/data' fails), then ::TZInfo will search for a zoneinfo directory instead (using the search path specified by DataSources::ZoneinfoDataSource.search_path).

Parameters:

  • data_source_or_type (Object)

    either :ruby, :zoneinfo or an instance of a DataSource.

  • args (Array<Object>)

    when data_source_or_type is a symbol, optional arguments to use when initializing the data source.

Raises:

  • (ArgumentError)

    if data_source_or_type is not :ruby, :zoneinfo or an instance of DataSource.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 127

def set(data_source_or_type, *args)
  if data_source_or_type.kind_of?(DataSource)
    @@instance = data_source_or_type
  elsif data_source_or_type == :ruby
    @@instance = DataSources::RubyDataSource.new
  elsif data_source_or_type == :zoneinfo
    @@instance = DataSources::ZoneinfoDataSource.new(*args)
  else
    raise ArgumentError, 'data_source_or_type must be a DataSource instance or a data source type (:ruby or :zoneinfo)'
  end
end

Instance Method Details

#build_timezone_identifiersArray<String> (private)

Combines #data_timezone_identifiers and #linked_timezone_identifiers to create an Array containing all valid time zone identifiers. If #linked_timezone_identifiers is empty, the #data_timezone_identifiers instance is returned.

The returned Array is frozen. The identifiers are sorted according to String#<=>.

Returns:

  • (Array<String>)

    an Array containing all valid time zone identifiers.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 366

def build_timezone_identifiers
  data = data_timezone_identifiers
  linked = linked_timezone_identifiers
  linked.empty? ? data : (data + linked).sort!.freeze
end

#country_codesArray<String>

Returns a frozen Array of all the available ISO 3166-1 alpha-2 country codes. The identifiers are sorted according to String#<=>.

Returns:

  • (Array<String>)

    a frozen Array of all the available ISO 3166-1 alpha-2 country codes.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 246

def country_codes
  raise_invalid_data_source('country_codes')
end

#data_timezone_identifiersArray<String>

Returns a frozen Array of all the available time zone identifiers for data time zones (i.e. those that actually contain definitions). The identifiers are sorted according to String#<=>.

Returns:

  • (Array<String>)

    a frozen Array of all the available time zone identifiers for data time zones.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 218

def data_timezone_identifiers
  raise_invalid_data_source('data_timezone_identifiers')
end

#eager_load!

Loads all timezone and country data into memory.

This may be desirable in production environments to improve copy-on-write performance and to avoid flushing the constant cache every time a new timezone or country is loaded from DataSources::RubyDataSource.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 255

def eager_load!
  timezone_identifiers.each {|identifier| load_timezone_info(identifier) }
  country_codes.each {|code| load_country_info(code) }
  nil
end

#find_timezone_identifier(identifier) ⇒ String (private)

If the given identifier is contained within the #timezone_identifiers Array, the String instance representing that identifier from #timezone_identifiers is returned. Otherwise, nil is returned.

:nocov_array_bsearch:

See additional method definition at line 382.

Parameters:

  • identifier (String)

    A time zone identifier to search for.

Returns:

  • (String)

    the String instance representing identifier from #timezone_identifiers if found, or nil if not found.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 398

def find_timezone_identifier(identifier)

  result = timezone_identifiers.bsearch {|i| i >= identifier }
  result == identifier ? result : nil
end

#get_country_info(code) ⇒ DataSources::CountryInfo

Parameters:

  • code (String)

    an ISO 3166-1 alpha-2 country code.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 237

def get_country_info(code)
  load_country_info(code)
end

#get_timezone_info(identifier) ⇒ DataSources::TimezoneInfo

Returns a DataSources::TimezoneInfo instance for the given identifier. The result will derive from either DataSources::DataTimezoneInfo for time zones that define their own data or DataSources::LinkedTimezoneInfo for links or aliases to other time zones.

get_timezone_info calls #load_timezone_info to create the DataSources::TimezoneInfo instance. The returned instance is cached and returned in subsequent calls to get_timezone_info for the identifier.

Parameters:

  • identifier (String)

    A time zone identifier.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 184

def get_timezone_info(identifier)
  result = @timezones[identifier]

  unless result
    # Thread-safety: It is possible that multiple equivalent TimezoneInfo
    # instances could be created here in concurrently executing threads. The
    # consequences of this are that the data may be loaded more than once
    # (depending on the data source). The performance benefit of ensuring
    # that only a single instance is created is unlikely to be worth the
    # overhead of only allowing one TimezoneInfo to be loaded at a time.

    result = load_timezone_info(identifier)
    @timezones[result.identifier] = result
  end

  result
end

#inspectString

Returns:

  • (String)

    the internal object state as a programmer-readable String.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 268

def inspect
  "#<#{self.class}>"
end

#linked_timezone_identifiersArray<String>

Returns a frozen Array of all the available time zone identifiers that are links to other time zones. The identifiers are sorted according to String#<=>.

Returns:

  • (Array<String>)

    a frozen Array of all the available time zone identifiers that are links to other time zones.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 228

def linked_timezone_identifiers
  raise_invalid_data_source('linked_timezone_identifiers')
end

#load_country_info(code) ⇒ DataSources::CountryInfo (protected)

Parameters:

  • code (String)

    an ISO 3166-1 alpha-2 country code.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 294

def load_country_info(code)
  raise_invalid_data_source('load_country_info')
end

#load_timezone_info(identifier) ⇒ DataSources::TimezoneInfo (protected)

Returns a DataSources::TimezoneInfo instance for the given time zone identifier. The result should derive from either DataSources::DataTimezoneInfo for time zones that define their own data or DataSources::LinkedTimezoneInfo for links to or aliases for other time zones.

Parameters:

  • identifier (String)

    A time zone identifier.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 285

def load_timezone_info(identifier)
  raise_invalid_data_source('load_timezone_info')
end

#lookup_country_info(hash, code, encoding = Encoding::UTF_8) ⇒ DataSources::CountryInfo (protected)

Looks up a given code in the given hash of code to DataSources::CountryInfo mappings. If the code is found the DataSources::CountryInfo is returned. Otherwise an InvalidCountryCode exception is raised.

Parameters:

  • hash (String, DataSources::CountryInfo)

    a mapping from ISO 3166-1 alpha-2 country codes to DataSources::CountryInfo instances.

  • code (String)

    a country code to lookup.

  • encoding (Encoding) (defaults to: Encoding::UTF_8)

    the encoding used for the country codes in hash.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 337

def lookup_country_info(hash, code, encoding = Encoding::UTF_8)
  raise InvalidCountryCode, "Invalid country code: #{code.nil? ? 'nil' : code}" unless code.kind_of?(String)

  info = try_with_encoding(code, encoding) {|c| hash[c] }
  return info if info

  raise InvalidCountryCode, "Invalid country code: #{code.encode(Encoding::UTF_8)}"
end

#raise_invalid_data_source(method_name) (private)

Raises InvalidDataSource to indicate that a method has not been overridden by a particular data source implementation.

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 352

def raise_invalid_data_source(method_name)
  raise InvalidDataSource, "#{method_name} not defined"
end

#timezone_identifier_encodingEncoding (protected)

Returns:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 300

def timezone_identifier_encoding
  Encoding::UTF_8
end

#timezone_identifiersArray<String>

Returns:

  • (Array<String>)

    a frozen Arrayof all the available time zone identifiers. The identifiers are sorted according toString#<=>`.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 204

def timezone_identifiers
  # Thread-safety: It is possible that the value of @timezone_identifiers
  # may be calculated multiple times in concurrently executing threads. It
  # is not worth the overhead of locking to ensure that
  # @timezone_identifiers is only calculated once.
  @timezone_identifiers ||= build_timezone_identifiers
end

#to_sString

Returns:

  • (String)

    a description of the DataSource.

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 262

def to_s
  "Default DataSource"
end

#try_with_encoding(string, encoding) {|s| ... } ⇒ Object (private)

Tries an operation using string directly. If the operation fails, the string is copied and encoded with encoding and the operation is tried again.

fails and string is already encoded with encoding.

Parameters:

  • string (String)

    The String to perform the operation on.

  • encoding (Encoding)

    The Encoding to use if the initial attempt fails.

Yields:

  • (s)

    the caller will be yielded to once or twice to attempt the operation.

Yield Parameters:

  • s (String)

    either string or an encoded copy of string.

Yield Returns:

  • (Object)

    The result of the operation. Must be truthy if successful.

Returns:

  • (Object)

    the result of the operation or nil if the first attempt

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 436

def try_with_encoding(string, encoding)
  result = yield string
  return result if result

  unless encoding == string.encoding
    string = string.encode(encoding)
    yield string
  end
end

#validate_timezone_identifier(identifier) ⇒ String (protected)

Checks that the given identifier is a valid time zone identifier (can be found in the #timezone_identifiers Array). If the identifier is valid, the String instance representing that identifier from timezone_identifiers is returned. Otherwise an InvalidTimezoneIdentifier exception is raised.

Parameters:

  • identifier (String)

    a time zone identifier to be validated.

Returns:

Raises:

[ GitHub ]

  
# File 'lib/tzinfo/data_source.rb', line 315

def validate_timezone_identifier(identifier)
  raise InvalidTimezoneIdentifier, "Invalid identifier: #{identifier.nil? ? 'nil' : identifier}" unless identifier.kind_of?(String)

  valid_identifier = try_with_encoding(identifier, timezone_identifier_encoding) {|id| find_timezone_identifier(id) }
  return valid_identifier if valid_identifier

  raise InvalidTimezoneIdentifier, "Invalid identifier: #{identifier.encode(Encoding::UTF_8)}"
end