a0-tzmigration-ruby 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b733404e9d1fc656401129fe8d37b28bedf56f0dc1482dcba9214780c79ef70
4
- data.tar.gz: 9282751867db162d40e3516cb6ca0da2785dd1c37e8130e375a063ceda493afa
3
+ metadata.gz: 5315ca942000e3f71a8f3e37927f20b2eec0ec7a29dbd8ab71808abe76d63b24
4
+ data.tar.gz: 8a0c381804c482cf2c04b67ad5db8f96c6ec8b71c15d8052e8a9f002f45c9806
5
5
  SHA512:
6
- metadata.gz: d1580f370557ce698906f8dc55a5f275bac7b9dc90ea07273782a9321288f74ca8b4e062037e427e4448573d6b8613dff0ae36b58ec5e094a2c5de6f2cb8a8ca
7
- data.tar.gz: aa0d5f86ab757f7e0605ced7e2980ed74cc23ce89d4b2742d8f83d6fcd498155fd5684672c5a62642305254f9604408f5cd86cb3dc775876ffbd41429ad44709
6
+ metadata.gz: c64c31ad265828ed9ca784856d8261e02a24c1c60f8e0538cd2ad6dfe38925108af6a5073378148a13a43ec02a95220eb2eb0e607b70a117b020b6d4a5dc6176
7
+ data.tar.gz: 597161ffde5f4d510ada563e354d4498458123022b9350db906482beb669de0f06efc9d8776223f87ad332f5e0b9c9dee13b73e5d359dd02d43ba40a6e051802
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- a0-tzmigration-ruby (0.1.1)
4
+ a0-tzmigration-ruby (1.0.0)
5
5
  rest-client
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,15 +1,14 @@
1
1
  # A0::TZMigration
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/a0/tzmigration`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ This gem provides utilities to help with migrations between timezone changes. Please check the official webpage for documentation:
4
+ [https://a0.github.io/a0-tzmigration/](https://a0.github.io/a0-tzmigration/)
6
5
 
7
6
  ## Installation
8
7
 
9
8
  Add this line to your application's Gemfile:
10
9
 
11
10
  ```ruby
