[DRE-commits] [ruby-tzinfo] 01/05: Imported Upstream version 1.2.0

Hleb Valoshka tsfgnu-guest at moszumanska.debian.org
Sat May 31 13:47:59 UTC 2014


This is an automated email from the git hooks/post-receive script.

tsfgnu-guest pushed a commit to branch master
in repository ruby-tzinfo.

commit 547dc59ee77cc7b94eebaf5d7462f4a082ad5137
Author: Hleb Valoshka <375gnu at gmail.com>
Date:   Sat May 31 16:41:01 2014 +0300

    Imported Upstream version 1.2.0
---
 CHANGES.md                                   |  33 +++
 LICENSE                                      |   2 +-
 README.md                                    |  67 ++---
 Rakefile                                     |  40 ++-
 checksums.yaml.gz                            | Bin 269 -> 269 bytes
 checksums.yaml.gz.sig                        | Bin 256 -> 256 bytes
 data.tar.gz.sig                              | Bin 256 -> 256 bytes
 lib/tzinfo.rb                                |  22 --
 lib/tzinfo/country.rb                        |  25 +-
 lib/tzinfo/country_index_definition.rb       |  22 --
 lib/tzinfo/country_info.rb                   |  22 --
 lib/tzinfo/country_timezone.rb               |  77 +++---
 lib/tzinfo/data_source.rb                    |  31 +--
 lib/tzinfo/data_timezone.rb                  |  29 +-
 lib/tzinfo/data_timezone_info.rb             |  22 --
 lib/tzinfo/info_timezone.rb                  |  22 --
 lib/tzinfo/linked_timezone.rb                |  29 +-
 lib/tzinfo/linked_timezone_info.rb           |  22 --
 lib/tzinfo/offset_rationals.rb               |  22 --
 lib/tzinfo/ruby_core_support.rb              |  36 +--
 lib/tzinfo/ruby_country_info.rb              |  28 +-
 lib/tzinfo/ruby_data_source.rb               |  24 +-
 lib/tzinfo/time_or_datetime.rb               |  40 +--
 lib/tzinfo/timezone.rb                       |  74 +++--
 lib/tzinfo/timezone_definition.rb            |  22 --
 lib/tzinfo/timezone_index_definition.rb      |  22 --
 lib/tzinfo/timezone_info.rb                  |  22 --
 lib/tzinfo/timezone_offset.rb                |  22 --
 lib/tzinfo/timezone_period.rb                |  24 +-
 lib/tzinfo/timezone_proxy.rb                 |  29 +-
 lib/tzinfo/timezone_transition.rb            |  26 +-
 lib/tzinfo/timezone_transition_definition.rb |  24 +-
 lib/tzinfo/transition_data_timezone_info.rb  |  22 --
 lib/tzinfo/zoneinfo_country_info.rb          |  24 +-
 lib/tzinfo/zoneinfo_data_source.rb           | 238 +++++++++++-----
 lib/tzinfo/zoneinfo_timezone_info.rb         |  22 --
 metadata.gz.sig                              |   4 +-
 metadata.yml                                 | 160 +++++------
 test/tc_country.rb                           |  30 +-
 test/tc_country_index_definition.rb          |  24 +-
 test/tc_country_info.rb                      |  24 +-
 test/tc_country_timezone.rb                  | 144 +++++-----
 test/tc_data_source.rb                       |  46 ++--
 test/tc_data_timezone.rb                     |  36 +--
 test/tc_data_timezone_info.rb                |  24 +-
 test/tc_info_timezone.rb                     |  24 +-
 test/tc_linked_timezone.rb                   |  61 +++--
 test/tc_linked_timezone_info.rb              |  24 +-
 test/tc_offset_rationals.rb                  |  24 +-
 test/tc_ruby_core_support.rb                 | 117 +++++---
 test/tc_ruby_country_info.rb                 |  32 +--
 test/tc_ruby_data_source.rb                  |  24 +-
 test/tc_time_or_datetime.rb                  |  80 +++---
 test/tc_timezone.rb                          |  80 +++---
 test/tc_timezone_definition.rb               |  24 +-
 test/tc_timezone_index_definition.rb         |  24 +-
 test/tc_timezone_info.rb                     |  24 +-
 test/tc_timezone_london.rb                   |  24 +-
 test/tc_timezone_melbourne.rb                |  24 +-
 test/tc_timezone_new_york.rb                 |  24 +-
 test/tc_timezone_offset.rb                   |  24 +-
 test/tc_timezone_period.rb                   |  32 +--
 test/tc_timezone_proxy.rb                    |  62 +++--
 test/tc_timezone_transition.rb               |  24 +-
 test/tc_timezone_transition_definition.rb    |  24 +-
 test/tc_timezone_utc.rb                      |  24 +-
 test/tc_transition_data_timezone_info.rb     |  28 +-
 test/tc_zoneinfo_country_info.rb             |  48 +---
 test/tc_zoneinfo_data_source.rb              | 392 ++++++++++++++++++++++++---
 test/tc_zoneinfo_timezone_info.rb            |  30 +-
 test/test_utils.rb                           |  56 ++--
 test/ts_all.rb                               |  22 --
 test/ts_all_ruby.rb                          |  22 --
 test/ts_all_zoneinfo.rb                      |  22 --
 test/tzinfo-data/tzinfo/data.rb              |  22 --
 test/tzinfo-data/tzinfo/data/version.rb      |  22 --
 tzinfo.gemspec                               |   4 +-
 77 files changed, 1293 insertions(+), 1853 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index af073a6..57c1a24 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,22 @@
+Version 1.2.0 - 26-May-2014
+---------------------------
+
+* Raise the minimum supported Ruby version to 1.8.7.
+* Support loading system zoneinfo data on FreeBSD, OpenBSD and Solaris.
+  Resolves #15.
+* Add canonical_identifier and canonical_zone methods to Timezone. Resolves #16.
+* Add a link to a DataSourceNotFound help page in the TZInfo::DataSourceNotFound
+  exception message.
+* Load iso3166.tab and zone.tab files as UTF-8.
+* Fix Timezone#local_to_utc returning local Time instances on systems using UTC
+  as the local time zone. Resolves #13.
+* Fix == methods raising an exception when passed an instance of a different
+  class by making <=> return nil if passed a non-comparable argument.
+* Eliminate "require 'rational'" warnings. Resolves #10.
+* Eliminate "assigned but unused variable - info" warnings. Resolves #11.
+* Switch to minitest v5 for unit tests. Resolves #18.
+
+
 Version 1.1.0 - 25-Sep-2013
 ---------------------------
 
