a0-tzmigration-ruby 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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