12
- gem 'a0-tzmigration'
11
+ gem 'a0-tzmigration-ruby'
13
12
  ```
14
13
 
15
14
  And then execute:
@@ -18,11 +17,56 @@ And then execute:
18
17
 
19
18
  Or install it yourself as:
20
19
 
21
- $ gem install a0-tzmigration
20
+ $ gem install a0-tzmigration-ruby
22
21
 
23
22
  ## Usage
24
23
 
25
- TODO: Write usage instructions here
24
+ ```ruby
25
+ require 'a0-tzmigration-ruby'
26
+
27
+ # calculate changes from America/Santiago 2015a to America/Punta_Arenas 2017a
28
+ version_a = A0::TZMigration::TZVersion.new('America/Santiago', '2015a')
29
+ version_b = A0::TZMigration::TZVersion.new('America/Punta_Arenas', '2017a')
30
+
31
+ version_a.changes(version_b)
32
+ # =>
33
+ [{:ini_str=>"-∞", :fin_str=>"1890-01-01 04:43:40 UTC", :off_str=>"-00:00:54", :ini=>-Infinity, :fin=>-2524504580, :off=>-54},
34
+ {:ini_str=>"1910-01-01 04:42:46 UTC", :fin_str=>"1910-01-10 04:42:46 UTC", :off_str=>"+00:17:14", :ini=>-1893439034, :fin=>-1892661434, :off=>1034},
35
+ {:ini_str=>"1918-09-01 04:42:46 UTC", :fin_str=>"1918-09-10 04:42:46 UTC", :off_str=>"-00:42:46", :ini=>-1619983034, :fin=>-1619205434, :off=>-2566},
36
+ {:ini_str=>"1946-09-01 03:00:00 UTC", :fin_str=>"1947-04-01 04:00:00 UTC", :off_str=>"+01:00:00", :ini=>-736376400, :fin=>-718056000, :off=>3600},
37
+ {:ini_str=>"1947-05-22 04:00:00 UTC", :fin_str=>"1947-05-22 05:00:00 UTC", :off_str=>"+01:00:00", :ini=>-713649600, :fin=>-713646000, :off=>3600},
38
+ {:ini_str=>"1988-10-02 04:00:00 UTC", :fin_str=>"1988-10-09 04:00:00 UTC", :off_str=>"-01:00:00", :ini=>591768000, :fin=>592372800, :off=>-3600},
39
+ {:ini_str=>"1990-03-11 03:00:00 UTC", :fin_str=>"1990-03-18 03:00:00 UTC", :off_str=>"-01:00:00", :ini=>637124400, :fin=>637729200, :off=>-3600},
40
+ {:ini_str=>"2016-05-15 03:00:00 UTC", :fin_str=>"2016-08-14 04:00:00 UTC", :off_str=>"-01:00:00", :ini=>1463281200, :fin=>1471147200, :off=>-3600}]
41
+
42
+
43
+ # get current known versions in the repository
44
+ A0::TZMigration::TZVersion.versions
45
+ # =>
46
+ { "2013c" =>
47
+ { "released_at" => "2013-04-19 16:17:40 -0700",
48
+ "timezones" => [
49
+ "Africa/Abidjan",
50
+ "Africa/Accra",
51
+ "Africa/Addis_Ababa",
52
+ "Africa/Algiers",
53
+ "Africa/Asmara",
54
+
55
+
56
+
57
+ # get current known timezones in the repository
58
+ A0::TZMigration::TZVersion.timezones
59
+ # =>
60
+ { "Africa/Abidjan" =>
61
+ { "versions" => [
62
+ "2013c",
63
+ "2013d",
64
+ "2013e",
65
+ "2013f",
66
+ "2013g",
67
+
68
+
69
+ ```
26
70
 
27
71
  ## Development
28
72
 
data/docs/index.md CHANGED
@@ -1,3 +1,9 @@
1
+ # Ruby API
2
+
3
+ API usage and documentation for the ruby gem and others can be found at the official webpage:
4
+ [https://a0.github.io/a0-tzmigration/](https://a0.github.io/a0-tzmigration/)
5
+
6
+
1
7
  # JSON data files
2
8
 
3
9
  All info is publicly available at the following URL:
@@ -2,6 +2,6 @@
2
2
 
3
3
  require 'a0/tzmigration/config'
4
4
  require 'a0/tzmigration/data_generator'
5
- require 'a0/tzmigration/range_list'
6
5
  require 'a0/tzmigration/tzversion'
6
+ require 'a0/tzmigration/util'
7
7
  require 'a0/tzmigration/version'
@@ -1,74 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rest-client'
4
-
5
3
  module A0
6
4
  module TZMigration
7
5
  class TZVersion
8
- attr_reader :path, :name, :version
6
+ attr_reader :name, :version
9
7
 
10
8
  def initialize(name, version)
11
9
  @name = name
12
10
  @version = version
13
-
14
- transitions
15
11
  end
16
12
 
17
- def self.load_from_network(path)
18
- url = "#{A0::TZMigration.config.base_url}/#{path}"
13
+ def data
14
+ return @data if defined? @data
19
15
 
20
- JSON.parse RestClient.get(url)
16
+ @data = Util.load_from_network_or_file("timezones/#{name}.json")
21
17
  end
22
18
 
23
- def self.load_from_file(path)
24
- conf = A0::TZMigration.config.data_dir
25
- file = File.join(conf, path)
26
-
27
- raise "File #{path} not found at #{conf}" unless File.exist? file
28
-
29
- JSON.parse File.read(file)
30
- end
19
+ def version_data
20
+ return @version_data if defined? @version_data
31
21
 
32
- def self.load_from_network_or_file(path)
33
- load_from_network(path)
34
- rescue StandardError => error
35
- warn "Unable to fetch from network, using local files (error was: #{error})"
36
- load_from_file(path)
37
- end
22
+ raise "Version #{@version} not found for #{@name}." unless (@version_data = data.dig('versions', @version))
38
23
 
39
- def data
40
- return @data if defined? @data
24
+ if @version_data['alias']
25
+ @link = TZVersion.new(@version_data['alias'], @version)
26
+ @version_data = @link.version_data
27
+ end
41
28
 
42
- @data = TZVersion.load_from_network_or_file("timezones/#{name}.json")
29
+ @version_data
43
30
  end
44
31
 
45
32
  def released_at
46
33
  return @released_at if defined? @released_at
47
34
 
48
- @released_at = Time.parse version_data['released_at']
35
+ @released_at = version_data['released_at']
49
36
  end
50
37
 
51
- def version_data
52
- return @version_data if defined? @version_data
53
-
54
- raise "Version #{@version} not found" unless (@version_data = data.dig('versions', @version))
38
+ def transitions
39
+ return @transitions if defined? @transitions
55
40
 
56
- @version_data
41
+ @transitions = version_data['transitions']
57
42
  end
58
43
 
59
- def transitions
60
- return @transitions if defined? @transitions
44
+ def transition_ranges # rubocop:disable Metrics/AbcSize
45
+ return @transition_ranges if defined? @transition_ranges
46
+
47
+ ini = -Float::INFINITY
48
+ fin = +Float::INFINITY
61
49
 
62
- if version_data['alias']
63
- @transitions = TZVersion.new(version_data['alias'], @version).transitions
64
- else
65
- @transitions = version_data['transitions']
66
- @transitions.each do |transition|
67
- transition['utc_time'] = Time.parse(transition['utc_time'])
68
- end
50
+ return @transition_ranges = [Util.range_item(ini, fin, 0)] if transitions.empty?
51
+
52
+ @transition_ranges = transitions.map do |transition|
53
+ Util.range_item(ini, (ini = transition['utc_timestamp']), transition['utc_prev_offset'])
69
54
  end
70
55
 
71
- @transitions
56
+ @transition_ranges << Util.range_item(@transition_ranges.last[:fin], fin, transitions.last['utc_offset'])
57
+
58
+ @transition_ranges
72
59
  end
73
60
 
74
61
  def timestamps
@@ -83,50 +70,28 @@ module A0
83
70
  @timestamps
84
71
  end
85
72
 
86
- def timezone_ranges
87
- return @timezone_ranges if defined? @timezone_ranges
88
-
89
- ini = -Float::INFINITY
90
- fin = +Float::INFINITY
91
-
92
- @timezone_ranges = transitions.map do |transition|
93
- { ini: ini, fin: (ini = transition['utc_timestamp']), off: transition['utc_prev_offset'] }
94
- end
95
-
96
- @timezone_ranges << { ini: @timezone_ranges.last[:fin], fin: fin, off: transitions.last['utc_offset'] } unless @timezone_ranges.empty?
97
-
98
- @timezone_ranges
99
- end
100
-
101
- def timezone_ranges_timed
102
- A0::TZMigration.timestamp_range_list!(Marshal.dump(timezone_ranges))
103
- end
104
-
105
- def delta_range_list(other) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
106
- raise "No transitions for self #{@name}/#{@version}" if transitions.empty?
107
- raise "No transitions for other #{name}/#{version}" if other.transitions.empty?
108
-
109
- timestamp_list = (timestamps + other.timestamps).uniq.sort
73
+ def changes(other) # rubocop:disable Metrics/AbcSize
74
+ timestamp_list = (timestamps + other.timestamps).sort.uniq
110
75
 
111
- list_a = A0::TZMigration.split_range_list!(Marshal.load(Marshal.dump(timezone_ranges)), timestamp_list)
112
- list_b = A0::TZMigration.split_range_list!(Marshal.load(Marshal.dump(other.timezone_ranges)), timestamp_list)
76
+ list_a = Util.split_ranges(transition_ranges, timestamp_list)
77
+ list_b = Util.split_ranges(other.transition_ranges, timestamp_list)
113
78
 
114
- delta = []
79
+ changes = []
115
80
  list_a.each_with_index do |range_a, index|
116
81
  range_b = list_b[index]
117
82
 
118
- delta << { ini: range_a[:ini], fin: range_a[:fin], off: range_b[:off] - range_a[:off] } if range_a[:off] != range_b[:off]
83
+ changes << Util.range_item(range_a[:ini], range_a[:fin], range_b[:off] - range_a[:off]) if range_a[:off] != range_b[:off]
119
84
  end
120
85
 
121
- A0::TZMigration.timestamp_range_list!(A0::TZMigration.compact_range_list!(delta))
86
+ Util.compact_ranges!(changes)
122
87
  end
123
88
 
124
89
  def self.versions
125
- @versions = load_from_network_or_file('versions/00-index.json')
90
+ @versions = Util.load_from_network_or_file('versions/00-index.json')['versions']
126
91
  end
127
92
 
128
93
  def self.timezones
129
- @timezones = load_from_network_or_file('timezones/00-index.json')
94
+ @timezones = Util.load_from_network_or_file('timezones/00-index.json')['timezones']
130
95
  end
131
96
  end
132
97
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-client'
4
+
5
+ module A0
6
+ module TZMigration
7
+ module Util
8
+ def self.compact_ranges!(range_list) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
9
+ index = 0
10
+
11
+ while index < range_list.count
12
+ curr_range = range_list[index]
13
+ next_range = range_list[index + 1]
14
+
15
+ if next_range && curr_range[:fin] == next_range[:ini] && curr_range[:off] == next_range[:off]
16
+ curr_range[:fin] = next_range[:fin]
17
+ curr_range[:fin_str] = next_range[:fin_str]
18
+ range_list.delete_at(index + 1)
19
+ else
20
+ index += 1
21
+ end
22
+ end
23
+
24
+ range_list
25
+ end
26
+
27
+ def self.offset_to_str(offset)
28
+ str = Time.at(offset.abs).utc.strftime('%H:%M:%S')
29
+ sig = offset.negative? ? '-' : '+'
30
+
31
+ "#{sig}#{str}"
32
+ end
33
+
34
+ def self.timestamp_to_str(timestamp)
35
+ if timestamp == -Float::INFINITY
36
+ '-∞'
37
+ elsif timestamp == +Float::INFINITY
38
+ '∞'
39
+ else
40
+ Time.at(timestamp).utc.to_s
41
+ end
42
+ end
43
+
44
+ def self.range_item(ini, fin, off)
45
+ { ini_str: timestamp_to_str(ini), fin_str: timestamp_to_str(fin), off_str: offset_to_str(off), ini: ini, fin: fin, off: off }
46
+ end
47
+
48
+ def self.next_index(index, range_list, timestamp)
49
+ index += 1 while range_list[index + 1] && range_list[index][:ini] < timestamp && range_list[index][:fin] <= timestamp
50
+
51
+ index
52
+ end
53
+
54
+ def self.split_ranges(range_list, timestamps) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
55
+ range_list = Marshal.load(Marshal.dump(range_list))
56
+ index = 0
57
+
58
+ timestamps.each do |timestamp|
59
+ index = next_index(index, range_list, timestamp)
60
+ range = range_list[index]
61
+
62
+ if index < range_list.count && timestamp > range[:ini] && timestamp < range[:fin]
63
+ range_list.insert index + 1, range.merge(ini: timestamp)
64
+ range_list[index][:fin] = timestamp
65
+ end
66
+ end
67
+
68
+ range_list
69
+ end
70
+
71
+ def self.load_from_network(path)
72
+ url = "#{A0::TZMigration.config.base_url}/#{path}"
73
+
74
+ JSON.parse RestClient.get(url)
75
+ end
76
+
77
+ def self.load_from_file(path)
78
+ conf = A0::TZMigration.config.data_dir
79
+ file = File.join(conf, path)
80
+
81
+ raise "File #{path} not found at #{conf}" unless File.exist? file
82
+
83
+ JSON.parse File.read(file)
84
+ end
85
+
86
+ def self.load_from_network_or_file(path)
87
+ load_from_network(path)
88
+ rescue StandardError => error
89
+ warn "Unable to fetch from network, using local files (error was: #{error})"
90
+ load_from_file(path)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module A0
4
4
  module TZMigration
5
- VERSION = '0.1.1'
5
+ VERSION = '1.0.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: a0-tzmigration-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldrin Martoq
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-31 00:00:00.000000000 Z
11
+ date: 2018-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -827,8 +827,8 @@ files:
827
827
  - lib/a0/tzmigration/data_generator/git.rb
828
828
  - lib/a0/tzmigration/data_generator/process.rb
829
829
  - lib/a0/tzmigration/data_generator/save.rb
830
- - lib/a0/tzmigration/range_list.rb
831
830
  - lib/a0/tzmigration/tzversion.rb
831
+ - lib/a0/tzmigration/util.rb
832
832
  - lib/a0/tzmigration/version.rb
833
833
  homepage: http://github.com/a0/a0-tzmigration-ruby
834
834
  licenses:
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module A0
4
- module TZMigration
5
- def self.compact_range_list!(range_list) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
6
- index = 0
7
-
8
- while index < range_list.count
9
- curr_range = range_list[index]
10
- next_range = range_list[index + 1]
11
-
12
- if next_range && curr_range[:fin] == next_range[:ini] && curr_range[:off] == next_range[:off]
13
- curr_range[:fin] = next_range[:fin]
14
- range_list.delete_at(index + 1)
15
- else
16
- index += 1
17
- end
18
- end
19
-
20
- range_list
21
- end
22
-
23
- def self.next_index(index, range_list, timestamp)
24
- index += 1 while range_list[index + 1] && range_list[index][:ini] < timestamp && range_list[index][:fin] <= timestamp
25
-
26
- index
27
- end
28
-
29
- def self.timestamp_to_utc(timestamp)
30
- Time.at(timestamp).utc if timestamp && timestamp != Float::INFINITY && timestamp != -Float::INFINITY
31
- end
32
-
33
- def self.timestamp_range_list!(range_list)
34
- range_list.each do |range|
35
- range[:utc_ini] = timestamp_to_utc(range[:ini])
36
- range[:utc_fin] = timestamp_to_utc(range[:fin])
37
- end
38
-
39
- range_list
40
- end
41
-
42
- def self.split_range_list!(range_list, timestamps) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
43
- index = next_index(0, range_list, timestamps.first)
44
-
45
- timestamps.each_with_index do |timestamp, timestamp_index|
46
- range = range_list[index]
47
-
48
- if timestamp > range[:ini] && timestamp < range[:fin] && index < range_list.count
49
- range_list.insert index + 1, range.merge(ini: timestamp)
50
- range_list[index][:fin] = timestamp
51
- end
52
-
53
- next_timestamp = timestamps[timestamp_index + 1]
54
-
55
- index = next_index(index, range_list, next_timestamp) if next_timestamp
56
- end
57
-
58
- range_list
59
- end
60
- end
61
- end