@@ -52,6 +71,20 @@ Version 1.0.0 - 2-Jun-2013
   use other TimezonePeriod instance methods instead (issue #7655).
 
 
+Version 0.3.39 (tzdata v2014a) - 9-Mar-2014
+-------------------------------------------
+
+* Updated to tzdata version 2014a
+  (http://mm.icann.org/pipermail/tz-announce/2014-March/000018.html).
+
+
+Version 0.3.38 (tzdata v2013g) - 8-Oct-2013
+-------------------------------------------
+
+* Updated to tzdata version 2013g
+  (http://mm.icann.org/pipermail/tz-announce/2013-October/000015.html).
+
+
 Version 0.3.37 (tzdata v2013b) - 11-Mar-2013
 --------------------------------------------
 
diff --git a/LICENSE b/LICENSE
index 7a23113..dd21221 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2013 Philip Ross
+Copyright (c) 2005-2014 Philip Ross
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of 
 this software and associated documentation files (the "Software"), to deal in 
diff --git a/README.md b/README.md
index d3fa744..cf8d6e8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
 TZInfo - Ruby Timezone Library
 ==============================
 
+[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](http://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo)
+
 [TZInfo](http://tzinfo.github.io) provides daylight savings aware 
 transformations between times in different timezones.
 
@@ -17,17 +19,19 @@ TZInfo requires a source of timezone data. There are two built-in options:
    [IANA Time Zone Database](http://www.iana.org/time-zones).
 
 By default, TZInfo::Data will be used. If TZInfo::Data is not available (i.e. 
-if require 'tzinfo/data' fails), then TZInfo will search for a zoneinfo
+if `require 'tzinfo/data'` fails), then TZInfo will search for a zoneinfo
 directory instead (using the search path specified by 
-TZInfo::ZoneinfoDataSource::DEFAULT_SEARCH_PATH).
+`TZInfo::ZoneinfoDataSource::DEFAULT_SEARCH_PATH`).
 
-If no data source can be found, a TZInfo::DataSourceNotFound exception will be
-raised when TZInfo is used.
+If no data source can be found, a `TZInfo::DataSourceNotFound` exception will be
+raised when TZInfo is used. Further information is available 
+[in the wiki](http://tzinfo.github.io/datasourcenotfound) to help with 
+resolving `TZInfo::DataSourceNotFound` errors.
 
 The default data source selection can be overridden using 
-TZInfo::DataSource.set.
+`TZInfo::DataSource.set`.
 
-Custom data sources can also be used. See TZInfo::DataSource.set for
+Custom data sources can also be used. See `TZInfo::DataSource.set` for
 further details.
 
 
@@ -48,24 +52,24 @@ Example Usage
 -------------
 
 The following code will obtain the America/New_York timezone (as an instance
-of TZInfo::Timezone) and covert a time in UTC to local New York time:
+of `TZInfo::Timezone`) and convert a time in UTC to local New York time:
 
     require 'tzinfo'
     
     tz = TZInfo::Timezone.get('America/New_York')
     local = tz.utc_to_local(Time.utc(2005,8,29,15,35,0))
 
-Note that the local Time returned will have a UTC timezone (local.zone will 
-return "UTC"). This is because the Ruby Time class only supports two timezones: 
+Note that the local Time returned will have a UTC timezone (`local.zone` will 
+return `"UTC"`). This is because the Ruby Time class only supports two timezones: 
 UTC and the current system local timezone.
   
-To convert from a local time to UTC, the local_to_utc method can be used as
+To convert from a local time to UTC, the `local_to_utc` method can be used as
 follows:
 
     utc = tz.local_to_utc(local)
 
 Note that the timezone information of the local Time object is ignored (TZInfo
-will just read the date and time and treat them as if there were in the 'tz'
+will just read the date and time and treat them as if there were in the `tz`
 timezone). The following two lines will return the same result regardless of 
 the system's local timezone:
 
@@ -73,50 +77,51 @@ the system's local timezone:
     tz.local_to_utc(Time.utc(2006,6,26,1,0,0))
   
 To obtain information about the rules in force at a particular UTC or local 
-time, the TZInfo::Timezone.period_for_utc and TZInfo::Timezone.period_for_local
-methods can be used. Both of these methods return TZInfo::TimezonePeriod 
-objects. The following gets the identifier for the period (in this case EDT).
+time, the `TZInfo::Timezone.period_for_utc` and 
+`TZInfo::Timezone.period_for_local` methods can be used. Both of these methods 
+return `TZInfo::TimezonePeriod` objects. The following gets the identifier for 
+the period (in this case EDT).
 
     period = tz.period_for_utc(Time.utc(2005,8,29,15,35,0))
     id = period.zone_identifier
   
-The current local time in a Timezone can be obtained with the 
-TZInfo::Timezone#now method:
+The current local time in a `Timezone` can be obtained with the 
+`TZInfo::Timezone#now` method:
 
     now = tz.now
 
-All methods in TZInfo that operate on a time can be used with either Time or 
-DateTime instances or with Integer timestamps (i.e. as returned by Time#to_i). 
-The type of the values returned will match the the type passed in.
+All methods in TZInfo that operate on a time can be used with either `Time` or 
+`DateTime` instances or with nteger timestamps (i.e. as returned by 
+`Time#to_i`). The type of the values returned will match the the type passed in.
 
-A list of all the available Timezone identifiers can be obtained using the
-TZInfo::Timezone.all_identifiers method. TZInfo::Timezone.all can be called
-to get an Array of all the TZInfo::Timezone instances.
+A list of all the available timezone identifiers can be obtained using the
+`TZInfo::Timezone.all_identifiers` method. `TZInfo::Timezone.all` can be called
+to get an `Array` of all the `TZInfo::Timezone` instances.
 
 Timezones can also be accessed by country (using an ISO 3166-1 alpha-2 country 
-code). The following code retrieves the TZInfo::Country instance representing 
-the USA (country code 'US') and then gets all the Timezone identifiers used in 
+code). The following code retrieves the `TZInfo::Country` instance representing 
+the USA (country code 'US') and then gets all the timezone identifiers used in 
 the USA.
 
     us = TZInfo::Country.get('US')
     timezones = us.zone_identifiers
   
-The TZInfo::Country#zone_info method provides an additional description and 
-geographic location for each Timezone in a Country.
+The `TZInfo::Country#zone_info` method provides an additional description and 
+geographic location for each timezone in a country.
 
 A list of all the available country codes can be obtained using the
-TZInfo::Country.all_codes method. TZInfo::Country.all can be called to get an 
-Array of all the Country instances.
+`TZInfo::Country.all_codes` method. `TZInfo::Country.all` can be called to get 
+an `Array` of all the `Country` instances.
   
 For further detail, please refer to the API documentation for the 
-TZInfo::Timezone and TZInfo::Country classes.
+`TZInfo::Timezone` and `TZInfo::Country` classes.
 
 
 Thread-Safety
 -------------
 
-The TZInfo::Country and TZInfo::Timezone classes are thread-safe. It is safe to 
-use class and instance methods of TZInfo::Country and TZInfo::Timezone in 
+The `TZInfo::Country` and `TZInfo::Timezone` classes are thread-safe. It is safe
+to use class and instance methods of `TZInfo::Country` and `TZInfo::Timezone` in 
 concurrently executing threads. Instances of both classes can be shared across 
 thread boundaries.
 
diff --git a/Rakefile b/Rakefile
index d513cd5..52a9032 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'rubygems'
 require 'rubygems/package_task'
 require 'fileutils'
@@ -53,7 +31,22 @@ class TZInfoPackageTask < Gem::PackageTask
   end
 end
 
-package_task = TZInfoPackageTask.new(spec) do |pkg|
+def add_signing_key(spec)
+  # Attempt to find the private key and add options to sign the gem if found.
+  private_key_path = File.expand_path(File.join(BASE_DIR, '..', 'key', 'gem-private_key.pem'))
+  
+  if File.exist?(private_key_path)
+    spec = spec.clone
+    spec.signing_key = private_key_path
+    spec.cert_chain = [File.join(BASE_DIR, 'gem-public_cert.pem')]
+  else
+    puts 'WARNING: Private key not found. Not signing gem file.'
+  end
+  
+  spec
+end
+
+package_task = TZInfoPackageTask.new(add_signing_key(spec)) do |pkg|
   pkg.need_zip = true
   pkg.need_tar_gz = true
   pkg.tar_command = '__tar_with_owner__'
@@ -99,6 +92,7 @@ end
 def setup_tests(test_task, type)
   test_task.libs = [File.join(BASE_DIR, 'lib')]
   test_task.pattern = File.join(BASE_DIR, 'test', "ts_all_#{type}.rb")
+  test_task.warning = true
 end
 
 Rake::TestTask.new(:test_ruby) do |t|
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
index 6519188..dc836e4 100644
Binary files a/checksums.yaml.gz and b/checksums.yaml.gz differ
diff --git a/checksums.yaml.gz.sig b/checksums.yaml.gz.sig
index d89f684..5ba77ed 100644
Binary files a/checksums.yaml.gz.sig and b/checksums.yaml.gz.sig differ
diff --git a/data.tar.gz.sig b/data.tar.gz.sig
index d259d07..5f998a8 100644
Binary files a/data.tar.gz.sig and b/data.tar.gz.sig differ
diff --git a/lib/tzinfo.rb b/lib/tzinfo.rb
index c5bbed2..0b64589 100644
--- a/lib/tzinfo.rb
+++ b/lib/tzinfo.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 # Top level module for TZInfo.
 module TZInfo
 end
diff --git a/lib/tzinfo/country.rb b/lib/tzinfo/country.rb
index 30f1704..9c532df 100644
--- a/lib/tzinfo/country.rb
+++ b/lib/tzinfo/country.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'thread_safe'
 
 module TZInfo
@@ -154,7 +132,10 @@ module TZInfo
         
     # Compare two Countries based on their code. Returns -1 if c is less
     # than self, 0 if c is equal to self and +1 if c is greater than self.
+    #
+    # Returns nil if c is not comparable with Country instances.
     def <=>(c)
+      return nil unless c.is_a?(Country)
       code <=> c.code
     end
     
diff --git a/lib/tzinfo/country_index_definition.rb b/lib/tzinfo/country_index_definition.rb
index 11911cd..431790b 100644
--- a/lib/tzinfo/country_index_definition.rb
+++ b/lib/tzinfo/country_index_definition.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # The country index file includes CountryIndexDefinition which provides
   # a country method used to define each country in the index.
diff --git a/lib/tzinfo/country_info.rb b/lib/tzinfo/country_info.rb
index 3b20b49..06c95d6 100644
--- a/lib/tzinfo/country_info.rb
+++ b/lib/tzinfo/country_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo  
   # Represents a country and references to its timezones as returned by a
   # DataSource.
diff --git a/lib/tzinfo/country_timezone.rb b/lib/tzinfo/country_timezone.rb
index 01c8c80..23dac7a 100644
--- a/lib/tzinfo/country_timezone.rb
+++ b/lib/tzinfo/country_timezone.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # A Timezone within a Country. This contains extra information about the
   # Timezone that is specific to the Country (a Timezone could be used by
@@ -33,19 +11,54 @@ module TZInfo
     # Timezone.
     attr_reader :description
     
+    class << self
+      # Creates a new CountryTimezone with a timezone identifier, latitude,
+      # longitude and description. The latitude and longitude are specified as
+      # rationals - a numerator and denominator. For performance reasons, the 
+      # numerators and denominators must be specified in their lowest form.
+      #
+      # For use internally within TZInfo.
+      #
+      # @!visibility private
+      alias :new! :new
+      
+      # Creates a new CountryTimezone with a timezone identifier, latitude,
+      # longitude and description. The latitude and longitude must be specified
+      # as instances of Rational.
+      #
+      # CountryTimezone instances should normally only be constructed when
+      # creating new DataSource implementations.
+      def new(identifier, latitude, longitude, description = nil)
+        super(identifier, latitude, nil, longitude, nil, description)      
+      end
+    end
+    
     # Creates a new CountryTimezone with a timezone identifier, latitude,
     # longitude and description. The latitude and longitude are specified as
     # rationals - a numerator and denominator. For performance reasons, the 
     # numerators and denominators must be specified in their lowest form.
     #
-    # CountryTimezone instances should not normally be constructed manually.
+    # @!visibility private
     def initialize(identifier, latitude_numerator, latitude_denominator, 
                    longitude_numerator, longitude_denominator, description = nil) #:nodoc:
       @identifier = identifier
-      @latitude_numerator = latitude_numerator
-      @latitude_denominator = latitude_denominator
-      @longitude_numerator = longitude_numerator
-      @longitude_denominator = longitude_denominator      
+      
+      if latitude_numerator.kind_of?(Rational)
+        @latitude = latitude_numerator
+      else
+        @latitude = nil
+        @latitude_numerator = latitude_numerator
+        @latitude_denominator = latitude_denominator
+      end
+      
+      if longitude_numerator.kind_of?(Rational)
+        @longitude = longitude_numerator
+      else
+        @longitude = nil
+        @longitude_numerator = longitude_numerator
+        @longitude_denominator = longitude_denominator
+      end
+        
       @description = description
     end
     
@@ -62,7 +75,7 @@ module TZInfo
     
     # The latitude of this timezone in degrees as a Rational.
     def latitude
-      # Thread-safey: It is possible that the value of @latitude may be 
+      # Thread-safety: It is possible that the value of @latitude may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @latitude is only 
       # calculated once.
@@ -71,7 +84,7 @@ module TZInfo
     
     # The longitude of this timezone in degrees as a Rational.
     def longitude
-      # Thread-safey: It is possible that the value of @longitude may be 
+      # Thread-safety: It is possible that the value of @longitude may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @longitude is only 
       # calculated once.
@@ -96,8 +109,10 @@ module TZInfo
     
     # Returns a hash of this CountryTimezone. 
     def hash
-      @identifier.hash ^ @latitude_numerator.hash ^ @latitude_denominator.hash ^ 
-        @longitude_numerator.hash ^ @longitude_denominator.hash ^ @description.hash
+      @identifier.hash ^ 
+        (@latitude ? @latitude.numerator.hash ^ @latitude.denominator.hash : @latitude_numerator.hash ^ @latitude_denominator.hash) ^
+        (@longitude ? @longitude.numerator.hash ^ @longitude.denominator.hash : @longitude_numerator.hash ^ @longitude_denominator.hash) ^
+        @description.hash
     end
     
     # Returns internal object state as a programmer-readable string.
diff --git a/lib/tzinfo/data_source.rb b/lib/tzinfo/data_source.rb
index 8b381c0..0ad0f5b 100644
--- a/lib/tzinfo/data_source.rb
+++ b/lib/tzinfo/data_source.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'thread'
 
 module TZInfo
@@ -81,6 +59,7 @@ module TZInfo
     #   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 
@@ -91,6 +70,11 @@ module TZInfo
     # directory as the data source. If the directory is not a valid zoneinfo
     # directory, an InvalidZoneinfoDirectory exception will be raised.
     #
+    # \DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_tab_file) uses the
+    # specified zoneinfo directory as the data source, but loads the iso3166.tab
+    # file from an alternate path. If the directory is not a valid zoneinfo
+    # directory, an InvalidZoneinfoDirectory exception will be raised.
+    #
     # Custom data sources can be created by subclassing TZInfo::DataSource and
     # implementing the following methods:
     #
@@ -120,7 +104,6 @@ module TZInfo
       elsif data_source_or_type == :ruby
         @@instance = RubyDataSource.new
       elsif data_source_or_type == :zoneinfo
-        raise ArgumentError, "wrong number of arguments #{args.length} for 1" if args.length > 1
         @@instance = ZoneinfoDataSource.new(*args)
       else
         raise ArgumentError, 'data_source_or_type must be a DataSource instance or a data source type (:ruby)'
@@ -196,7 +179,7 @@ module TZInfo
       begin
         return ZoneinfoDataSource.new
       rescue ZoneinfoDirectoryNotFound
-        raise DataSourceNotFound, 'No timezone data source could be found. To resolve this, either install TZInfo::Data (e.g. by running `gem install tzinfo-data`) or specify a zoneinfo directory using `TZInfo::DataSource.set(:zoneinfo, zoneinfo_path)`.'
+        raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to http://tzinfo.github.io/datasourcenotfound for help resolving this error."
       end
     end
   end
diff --git a/lib/tzinfo/data_timezone.rb b/lib/tzinfo/data_timezone.rb
index f735802..94958c9 100644
--- a/lib/tzinfo/data_timezone.rb
+++ b/lib/tzinfo/data_timezone.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
 
   # A Timezone based on a DataTimezoneInfo.
@@ -69,5 +47,12 @@ module TZInfo
     def transitions_up_to(utc_to, utc_from = nil)
       info.transitions_up_to(utc_to, utc_from)
     end
+    
+    # Returns the canonical zone for this Timezone.
+    #
+    # For a DataTimezone, this is always self.
+    def canonical_zone
+      self
+    end
   end
 end
diff --git a/lib/tzinfo/data_timezone_info.rb b/lib/tzinfo/data_timezone_info.rb
index f784b10..083b2db 100644
--- a/lib/tzinfo/data_timezone_info.rb
+++ b/lib/tzinfo/data_timezone_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Represents a defined timezone containing transition data.
   class DataTimezoneInfo < TimezoneInfo  
diff --git a/lib/tzinfo/info_timezone.rb b/lib/tzinfo/info_timezone.rb
index 638ebcc..4eb014b 100644
--- a/lib/tzinfo/info_timezone.rb
+++ b/lib/tzinfo/info_timezone.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
 
   # A Timezone based on a TimezoneInfo.
diff --git a/lib/tzinfo/linked_timezone.rb b/lib/tzinfo/linked_timezone.rb
index 7a62374..c4f4a0d 100644
--- a/lib/tzinfo/linked_timezone.rb
+++ b/lib/tzinfo/linked_timezone.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
 
   # A Timezone based on a LinkedTimezoneInfo.
@@ -69,6 +47,13 @@ module TZInfo
       @linked_timezone.transitions_up_to(utc_to, utc_from)
     end
     
+    # Returns the canonical zone for this Timezone.
+    #
+    # For a LinkedTimezone, this is the canonical zone of the link target.
+    def canonical_zone
+      @linked_timezone.canonical_zone
+    end
+    
     protected
       def setup(info)
         super(info)
diff --git a/lib/tzinfo/linked_timezone_info.rb b/lib/tzinfo/linked_timezone_info.rb
index 8082561..f7961d5 100644
--- a/lib/tzinfo/linked_timezone_info.rb
+++ b/lib/tzinfo/linked_timezone_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Represents a timezone that is defined as a link or alias to another zone.
   class LinkedTimezoneInfo < TimezoneInfo
diff --git a/lib/tzinfo/offset_rationals.rb b/lib/tzinfo/offset_rationals.rb
index bd33941..2a50a08 100644
--- a/lib/tzinfo/offset_rationals.rb
+++ b/lib/tzinfo/offset_rationals.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'rational' unless defined?(Rational)
 
 module TZInfo
diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb
index eb81e2c..c97819e 100644
--- a/lib/tzinfo/ruby_core_support.rb
+++ b/lib/tzinfo/ruby_core_support.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2008-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'date'
 require 'rational' unless defined?(Rational)
 
@@ -150,5 +128,19 @@ module TZInfo
         str
       end
     end
+    
+    
+    # Wrapper for File.open that supports passing hash options for specifying
+    # encodings on Ruby 1.9+. The options are ignored on earlier versions of
+    # Ruby.
+    if RUBY_VERSION =~ /\A1\.[0-8]\./
+      def self.open_file(file_name, mode, opts, &block)
+        File.open(file_name, mode, &block)
+      end
+    else
+      def self.open_file(file_name, mode, opts, &block)
+        File.open(file_name, mode, opts, &block)
+      end
+    end
   end
 end
diff --git a/lib/tzinfo/ruby_country_info.rb b/lib/tzinfo/ruby_country_info.rb
index da0596c..b23fe8a 100644
--- a/lib/tzinfo/ruby_country_info.rb
+++ b/lib/tzinfo/ruby_country_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo  
   # Represents information about a country returned by RubyDataSource.
   #
@@ -38,7 +16,7 @@ module TZInfo
     # Returns a frozen array of all the zone identifiers for the country. These
     # are in the order they were added using the timezone method.
     def zone_identifiers
-      # Thread-safey: It is possible that the value of @zone_identifiers may be 
+      # Thread-safety: It is possible that the value of @zone_identifiers may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @zone_identifiers is only 
       # calculated once.
@@ -54,7 +32,7 @@ module TZInfo
     # CountryTimezone instances. These are in the order they were added using 
     # the timezone method.
     def zones
-      # Thread-safey: It is possible that the value of @zones may be 
+      # Thread-safety: It is possible that the value of @zones may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @zones is only 
       # calculated once.
@@ -83,7 +61,7 @@ module TZInfo
       # Called by the index data to define a timezone for the country.
       def timezone(identifier, latitude_numerator, latitude_denominator, 
                    longitude_numerator, longitude_denominator, description = nil)          
-        @list << CountryTimezone.new(identifier, latitude_numerator, 
+        @list << CountryTimezone.new!(identifier, latitude_numerator, 
           latitude_denominator, longitude_numerator, longitude_denominator,
           description)     
       end
diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb
index fd519e1..8acc2f9 100644
--- a/lib/tzinfo/ruby_data_source.rb
+++ b/lib/tzinfo/ruby_data_source.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # A DataSource that loads data from the set of Ruby modules included in the
   # TZInfo::Data library (tzinfo-data gem).
@@ -59,7 +37,7 @@ module TZInfo
           m = m.const_get(part)
         }
         
-        info = m.get
+        m.get
       rescue LoadError, NameError => e
         raise InvalidTimezoneIdentifier, e.message
       end
diff --git a/lib/tzinfo/time_or_datetime.rb b/lib/tzinfo/time_or_datetime.rb
index aa70253..248d095 100644
--- a/lib/tzinfo/time_or_datetime.rb
+++ b/lib/tzinfo/time_or_datetime.rb
@@ -1,27 +1,5 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'date'
-require 'rational'
+require 'rational' unless defined?(Rational)
 require 'time'
 
 module TZInfo
@@ -48,7 +26,7 @@ module TZInfo
         nsec = RubyCoreSupport.time_nsec(@time)
         usec = nsec % 1000 == 0 ? nsec / 1000 : Rational(nsec, 1000)
         
-        @time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec, usec) unless @time.zone == 'UTC'        
+        @time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec, usec) unless @time.utc?        
         @orig = @time
       elsif timeOrDateTime.is_a?(DateTime)
         @datetime = timeOrDateTime
@@ -70,7 +48,7 @@ module TZInfo
     # When converting from a DateTime, the result is truncated to microsecond
     # precision.
     def to_time
-      # Thread-safey: It is possible that the value of @time may be 
+      # Thread-safety: It is possible that the value of @time may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @time is only 
       # calculated once.
@@ -91,7 +69,7 @@ module TZInfo
     # When converting from a Time, the result is truncated to microsecond
     # precision.
     def to_datetime
-      # Thread-safey: It is possible that the value of @datetime may be 
+      # Thread-safety: It is possible that the value of @datetime may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @datetime is only 
       # calculated once.
@@ -108,7 +86,7 @@ module TZInfo
     
     # Returns the time as an integer timestamp.
     def to_i
-      # Thread-safey: It is possible that the value of @timestamp may be 
+      # Thread-safety: It is possible that the value of @timestamp may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @timestamp is only 
       # calculated once.
@@ -230,11 +208,19 @@ module TZInfo
     # whether the receiver is less than, equal to, or greater than 
     # timeOrDateTime.
     #
+    # Returns nil if the passed in timeOrDateTime is not comparable with 
+    # TimeOrDateTime instances.
+    #
     # Comparisons involving a DateTime will be performed using DateTime#<=>.
     # Comparisons that don't involve a DateTime, but include a Time will be
     # performed with Time#<=>. Otherwise comparisons will be performed with
     # Integer#<=>.    
     def <=>(timeOrDateTime)
+      return nil unless timeOrDateTime.is_a?(TimeOrDateTime) || 
+                        timeOrDateTime.is_a?(Time) ||
+                        timeOrDateTime.is_a?(DateTime) ||
+                        timeOrDateTime.respond_to?(:to_i)
+    
       unless timeOrDateTime.is_a?(TimeOrDateTime)
         timeOrDateTime = TimeOrDateTime.wrap(timeOrDateTime)
       end
diff --git a/lib/tzinfo/timezone.rb b/lib/tzinfo/timezone.rb
index d48e3b5..ee0ee9f 100644
--- a/lib/tzinfo/timezone.rb
+++ b/lib/tzinfo/timezone.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'date'
 require 'set'
 require 'thread_safe'
@@ -327,6 +305,45 @@ module TZInfo
       raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
     end
     
+    # Returns the canonical Timezone instance for this Timezone.
+    #
+    # The IANA Time Zone database contains two types of definition: Zones and 
+    # Links. Zones are defined by rules that set out when transitions occur. 
+    # Links are just references to fully defined Zone, creating an alias for 
+    # that Zone.
+    #
+    # Links are commonly used where a time zone has been renamed in a 
+    # release of the Time Zone database. For example, the Zone US/Eastern was 
+    # renamed as America/New_York. A US/Eastern Link was added in its place,
+    # linking to (and creating an alias for) for America/New_York.
+    #
+    # Links are also used for time zones that are currently identical to a full 
+    # Zone, but that are administered seperately. For example, Europe/Vatican is
+    # a Link to (and alias for) Europe/Rome.
+    #
+    # For a full Zone, canonical_zone returns self.
+    #
+    # For a Link, canonical_zone returns a Timezone instance representing the 
+    # full Zone that the link targets.
+    #
+    # TZInfo can be used with different data sources (see the documentation for
+    # TZInfo::DataSource). Please note that some DataSource implementations may 
+    # not support distinguishing between full Zones and Links and will treat all
+    # time zones as full Zones. In this case, the canonical_zone will always 
+    # return self.
+    #
+    # There are two built-in DataSource implementations. RubyDataSource (which
+    # will be used if the tzinfo-data gem is available) supports Link zones.
+    # ZoneinfoDataSource returns Link zones as if they were full Zones. If the 
+    # canonical_zone or canonical_identifier methods are required, the 
+    # tzinfo-data gem should be installed.
+    #
+    # The TZInfo::DataSource.get method can be used to check which DataSource 
+    # implementation is being used.
+    def canonical_zone
+      raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
+    end
+    
     # Returns the TimezonePeriod for the given local time. local can either be
     # a DateTime, Time or integer timestamp (Time.to_i). Any timezone 
     # information in local is ignored (it is treated as a time in the current 
@@ -514,6 +531,14 @@ module TZInfo
       end
     end
     
+    # Returns the canonical identifier for this Timezone.
+    #
+    # This is a shortcut for calling canonical_zone.identifier. Please refer
+    # to the canonical_zone documentation for further information.
+    def canonical_identifier
+      canonical_zone.identifier
+    end
+    
     # Returns the current time in the timezone as a Time.
     def now
       utc_to_local(Time.now.utc)
@@ -558,10 +583,13 @@ module TZInfo
     
     # Compares 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.
+    #
+    # Returns nil if tz is not comparable with Timezone instances.
     def <=>(tz)
+      return nil unless tz.is_a?(Timezone)
       identifier <=> tz.identifier
     end
-    
+
     # Returns true if and only if the identifier of tz is equal to the 
     # identifier of this Timezone.
     def eql?(tz)
diff --git a/lib/tzinfo/timezone_definition.rb b/lib/tzinfo/timezone_definition.rb
index bb011ea..5ceb686 100644
--- a/lib/tzinfo/timezone_definition.rb
+++ b/lib/tzinfo/timezone_definition.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   
   # TimezoneDefinition is included into Timezone definition modules.
diff --git a/lib/tzinfo/timezone_index_definition.rb b/lib/tzinfo/timezone_index_definition.rb
index a6a3a8a..59dc953 100644
--- a/lib/tzinfo/timezone_index_definition.rb
+++ b/lib/tzinfo/timezone_index_definition.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # The timezone index file includes TimezoneIndexDefinition which provides
   # methods used to define timezones in the index.
diff --git a/lib/tzinfo/timezone_info.rb b/lib/tzinfo/timezone_info.rb
index 5a55fee..2c0e850 100644
--- a/lib/tzinfo/timezone_info.rb
+++ b/lib/tzinfo/timezone_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Represents a timezone defined by a data source.
   class TimezoneInfo
diff --git a/lib/tzinfo/timezone_offset.rb b/lib/tzinfo/timezone_offset.rb
index 4cfc7da..bcf39ab 100644
--- a/lib/tzinfo/timezone_offset.rb
+++ b/lib/tzinfo/timezone_offset.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Represents an offset defined in a Timezone data file.
   class TimezoneOffset
diff --git a/lib/tzinfo/timezone_period.rb b/lib/tzinfo/timezone_period.rb
index 42ca634..50c7824 100644
--- a/lib/tzinfo/timezone_period.rb
+++ b/lib/tzinfo/timezone_period.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # A period of time in a timezone where the same offset from UTC applies.
   #
@@ -87,7 +65,7 @@ module TZInfo
     
     # Total offset from UTC (days). Result is a Rational.
     def utc_total_offset_rational
-      # Thread-safey: It is possible that the value of 
+      # Thread-safety: It is possible that the value of 
       # @utc_total_offset_rational may be calculated multiple times in 
       # concurrently executing threads. It is not worth the overhead of locking
       # to ensure that @zone_identifiers is only calculated once.
diff --git a/lib/tzinfo/timezone_proxy.rb b/lib/tzinfo/timezone_proxy.rb
index 1dbc922..866098d 100644
--- a/lib/tzinfo/timezone_proxy.rb
+++ b/lib/tzinfo/timezone_proxy.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
 
   # A proxy class representing a timezone with a given identifier. TimezoneProxy
@@ -60,6 +38,11 @@ module TZInfo
       real_timezone.periods_for_local(local)
     end
     
+    # Returns the canonical zone for this Timezone.
+    def canonical_zone
+      real_timezone.canonical_zone
+    end
+    
     # Dumps this TimezoneProxy for marshalling.
     def _dump(limit)
       identifier
@@ -77,7 +60,7 @@ module TZInfo
       end  
     
       def real_timezone
-        # Thread-safey: It is possible that the value of @real_timezone may be 
+        # Thread-safety: It is possible that the value of @real_timezone may be 
         # calculated multiple times in concurrently executing threads. It is not 
         # worth the overhead of locking to ensure that @real_timezone is only 
         # calculated once.
diff --git a/lib/tzinfo/timezone_transition.rb b/lib/tzinfo/timezone_transition.rb
index 189565f..84a2fac 100644
--- a/lib/tzinfo/timezone_transition.rb
+++ b/lib/tzinfo/timezone_transition.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Represents a transition from one timezone offset to another at a particular
   # date and time.
@@ -60,7 +38,7 @@ module TZInfo
     # causes the previous observance to end (calculated from at using 
     # previous_offset).
     def local_end_at
-      # Thread-safey: It is possible that the value of @local_end_at may be
+      # Thread-safety: It is possible that the value of @local_end_at may be
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @local_end_at is only
       # calculated once.
@@ -84,7 +62,7 @@ module TZInfo
     # A TimeOrDateTime instance representing the local time when this transition
     # causes the next observance to start (calculated from at using offset).
     def local_start_at
-      # Thread-safey: It is possible that the value of @local_start_at may be
+      # Thread-safety: It is possible that the value of @local_start_at may be
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @local_start_at is only
       # calculated once.
diff --git a/lib/tzinfo/timezone_transition_definition.rb b/lib/tzinfo/timezone_transition_definition.rb
index 52fdf20..26c7da8 100644
--- a/lib/tzinfo/timezone_transition_definition.rb
+++ b/lib/tzinfo/timezone_transition_definition.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # A TimezoneTransition defined by as integer timestamp, as a rational to
   # create a DateTime or as both.
@@ -88,7 +66,7 @@ module TZInfo
     # A TimeOrDateTime instance representing the UTC time when this transition
     # occurs.
     def at
-      # Thread-safey: It is possible that the value of @at may be calculated
+      # Thread-safety: It is possible that the value of @at may be calculated
       # multiple times in concurrently executing threads. It is not worth the
       # overhead of locking to ensure that @at is only calculated once.
       
diff --git a/lib/tzinfo/transition_data_timezone_info.rb b/lib/tzinfo/transition_data_timezone_info.rb
index 940d5ea..026bf22 100644
--- a/lib/tzinfo/transition_data_timezone_info.rb
+++ b/lib/tzinfo/transition_data_timezone_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # Raised if no offsets have been defined when calling period_for_utc or
   # periods_for_local. Indicates an error in the timezone data.
diff --git a/lib/tzinfo/zoneinfo_country_info.rb b/lib/tzinfo/zoneinfo_country_info.rb
index f486cbb..a1230e6 100644
--- a/lib/tzinfo/zoneinfo_country_info.rb
+++ b/lib/tzinfo/zoneinfo_country_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo  
   # Represents information about a country returned by ZoneinfoDataSource.
   #
@@ -36,7 +14,7 @@ module TZInfo
     # Returns a frozen array of all the zone identifiers for the country ordered
     # geographically, most populous first.
     def zone_identifiers
-      # Thread-safey: It is possible that the value of @zone_identifiers may be 
+      # Thread-safety: It is possible that the value of @zone_identifiers may be 
       # calculated multiple times in concurrently executing threads. It is not 
       # worth the overhead of locking to ensure that @zone_identifiers is only 
       # calculated once.
diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb
index 9d1aaf5..b8130c5 100644
--- a/lib/tzinfo/zoneinfo_data_source.rb
+++ b/lib/tzinfo/zoneinfo_data_source.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # An InvalidZoneinfoDirectory exception is raised if the DataSource is
   # set to a specific zoneinfo path, which is not a valid zoneinfo directory
@@ -95,12 +73,19 @@ module TZInfo
   # zoneinfo support, then you may want to consider using TZInfo::RubyDataSource 
   # instead.
   class ZoneinfoDataSource < DataSource
-    # The default value of ZoneInfoDataSource.search_path.
+    # The default value of ZoneinfoDataSource.search_path.
     DEFAULT_SEARCH_PATH = ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].freeze
     
+    # The default value of ZoneinfoDataSource.alternate_iso3166_tab_search_path.
+    DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].freeze
+    
     # Paths to be checked to find the system zoneinfo directory.
     @@search_path = DEFAULT_SEARCH_PATH.dup
     
