greenwich 0.0.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,12 @@
1
1
  *.gem
2
+ *.rbc
2
3
  .bundle
4
+ .config
5
+ .yardoc
3
6
  Gemfile.lock
4
- pkg/*
7
+ _yardoc
8
+ coverage
9
+ lib/bundler/man
10
+ pkg
11
+ spec/reports
12
+ tmp
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm use --create ruby-1.9.2-p180@greenwich
1
+ rvm use --create ruby-1.9.3-p125@greenwich
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in greenwich.gemspec
3
+ gem 'rake'
4
+
4
5
  gemspec
6
+
data/README.md CHANGED
@@ -29,90 +29,95 @@ Initialization
29
29
 
30
30
  You add Greenwich to your models like so:
31
31
 
32
- date_with_time_zone :field_name
32
+ time_with_time_zone :your_column_name_utc
33
33
 
34
- Meta-Programming Magic
35
- --------------------------------
34
+ By default, Greenwich removes the `_utc` from the column name and uses
35
+ the resulting string as the composed field.
36
36
 
37
- By default Greenwich looks for a few different columns in your model depending on the
38
- field name you passed in. Let's look at some examples.
37
+ For example, the above call would result in a composed field called
38
+ `your_column_name`.
39
39
 
40
- **DateTime Field Lookup**
40
+ ### Time Zones ###
41
41
 
42
- Greenwich will lookup `:field_name` based on a couple different standard column suffixes.
42
+ Greenwich will try to use convention to choose a time zone column name
43
+ based on the time field you choose.
43
44
 
44
- * `_at`
45
- * `_datetime`
45
+ time_with_time_zone :started_at_utc
46
46
 
47
- For example, if you specify:
47
+ Will look for a column called `started_at_time_zone` which contains the
48
+ time zone for the time field.
48
49
 
49
- date_with_time_zone :start
50
+ #### Custom Time Zone ####
50
51
 
51
- Greenwich will look for the columns `start_at` and `start_datetime` (in that order).
52
+ If you want to use the same time zone for multiple time fields or just
53
+ don't like the custom one we choose for you, you can pass a `:time_zone`
54
+ option which will override our default.
52
55
 
53
- **Time Zone Field Lookup**
56
+ time_with_time_zone :started_at_utc, :time_zone => :started_at_zone
54
57
 
55
- Time Zone lookups default to a per-field or per-model specification. If you specify:
58
+ Will tell Greenwich that when the user accesses the `started_at` method,
59
+ it will use the information from the `started_at_zone` column for its
60
+ time zone.
56
61
 
57
- date_with_time_zone :start
62
+ ### Convention... but missing a little configuration ###
58
63
 
59
- Greenwich will lookup the time zone from `:start_time_zone` first, and if it doesn't
60
- find a field by that name, it will use `:time_zone`.
64
+ We're on `v1.0.0` and have more features planned in the future, however
65
+ for now, all columns passed to `time_with_time_zone` _must_ end in `_utc`.
61
66
 
62
67
  Usage
63
68
  --------------------------------
64
- * **Note:** _These examples assume the application's default time zone is set to UTC.
65
- If you have modified the default time zone, directly accessing your DateTime field
66
- will render it in _that_ time zone and not UTC._
69
+ **Note:** _These examples assume the application's default time zone is set to UTC.
70
+ If you have modified the default time zone, directly accessing your DateTime field
71
+ will render it in **that** time zone and not UTC._
67
72
 
68
73
  When working with your instances, Greenwich will convert to the proper time zone when
69
- you access it. So if you've previously saved a DateTime like this:
74
+ you access it. So if you've defined a Greenwich time field like this:
75
+
76
+ time_with_time_zone :started_at_utc
70
77
 
71
- my_model.start = Time.strptime('2011-07-04 13:00:00 -600 CST')
78
+ And if you've previously saved a DateTime like this:
79
+
80
+ my_model.started_at_utc = Time.utc(2011, 7, 4, 13, 0, 0)
81
+ my_model.started_at_time_zone = 'Alaska'
72
82
 
73
83
  Then that will result in your model returning the following values (assuming these
74
84
  particular columns exist in the database):
75
85
 
76
- my_model.start_at # => 2011-07-04 19:00:00 GMT
77
- my_model.start_datetime # => 2011-07-04 19:00:00 GMT
78
- my_model.start_time_zone # => 'Central Standard Time'
79
- my_model.time_zone # => 'Central Standard Time'
86
+ my_model.started_at_utc # => ActiveSupport::TimeWithZone 2011-07-04 13:00:00 UTC
87
+ my_model.started_at_time_zone # => ActiveSupport::TimeZone 'Alaska'
80
88
 
81
- Whereas asking Greenwich for the value of `start` will result in:
89
+ Whereas asking Greenwich for the value of `started_at` will result in:
82
90
 
83
- my_model.start # => 2011-07-04 13:00:00 CST
91
+ my_model.started_at # => ActiveSupport::TimeWithZone 2011-07-04 04:00:00 AKDT
84
92
 
85
93
  If you then change your time zone:
86
94
 
87
- my_model.start_time_zone = 'Eastern Standard Time'
95
+ my_model.started_at_time_zone = 'Hawaii'
88
96
 
89
97
  Then calling the attributes on your model will result in the following:
90
98
 
91
- my_model.start_at # => 2011-07-04 19:00:00 GMT
92
- my_model.start_datetime # => 2011-07-04 19:00:00 GMT
93
- my_model.start_time_zone # => 'Eastern Standard Time'
94
- my_model.time_zone # => 'Eastern Standard Time'
99
+ my_model.started_at_utc # => ActiveSupport::TimeWithZone 2011-07-04 13:00:00 UTC
100
+ my_model.started_at_time_zone # => ActiveSupport::TimeZone 'Hawaii'
95
101
 
96
- And again, asking Greenwich for the value of `start` will result in:
102
+ And again, asking Greenwich for the value of `started_at` will result in:
97
103
 
98
- my_model.start # => 2011-07-04 13:00:00 EST
104
+ my_model.started_at # => ActiveSupport::TimeWithZone 2011-07-04 03:00:00 HADT
99
105
 
100
106
  Issues
101
107
  ------
102
108
 
103
- If you have problems, please create a [Github issue](https://github.com/jfelchner/greenwich/issues).
109
+ If you have problems, please create a [Github issue](issues).
104
110
 
105
111
  Credits
106
112
  -------
107
113
 
108
- ![thekompanee](http://www.thekompanee.com/public_files/kompanee-github-readme-logo.png)
114
+ ![chirrpy](https://dl.dropbox.com/s/f9s2qd0kmbc8nwl/github_logo.png?dl=1)
109
115
 
110
- greenwich is maintained by [The Kompanee, Ltd.](http://www.thekompanee.com)
116
+ greenwich is maintained by [Chrrpy, LLC](http://chirrpy.com)
111
117
 
112
- The names and logos for The Kompanee are trademarks of The Kompanee, Ltd.
118
+ The names and logos for Chirrpy are trademarks of Chrrpy, LLC
113
119
 
114
120
  License
115
121
  -------
116
122
 
117
- greenwich is Copyright © 2011 The Kompanee. It is free software, and may be redistributed under the terms specified in the LICENSE file.
118
-
123
+ greenwich is Copyright © 2011 Chirrpy. It is free software, and may be redistributed under the terms specified in the LICENSE file.
data/Rakefile CHANGED
@@ -1 +1,2 @@
1
+ #!/usr/bin/env rake
1
2
  require 'bundler/gem_tasks'
data/greenwich.gemspec CHANGED
@@ -11,9 +11,9 @@ Gem::Specification.new do |s|
11
11
  s.version = Greenwich::VERSION
12
12
  s.platform = Gem::Platform::RUBY
13
13
 
14
- s.authors = ["thekompanee", "jfelchner"]
15
- s.email = ["support@thekompanee.com"]
16
- s.homepage = "http://github.com/jfelchner/greenwich"
14
+ s.authors = ["jfelchner", "m5rk"]
15
+ s.email = ["jeff+greenwich@chirrpy.com"]
16
+ s.homepage = "http://github.com/chirrpy/greenwich"
17
17
 
18
18
  s.summary = %q{Allowing users to select dates with custom time zones since 2011.}
19
19
  s.description = %q{Store all of your times in the database as UTC but want to give your users the ability to choose a custom time zone for each instance of a DateTime field?}
@@ -28,9 +28,13 @@ Gem::Specification.new do |s|
28
28
  s.require_paths = ["lib"]
29
29
  #= Manifest =#
30
30
 
31
- s.add_dependency('activerecord', '~> 3.0')
31
+ s.add_dependency('activerecord', '~> 3.0')
32
+ s.add_dependency('activesupport', '~> 3.0')
33
+ s.add_dependency('tzinfo', '~> 0.3')
32
34
 
33
35
  s.add_development_dependency('bundler', '~> 1.0')
34
36
  s.add_development_dependency('rspec', '~> 2.6')
35
37
  s.add_development_dependency('yard', '~> 0.7')
38
+ s.add_development_dependency('sqlite3', '~> 1.3')
39
+ s.add_development_dependency('pry')
36
40
  end
data/lib/greenwich.rb CHANGED
@@ -1,5 +1,7 @@
1
- require "greenwich/version"
2
- require "greenwich/utilities"
3
- require "greenwich/time_with_zone"
4
- require "greenwich/rails"
5
-
1
+ require 'tzinfo'
2
+ require 'active_record'
3
+ require 'active_support/all'
4
+ require 'greenwich/conversion'
5
+ require 'greenwich/time_zone'
6
+ require 'greenwich/utilities'
7
+ require 'greenwich/version'
@@ -0,0 +1,82 @@
1
+ module Greenwich
2
+ module Conversion
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def time_with_time_zone(utc_time_field, options = {})
7
+ time_field = utc_time_field.to_s.gsub /_utc$/, ''
8
+ time_zone_field = options[:time_zone] || "#{time_field}_time_zone"
9
+
10
+ class_eval do
11
+ columns_hash[time_field] = ActiveRecord::ConnectionAdapters::Column.new(time_field, nil, "datetime")
12
+ end
13
+
14
+ define_method "#{time_field}_utc=" do |value|
15
+ greenwich_time_fields_converted["#{time_field}_utc"] = true
16
+
17
+ super(value)
18
+ end
19
+
20
+ define_method time_field do
21
+ time_zone = Greenwich::Utilities.get_time_zone(self, time_zone_field)
22
+ time = read_attribute(utc_time_field)
23
+ time = time.in_time_zone(time_zone) if time && time_zone
24
+
25
+ time
26
+ end
27
+
28
+ define_method "#{time_field}=" do |value|
29
+ time_zone = Greenwich::Utilities.get_time_zone(self, time_zone_field)
30
+ time = Greenwich::Utilities.coerce_to_time_without_zone(value)
31
+ time = ActiveSupport::TimeWithZone.new(nil, time_zone, time) if time && time_zone
32
+
33
+ greenwich_time_fields_converted["#{time_field}_utc"] = true unless time_zone.nil?
34
+
35
+ write_attribute(utc_time_field, time)
36
+ end
37
+
38
+ time_zone time_zone_field.to_sym, :for => utc_time_field.to_sym
39
+ end
40
+
41
+ def time_zone(name, options = {})
42
+ associated_time_fields = Array.wrap(options[:for]).map {|f| f.to_s.gsub /_utc$/, ''}
43
+
44
+ define_method name do
45
+ time_zone_name = read_attribute(name)
46
+
47
+ Greenwich::Utilities.coerce_to_time_zone(time_zone_name)
48
+ end
49
+
50
+ define_method "#{name}=" do |value|
51
+ time_zone = Greenwich::Utilities.coerce_to_time_zone_name(value)
52
+ write_attribute(name, time_zone)
53
+
54
+ associated_time_fields.each do |time_field|
55
+ if greenwich_time_field_needs_conversion?(time_field, name)
56
+ send("#{time_field}=".to_sym, read_attribute("#{time_field}_utc"))
57
+
58
+ greenwich_time_fields_converted["#{time_field}_utc"] = true
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def greenwich_time_fields_converted
66
+ @greenwich_time_fields_converted ||= {}
67
+ end
68
+
69
+ def greenwich_time_fields_converted=(value)
70
+ @greenwich_time_fields_converted = value
71
+ end
72
+
73
+ private
74
+ def greenwich_time_field_needs_conversion?(time_field, time_zone_field)
75
+ (send("#{time_zone_field}_was".to_sym).nil? || send("#{time_field}_utc_was").nil?) &&
76
+ read_attribute("#{time_field}_utc").present? &&
77
+ self.greenwich_time_fields_converted["#{time_field}_utc"].nil?
78
+ end
79
+ end
80
+ end
81
+
82
+ ActiveRecord::Base.send :include, Greenwich::Conversion
@@ -0,0 +1,7 @@
1
+ module ActiveSupport
2
+ class TimeZone
3
+ def to_s
4
+ name
5
+ end
6
+ end
7
+ end
@@ -6,22 +6,34 @@ module Greenwich
6
6
  get_target_column(target_columns, columns)
7
7
  end
8
8
 
9
- def self.get_time_field(name, columns)
10
- target_columns = ["#{name}_at", "#{name}_datetime", "#{name}_time"]
9
+ def self.get_time_zone(object, time_zone_field_name)
10
+ begin
11
+ time_zone_name = object.send(time_zone_field_name.to_sym)
12
+ rescue
13
+ time_zone_name = ''
14
+ end
11
15
 
12
- get_target_column(target_columns, columns)
16
+ Greenwich::Utilities.coerce_to_time_zone(time_zone_name)
13
17
  end
14
18
 
15
- def self.get_time_zone_from(value)
16
- return nil if [nil, ''].include? value
19
+ def self.coerce_to_time_zone(value)
20
+ return nil if value.nil?
21
+ return value if value.is_a? ActiveSupport::TimeZone
17
22
 
18
- begin
19
- value = ActiveSupport::TimeZone.new(value) unless value.is_a? ActiveSupport::TimeZone
20
- rescue ArgumentError
21
- raise ArgumentError, "'#{value}' cannot be converted into a TimeZone."
22
- end
23
+ ActiveSupport::TimeZone.new(value)
24
+ end
25
+
26
+ def self.coerce_to_time_zone_name(value)
27
+ coerce_to_time_zone(value).try(:name)
28
+ end
23
29
 
24
- value
30
+ def self.coerce_to_time_without_zone(value)
31
+ return value if value.is_a?(Time)
32
+
33
+ value.gsub! /\s[-+]\d{4}$/, '' if value.respond_to? :gsub!
34
+ value.to_time if value.respond_to? :to_time
35
+ rescue ArgumentError
36
+ nil
25
37
  end
26
38
 
27
39
  private
@@ -1,3 +1,3 @@
1
1
  module Greenwich
2
- VERSION = "0.0.5"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,313 @@
1
+ require 'spec_helper'
2
+ require 'pry'
3
+
4
+ root = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
5
+ db_root = File.join(root, 'db')
6
+
7
+ Dir.mkdir(db_root) unless File.exists?(db_root)
8
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3',
9
+ :database => "#{db_root}/conversion.db")
10
+
11
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'model_with_time_zones'")
12
+ ActiveRecord::Base.connection.create_table(:model_with_time_zones) do |t|
13
+ t.datetime :started_at_utc
14
+ t.datetime :ended_at_utc
15
+ t.string :time_zone
16
+ end
17
+
18
+ class ModelWithTimeZone < ActiveRecord::Base
19
+ include Greenwich::Conversion
20
+
21
+ attr_accessible :started_at_utc,
22
+ :ended_at_utc,
23
+ :time_zone
24
+
25
+ time_with_time_zone :started_at_utc, :time_zone => :time_zone
26
+ time_with_time_zone :ended_at_utc, :time_zone => :time_zone
27
+
28
+ time_zone :time_zone, :for => [:started_at_utc, :ended_at_utc]
29
+ end
30
+
31
+ describe Greenwich::Conversion do
32
+ describe '.time_with_time_zone' do
33
+ let(:model) { ModelWithTimeZone.new }
34
+ let(:alaskan_time_zone) { ActiveSupport::TimeZone.new('Alaska') }
35
+
36
+ describe '.columns_hash' do
37
+ it 'adds the virtual column to the columns_hash for the time field' do
38
+ ModelWithTimeZone.columns_hash.keys.should include 'started_at'
39
+ end
40
+
41
+ it 'adds the virtual column with a `datetime` type' do
42
+ ModelWithTimeZone.columns_hash['started_at'].type.should eql :datetime
43
+ end
44
+ end
45
+
46
+ describe '#time_field_utc=' do
47
+ let(:model) { ModelWithTimeZone.new }
48
+ let(:alaskan_time_zone) { ActiveSupport::TimeZone.new('Alaska') }
49
+ let(:raw_time_field) { model.send(:read_attribute, :started_at_utc) }
50
+
51
+ context 'when the time field is set via the writer' do
52
+ before { model.started_at_utc = Time.utc(2012, 1, 2, 12, 59, 1) }
53
+
54
+ it 'is not changed' do
55
+ raw_time_field.should eql Time.utc(2012, 1, 2, 12, 59, 1)
56
+ end
57
+
58
+ it 'is not eligible for conversion' do
59
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_false
60
+ end
61
+
62
+ context 'and the time zone is subsequently set' do
63
+ before { model.time_zone = alaskan_time_zone }
64
+
65
+ it 'is not changed' do
66
+ raw_time_field.should eql Time.utc(2012, 1, 2, 12, 59, 1)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#time_field' do
73
+ context 'when it is nil' do
74
+ before { model.send :write_attribute, :started_at_utc, nil }
75
+
76
+ it 'is nil' do
77
+ model.started_at.should be_nil
78
+ end
79
+ end
80
+
81
+ context 'when there is no time zone' do
82
+ let(:raw_time_value) { Time.utc(2012, 1, 1, 12, 0, 0) }
83
+
84
+ before do
85
+ model.send :write_attribute, :started_at_utc, raw_time_value
86
+ model.stub(:time_zone).and_return nil
87
+ end
88
+
89
+ it 'returns the raw time field' do
90
+ model.started_at.should eql raw_time_value
91
+ end
92
+ end
93
+
94
+ context 'when it is something other than a Time' do
95
+ before { model.send :write_attribute, :started_at_utc, 5 }
96
+
97
+ it 'returns the raw value' do
98
+ model.started_at.should eql 5
99
+ end
100
+ end
101
+
102
+ context 'when the time zone is set properly' do
103
+ before { model.stub(:time_zone).and_return 'Alaska' }
104
+
105
+ context 'when it is a time' do
106
+ before { model.send :write_attribute, :started_at_utc, Time.utc(2012, 1, 2, 12, 59, 1) }
107
+
108
+ it 'returns the time in the time zone' do
109
+ model.started_at.should eql alaskan_time_zone.parse('2012-01-02 3:59:01')
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#time_field=' do
116
+ let(:raw_time_field) { model.started_at_utc }
117
+
118
+ context 'when the time zone is set' do
119
+ before { model.time_zone = alaskan_time_zone.name }
120
+
121
+ context 'and the field is set to nil' do
122
+ before { model.started_at = nil }
123
+
124
+ it 'the time field is nil' do
125
+ raw_time_field.should be_nil
126
+ end
127
+
128
+ it 'does not need to be converted' do
129
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_false
130
+ end
131
+ end
132
+
133
+ context 'and the field is set to something which cannot be converted to a time' do
134
+ before { model.started_at = 'foo' }
135
+
136
+ it 'the time field is nil' do
137
+ raw_time_field.should be_nil
138
+ end
139
+
140
+ it 'does not need to be converted' do
141
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_false
142
+ end
143
+ end
144
+
145
+ context 'and the field is set with UTC time' do
146
+ before { model.started_at = Time.utc(2012, 1, 2, 12, 59, 1) }
147
+
148
+ it 'the time field is adjusted for the time zone' do
149
+ raw_time_field.should eql Time.utc(2012, 1, 2, 21, 59, 1)
150
+ end
151
+
152
+ it 'does not need to be converted' do
153
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_false
154
+ end
155
+ end
156
+ end
157
+
158
+ context 'when the time zone is not set' do
159
+ before { model.time_zone = nil }
160
+
161
+ context 'and the time field is set' do
162
+ before { model.started_at = Time.utc(2012, 1, 2, 12, 59, 1) }
163
+
164
+ it 'the time field is not adjusted' do
165
+ raw_time_field.should eql Time.utc(2012, 1, 2, 12, 59, 1)
166
+ end
167
+
168
+ it 'needs to be converted' do
169
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_true
170
+ end
171
+ end
172
+
173
+ context 'and the time field is not set' do
174
+ before { model.started_at = nil }
175
+
176
+ it 'the time field is nil' do
177
+ raw_time_field.should be_nil
178
+ end
179
+
180
+ it 'does not need to be converted' do
181
+ model.send(:greenwich_time_field_needs_conversion?, 'started_at', 'time_zone').should be_false
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ context 'when it is saved to the database and reloaded' do
188
+ before do
189
+ model.started_at_utc = Time.utc(2012, 1, 2, 21, 59, 1)
190
+ model.time_zone = 'Alaska'
191
+ model.save!
192
+
193
+ model.reload
194
+ end
195
+
196
+ context 'and the time zone is set' do
197
+ before { model.time_zone = 'Hawaii' }
198
+
199
+ it 'does not modify the UTC time' do
200
+ model.started_at_utc.should eql Time.utc(2012, 1, 2, 21, 59, 1)
201
+ end
202
+ end
203
+
204
+ it 'converts the time field to the local time' do
205
+ model.started_at.should_not be_utc
206
+ model.started_at.should eql alaskan_time_zone.parse('2012-01-02 12:59:01')
207
+ end
208
+
209
+ it 'converts the time field to a TimeWithZone' do
210
+ model.started_at.should be_a ActiveSupport::TimeWithZone
211
+ end
212
+ end
213
+ end
214
+
215
+ describe '.time_zone' do
216
+ let(:model) { ModelWithTimeZone.new }
217
+ let(:alaskan_time_zone) { ActiveSupport::TimeZone.new('Alaska') }
218
+
219
+ context '#time_zone' do
220
+ context 'when the object does have a time zone' do
221
+ before { model.send(:write_attribute, :time_zone, alaskan_time_zone.name) }
222
+
223
+ it 'is the time zone' do
224
+ model.time_zone.should eql alaskan_time_zone
225
+ end
226
+ end
227
+
228
+ context 'when the object does not have a time zone' do
229
+ before { model.send(:write_attribute, :time_zone, nil) }
230
+
231
+ it 'is nil' do
232
+ model.time_zone.should be_nil
233
+ end
234
+ end
235
+ end
236
+
237
+ describe '#time_zone=' do
238
+ let(:model) { ModelWithTimeZone.new }
239
+ let(:alaskan_time_zone) { ActiveSupport::TimeZone.new('Alaska') }
240
+ let(:hawaii_time_zone) { ActiveSupport::TimeZone.new('Hawaii') }
241
+ let(:raw_time_field) { model.started_at_utc }
242
+ let(:raw_time_zone) { model.read_attribute(:time_zone) }
243
+
244
+ context 'when it is set after the time field is set' do
245
+ before do
246
+ model.send :write_attribute, :started_at_utc, Time.utc(2012, 1, 2, 12, 59, 1)
247
+ model.time_zone = alaskan_time_zone
248
+ end
249
+
250
+ it 'triggers the time field to be converted' do
251
+ raw_time_field.should eql Time.utc(2012, 1, 2, 21, 59, 1)
252
+ end
253
+
254
+ context 'but when it is set subsequently' do
255
+ before do
256
+ model.time_zone.should_not be_nil
257
+ model.started_at_utc.should_not be_nil
258
+ model.time_zone = hawaii_time_zone
259
+ end
260
+
261
+ it 'does not convert the time field' do
262
+ raw_time_field.should eql Time.utc(2012, 1, 2, 21, 59, 1)
263
+ end
264
+
265
+ context 'if some other time field is then set' do
266
+ before do
267
+ model.send :write_attribute, :ended_at_utc, Time.utc(2012, 1, 2, 13, 59, 1)
268
+ model.time_zone = alaskan_time_zone
269
+ end
270
+
271
+ it 'converts only the time fields that have not already been converted' do
272
+ raw_time_field.should eql Time.utc(2012, 1, 2, 21, 59, 1)
273
+ model.ended_at_utc.should eql Time.utc(2012, 1, 2, 22, 59, 1)
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ context 'when it is set before the time field is set' do
280
+ before { model.time_zone = alaskan_time_zone }
281
+
282
+ it 'sets the time zone but does not touch the time' do
283
+ raw_time_field.should be_nil
284
+ raw_time_zone.should eql 'Alaska'
285
+ end
286
+ end
287
+
288
+ context 'when it is set to an ActiveSupport::TimeZone' do
289
+ before { model.time_zone = alaskan_time_zone }
290
+
291
+ it 'is set properly' do
292
+ raw_time_zone.should eql 'Alaska'
293
+ end
294
+ end
295
+
296
+ context 'when it is set to a time zone name' do
297
+ before { model.time_zone = 'Alaska' }
298
+
299
+ it 'is set properly' do
300
+ raw_time_zone.should eql 'Alaska'
301
+ end
302
+ end
303
+
304
+ context 'when it is set to an invalid time zone' do
305
+ before { model.time_zone = 'I am not a time zone' }
306
+
307
+ it 'is nil' do
308
+ raw_time_zone.should be_nil
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end