+    # Paths to possible alternate iso3166.tab files (used to locate the 
+    # system-wide iso3166.tab files on FreeBSD and OpenBSD).
+    @@alternate_iso3166_tab_search_path = DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH.dup
+    
     # An Array of directories that will be checked to find the system zoneinfo
     # directory.
     #
@@ -121,15 +106,31 @@ module TZInfo
     #
     # Set to nil to revert to the default paths.
     def self.search_path=(search_path)
-      if search_path
-        if search_path.kind_of?(String)
-          @@search_path = search_path.split(File::PATH_SEPARATOR)
-        else
-          @@search_path = search_path.collect {|p| p.to_s}
-        end
-      else
-        @@search_path = DEFAULT_SEARCH_PATH.dup
-      end
+      @@search_path = process_search_path(search_path, DEFAULT_SEARCH_PATH)      
+    end
+    
+    # An Array of paths that will be checked to find an alternate iso3166.tab 
+    # file if one was not included in the zoneinfo directory (for example, on 
+    # FreeBSD and OpenBSD systems).
+    #
+    # Paths are checked in the order they appear in the array.
+    #
+    # The default value is ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].
+    def self.alternate_iso3166_tab_search_path
+      @@alternate_iso3166_tab_search_path
+    end
+    
+    # Sets the paths to check to locate an alternate iso3166.tab file if one was
+    # not included in the zoneinfo directory.
+    #
+    # Can be set to an Array of directories or a String containing directories
+    # separated with File::PATH_SEPARATOR.
+    #
+    # Paths are checked in the order they appear in the array.
+    #
+    # Set to nil to revert to the default paths.
+    def self.alternate_iso3166_tab_search_path=(alternate_iso3166_tab_search_path)
+      @@alternate_iso3166_tab_search_path = process_search_path(alternate_iso3166_tab_search_path, DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH)
     end
     
     # The zoneinfo directory being used.
@@ -138,33 +139,50 @@ module TZInfo
     # Creates a new ZoneinfoDataSource.
     #
     # If zoneinfo_dir is specified, it will be checked and used as the source
-    # of zoneinfo files. If the directory does not contain zone.tab and 
-    # iso3166.tab files, InvalidZoneinfoDirectory will be raised.
+    # of zoneinfo files. 
+    #
+    # The directory must contain iso3166.tab and zone.tab files. These may 
+    # either be included in the root of the directory or in a 'tab' 
+    # sub-directory named 'country.tab' and 'zone_sun.tab' respectively (as is
+    # the case on Solaris.
+    #
+    # Additionally, the path to iso3166.tab can be overridden using the 
+    # alternate_iso3166_tab_path parameter.
+    #
+    # InvalidZoneinfoDirectory will be raised if the iso3166.tab and zone.tab
+    # files cannot be found using the zoneinfo_dir and alternate_iso3166_tab_path
+    # parameters.
     # 
     # If zoneinfo_dir is not specified or nil, the paths referenced in
     # search_path are searched in order to find a valid zoneinfo directory 
-    # (one that contains files named zone.tab and iso3166.tab). If no valid 
-    # zoneinfo directory is found ZoneinfoDirectoryNotFound will be raised.
-    def initialize(zoneinfo_dir = nil)
+    # (one that contains zone.tab and iso3166.tab files as above).
+    #
+    # The paths referenced in alternate_iso3166_tab_search_path are also
+    # searched to find an iso3166.tab file if one of the searched zoneinfo
+    # directories doesn't contain an iso3166.tab file.
+    # 
+    # If no valid directory can be found by searching, ZoneinfoDirectoryNotFound
+    # will be raised.
+    def initialize(zoneinfo_dir = nil, alternate_iso3166_tab_path = nil)
       if zoneinfo_dir
-        unless valid_zoneinfo_dir?(zoneinfo_dir)
+        iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(zoneinfo_dir, alternate_iso3166_tab_path)
+      
+        unless iso3166_tab_path && zone_tab_path
           raise InvalidZoneinfoDirectory, "#{zoneinfo_dir} is not a directory or doesn't contain iso3166.tab and zone.tab files." 
         end
+        
         @zoneinfo_dir = zoneinfo_dir
       else
-        @zoneinfo_dir = self.class.search_path.detect do |path|
-          valid_zoneinfo_dir?(path)
-        end
+        @zoneinfo_dir, iso3166_tab_path, zone_tab_path = find_zoneinfo_dir
         
-        unless @zoneinfo_dir
+        unless @zoneinfo_dir && iso3166_tab_path && zone_tab_path
           raise ZoneinfoDirectoryNotFound, "None of the paths included in TZInfo::ZoneinfoDataSource.search_path are valid zoneinfo directories."
         end
       end
       
       @zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze
-      @zoneinfo_prefix = (@zoneinfo_dir + File::SEPARATOR).freeze
       @timezone_index = load_timezone_index.freeze
-      @country_index = load_country_index.freeze
+      @country_index = load_country_index(iso3166_tab_path, zone_tab_path).freeze
     end
     
     # Returns a TimezoneInfo instance for a given identifier. 
@@ -244,10 +262,77 @@ module TZInfo
     
     private
     
-    # Tests whether a path represents a valid zoneinfo directory (i.e.
-    # is a directory and contains zone.tab and iso3166.tab files).
-    def valid_zoneinfo_dir?(path)
-      File.directory?(path) && File.file?(File.join(path, 'zone.tab')) && File.file?(File.join(path, 'iso3166.tab'))
+    # Processes a path for use as the search_path or
+    # alternate_iso3166_tab_search_path.
+    def self.process_search_path(path, default)
+      if path
+        if path.kind_of?(String)
+          path.split(File::PATH_SEPARATOR)
+        else
+          path.collect {|p| p.to_s}
+        end
+      else
+        default.dup
+      end
+    end
+    
+    # Validates a zoneinfo directory and returns the paths to the iso3166.tab 
+    # and zone.tab files if valid. If the directory is not valid, returns nil.
+    #
+    # The path to the iso3166.tab file may be overriden by passing in a path.
+    # This is treated as either absolute or relative to the current working
+    # directory.    
+    def validate_zoneinfo_dir(path, iso3166_tab_path = nil)
+      if File.directory?(path)
+        if iso3166_tab_path
+          return nil unless File.file?(iso3166_tab_path)
+        else
+          iso3166_tab_path = resolve_tab_path(path, 'iso3166.tab', 'country.tab')
+          return nil unless iso3166_tab_path          
+        end
+        
+        zone_tab_path = resolve_tab_path(path, 'zone.tab', 'zone_sun.tab')
+        return nil unless zone_tab_path
+      
+        [iso3166_tab_path, zone_tab_path]
+      else
+        nil
+      end
+    end
+    
+    # Attempts to resolve the path to a tab file given its standard name and
+    # tab sub-directory name (as used on Solaris).
+    def resolve_tab_path(zoneinfo_path, standard_name, tab_name)
+      path = File.join(zoneinfo_path, standard_name)      
+      return path if File.file?(path)
+      
+      path = File.join(zoneinfo_path, 'tab', tab_name)
+      return path if File.file?(path)
+      
+      nil
+    end
+    
+    # Finds a zoneinfo directory using search_path and 
+    # alternate_iso3166_tab_search_path. Returns the paths to the directory,
+    # the iso3166.tab file and the zone.tab file or nil if not found.
+    def find_zoneinfo_dir
+      alternate_iso3166_tab_path = self.class.alternate_iso3166_tab_search_path.detect do |path|
+        File.file?(path)
+      end
+      
+      self.class.search_path.each do |path|
+        # Try without the alternate_iso3166_tab_path first.
+        iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path)        
+        return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
+        
+        if alternate_iso3166_tab_path
+          iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path, alternate_iso3166_tab_path)        
+          return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
+        end
+      end
+      
+      # Not found.
+      nil
     end
        
     # Scans @zoneinfo_dir and returns an Array of available timezone 
@@ -256,12 +341,13 @@ module TZInfo
       index = []
       
       # Ignoring particular files:
-      # +VERSION is included in Mac OS X.
+      # +VERSION is included on Mac OS X.
       # localtime current local timezone (may be a link).
       # posix, posixrules and right are directories containing other versions of the zoneinfo files.
+      # src is a directory containing the tzdata source included on Solaris.
       # Factory is the compiled in default timezone.
       
-      enum_timezones(nil, ['+VERSION', 'localtime', 'posix', 'posixrules', 'right', 'Factory']) do |identifier|
+      enum_timezones(nil, ['+VERSION', 'localtime', 'posix', 'posixrules', 'right', 'src', 'Factory']) do |identifier|
         index << identifier
       end
       
@@ -288,14 +374,31 @@ module TZInfo
     
     # Uses the iso3166.tab and zone.tab files to build an index of the 
     # available countries and their timezones.
-    def load_country_index
-      zones = {}
+    def load_country_index(iso3166_tab_path, zone_tab_path)
+      
+      # Handle standard 3 to 4 column zone.tab files as well as the 4 to 5 
+      # column format used by Solaris.
+      #
+      # On Solaris, an extra column before the comment gives an optional 
+      # linked/alternate timezone identifier (or '-' if not set).
+      #
+      # Additionally, there is a section at the end of the file for timezones
+      # covering regions. These are given lower-case "country" codes. The timezone
+      # identifier column refers to a continent instead of an identifier. These
+      # lines will be ignored by TZInfo.
+      #
+      # Since the last column is optional in both formats, testing for the 
+      # Solaris format is done in two passes. The first pass identifies if there
+      # are any lines using 5 columns.
       
-      File.open(File.join(@zoneinfo_dir, 'zone.tab')) do |file|
+      file_is_5_column = false
+      zone_tab = []
+      
+      RubyCoreSupport.open_file(zone_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
         file.each_line do |line|
           line.chomp!
           
-          if line =~ /\A([A-Z]{2})\t(?:([+\-])(\d{2})(\d{2})([+\-])(\d{3})(\d{2})|([+\-])(\d{2})(\d{2})(\d{2})([+\-])(\d{3})(\d{2})(\d{2}))\t([^\t]+)(?:\t([^\t]+))?\z/
+          if line =~ /\A([A-Z]{2})\t(?:([+\-])(\d{2})(\d{2})([+\-])(\d{3})(\d{2})|([+\-])(\d{2})(\d{2})(\d{2})([+\-])(\d{3})(\d{2})(\d{2}))\t([^\t]+)(?:\t([^\t]+))?(?:\t([^\t]+))?\z/
             code = $1
             
             if $2
@@ -307,22 +410,35 @@ module TZInfo
             end
             
             zone_identifier = $16
-            description = $17
+            column4 = $17
+            column5 = $18
+            
+            file_is_5_column = true if column5
             
-            (zones[code] ||= []) << 
-              CountryTimezone.new(zone_identifier, latitude.numerator, latitude.denominator, 
-                                  longitude.numerator, longitude.denominator, description)
+            zone_tab << [code, zone_identifier, latitude, longitude, column4, column5]
           end
         end
       end
       
+      zones = {}
+      
+      zone_tab.each do |code, zone_identifier, latitude, longitude, column4, column5|
+        description = file_is_5_column ? column5 : column4
+           
+        (zones[code] ||= []) << CountryTimezone.new(zone_identifier, latitude, longitude, description)
+      end
+      
       countries = {}
       
-      File.open(File.join(@zoneinfo_dir, 'iso3166.tab')) do |file|
+      RubyCoreSupport.open_file(iso3166_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
         file.each_line do |line|
           line.chomp!
           
-          if line =~ /\A([A-Z]{2})\t(.+)\z/
+          # Handle both the two column alpha-2 and name format used in the tz 
+          # database as well as the 4 column alpha-2, alpha-3, numeric-3 and 
+          # name format used by FreeBSD and OpenBSD.
+          
+          if line =~ /\A([A-Z]{2})(?:\t[A-Z]{3}\t[0-9]{3})?\t(.+)\z/
             code = $1
             name = $2
             countries[code] = ZoneinfoCountryInfo.new(code, name, zones[code] || [])
@@ -333,7 +449,7 @@ module TZInfo
       countries
     end
     
-    # Converts degrees, miunutes and seconds to a Rational
+    # Converts degrees, minutes and seconds to a Rational.
     def dms_to_rational(sign, degrees, minutes, seconds = nil)
       result = degrees.to_i + Rational(minutes.to_i, 60)
       result += Rational(seconds.to_i, 3600) if seconds
diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb
index fcaed89..ad0af7c 100644
--- a/lib/tzinfo/zoneinfo_timezone_info.rb
+++ b/lib/tzinfo/zoneinfo_timezone_info.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2008-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   # An InvalidZoneinfoFile exception is raised if an attempt is made to load an
   # invalid zoneinfo file.
diff --git a/metadata.gz.sig b/metadata.gz.sig
index 8bd507f..a2585e0 100644
--- a/metadata.gz.sig
+++ b/metadata.gz.sig
@@ -1,3 +1 @@
-
{$����#xP U���ɐfr�h���TT�>h����ƣ�Y�1J���<�u��Q�B�.2h'��M�H�Uf)�L��:�
-���@rQ0������T�b}!���Z� `
3����Ws��/�vQ����2�M	)[˟<�CV��73��z�wY�����D������,�W����%�����θ�<���
-X:�QP��ջ+�Å2�]�f���^k��A���MSm��'?XP�5ԕR�
+%
����xlVJ{��2�"d�r
�%�Co	)�
��o0f��mi���
ѯԾ�1"H�����!jłZ�؀R@εo��-���:DG�dWW�2C��
��	KKl�
��'g�h`�e�
�5n�Ө�~
$��dQ#�	����M�
ύߕ�gC.��{Ɣ���%�i?m�3l�#dQ�Al; �A�_�X�O�]�h���h���6S��r���즼�C��A
�$�y��\h�	�2ڠ�_:~�e����
\ No newline at end of file
diff --git a/metadata.yml b/metadata.yml
index 44801bd..bafca55 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,7 +1,7 @@
 --- !ruby/object:Gem::Specification
 name: tzinfo
 version: !ruby/object:Gem::Version
-  version: 1.1.0
+  version: 1.2.0
 platform: ruby
 authors:
 - Philip Ross
@@ -30,20 +30,20 @@ cert_chain:
   fs7XSYlwQp0zY7PFSMwJeBpQFDBnShcweRQ+0QdUUS4FHrwfXex0QsXp9UROUX+4
   6BVw9ZDNFnDH4PQjZGbdwanB7kzm+TEi
   -----END CERTIFICATE-----
-date: 2013-09-25 00:00:00.000000000 Z
+date: 2014-05-26 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: thread_safe
   requirement: !ruby/object:Gem::Requirement
     requirements:
-    - - ~>
+    - - "~>"
       - !ruby/object:Gem::Version
         version: '0.1'
   type: :runtime
   prerelease: false
   version_requirements: !ruby/object:Gem::Requirement
     requirements:
-    - - ~>
+    - - "~>"
       - !ruby/object:Gem::Version
         version: '0.1'
 description: TZInfo provides daylight savings aware transformations between times
@@ -56,135 +56,135 @@ extra_rdoc_files:
 - CHANGES.md
 - LICENSE
 files:
+- ".yardopts"
 - CHANGES.md
 - LICENSE
-- Rakefile
 - README.md
-- tzinfo.gemspec
-- .yardopts
+- Rakefile
 - lib/tzinfo.rb
-- lib/tzinfo/info_timezone.rb
-- lib/tzinfo/timezone_info.rb
-- lib/tzinfo/data_timezone.rb
-- lib/tzinfo/ruby_data_source.rb
-- lib/tzinfo/timezone_period.rb
-- lib/tzinfo/timezone_transition_definition.rb
+- lib/tzinfo/country.rb
+- lib/tzinfo/country_index_definition.rb
+- lib/tzinfo/country_info.rb
 - lib/tzinfo/country_timezone.rb
-- lib/tzinfo/ruby_country_info.rb
-- lib/tzinfo/linked_timezone.rb
 - lib/tzinfo/data_source.rb
-- lib/tzinfo/country.rb
-- lib/tzinfo/timezone_transition.rb
+- lib/tzinfo/data_timezone.rb
 - lib/tzinfo/data_timezone_info.rb
-- lib/tzinfo/zoneinfo_timezone_info.rb
-- lib/tzinfo/country_index_definition.rb
-- lib/tzinfo/zoneinfo_data_source.rb
-- lib/tzinfo/time_or_datetime.rb
+- lib/tzinfo/info_timezone.rb
+- lib/tzinfo/linked_timezone.rb
+- lib/tzinfo/linked_timezone_info.rb
+- lib/tzinfo/offset_rationals.rb
 - lib/tzinfo/ruby_core_support.rb
-- lib/tzinfo/timezone_definition.rb
+- lib/tzinfo/ruby_country_info.rb
+- lib/tzinfo/ruby_data_source.rb
+- lib/tzinfo/time_or_datetime.rb
 - lib/tzinfo/timezone.rb
-- lib/tzinfo/transition_data_timezone_info.rb
+- lib/tzinfo/timezone_definition.rb
 - lib/tzinfo/timezone_index_definition.rb
-- lib/tzinfo/timezone_proxy.rb
+- lib/tzinfo/timezone_info.rb
 - lib/tzinfo/timezone_offset.rb
-- lib/tzinfo/linked_timezone_info.rb
-- lib/tzinfo/country_info.rb
-- lib/tzinfo/offset_rationals.rb
+- lib/tzinfo/timezone_period.rb
+- lib/tzinfo/timezone_proxy.rb
+- lib/tzinfo/timezone_transition.rb
+- lib/tzinfo/timezone_transition_definition.rb
+- lib/tzinfo/transition_data_timezone_info.rb
 - lib/tzinfo/zoneinfo_country_info.rb
-- test/tc_info_timezone.rb
-- test/tc_timezone_transition_definition.rb
+- lib/tzinfo/zoneinfo_data_source.rb
+- lib/tzinfo/zoneinfo_timezone_info.rb
+- test/tc_country.rb
+- test/tc_country_index_definition.rb
+- test/tc_country_info.rb
+- test/tc_country_timezone.rb
 - test/tc_data_source.rb
-- test/test_utils.rb
-- test/tc_timezone.rb
-- test/tc_zoneinfo_country_info.rb
+- test/tc_data_timezone.rb
 - test/tc_data_timezone_info.rb
-- test/tc_timezone_london.rb
-- test/tc_timezone_index_definition.rb
+- test/tc_info_timezone.rb
+- test/tc_linked_timezone.rb
 - test/tc_linked_timezone_info.rb
-- test/ts_all_ruby.rb
-- test/tc_timezone_definition.rb
-- test/tc_ruby_data_source.rb
-- test/tc_timezone_period.rb
-- test/tc_country_timezone.rb
-- test/tc_timezone_offset.rb
+- test/tc_offset_rationals.rb
 - test/tc_ruby_core_support.rb
-- test/tc_country_index_definition.rb
+- test/tc_ruby_country_info.rb
+- test/tc_ruby_data_source.rb
+- test/tc_time_or_datetime.rb
+- test/tc_timezone.rb
+- test/tc_timezone_definition.rb
+- test/tc_timezone_index_definition.rb
+- test/tc_timezone_info.rb
+- test/tc_timezone_london.rb
 - test/tc_timezone_melbourne.rb
+- test/tc_timezone_new_york.rb
+- test/tc_timezone_offset.rb
+- test/tc_timezone_period.rb
+- test/tc_timezone_proxy.rb
+- test/tc_timezone_transition.rb
+- test/tc_timezone_transition_definition.rb
+- test/tc_timezone_utc.rb
+- test/tc_transition_data_timezone_info.rb
+- test/tc_zoneinfo_country_info.rb
 - test/tc_zoneinfo_data_source.rb
+- test/tc_zoneinfo_timezone_info.rb
+- test/test_utils.rb
+- test/ts_all.rb
+- test/ts_all_ruby.rb
+- test/ts_all_zoneinfo.rb
 - test/tzinfo-data/tzinfo/data.rb
+- test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb
+- test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb
+- test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb
 - test/tzinfo-data/tzinfo/data/definitions/EST.rb
-- test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb
 - test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb
+- test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb
 - test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb
-- test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb
-- test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb
 - test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb
-- test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb
 - test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb
+- test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb
+- test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb
 - test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb
-- test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb
-- test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb
 - test/tzinfo-data/tzinfo/data/definitions/UTC.rb
-- test/tzinfo-data/tzinfo/data/version.rb
-- test/tzinfo-data/tzinfo/data/indexes/timezones.rb
 - test/tzinfo-data/tzinfo/data/indexes/countries.rb
-- test/tc_time_or_datetime.rb
-- test/tc_country.rb
-- test/tc_zoneinfo_timezone_info.rb
-- test/tc_offset_rationals.rb
-- test/ts_all_zoneinfo.rb
-- test/tc_ruby_country_info.rb
-- test/tc_transition_data_timezone_info.rb
-- test/tc_timezone_utc.rb
-- test/tc_timezone_info.rb
-- test/ts_all.rb
-- test/tc_timezone_new_york.rb
-- test/tc_timezone_proxy.rb
-- test/tc_linked_timezone.rb
-- test/tc_timezone_transition.rb
-- test/tc_data_timezone.rb
-- test/tc_country_info.rb
-- test/zoneinfo/right/Europe/London
+- test/tzinfo-data/tzinfo/data/indexes/timezones.rb
+- test/tzinfo-data/tzinfo/data/version.rb
+- test/zoneinfo/America/Argentina/Buenos_Aires
+- test/zoneinfo/America/New_York
+- test/zoneinfo/Australia/Melbourne
 - test/zoneinfo/EST
 - test/zoneinfo/Etc/UTC
-- test/zoneinfo/Australia/Melbourne
-- test/zoneinfo/zone.tab
-- test/zoneinfo/Europe/Prague
+- test/zoneinfo/Europe/Amsterdam
 - test/zoneinfo/Europe/Andorra
-- test/zoneinfo/Europe/Paris
 - test/zoneinfo/Europe/London
-- test/zoneinfo/Europe/Amsterdam
+- test/zoneinfo/Europe/Paris
+- test/zoneinfo/Europe/Prague
 - test/zoneinfo/Factory
-- test/zoneinfo/America/Argentina/Buenos_Aires
-- test/zoneinfo/America/New_York
-- test/zoneinfo/posix/Europe/London
 - test/zoneinfo/iso3166.tab
+- test/zoneinfo/posix/Europe/London
 - test/zoneinfo/posixrules
+- test/zoneinfo/right/Europe/London
+- test/zoneinfo/zone.tab
+- tzinfo.gemspec
 homepage: http://tzinfo.github.io
 licenses:
 - MIT
 metadata: {}
 post_install_message: 
 rdoc_options:
-- --title
+- "--title"
 - TZInfo
-- --main
+- "--main"
 - README.md
 require_paths:
 - lib
 required_ruby_version: !ruby/object:Gem::Requirement
   requirements:
-  - - '>='
+  - - ">="
     - !ruby/object:Gem::Version
-      version: 1.8.6
+      version: 1.8.7
 required_rubygems_version: !ruby/object:Gem::Requirement
   requirements:
-  - - '>='
+  - - ">="
     - !ruby/object:Gem::Version
       version: '0'
 requirements: []
 rubyforge_project: 
-rubygems_version: 2.1.4
+rubygems_version: 2.2.2
 signing_key: 
 specification_version: 4
 summary: Daylight savings aware timezone library
diff --git a/test/tc_country.rb b/test/tc_country.rb
index 0b6dc5b..e5c990f 100644
--- a/test/tc_country.rb
+++ b/test/tc_country.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCCountry < Test::Unit::TestCase
+class TCCountry < Minitest::Test
   def setup
     @orig_data_source = DataSource.get
     Country.send :init_countries
@@ -105,7 +83,7 @@ class TCCountry < Test::Unit::TestCase
     
   def test_new_nil
     assert_raises(InvalidCountryCode) {
-      c = Country.new(nil)
+      Country.new(nil)
     }        
   end
   
@@ -180,6 +158,10 @@ class TCCountry < Test::Unit::TestCase
     assert_equal(1, Country.get('US') <=> Country.get('FR'))
   end
   
+  def test_compare_non_comparable
+    assert_nil(Country.get('GB') <=> Object.new)
+  end
+  
   def test_equality
     assert_equal(true, Country.get('GB') == Country.get('GB'))
     assert_equal(false, Country.get('GB') == Country.get('US'))
diff --git a/test/tc_country_index_definition.rb b/test/tc_country_index_definition.rb
index bedf843..16f76e0 100644
--- a/test/tc_country_index_definition.rb
+++ b/test/tc_country_index_definition.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCCountryIndexDefinition < Test::Unit::TestCase
+class TCCountryIndexDefinition < Minitest::Test
 
   module CountriesTest1     
     include CountryIndexDefinition
diff --git a/test/tc_country_info.rb b/test/tc_country_info.rb
index a954c8f..12002d9 100644
--- a/test/tc_country_info.rb
+++ b/test/tc_country_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCCountryInfo < Test::Unit::TestCase
+class TCCountryInfo < Minitest::Test
   
   def test_code
     ci = CountryInfo.new('ZZ', 'Zzz') {|c| }
diff --git a/test/tc_country_timezone.rb b/test/tc_country_timezone.rb
index 5a48ede..1bf85bf 100644
--- a/test/tc_country_timezone.rb
+++ b/test/tc_country_timezone.rb
@@ -1,139 +1,159 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCCountryTimezone < Test::Unit::TestCase
-  def test_identifier
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)    
+class TCCountryTimezone < Minitest::Test
+  def test_identifier_new!
+    ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)    
     assert_equal('Europe/London', ct.identifier)
-  end    
+  end
   
-  def test_latitude
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+  def test_identifier_new
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))    
+    assert_equal('Europe/London', ct.identifier)
+  end
+  
+  def test_latitude_new!
+    ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
     assert_equal(Rational(2059, 40), ct.latitude)
   end
   
-  def test_longitude
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+  def test_latitude_new
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
+    assert_equal(Rational(2059, 40), ct.latitude)
+  end
+  
+  def test_longitude_new!
+    ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    assert_equal(Rational(-5, 16), ct.longitude)
+  end
+  
+  def test_longitude_new
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     assert_equal(Rational(-5, 16), ct.longitude)
   end
   
-  def test_description_omit
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+  def test_description_omit_new!
+    ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
     assert_nil(ct.description)
   end
   
-  def test_description_nil
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16, nil)
+  def test_description_omit_new
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     assert_nil(ct.description)
   end
   
-  def test_description
-    ct = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+  def test_description_nil_new!
+    ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16, nil)
+    assert_nil(ct.description)
+  end
+  
+  def test_description_nil_new
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16), nil)
+    assert_nil(ct.description)
+  end
+  
+  def test_description_new!
+    ct = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+    assert_equal('Eastern Time', ct.description)
+  end
+  
+  def test_description_new
+    ct = CountryTimezone.new('America/New_York', Rational(48857, 1200), Rational(-266423, 3600), 'Eastern Time')
     assert_equal('Eastern Time', ct.description)
   end
   
   def test_timezone    
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     assert_kind_of(TimezoneProxy, ct.timezone)
     assert_equal('Europe/London', ct.timezone.identifier)
   end
   
   def test_description_or_friendly_idenfier_no_description
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     assert_equal('London', ct.description_or_friendly_identifier)
   end
   
   def test_description_or_friendly_idenfier_description
-    ct = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+    ct = CountryTimezone.new('America/New_York', Rational(48857, 1200), Rational(-266423, 3600), 'Eastern Time')
     assert_equal('Eastern Time', ct.description_or_friendly_identifier)
   end
   
   def test_equality_1
-    ct1 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
-    ct2 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
-    ct3 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16, 'Description')
-    ct4 = CountryTimezone.new('Europe/LondonB', 2059, 40, -5, 16)
-    ct5 = CountryTimezone.new('Europe/London', 2060, 40, -5, 16)
-    ct6 = CountryTimezone.new('Europe/London', 2059, 40, -6, 16)
+    ct1 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    ct2 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    ct3 = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
+    ct4 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16, 'Description')
+    ct5 = CountryTimezone.new!('Europe/LondonB', 2059, 40, -5, 16)
+    ct6 = CountryTimezone.new!('Europe/London', 2060, 40, -5, 16)
+    ct7 = CountryTimezone.new!('Europe/London', 2059, 40, -6, 16)
     
     assert_equal(true, ct1 == ct1)
     assert_equal(true, ct1 == ct2)
-    assert_equal(false, ct1 == ct3)
+    assert_equal(true, ct1 == ct3)
     assert_equal(false, ct1 == ct4)
     assert_equal(false, ct1 == ct5)
     assert_equal(false, ct1 == ct6)
+    assert_equal(false, ct1 == ct7)
   end
   
   def test_equality_2
-    ct1 = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
-    ct2 = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time2')
+    ct1 = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+    ct2 = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time2')
     
     assert_equal(true, ct1 == ct1)
     assert_equal(false, ct1 == ct2)    
   end
   
   def test_equality_non_country_timezone
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     
     assert_equal(false, ct == Object.new)
   end
   
   def test_eql_1
-    ct1 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
-    ct2 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
-    ct3 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16, 'Description')
-    ct4 = CountryTimezone.new('Europe/LondonB', 2059, 40, -5, 16)
-    ct5 = CountryTimezone.new('Europe/London', 2060, 40, -5, 16)
-    ct6 = CountryTimezone.new('Europe/London', 2059, 40, -6, 16)
+    ct1 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    ct2 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    ct3 = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
+    ct4 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16, 'Description')
+    ct5 = CountryTimezone.new!('Europe/LondonB', 2059, 40, -5, 16)
+    ct6 = CountryTimezone.new!('Europe/London', 2060, 40, -5, 16)
+    ct7 = CountryTimezone.new!('Europe/London', 2059, 40, -6, 16)
     
     assert_equal(true, ct1.eql?(ct1))
     assert_equal(true, ct1.eql?(ct2))
-    assert_equal(false, ct1.eql?(ct3))
+    assert_equal(true, ct1.eql?(ct3))
     assert_equal(false, ct1.eql?(ct4))
     assert_equal(false, ct1.eql?(ct5))
     assert_equal(false, ct1.eql?(ct6))
+    assert_equal(false, ct1.eql?(ct7))
   end
   
   def test_eql_2
-    ct1 = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
-    ct2 = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time2')
+    ct1 = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+    ct2 = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time2')
     
     assert_equal(true, ct1.eql?(ct1))
     assert_equal(false, ct1.eql?(ct2))    
   end
   
   def test_eql_non_country_timezone
-    ct = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
+    ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
     
     assert_equal(false, ct.eql?(Object.new))
   end
   
-  def test_hash
-    ct1 = CountryTimezone.new('Europe/London', 2059, 40, -5, 16)
-    ct2 = CountryTimezone.new('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+  def test_hash_new!
+    ct1 = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
+    ct2 = CountryTimezone.new!('America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time')
+    
+    assert_equal('Europe/London'.hash ^ 2059.hash ^ 40.hash ^ -5.hash ^ 16.hash ^ nil.hash, ct1.hash)
+    assert_equal('America/New_York'.hash ^ 48857.hash ^ 1200.hash ^ -266423.hash ^ 3600.hash ^ 'Eastern Time'.hash, ct2.hash)
+  end
+  
+  def test_hash_new
+    ct1 = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
+    ct2 = CountryTimezone.new('America/New_York', Rational(48857, 1200), Rational(-266423, 3600), 'Eastern Time')
     
     assert_equal('Europe/London'.hash ^ 2059.hash ^ 40.hash ^ -5.hash ^ 16.hash ^ nil.hash, ct1.hash)
     assert_equal('America/New_York'.hash ^ 48857.hash ^ 1200.hash ^ -266423.hash ^ 3600.hash ^ 'Eastern Time'.hash, ct2.hash)
diff --git a/test/tc_data_source.rb b/test/tc_data_source.rb
index 511ba31..15d2fb7 100644
--- a/test/tc_data_source.rb
+++ b/test/tc_data_source.rb
@@ -1,31 +1,9 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 require 'tmpdir'
 
 include TZInfo
 
-class TCDataSource < Test::Unit::TestCase
+class TCDataSource < Minitest::Test
   class InitDataSource < DataSource
   end
   
@@ -162,6 +140,26 @@ class TCDataSource < Test::Unit::TestCase
     end
   end
   
+  def test_set_standard_zoneinfo_explicit_alternate_iso3166
+    Dir.mktmpdir('tzinfo_test_dir') do |dir|
+      zoneinfo_dir = File.join(dir, 'zoneinfo')
+      tab_dir = File.join(dir, 'tab')
+      
+      FileUtils.mkdir(zoneinfo_dir)
+      FileUtils.mkdir(tab_dir)
+    
+      FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
+      
+      iso3166_file = File.join(tab_dir, 'iso3166.tab')
+      FileUtils.touch(iso3166_file)
+      
+      DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_file)
+      data_source = DataSource.get
+      assert_kind_of(ZoneinfoDataSource, data_source)
+      assert_equal(zoneinfo_dir, data_source.zoneinfo_dir)
+    end
+  end
+  
   def test_set_standard_zoneinfo_search_not_found
     Dir.mktmpdir('tzinfo_test_dir') do |dir|
       ZoneinfoDataSource.search_path = [dir]
@@ -186,7 +184,7 @@ class TCDataSource < Test::Unit::TestCase
   
   def test_set_standard_zoneinfo_wrong_arg_count
     assert_raises(ArgumentError) do
-      DataSource.set(:zoneinfo, 1, 2)
+      DataSource.set(:zoneinfo, 1, 2, 3)
     end
     
     assert_kind_of(InitDataSource, DataSource.get)
diff --git a/test/tc_data_timezone.rb b/test/tc_data_timezone.rb
index e4d71b7..15ee27b 100644
--- a/test/tc_data_timezone.rb
+++ b/test/tc_data_timezone.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCDataTimezone < Test::Unit::TestCase
+class TCDataTimezone < Minitest::Test
   
   class TestTimezoneInfo < TimezoneInfo
     attr_reader :utc
@@ -107,5 +85,15 @@ class TCDataTimezone < Test::Unit::TestCase
     assert_same(transitions, tz.transitions_up_to(utc_to, utc_from))
     assert_same(utc_to, tti.utc_to)
     assert_same(utc_from, tti.utc_from)
-  end    
+  end
+  
+  def test_canonical_identifier
+    tz = DataTimezone.new(TestTimezoneInfo.new('Test/Zone', nil, [], []))
+    assert_equal('Test/Zone', tz.canonical_identifier)    
+  end
+  
+  def test_canonical_zone
+    tz = DataTimezone.new(TestTimezoneInfo.new('Test/Zone', nil, [], []))
+    assert_same(tz, tz.canonical_zone)
+  end
 end
diff --git a/test/tc_data_timezone_info.rb b/test/tc_data_timezone_info.rb
index aac5e48..1b9d144 100644
--- a/test/tc_data_timezone_info.rb
+++ b/test/tc_data_timezone_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCDataTimezoneInfo < Test::Unit::TestCase
+class TCDataTimezoneInfo < Minitest::Test
   
   def test_identifier
     ti = DataTimezoneInfo.new('Test/Zone')
diff --git a/test/tc_info_timezone.rb b/test/tc_info_timezone.rb
index deb5521..c70a098 100644
--- a/test/tc_info_timezone.rb
+++ b/test/tc_info_timezone.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCInfoTimezone < Test::Unit::TestCase
+class TCInfoTimezone < Minitest::Test
   
   class TestInfoTimezone < InfoTimezone
     attr_reader :setup_info
diff --git a/test/tc_linked_timezone.rb b/test/tc_linked_timezone.rb
index 560e508..5da9303 100644
--- a/test/tc_linked_timezone.rb
+++ b/test/tc_linked_timezone.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCLinkedTimezone < Test::Unit::TestCase
+class TCLinkedTimezone < Minitest::Test
   
   class TestTimezone < Timezone
     attr_reader :utc_period
@@ -62,6 +40,10 @@ class TCLinkedTimezone < Test::Unit::TestCase
       @up_to_transitions
     end
     
+    def canonical_zone
+      self
+    end
+    
     private
       def setup(identifier, no_local_periods)
         @identifier = identifier
@@ -84,7 +66,10 @@ class TCLinkedTimezone < Test::Unit::TestCase
         raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier == 'Invalid/Identifier'
        
         @timezones ||= {}
-        @timezones[identifier] ||= TestTimezone.new(identifier, identifier == 'Test/No/Local')        
+        @timezones[identifier] ||= 
+          identifier == 'Test/Recursive/Linked' ? 
+            LinkedTimezone.new(LinkedTimezoneInfo.new(identifier, 'Test/Recursive/Data')) :
+            TestTimezone.new(identifier, identifier == 'Test/No/Local')
       end
     end
   end
@@ -139,4 +124,32 @@ class TCLinkedTimezone < Test::Unit::TestCase
     assert_same(utc_to, linked_tz.utc_to)
     assert_same(utc_from, linked_tz.utc_from)
   end
+  
+  def test_canonical_identifier
+    tz = LinkedTimezone.new(LinkedTimezoneInfo.new('Test/Zone', 'Test/Linked'))
+    assert_equal('Test/Linked', tz.canonical_identifier)
+  end
+  
+  def test_canonical_identifier_recursive
+    # Recursive links are not currently used in the Time Zone database, but 
+    # will be supported by TZInfo.
+  
+    tz = LinkedTimezone.new(LinkedTimezoneInfo.new('Test/Zone', 'Test/Recursive/Linked'))
+    assert_equal('Test/Recursive/Data', tz.canonical_identifier)
+  end
+  
+  def test_canonical_zone
+    tz = LinkedTimezone.new(LinkedTimezoneInfo.new('Test/Zone', 'Test/Linked'))
+    linked_tz = Timezone.get('Test/Linked')
+    assert_same(linked_tz, tz.canonical_zone)
+  end
+  
+  def test_canonical_zone_recursive
+    # Recursive links are not currently used in the Time Zone database, but 
+    # will be supported by TZInfo.
+  
+    tz = LinkedTimezone.new(LinkedTimezoneInfo.new('Test/Zone', 'Test/Recursive/Linked'))
+    linked_tz = Timezone.get('Test/Recursive/Data')
+    assert_same(linked_tz, tz.canonical_zone)
+  end
 end
diff --git a/test/tc_linked_timezone_info.rb b/test/tc_linked_timezone_info.rb
index fdc48b0..4e55f4d 100644
--- a/test/tc_linked_timezone_info.rb
+++ b/test/tc_linked_timezone_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCLinkedTimezoneInfo < Test::Unit::TestCase
+class TCLinkedTimezoneInfo < Minitest::Test
   
   def test_identifier
     lti = LinkedTimezoneInfo.new('Test/Zone', 'Test/Linked')
diff --git a/test/tc_offset_rationals.rb b/test/tc_offset_rationals.rb
index a787585..2881fc7 100644
--- a/test/tc_offset_rationals.rb
+++ b/test/tc_offset_rationals.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCOffsetRationals < Test::Unit::TestCase
+class TCOffsetRationals < Minitest::Test
   def test_rational_for_offset
     [0,1,2,3,4,-1,-2,-3,-4,30*60,-30*60,61*60,-61*60,14*60*60,-14*60*60,20*60*60,-20*60*60].each {|seconds|
       assert_equal(Rational(seconds, 86400), OffsetRationals.rational_for_offset(seconds))      
diff --git a/test/tc_ruby_core_support.rb b/test/tc_ruby_core_support.rb
index e59608f..56c5a8a 100644
--- a/test/tc_ruby_core_support.rb
+++ b/test/tc_ruby_core_support.rb
@@ -1,62 +1,40 @@
 # encoding: UTF-8
 
-#--
-# Copyright (c) 2008-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCRubyCoreSupport < Test::Unit::TestCase
+class TCRubyCoreSupport < Minitest::Test
   def test_rational_new!
     assert_equal(Rational(3,4), RubyCoreSupport.rational_new!(3,4))
   end
   
   def test_datetime_new!
     assert_equal(DateTime.new(2008,10,5,12,0,0, 0, Date::ITALY), RubyCoreSupport.datetime_new!(2454745,0,2299161))
-    assert_equal(DateTime.new(2008,10,6,12,0,0, 1, Date::ITALY), RubyCoreSupport.datetime_new!(2454745,1,2299161))
+    assert_equal(DateTime.new(2008,10,5,13,0,0, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(2454745,Rational(1, 24),2299161))
     
     assert_equal(DateTime.new(2008,10,5,20,30,0, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(117827777, 48), 0, 2299161))
-    assert_equal(DateTime.new(2008,10,6,20,30,0, 1, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(117827777, 48), 1, 2299161))
+    assert_equal(DateTime.new(2008,10,5,21,30,0, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(Rational(117827777, 48), Rational(1, 24), 2299161))
     
     assert_equal(DateTime.new(2008,10,6,6,26,21, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(70696678127,28800), 0, 2299161))
-    assert_equal(DateTime.new(2008,10,7,6,26,21, 1, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(70696678127, 28800), 1, 2299161))
+    assert_equal(DateTime.new(2008,10,6,7,26,21, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(Rational(70696678127, 28800), Rational(1, 24), 2299161))
     
     assert_equal(DateTime.new(-4712,1,1,12,0,0, 0, Date::ITALY), RubyCoreSupport.datetime_new!(0, 0, 2299161))
-    assert_equal(DateTime.new(-4712,1,2,12,0,0, 1, Date::ITALY), RubyCoreSupport.datetime_new!(0, 1, 2299161))
+    assert_equal(DateTime.new(-4712,1,1,13,0,0, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(0, Rational(1, 24), 2299161))
     
-    assert_equal(DateTime.new(-4713,12,31,10,58,59, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-90061, 86400), 0, 2299161))
-    assert_equal(DateTime.new(-4712,1,1,10,58,59, 1, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-90061, 86400), 1, 2299161))
+    assert_equal(DateTime.new(-4713,12,31,23,58,59, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-43261, 86400), 0, 2299161))
+    assert_equal(DateTime.new(-4712,1,1,0,58,59, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-43261, 86400), Rational(1, 24), 2299161))
     
-    assert_equal(DateTime.new(-4713,12,30,10,58,59, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-176461, 86400), 0, 2299161))
-    assert_equal(DateTime.new(-4713,12,31,10,58,59, 1, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-176461, 86400), 1, 2299161))
+    assert_equal(DateTime.new(-4713,12,30,23,58,59, 0, Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-129661, 86400), 0, 2299161))
+    assert_equal(DateTime.new(-4713,12,31,0,58,59, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new!(Rational(-129661, 86400), Rational(1, 24), 2299161))
   end
   
   def test_datetime_new
     assert_equal(DateTime.new(2012, 12, 31, 23, 59, 59, 0, Date::ITALY), RubyCoreSupport.datetime_new(2012, 12, 31, 23, 59, 59, 0, Date::ITALY))
-    assert_equal(DateTime.new(2013, 2, 6, 23, 2, 36, 1, Date::ITALY), RubyCoreSupport.datetime_new(2013, 2, 6, 23, 2, 36, 1, Date::ITALY))
+    assert_equal(DateTime.new(2013, 2, 6, 23, 2, 36, Rational(1, 24), Date::ITALY), RubyCoreSupport.datetime_new(2013, 2, 6, 23, 2, 36, Rational(1,24), Date::ITALY))
     
     assert_equal(DateTime.new(2012, 12, 31, 23, 59, 59, 0, Date::ITALY) + Rational(1, 86400000), RubyCoreSupport.datetime_new(2012, 12, 31, 23, 59, 59 + Rational(1, 1000), 0, Date::ITALY))
-    assert_equal(DateTime.new(2001, 10, 12, 12, 22, 59, 1, Date::ITALY) + Rational(501, 86400000), RubyCoreSupport.datetime_new(2001, 10, 12, 12, 22, 59 + Rational(501, 1000), 1, Date::ITALY))
+    assert_equal(DateTime.new(2001, 10, 12, 12, 22, 59, Rational(1, 24), Date::ITALY) + Rational(501, 86400000), RubyCoreSupport.datetime_new(2001, 10, 12, 12, 22, 59 + Rational(501, 1000), Rational(1, 24), Date::ITALY))
   end
   
   def test_time_supports_negative
@@ -114,4 +92,77 @@ class TCRubyCoreSupport < Test::Unit::TestCase
       assert_equal('©', s)
     end
   end
+  
+  begin
+    SUPPORTS_ENCODING = !!Encoding
+  rescue NameError
+    SUPPORTS_ENCODING = false
+  end
+
+  def check_open_file_test_file_bytes(test_file)
+    if SUPPORTS_ENCODING
+      File.open(test_file, 'r') do |file|
+        file.binmode
+        data = file.read(2)
+        refute_nil(data)
+        assert_equal(2, data.length)
+        bytes = data.unpack('C2')
+        assert_equal(0xC2, bytes[0])
+        assert_equal(0xA9, bytes[1])
+      end
+    end
+  end
+  
+  def check_open_file_test_file_content(file)
+    content = file.gets
+    refute_nil(content)
+    content.chomp!
+    
+    if SUPPORTS_ENCODING            
+      assert_equal('UTF-8', content.encoding.name)
+      assert_equal(1, content.length)
+      assert_equal(2, content.bytesize) 
+      assert_equal('©', content)
+    else
+      assert_equal('x', content)
+    end
+  end
+
+  def test_open_file
+    Dir.mktmpdir('tzinfo_test') do |dir|          
+      test_file = File.join(dir, 'test.txt')
+    
+      file = RubyCoreSupport.open_file(test_file, 'w', :external_encoding => 'UTF-8')
+      begin        
+        file.puts(SUPPORTS_ENCODING ? '©' : 'x')
+      ensure
+        file.close
+      end
+      
+      check_open_file_test_file_bytes(test_file)
+      
+      file = RubyCoreSupport.open_file(test_file, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8')
+      begin
+        check_open_file_test_file_content(file)
+      ensure
+        file.close
+      end
+    end
+  end
+    
+  def test_open_file_block
+    Dir.mktmpdir('tzinfo_test') do |dir|          
+      test_file = File.join(dir, 'test.txt')
+    
+      RubyCoreSupport.open_file(test_file, 'w', :external_encoding => 'UTF-8') do |file|
+        file.puts(SUPPORTS_ENCODING ? '©' : 'x')
+      end
+      
+      check_open_file_test_file_bytes(test_file)
+      
+      RubyCoreSupport.open_file(test_file, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
+        check_open_file_test_file_content(file)
+      end
+    end
+  end
 end
diff --git a/test/tc_ruby_country_info.rb b/test/tc_ruby_country_info.rb
index 39ae029..7893f79 100644
--- a/test/tc_ruby_country_info.rb
+++ b/test/tc_ruby_country_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCRubyCountryInfo < Test::Unit::TestCase
+class TCRubyCountryInfo < Minitest::Test
   
   def test_code
     ci = RubyCountryInfo.new('ZZ', 'Zzz') {|c| }
@@ -80,10 +58,10 @@ class TCRubyCountryInfo < Test::Unit::TestCase
       c.timezone('ZZ/TimezoneD', -10, 3, -20, 7)
     end
     
-    assert_equal([CountryTimezone.new('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
-      CountryTimezone.new('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
-      CountryTimezone.new('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
-      CountryTimezone.new('ZZ/TimezoneD', -10, 3, -20, 7)],
+    assert_equal([CountryTimezone.new!('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
+      CountryTimezone.new!('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
+      CountryTimezone.new!('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
+      CountryTimezone.new!('ZZ/TimezoneD', -10, 3, -20, 7)],
       ci.zones)
     assert(ci.zones.frozen?)
   end
diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb
index 9104449..173d05c 100644
--- a/test/tc_ruby_data_source.rb
+++ b/test/tc_ruby_data_source.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCRubyDataSource < Test::Unit::TestCase
+class TCRubyDataSource < Minitest::Test
   def setup
     @data_source = RubyDataSource.new
   end
diff --git a/test/tc_time_or_datetime.rb b/test/tc_time_or_datetime.rb
index 4e21877..05f574d 100644
--- a/test/tc_time_or_datetime.rb
+++ b/test/tc_time_or_datetime.rb
@@ -1,31 +1,9 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
-require 'rational'
+require 'rational' unless defined?(Rational)
 
 include TZInfo
 
-class TCTimeOrDateTime < Test::Unit::TestCase
+class TCTimeOrDateTime < Minitest::Test
   def test_initialize_time
     assert_nothing_raised do
       TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000))
@@ -37,6 +15,7 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(Time.utc(2006, 3, 24, 15, 32, 3), tdt.to_time)
     assert_equal(Time.utc(2006, 3, 24, 15, 32, 3), tdt.to_orig)
     assert(tdt.to_time.utc?)
+    assert(tdt.to_orig.utc?)
   end
   
   def test_intialize_time_local_usec
@@ -44,6 +23,7 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(Time.utc(2006, 3, 24, 15, 32, 3, 721123), tdt.to_time)
     assert_equal(Time.utc(2006, 3, 24, 15, 32, 3, 721123), tdt.to_orig)
     assert(tdt.to_time.utc?)
+    assert(tdt.to_orig.utc?)
   end
   
   if Time.utc(2013, 1, 1).respond_to?(:nsec)
@@ -52,6 +32,28 @@ class TCTimeOrDateTime < Test::Unit::TestCase
       assert_equal(Time.utc(2006, 3, 24, 15, 32, 3, 721123 + Rational(456,1000)), tdt.to_time)
       assert_equal(Time.utc(2006, 3, 24, 15, 32, 3, 721123 + Rational(456,1000)), tdt.to_orig)
       assert(tdt.to_time.utc?)
+      assert(tdt.to_orig.utc?)
+    end
+  end
+  
+  def test_initialize_time_utc_local
+    # Check that local Time instances on systems using UTC as the system 
+    # time zone are still converted to UTC Time instances.
+    
+    # Note that this will only test will only work correctly on platforms where
+    # setting the TZ environment variable has an effect. If setting TZ has no
+    # effect, then this test will still pass.
+    
+    old_tz = ENV['TZ']
+    begin
+      ENV['TZ'] = 'UTC'
+      tdt = TimeOrDateTime.new(Time.local(2014, 1, 11, 17, 18, 41))
+      assert_equal(Time.utc(2014, 1, 11, 17, 18, 41), tdt.to_time)
+      assert_equal(Time.utc(2014, 1, 11, 17, 18, 41), tdt.to_orig)
+      assert(tdt.to_time.utc?)
+      assert(tdt.to_orig.utc?)
+    ensure
+      ENV['TZ'] = old_tz
     end
   end
   
@@ -281,9 +283,9 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500001)))
     assert_equal(0, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)))
     assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 499999)))
-    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500001)))
+    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000 + DATETIME_RESOLUTION)))
     assert_equal(0, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)))
-    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 499999)))
+    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000 - DATETIME_RESOLUTION)))
   end
   
   def test_compare_timeordatetime_datetime
@@ -305,12 +307,12 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(1, TimeOrDateTime.new(1143214323) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 2)))
     assert_equal(1, TimeOrDateTime.new(1143214323) <=> TimeOrDateTime.new(DateTime.new(1960, 3, 24, 15, 32, 3)))
     
-    assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500001, 1000000))))
+    assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 + DATETIME_RESOLUTION, 1000000))))
     assert_equal(0, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))))
-    assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(499999, 1000000))))
-    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500001, 1000000))))
+    assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 - DATETIME_RESOLUTION, 1000000))))
+    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 + DATETIME_RESOLUTION, 1000000))))
     assert_equal(0, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))))
-    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(499999, 1000000))))
+    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 - DATETIME_RESOLUTION, 1000000))))
   end
   
   def test_compare_timeordatetime_timestamp
@@ -355,9 +357,9 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500001))
     assert_equal(0, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500000))
     assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> Time.utc(2006, 3, 24, 15, 32, 3, 499999))
-    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500001))
+    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500000 + DATETIME_RESOLUTION))
     assert_equal(0, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500000))
-    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> Time.utc(2006, 3, 24, 15, 32, 3, 499999))
+    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> Time.utc(2006, 3, 24, 15, 32, 3, 500000 - DATETIME_RESOLUTION))
   end
   
   def test_compare_datetime
@@ -379,12 +381,12 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(1, TimeOrDateTime.new(1143214323) <=> DateTime.new(2006, 3, 24, 15, 32, 2))
     assert_equal(1, TimeOrDateTime.new(1143214323) <=> DateTime.new(1960, 3, 24, 15, 32, 3))
     
-    assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500001, 1000000)))
+    assert_equal(-1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 + DATETIME_RESOLUTION, 1000000)))
     assert_equal(0, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000)))
-    assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(499999, 1000000)))
-    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500001, 1000000)))
+    assert_equal(1, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 500000)) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 - DATETIME_RESOLUTION, 1000000)))
+    assert_equal(-1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 + DATETIME_RESOLUTION, 1000000)))
     assert_equal(0, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000)))
-    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(499999, 1000000)))
+    assert_equal(1, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000, 1000000))) <=> DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(500000 - DATETIME_RESOLUTION, 1000000)))
   end
   
   def test_compare_timestamp
@@ -427,6 +429,12 @@ class TCTimeOrDateTime < Test::Unit::TestCase
     assert_equal(1, TimeOrDateTime.new(1143214323) <=> '1111678323')
   end
   
+  def test_compare_non_comparable
+    assert_nil(TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)) <=> Object.new)
+    assert_nil(TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)) <=> Object.new)
+    assert_nil(TimeOrDateTime.new(1143214323) <=> Object.new)
+  end
+  
   def test_eql
     assert_equal(true, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).eql?(TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3))))
     assert_equal(false, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).eql?(TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3))))
diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb
index 9a3725b..9206d49 100644
--- a/test/tc_timezone.rb
+++ b/test/tc_timezone.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezone < Test::Unit::TestCase
+class TCTimezone < Minitest::Test
 
   class BlockCalled < StandardError
   end
@@ -312,6 +290,8 @@ class TCTimezone < Test::Unit::TestCase
     assert_raises(UnknownTimezone) { tz.now }
     assert_raises(UnknownTimezone) { tz.current_period_and_time }
     assert_raises(UnknownTimezone) { tz.transitions_up_to(DateTime.new(2006,1,1,1,0,0)) }
+    assert_raises(UnknownTimezone) { tz.canonical_identifier }
+    assert_raises(UnknownTimezone) { tz.canonical_zone }
   end
   
   def test_new_nil
@@ -327,6 +307,8 @@ class TCTimezone < Test::Unit::TestCase
     assert_raises(UnknownTimezone) { tz.now }
     assert_raises(UnknownTimezone) { tz.current_period_and_time }
     assert_raises(UnknownTimezone) { tz.transitions_up_to(DateTime.new(2006,1,1,1,0,0)) }
+    assert_raises(UnknownTimezone) { tz.canonical_identifier }
+    assert_raises(UnknownTimezone) { tz.canonical_zone }
   end
   
   def test_new_arg
@@ -489,6 +471,7 @@ class TCTimezone < Test::Unit::TestCase
     
     assert_equal(period, dt_period)
     assert_equal(period, dt2_period)
+    assert_equal(period, dt3_period)
     assert_equal(period, t_period)
     assert_equal(period, t2_period)
     assert_equal(period, t3_period)
@@ -535,16 +518,6 @@ class TCTimezone < Test::Unit::TestCase
   end
   
   def test_period_for_local_not_found
-    o1 = TimezoneOffset.new(-18000, 0, :EST)
-    o2 = TimezoneOffset.new(-18000, 3600, :EDT)
-    
-    t1 = TestTimezoneTransition.new(o1, o2, 1067148000)
-    t2 = TestTimezoneTransition.new(o2, o1, 1081062000)
-    t3 = TestTimezoneTransition.new(o1, o2, 1099202400)
-    
-    p1 = TimezonePeriod.new(t1, t2)
-    p2 = TimezonePeriod.new(t2, t3)
-    
     dt = DateTime.new(2004,4,4,2,0,0)
     dt2 = DateTime.new(2004,4,4,2,0,Rational(987,1000))
     t = Time.utc(2004,4,4,2,30,0)
@@ -849,6 +822,28 @@ class TCTimezone < Test::Unit::TestCase
     assert(TestTimezone.new('Europe/London', nil, [period], tu2).local_to_utc(tu2).utc?)
   end
   
+  def test_local_to_utc_utc_local_returns_utc
+    # Check that UTC time instances are always returned even if the system
+    # is using UTC as the time zone.
+    
+    # Note that this will only test will only work correctly on platforms where
+    # setting the TZ environment variable has an effect. If setting TZ has no
+    # effect, then this test will still pass.
+    
+    old_tz = ENV['TZ']
+    begin
+      ENV['TZ'] = 'UTC'
+      
+      tz = Timezone.get('America/New_York')
+      
+      t = tz.local_to_utc(Time.local(2014, 1, 11, 17, 18, 41))
+      assert_equal(Time.utc(2014, 1, 11, 22, 18, 41), t)      
+      assert(t.utc?)
+    ensure
+      ENV['TZ'] = old_tz
+    end
+  end
+  
   def test_local_to_utc_invalid
     dt = DateTime.new(2004,4,4,2,30,0)
     tz = TestTimezone.new('America/New_York', nil, [], dt)        
@@ -892,16 +887,6 @@ class TCTimezone < Test::Unit::TestCase
   end
   
   def test_local_to_utc_not_found
-    o1 = TimezoneOffset.new(-18000, 0, :EST)
-    o2 = TimezoneOffset.new(-18000, 3600, :EDT)
-    
-    t1 = TestTimezoneTransition.new(o1, o2, 1067148000)
-    t2 = TestTimezoneTransition.new(o2, o1, 1081062000)
-    t3 = TestTimezoneTransition.new(o1, o2, 1099202400)
-    
-    p1 = TimezonePeriod.new(t1, t2)
-    p2 = TimezonePeriod.new(t2, t3)
-    
     dt = DateTime.new(2004,4,4,2,0,0)
     t = Time.utc(2004,4,4,2,30,0)
     i = Time.utc(2004,4,4,2,59,59).to_i
@@ -1065,9 +1050,6 @@ class TCTimezone < Test::Unit::TestCase
     dt = DateTime.new(2004,10,31,1,30,0)    
     tz = TestTimezone.new('America/New_York', nil, [p1, p2], dt)
     
-    dt = DateTime.new(2004,10,31,1,30,0)
-    tz = Timezone.get('America/New_York')
-    
     assert_raises(AmbiguousTime) { tz.local_to_utc(dt) {|periods| nil} }    
     assert_raises(AmbiguousTime) { tz.local_to_utc(dt) {|periods| periods} }     
     assert_raises(AmbiguousTime) { tz.local_to_utc(dt) {|periods| []} }    
@@ -1173,8 +1155,6 @@ class TCTimezone < Test::Unit::TestCase
   end
   
   def test_offsets_up_to_utc_to_not_greater_than_utc_from
-    o = TimezoneOffset.new(600, 0, :LMT)
-    
     assert_raises(ArgumentError) do
       OffsetsUpToTestTimezone.new('Test/Zone', Time.utc(2012,8,1,0,0,0), Time.utc(2012,8,1,0,0,0), []).
         offsets_up_to(Time.utc(2012,8,1,0,0,0), Time.utc(2012,8,1,0,0,0))
@@ -1222,6 +1202,10 @@ class TCTimezone < Test::Unit::TestCase
     assert_equal(1, TestTimezone.new('Europe/Paris') <=> TestTimezone.new('America/New_York'))    
   end
   
+  def test_compare_non_comparable
+    assert_nil(TestTimezone.new('Europe/London') <=> Object.new)
+  end
+  
   def test_equality
     assert_equal(true, TestTimezone.new('Europe/London') == TestTimezone.new('Europe/London'))
     assert_equal(false, TestTimezone.new('Europe/London') == TestTimezone.new('Europe/london'))
diff --git a/test/tc_timezone_definition.rb b/test/tc_timezone_definition.rb
index 8635940..f7f2388 100644
--- a/test/tc_timezone_definition.rb
+++ b/test/tc_timezone_definition.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneDefinition < Test::Unit::TestCase
+class TCTimezoneDefinition < Minitest::Test
 
   module DataTest
     include TimezoneDefinition
diff --git a/test/tc_timezone_index_definition.rb b/test/tc_timezone_index_definition.rb
index 446c0c9..cd55dd9 100644
--- a/test/tc_timezone_index_definition.rb
+++ b/test/tc_timezone_index_definition.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneIndexDefinition < Test::Unit::TestCase
+class TCTimezoneIndexDefinition < Minitest::Test
   
   module TimezonesTest1
     include TimezoneIndexDefinition
diff --git a/test/tc_timezone_info.rb b/test/tc_timezone_info.rb
index 5f9262a..fd70929 100644
--- a/test/tc_timezone_info.rb
+++ b/test/tc_timezone_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneInfo < Test::Unit::TestCase
+class TCTimezoneInfo < Minitest::Test
   
   def test_identifier
     ti = TimezoneInfo.new('Test/Zone')
diff --git a/test/tc_timezone_london.rb b/test/tc_timezone_london.rb
index 861446b..f27837f 100644
--- a/test/tc_timezone_london.rb
+++ b/test/tc_timezone_london.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneLondon < Test::Unit::TestCase
+class TCTimezoneLondon < Minitest::Test
   def test_2004
     #Europe/London  Sun Mar 28 00:59:59 2004 UTC = Sun Mar 28 00:59:59 2004 GMT isdst=0 gmtoff=0
     #Europe/London  Sun Mar 28 01:00:00 2004 UTC = Sun Mar 28 02:00:00 2004 BST isdst=1 gmtoff=3600
diff --git a/test/tc_timezone_melbourne.rb b/test/tc_timezone_melbourne.rb
index 854666e..f68f57b 100644
--- a/test/tc_timezone_melbourne.rb
+++ b/test/tc_timezone_melbourne.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneMelbourne < Test::Unit::TestCase
+class TCTimezoneMelbourne < Minitest::Test
   def test_2004
     #Australia/Melbourne  Sat Mar 27 15:59:59 2004 UTC = Sun Mar 28 02:59:59 2004 EST isdst=1 gmtoff=39600
     #Australia/Melbourne  Sat Mar 27 16:00:00 2004 UTC = Sun Mar 28 02:00:00 2004 EST isdst=0 gmtoff=36000
diff --git a/test/tc_timezone_new_york.rb b/test/tc_timezone_new_york.rb
index c341e3d..3e270a9 100644
--- a/test/tc_timezone_new_york.rb
+++ b/test/tc_timezone_new_york.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneNewYork < Test::Unit::TestCase
+class TCTimezoneNewYork < Minitest::Test
   def test_2004
     #America/New_York  Sun Apr  4 06:59:59 2004 UTC = Sun Apr  4 01:59:59 2004 EST isdst=0 gmtoff=-18000
     #America/New_York  Sun Apr  4 07:00:00 2004 UTC = Sun Apr  4 03:00:00 2004 EDT isdst=1 gmtoff=-14400
diff --git a/test/tc_timezone_offset.rb b/test/tc_timezone_offset.rb
index d317c9a..6e29e4d 100644
--- a/test/tc_timezone_offset.rb
+++ b/test/tc_timezone_offset.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneOffset < Test::Unit::TestCase
+class TCTimezoneOffset < Minitest::Test
   
   def test_utc_offset
     o1 = TimezoneOffset.new(18000, 0, :TEST)
diff --git a/test/tc_timezone_period.rb b/test/tc_timezone_period.rb
index 77c21a9..be7ef3f 100644
--- a/test/tc_timezone_period.rb
+++ b/test/tc_timezone_period.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezonePeriod < Test::Unit::TestCase
+class TCTimezonePeriod < Minitest::Test
   
   class TestTimezoneTransition < TimezoneTransition
     def initialize(offset, previous_offset, at)
@@ -558,13 +536,13 @@ class TCTimezonePeriod < Test::Unit::TestCase
     t4 = TestTimezoneTransition.new(o1, o2, 1149541200)
     
     p1 = TimezonePeriod.new(t1, t3)    
-    p2 = TimezonePeriod.new(t3, nil)
-    p3 = TimezonePeriod.new(nil, t3)
+    p2 = TimezonePeriod.new(t2, nil)
+    p3 = TimezonePeriod.new(nil, t4)
     p4 = TimezonePeriod.new(nil, nil, o1)
 
     assert_equal(t1.hash ^ t3.hash, p1.hash)
-    assert_equal(t3.hash ^ nil.hash, p2.hash)
-    assert_equal(nil.hash ^ t3.hash, p3.hash)
+    assert_equal(t2.hash ^ nil.hash, p2.hash)
+    assert_equal(nil.hash ^ t4.hash, p3.hash)
     assert_equal(nil.hash ^ nil.hash ^ o1.hash, p4.hash)    
   end
 end
diff --git a/test/tc_timezone_proxy.rb b/test/tc_timezone_proxy.rb
index 34f02f8..a2dcf56 100644
--- a/test/tc_timezone_proxy.rb
+++ b/test/tc_timezone_proxy.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneProxy < Test::Unit::TestCase
+class TCTimezoneProxy < Minitest::Test
   def test_not_exist
     proxy = TimezoneProxy.new('Nothing/Special')
     assert_equal('Nothing/Special', proxy.identifier)
@@ -36,17 +14,18 @@ class TCTimezoneProxy < Test::Unit::TestCase
     assert_raises(InvalidTimezoneIdentifier) { proxy.local_to_utc(DateTime.new(2006,1,1,0,0,0)) }
     assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_utc(DateTime.new(2006,1,1,0,0,0)) }
     assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_local(DateTime.new(2006,1,1,0,0,0)) }
+    assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_identifier }
+    assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_zone }
   end
   
   def test_valid
     proxy = TimezoneProxy.new('Europe/London')
     assert_equal('Europe/London', proxy.identifier)
     
-    # Test nothing raised
-    proxy.now
-    proxy.current_period
-    proxy.current_period_and_time
-    proxy.current_time_and_period
+    assert_nothing_raised { proxy.now }
+    assert_nothing_raised { proxy.current_period }
+    assert_nothing_raised { proxy.current_period_and_time }
+    assert_nothing_raised { proxy.current_time_and_period }
     
     real = Timezone.get('Europe/London')
     
@@ -60,6 +39,8 @@ class TCTimezoneProxy < Test::Unit::TestCase
     assert_equal(real.friendly_identifier(true), proxy.friendly_identifier(true))
     assert_equal(real.friendly_identifier(false), proxy.friendly_identifier(false))
     assert_equal(real.friendly_identifier, proxy.friendly_identifier)
+    assert_equal(real.canonical_identifier, proxy.canonical_identifier)
+    assert_same(real.canonical_zone, proxy.canonical_zone)
     
     assert_equal('Europe/London', proxy.identifier)
     
@@ -69,6 +50,31 @@ class TCTimezoneProxy < Test::Unit::TestCase
     assert_equal(0, proxy <=> real)
   end
   
+  def test_canonical_linked
+    # Test that the implementation of canonical_zone and canonical_identifier
+    # are actually calling the real timezone and not just returning it and
+    # its identifier.
+    
+    real = Timezone.get('UTC')
+    proxy = TimezoneProxy.new('UTC')
+    
+    # ZoneinfoDataSource doesn't return LinkedTimezoneInfo instances for any 
+    # timezone.
+    if real.kind_of?(LinkedTimezone)
+      assert_equal('Etc/UTC', proxy.canonical_identifier)
+      assert_same(Timezone.get('Etc/UTC'), proxy.canonical_zone)
+    else    
+      if DataSource.get.kind_of?(RubyDataSource)
+        # Not got a LinkedTimezone despite using a DataSource that supports it.
+        # Raise an exception as this shouldn't happen.
+        raise 'Non-LinkedTimezone instance returned for UTC using RubyDataSource'
+      end
+      
+      assert_equal('UTC', proxy.canonical_identifier)
+      assert_same(Timezone.get('UTC'), proxy.canonical_zone)
+    end
+  end
+  
   def test_equals
     assert_equal(true, TimezoneProxy.new('Europe/London') == TimezoneProxy.new('Europe/London'))
     assert_equal(false, TimezoneProxy.new('Europe/London') == TimezoneProxy.new('Europe/Paris'))
diff --git a/test/tc_timezone_transition.rb b/test/tc_timezone_transition.rb
index 6ea510c..8941ec9 100644
--- a/test/tc_timezone_transition.rb
+++ b/test/tc_timezone_transition.rb
@@ -1,31 +1,9 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 require 'date'
 
 include TZInfo
 
-class TCTimezoneTransition < Test::Unit::TestCase
+class TCTimezoneTransition < Minitest::Test
   
   class TestTimezoneTransition < TimezoneTransition
     def initialize(offset, previous_offset, at)
diff --git a/test/tc_timezone_transition_definition.rb b/test/tc_timezone_transition_definition.rb
index 5a1b006..a542c12 100644
--- a/test/tc_timezone_transition_definition.rb
+++ b/test/tc_timezone_transition_definition.rb
@@ -1,31 +1,9 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 require 'date'
 
 include TZInfo
 
-class TCTimezoneTransitionDefinition < Test::Unit::TestCase
+class TCTimezoneTransitionDefinition < Minitest::Test
   def test_initialize_timestamp_only
     assert_nothing_raised do
       TimezoneTransitionDefinition.new(TimezoneOffset.new(3600, 3600, :TDT),
diff --git a/test/tc_timezone_utc.rb b/test/tc_timezone_utc.rb
index 2eff7a2..212bca0 100644
--- a/test/tc_timezone_utc.rb
+++ b/test/tc_timezone_utc.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTimezoneUTC < Test::Unit::TestCase
+class TCTimezoneUTC < Minitest::Test
   def test_2004        
     tz = Timezone.get('UTC')
     
diff --git a/test/tc_transition_data_timezone_info.rb b/test/tc_transition_data_timezone_info.rb
index 168e434..a1614ee 100644
--- a/test/tc_transition_data_timezone_info.rb
+++ b/test/tc_transition_data_timezone_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2006-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCTransitionDataTimezoneInfo < Test::Unit::TestCase
+class TCTransitionDataTimezoneInfo < Minitest::Test
   
   def test_identifier
     dti = TransitionDataTimezoneInfo.new('Test/Zone')
@@ -378,10 +356,10 @@ class TCTransitionDataTimezoneInfo < Test::Unit::TestCase
     assert_equal([], dti.transitions_up_to(DateTime.new(2011,3,1,1,0,0), DateTime.new(2010,10,1,1,0,1)))
     assert_equal([t1,t2,t3,t4], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,0)))
     assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1)))
-    assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,Rational(1,1000000))))
+    assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,Rational(DATETIME_RESOLUTION,1000000))))
     assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,0)))
     assert_equal([t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,1)))
-    assert_equal([t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,Rational(1,1000000))))
+    assert_equal([t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,Rational(DATETIME_RESOLUTION,1000000))))
     assert_equal([t5], dti.transitions_up_to(DateTime.new(2015,1,1,0,0,0), DateTime.new(2011,10,1,1,0,0)))
     assert_equal([], dti.transitions_up_to(DateTime.new(2015,1,1,0,0,0), DateTime.new(2011,10,1,1,0,1)))
   end
diff --git a/test/tc_zoneinfo_country_info.rb b/test/tc_zoneinfo_country_info.rb
index b00c8c0..67a0631 100644
--- a/test/tc_zoneinfo_country_info.rb
+++ b/test/tc_zoneinfo_country_info.rb
@@ -1,30 +1,8 @@
-#--
-# Copyright (c) 2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 
 include TZInfo
 
-class TCZoneinfoCountryInfo < Test::Unit::TestCase
+class TCZoneinfoCountryInfo < Minitest::Test
   
   def test_code
     ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', []) {|c| }
@@ -44,10 +22,10 @@ class TCZoneinfoCountryInfo < Test::Unit::TestCase
   
   def test_zone_identifiers
     zones = [
-      CountryTimezone.new('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
-      CountryTimezone.new('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
-      CountryTimezone.new('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
-      CountryTimezone.new('ZZ/TimezoneD', -10, 3, -20, 7)
+      CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
+      CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
+      CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
+      CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
     ]
   
     ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
@@ -66,18 +44,18 @@ class TCZoneinfoCountryInfo < Test::Unit::TestCase
   
   def test_zones
     zones = [
-      CountryTimezone.new('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
-      CountryTimezone.new('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
-      CountryTimezone.new('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
-      CountryTimezone.new('ZZ/TimezoneD', -10, 3, -20, 7)
+      CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
+      CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
+      CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
+      CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
     ]
   
     ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
     
-    assert_equal([CountryTimezone.new('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
-      CountryTimezone.new('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
-      CountryTimezone.new('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
-      CountryTimezone.new('ZZ/TimezoneD', -10, 3, -20, 7)],
+    assert_equal([CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
+      CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
+      CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
+      CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))],
       ci.zones)
     assert(ci.zones.frozen?)
     assert(!ci.zones.equal?(zones))
diff --git a/test/tc_zoneinfo_data_source.rb b/test/tc_zoneinfo_data_source.rb
index e74bd9c..bb93536 100644
--- a/test/tc_zoneinfo_data_source.rb
+++ b/test/tc_zoneinfo_data_source.rb
@@ -1,24 +1,4 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
+# encoding: UTF-8
 
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 require 'fileutils'
@@ -27,11 +7,12 @@ require 'tmpdir'
 
 include TZInfo
 
-class TCZoneinfoDataSource < Test::Unit::TestCase
+class TCZoneinfoDataSource < Minitest::Test
   ZONEINFO_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint
   
   def setup
     @orig_search_path = ZoneinfoDataSource.search_path.clone
+    @orig_alternate_iso3166_tab_search_path = ZoneinfoDataSource.alternate_iso3166_tab_search_path.clone
     @orig_pwd = FileUtils.pwd
     
     # A zoneinfo directory containing files needed by the tests.
@@ -41,6 +22,7 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
   
   def teardown
     ZoneinfoDataSource.search_path = @orig_search_path
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = @orig_alternate_iso3166_tab_search_path
     FileUtils.chdir(@orig_pwd)
   end
   
@@ -62,7 +44,7 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     path = ['/tmp/zoneinfo1', '/tmp/zoneinfo2']
     ZoneinfoDataSource.search_path = path
     assert_equal(['/tmp/zoneinfo1', '/tmp/zoneinfo2'], ZoneinfoDataSource.search_path)
-    assert_not_same(path, ZoneinfoDataSource.search_path)
+    refute_same(path, ZoneinfoDataSource.search_path)
   end
   
   def test_set_search_path_array_to_s  
@@ -75,6 +57,37 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     assert_equal(['/tmp/zoneinfo4', '/tmp/zoneinfo5'], ZoneinfoDataSource.search_path)
   end
   
+  def test_default_alternate_iso3166_tab_search_path
+    assert_equal(['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+    assert_equal(false, ZoneinfoDataSource.alternate_iso3166_tab_search_path.frozen?)
+  end
+  
+  def test_set_alternate_iso3166_tab_search_path_default
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = ['/tmp/iso3166.tab', '/tmp/iso3166']
+    assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+    
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = nil
+    assert_equal(['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+    assert_equal(false, ZoneinfoDataSource.alternate_iso3166_tab_search_path.frozen?)
+  end
+  
+  def test_set_alternate_iso3166_tab_search_path_array
+    path = ['/tmp/iso3166.tab', '/tmp/iso3166']
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = path
+    assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+    refute_same(path, ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+  end
+  
+  def test_set_alternate_iso3166_tab_search_path_array_to_s  
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = [Pathname.new('/tmp/iso3166.tab')]
+    assert_equal(['/tmp/iso3166.tab'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+  end
+  
+  def test_set_alternate_iso3166_tab_search_path_string
+    ZoneinfoDataSource.alternate_iso3166_tab_search_path = ['/tmp/iso3166.tab', '/tmp/iso3166'].join(File::PATH_SEPARATOR)
+    assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
+  end
+  
   def test_new_search
     Dir.mktmpdir('tzinfo_test_dir1') do |dir1|
       Dir.mktmpdir('tzinfo_test_dir2') do |dir2|
@@ -87,6 +100,7 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
             FileUtils.touch(File.join(dir4, 'iso3166.tab'))
             
             ZoneinfoDataSource.search_path = [file, dir2, dir3, dir4]
+            ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
             
             data_source = ZoneinfoDataSource.new
             assert_equal(dir4, data_source.zoneinfo_dir)
@@ -96,6 +110,46 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     end
   end
   
+  def test_new_search_solaris_tab_files
+    # Solaris names the tab files 'tab/country.tab' (iso3166.tab) and 
+    # 'tab/zone_sun.tab' (zone.tab).
+    
+    Dir.mktmpdir('tzinfo_test_dir') do |dir|
+      tab = File.join(dir, 'tab')
+      FileUtils.mkdir(tab)
+      FileUtils.touch(File.join(tab, 'country.tab'))
+      FileUtils.touch(File.join(tab, 'zone_sun.tab'))
+      
+      ZoneinfoDataSource.search_path = [dir]
+      ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
+      
+      data_source = ZoneinfoDataSource.new
+      assert_equal(dir, data_source.zoneinfo_dir)
+    end
+  end
+  
+  def test_new_search_alternate_iso3166_path
+    Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
+      Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
+        FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
+        
+        tab_file = File.join(tab_dir, 'iso3166')
+        
+        ZoneinfoDataSource.search_path = [zoneinfo_dir]
+        ZoneinfoDataSource.alternate_iso3166_tab_search_path = [tab_file]
+        
+        assert_raises(ZoneinfoDirectoryNotFound) do
+          ZoneinfoDataSource.new
+        end
+        
+        FileUtils.touch(tab_file)
+      
+        data_source = ZoneinfoDataSource.new
+        assert_equal(zoneinfo_dir, data_source.zoneinfo_dir)
+      end
+    end
+  end
+  
   def test_new_search_not_found
     Dir.mktmpdir('tzinfo_test_dir1') do |dir1|
       Dir.mktmpdir('tzinfo_test_dir2') do |dir2|
@@ -107,6 +161,7 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
             FileUtils.touch(File.join(dir3, 'iso3166.tab'))
             
             ZoneinfoDataSource.search_path = [file, dir2, dir3, dir4]
+            ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
                       
             assert_raises(ZoneinfoDirectoryNotFound) do
               ZoneinfoDataSource.new
@@ -125,8 +180,9 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
       FileUtils.chdir(dir)
       
       ZoneinfoDataSource.search_path = ['.']
+      ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
       data_source = ZoneinfoDataSource.new
-      assert_equal(Pathname.new(dir).realpath, Pathname.new(dir).realpath)
+      assert_equal(Pathname.new(dir).realpath.to_s, data_source.zoneinfo_dir)
       
       # Change out of the directory to allow it to be deleted on Windows.
       FileUtils.chdir(@orig_pwd)
@@ -143,6 +199,43 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     end
   end
   
+  def test_new_dir_solaris_tab_files
+    # Solaris names the tab files 'tab/country.tab' (iso3166.tab) and 
+    # 'tab/zone_sun.tab' (zone.tab).
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      tab = File.join(dir, 'tab')
+      FileUtils.mkdir(tab)
+      FileUtils.touch(File.join(tab, 'country.tab'))
+      FileUtils.touch(File.join(tab, 'zone_sun.tab'))
+            
+      data_source = ZoneinfoDataSource.new(dir)
+      assert_equal(dir, data_source.zoneinfo_dir)
+    end
+  end
+  
+  def test_new_dir_alternate_iso3166_path
+    Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
+      Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
+        FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
+        
+        tab_file = File.join(tab_dir, 'iso3166')
+        FileUtils.touch(tab_file)
+        
+        ZoneinfoDataSource.alternate_iso3166_tab_search_path = [tab_file]
+        
+        assert_raises(InvalidZoneinfoDirectory) do
+          # The alternate_iso3166_tab_search_path should not be used. This should raise 
+          # an exception.
+          ZoneinfoDataSource.new(zoneinfo_dir)
+        end
+        
+        data_source = ZoneinfoDataSource.new(zoneinfo_dir, tab_file)
+        assert_equal(zoneinfo_dir, data_source.zoneinfo_dir)
+      end
+    end
+  end
+  
   def test_new_dir_invalid
     Dir.mktmpdir('tzinfo_test') do |dir|
       assert_raises(InvalidZoneinfoDirectory) do
@@ -151,6 +244,31 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     end
   end
   
+  def test_new_dir_invalid_alternate_iso3166_path
+    Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
+      Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
+        FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
+        
+        assert_raises(InvalidZoneinfoDirectory) do
+          ZoneinfoDataSource.new(zoneinfo_dir, File.join(tab_dir, 'iso3166'))
+        end
+      end
+    end
+  end
+  
+  def test_new_dir_invalid_alternate_iso3166_path_overrides_valid
+    Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
+      Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
+        FileUtils.touch(File.join(zoneinfo_dir, 'iso3166.tab'))
+        FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
+        
+        assert_raises(InvalidZoneinfoDirectory) do
+          ZoneinfoDataSource.new(zoneinfo_dir, File.join(tab_dir, 'iso3166'))
+        end
+      end
+    end
+  end
+  
   def test_new_file
     Dir.mktmpdir('tzinfo_test') do |dir|
       file = File.join(dir, 'file')
@@ -170,7 +288,7 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
       FileUtils.chdir(dir)
       
       data_source = ZoneinfoDataSource.new('.')
-      assert_equal(Pathname.new(dir).realpath, Pathname.new(dir).realpath)
+      assert_equal(Pathname.new(dir).realpath.to_s, data_source.zoneinfo_dir)
       
       # Change out of the directory to allow it to be deleted on Windows.
       FileUtils.chdir(@orig_pwd)
@@ -493,8 +611,6 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
   def get_timezone_filenames(directory)
     entries = Dir.glob(File.join(directory, '**', '*'))
     
-    prefix = File.expand_path(directory) + File::SEPARATOR
-    
     entries = entries.select do |file|
       file.untaint
       File.file?(file)
@@ -563,9 +679,32 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     end
   end
   
+  def test_timezone_identifiers_ignored_src_directory
+    # Solaris includes a src directory containing the source timezone data files
+    # from the tzdata distribution. These should be ignored.
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      FileUtils.touch(File.join(dir, 'zone.tab'))
+      FileUtils.touch(File.join(dir, 'iso3166.tab'))
+      FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
+      
+      src_dir = File.join(dir, 'src')
+      FileUtils.mkdir(src_dir)
+      
+      File.open(File.join(src_dir, 'europe'), 'w') do |f|
+        f.binmode
+        f.write("Zone\tEurope/London\t0:00\tEU\tGMT/BST\n")
+      end      
+      
+      data_source = ZoneinfoDataSource.new(dir)
+      assert_array_same_items(['EST'], data_source.timezone_identifiers)
+    end
+  end
+  
   def test_load_country_info
     info = @data_source.load_country_info('GB')
     assert_equal('GB', info.code)
+    assert_equal('Britain (UK)', info.name)
   end
     
   def test_load_country_info_not_exist
@@ -611,12 +750,16 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
   
   def test_load_country_info_check_zones
     Dir.mktmpdir('tzinfo_test') do |dir|
-      File.open(File.join(dir, 'iso3166.tab'), 'w') do |iso3166|
+      RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|      
+        iso3166.puts('# iso3166.tab')
+        iso3166.puts('')
         iso3166.puts("FC\tFake Country")
         iso3166.puts("OC\tOther Country")
       end
       
-      File.open(File.join(dir, 'zone.tab'), 'w') do |zone|
+      RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
+        zone.puts('# zone.tab')
+        zone.puts('')
         zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
         zone.puts("FC\t+353916+1394441\tFake/Two\tAnother description")
         zone.puts("FC\t-2332-04637\tFake/Three\tThis is Three")
@@ -627,27 +770,185 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
       
       info = data_source.load_country_info('FC')
       assert_equal('FC', info.code)
+      assert_equal('Fake Country', info.name)
       assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
       assert_equal(true, info.zone_identifiers.frozen?)
       assert_equal([
-        CountryTimezone.new('Fake/One', 6181, 120, -451, 3600, 'Description of one'),
-        CountryTimezone.new('Fake/Two', 32089, 900, 503081, 3600, 'Another description'),
-        CountryTimezone.new('Fake/Three', -353, 15, -2797, 60, 'This is Three')], info.zones)
+        CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
+        CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
+        CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
       assert_equal(true, info.zones.frozen?)
       
       info = data_source.load_country_info('OC')
       assert_equal('OC', info.code)
+      assert_equal('Other Country', info.name)
       assert_equal(['Other/One'], info.zone_identifiers)
       assert_equal(true, info.zone_identifiers.frozen?)
-      assert_equal([CountryTimezone.new('Other/One', 601, 12, 433, 30)], info.zones)
+      assert_equal([CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30))], info.zones)
       assert_equal(true, info.zones.frozen?)
     end
   end
   
+  def test_load_country_info_check_zones_solaris_tab_files
+    # Solaris uses 5 columns instead of the usual 4 in zone_sun.tab.
+    # An extra column before the comment gives an optional linked/alternate
+    # timezone identifier (or '-' if not set).
+    #
+    # Additionally, there is a section at the end of the file for timezones
+    # covering regions. These are given lower-case "country" codes. The timezone
+    # identifier column refers to a continent instead of an identifier. These
+    # lines will be ignored by TZInfo.
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      tab_dir = File.join(dir, 'tab')
+      FileUtils.mkdir(tab_dir)
+    
+      RubyCoreSupport.open_file(File.join(tab_dir, 'country.tab'), 'w', :external_encoding => 'UTF-8') do |country|
+        country.puts('# country.tab')
+        country.puts('# Solaris')
+        country.puts("FC\tFake Country")
+        country.puts("OC\tOther Country")
+      end
+      
+      RubyCoreSupport.open_file(File.join(tab_dir, 'zone_sun.tab'), 'w', :external_encoding => 'UTF-8') do |zone_sun|
+        zone_sun.puts('# zone_sun.tab')
+        zone_sun.puts('# Solaris')
+        zone_sun.puts('# Countries')
+        zone_sun.puts("FC\t+513030-0000731\tFake/One\t-\tDescription of one")
+        zone_sun.puts("FC\t+353916+1394441\tFake/Two\tFake/Alias/Two\tAnother description")
+        zone_sun.puts("FC\t-2332-04637\tFake/Three\tFake/Alias/Three\tThis is Three")
+        zone_sun.puts("OC\t+5005+01426\tOther/One\tOther/Linked/One")
+        zone_sun.puts("OC\t+5015+01436\tOther/Two\t-")
+        zone_sun.puts('# Regions')
+        zone_sun.puts("ee\t+0000+00000\tEurope/\tEET")
+        zone_sun.puts("me\t+0000+00000\tEurope/\tMET")
+        zone_sun.puts("we\t+0000+00000\tEurope/\tWET")
+      end
+      
+      data_source = ZoneinfoDataSource.new(dir)
+      
+      info = data_source.load_country_info('FC')
+      assert_equal('FC', info.code)
+      assert_equal('Fake Country', info.name)
+      assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
+      assert_equal(true, info.zone_identifiers.frozen?)
+      assert_equal([
+        CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
+        CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
+        CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
+      assert_equal(true, info.zones.frozen?)
+      
+      info = data_source.load_country_info('OC')
+      assert_equal('OC', info.code)
+      assert_equal('Other Country', info.name)
+      assert_equal(['Other/One', 'Other/Two'], info.zone_identifiers)
+      assert_equal(true, info.zone_identifiers.frozen?)
+      assert_equal([
+        CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30)),
+        CountryTimezone.new('Other/Two', Rational(201, 4), Rational(73, 5))], info.zones)
+      assert_equal(true, info.zones.frozen?)
+    end
+  end
+  
+  def test_load_country_info_check_zones_alternate_iso3166_file
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      zoneinfo_dir = File.join(dir, 'zoneinfo')
+      tab_dir = File.join(dir, 'tab')
+      FileUtils.mkdir(zoneinfo_dir)
+      FileUtils.mkdir(tab_dir)
+      
+      tab_file = File.join(tab_dir, 'iso3166')
+      RubyCoreSupport.open_file(tab_file, 'w', :external_encoding => 'UTF-8') do |iso3166|
+        # Use the BSD 4 column format (alternate iso3166 is used on BSD).
+        iso3166.puts("FC\tFCC\t001\tFake Country")
+        iso3166.puts("OC\tOCC\t002\tOther Country")
+      end
+      
+      RubyCoreSupport.open_file(File.join(zoneinfo_dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
+        zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
+        zone.puts("FC\t+353916+1394441\tFake/Two\tAnother description")
+        zone.puts("FC\t-2332-04637\tFake/Three\tThis is Three")
+        zone.puts("OC\t+5005+01426\tOther/One")
+      end
+      
+      data_source = ZoneinfoDataSource.new(zoneinfo_dir, tab_file)
+      
+      info = data_source.load_country_info('FC')
+      assert_equal('FC', info.code)
+      assert_equal('Fake Country', info.name)
+      assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
+      assert_equal(true, info.zone_identifiers.frozen?)
+      assert_equal([
+        CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
+        CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
+        CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
+      assert_equal(true, info.zones.frozen?)
+      
+      info = data_source.load_country_info('OC')
+      assert_equal('OC', info.code)
+      assert_equal('Other Country', info.name)
+      assert_equal(['Other/One'], info.zone_identifiers)
+      assert_equal(true, info.zone_identifiers.frozen?)
+      assert_equal([CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30))], info.zones)
+      assert_equal(true, info.zones.frozen?)
+    end
+  end
+  
+  def test_load_country_info_four_column_iso31611
+    # OpenBSD and FreeBSD use a 4 column iso3166.tab file that includes
+    # alpha-3 and numeric-3 codes in addition to the alpha-2 and name in the
+    # tz database version.
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
+        iso3166.puts("FC\tFCC\t001\tFake Country")
+        iso3166.puts("OC\tOCC\t002\tOther Country")
+      end
+      
+      RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
+        zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
+        zone.puts("OC\t+5005+01426\tOther/One")
+      end
+      
+      data_source = ZoneinfoDataSource.new(dir)
+      
+      info = data_source.load_country_info('FC')
+      assert_equal('FC', info.code)
+      assert_equal('Fake Country', info.name)
+      
+      info = data_source.load_country_info('OC')
+      assert_equal('OC', info.code)
+      assert_equal('Other Country', info.name)
+    end
+  end
+
+  def test_load_country_info_utf8
+    # Files are in ASCII, but may change to UTF-8 (a superset of ASCII) in
+    # the future.
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
+        iso3166.puts("UT\tUnicode Test ✓")
+      end
+      
+      RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
+        zone.puts("UT\t+513030-0000731\tUnicode✓/One\tUnicode Description ✓")
+      end
+      
+      data_source = ZoneinfoDataSource.new(dir)
+      
+      info = data_source.load_country_info('UT')
+      assert_equal('UT', info.code)
+      assert_equal('Unicode Test ✓', info.name)
+      assert_equal(['Unicode✓/One'], info.zone_identifiers)
+      assert_equal([CountryTimezone.new('Unicode✓/One', Rational(6181, 120), Rational(-451, 3600), 'Unicode Description ✓')], info.zones)
+    end
+  end
+  
   def test_country_codes
     file_codes = []
         
-    File.open(File.join(@data_source.zoneinfo_dir, 'iso3166.tab')) do |file|
+    RubyCoreSupport.open_file(File.join(@data_source.zoneinfo_dir, 'iso3166.tab'), 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
       file.each_line do |line|
         line.chomp!
         file_codes << $1 if line =~ /\A([A-Z]{2})\t/
@@ -658,4 +959,27 @@ class TCZoneinfoDataSource < Test::Unit::TestCase
     assert_array_same_items(file_codes, codes)
     assert_equal(true, codes.frozen?)
   end
+  
+  def test_country_codes_four_column_iso3166
+    # OpenBSD and FreeBSD use a 4 column iso3166.tab file that includes
+    # alpha-3 and numeric-3 codes in addition to the alpha-2 and name in the
+    # tz database version.
+    
+    Dir.mktmpdir('tzinfo_test') do |dir|
+      RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
+        iso3166.puts("FC\tFCC\t001\tFake Country")
+        iso3166.puts("OC\tOCC\t002\tOther Country")
+      end
+      
+      RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
+        zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
+        zone.puts("OC\t+5005+01426\tOther/One")
+      end
+      
+      data_source = ZoneinfoDataSource.new(dir)
+      
+      codes = data_source.country_codes
+      assert_array_same_items(%w(FC OC), codes)
+    end
+  end
 end
diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb
index 572e2a3..f847316 100644
--- a/test/tc_zoneinfo_timezone_info.rb
+++ b/test/tc_zoneinfo_timezone_info.rb
@@ -1,35 +1,11 @@
 # encoding: UTF-8
 
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
-
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
 require 'tempfile'
 
 include TZInfo
 
-class TCZoneinfoTimezoneInfo < Test::Unit::TestCase
+class TCZoneinfoTimezoneInfo < Minitest::Test
 
   begin
     Time.at(-2147483649)
@@ -55,14 +31,14 @@ class TCZoneinfoTimezoneInfo < Test::Unit::TestCase
     assert_equal(dst, period.dst?)
     
     if start_at
-      assert_not_nil(period.utc_start_time)
+      refute_nil(period.utc_start_time)
       assert_equal(start_at, period.utc_start_time)
     else
       assert_nil(period.utc_start_time)
     end
     
     if end_at
-      assert_not_nil(period.utc_end_time)
+      refute_nil(period.utc_end_time)
       assert_equal(end_at, period.utc_end_time)
     else
       assert_nil(period.utc_end_time)
diff --git a/test/test_utils.rb b/test/test_utils.rb
index f46345d..cadb72a 100644
--- a/test/test_utils.rb
+++ b/test/test_utils.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2008-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 TESTS_DIR = File.expand_path(File.dirname(__FILE__)).untaint
 TZINFO_LIB_DIR = File.expand_path(File.join(TESTS_DIR, '..', 'lib'))
 TZINFO_TEST_DATA_DIR = File.join(TESTS_DIR, 'tzinfo-data')
@@ -31,7 +9,7 @@ $:.unshift(TZINFO_LIB_DIR) unless $:.include?(TZINFO_LIB_DIR)
 # Add it to the load path.
 $:.unshift(TZINFO_TEST_DATA_DIR) unless $:.include?(TZINFO_TEST_DATA_DIR)
 
-require 'test/unit'
+require 'minitest/autorun'
 require 'tzinfo'
 require 'fileutils'
 require 'rbconfig'
@@ -46,7 +24,7 @@ module TestUtils
     ZONEINFO_SYMLINKS.each do |file, target|
       path = File.join(TZINFO_TEST_ZONEINFO_DIR, file)
       
-      File.delete(path) if File.exists?(path)
+      File.delete(path) if File.exist?(path)
     
       begin
         FileUtils.ln_s(target, path)
@@ -88,12 +66,10 @@ module Kernel
     end
   end
   
-  def assert_array_same_items(expected, actual, message = nil)
-    full_message = build_message(message, "<?> expected but was <?>.", expected, actual)
-    
-    assert_block(full_message) do
-      (expected.size == actual.size) && (expected - actual == [])
-    end
+  def assert_array_same_items(expected, actual, msg = nil)
+    full_message = message(msg, '') { diff(expected, actual) }
+    condition = (expected.size == actual.size) && (expected - actual == [])
+    assert(condition, full_message)
   end
   
   def assert_sub_process_returns(expected_lines, code, extra_load_path = [], required = ['tzinfo'])
@@ -133,4 +109,24 @@ module Kernel
       assert_equal(expected_lines, actual_lines)
     end
   end
+
+  def assert_nothing_raised(msg = nil)
+    begin
+      yield
+    rescue => e
+      full_message = message(msg) { exception_details(e, 'Exception raised: ') }
+      assert(false, full_message)
+    end
+  end
 end
+
+
+# JRuby 1.7.5 to 1.7.9 consider DateTime instances that differ by less than 
+# 1 millisecond to be equivalent (https://github.com/jruby/jruby/issues/1311).
+#
+# A few test cases compare at a resolution of 1 microsecond, so this causes
+# failures on JRuby 1.7.5 to 1.7.9.
+#
+# Determine what the platform supports and adjust the tests accordingly.
+DATETIME_RESOLUTION = (0..5).collect {|i| 10**i}.find {|i| (DateTime.new(2013,1,1,0,0,0) <=> DateTime.new(2013,1,1,0,0,Rational(i,1000000))) < 0}
+raise 'Unable to compare DateTimes at a resolution less than one second on this platform' unless DATETIME_RESOLUTION
diff --git a/test/ts_all.rb b/test/ts_all.rb
index 5a1f42a..f90836d 100644
--- a/test/ts_all.rb
+++ b/test/ts_all.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2005-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 # Force a particular timezone to be local (helps find issues when local
 # timezone isn't GMT). This won't work on Windows.
 ENV['TZ'] = 'America/Los_Angeles'
diff --git a/test/ts_all_ruby.rb b/test/ts_all_ruby.rb
index 9078365..5b822bf 100644
--- a/test/ts_all_ruby.rb
+++ b/test/ts_all_ruby.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils.rb')
 
 TZInfo::DataSource.set(:ruby)
diff --git a/test/ts_all_zoneinfo.rb b/test/ts_all_zoneinfo.rb
index 9f983cd..95c4685 100644
--- a/test/ts_all_zoneinfo.rb
+++ b/test/ts_all_zoneinfo.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils.rb')
 
 # Use a zoneinfo directory containing files needed by the tests.
diff --git a/test/tzinfo-data/tzinfo/data.rb b/test/tzinfo-data/tzinfo/data.rb
index 9af7bc8..4b1489a 100644
--- a/test/tzinfo-data/tzinfo/data.rb
+++ b/test/tzinfo-data/tzinfo/data.rb
@@ -1,23 +1 @@
-#--
-# Copyright (c) 2012 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 require 'tzinfo/data/version'
diff --git a/test/tzinfo-data/tzinfo/data/version.rb b/test/tzinfo-data/tzinfo/data/version.rb
index 82fda5a..0ebb288 100644
--- a/test/tzinfo-data/tzinfo/data/version.rb
+++ b/test/tzinfo-data/tzinfo/data/version.rb
@@ -1,25 +1,3 @@
-#--
-# Copyright (c) 2012-2013 Philip Ross
-# 
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
 module TZInfo
   module Data
     # TZInfo::Data version information.
diff --git a/tzinfo.gemspec b/tzinfo.gemspec
index 49b1d5c..567e419 100644
--- a/tzinfo.gemspec
+++ b/tzinfo.gemspec
@@ -1,6 +1,6 @@
 Gem::Specification.new do |s|
   s.name = 'tzinfo'
-  s.version = '1.1.0'  
+  s.version = '1.2.0'  
   s.summary = 'Daylight savings aware timezone library'
   s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.'
   s.author = 'Philip Ross'
@@ -16,6 +16,6 @@ Gem::Specification.new do |s|
   s.rdoc_options << '--title' << 'TZInfo' << 
                     '--main' << 'README.md'
   s.extra_rdoc_files = ['README.md', 'CHANGES.md', 'LICENSE']
-  s.required_ruby_version = '>= 1.8.6'
+  s.required_ruby_version = '>= 1.8.7'
   s.add_dependency 'thread_safe', '~> 0.1'
 end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-tzinfo.git



More information about the Pkg-ruby-extras-commits mailing list