duranged 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Duranged
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require File.join(File.dirname(__FILE__), '..', 'shared/base_examples.rb')
3
+
4
+ RSpec.describe Duranged::Base do
5
+ it_behaves_like "the base class", Duranged::Base
6
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require File.join(File.dirname(__FILE__), '..', 'shared/base_examples.rb')
3
+
4
+ RSpec.describe Duranged::Duration do
5
+ it_behaves_like "the base class", Duranged::Duration
6
+
7
+ describe '#duration' do
8
+ subject { Duranged::Duration.new(30.seconds) }
9
+
10
+ it 'returns the duration value' do
11
+ expect(subject.duration).to eq(subject.value)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require File.join(File.dirname(__FILE__), '..', 'shared/base_examples.rb')
3
+
4
+ RSpec.describe Duranged::Interval do
5
+ it_behaves_like "the base class", Duranged::Interval
6
+
7
+ describe '#interval' do
8
+ subject { Duranged::Interval.new(30.seconds) }
9
+
10
+ it 'returns the interval value' do
11
+ expect(subject.interval).to eq(subject.value)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Duranged::Occurrence do
4
+ subject { Duranged::Occurrence.new(5, 36.hours) }
5
+
6
+ describe 'dump' do
7
+ it 'dumps the occurrence as a JSON hash' do
8
+ expect(Duranged::Occurrence.dump(subject)).to eq subject.to_json
9
+ end
10
+ end
11
+
12
+ describe 'load' do
13
+ context 'without a range' do
14
+ it 'creates a new occurrence from a JSON hash' do
15
+ expect(Duranged::Occurrence.load(subject.to_json)).to be_an_instance_of Duranged::Occurrence
16
+ expect(Duranged::Occurrence.load(subject.to_json).as_json).to eq subject.as_json
17
+ end
18
+ end
19
+
20
+ context 'with a range' do
21
+ subject { Duranged::Occurrence.new(5, 36.hours, 10.minutes, Time.now, Time.now + 3.days) }
22
+
23
+ it 'creates a new occurrence from a JSON hash' do
24
+ expect(Duranged::Occurrence.load(subject.to_json)).to be_an_instance_of Duranged::Occurrence
25
+ expect(Duranged::Occurrence.load(subject.to_json).as_json).to eq subject.as_json
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#initialize' do
31
+ context 'with occurrences' do
32
+ it 'sets occurrences' do
33
+ expect(subject.occurrences).to eq 5
34
+ end
35
+ end
36
+
37
+ context 'without occurrences' do
38
+ subject { Duranged::Occurrence.new }
39
+
40
+ it 'sets occurrences to 1' do
41
+ expect(subject.occurrences).to eq 1
42
+ end
43
+ end
44
+
45
+ context 'with interval' do
46
+ it 'sets interval' do
47
+ expect(subject.interval).to eq 36.hours
48
+ end
49
+ end
50
+
51
+ context 'without interval' do
52
+ subject { Duranged::Occurrence.new }
53
+
54
+ it 'sets interval to 0' do
55
+ expect(subject.interval).to eq nil
56
+ end
57
+ end
58
+
59
+ context 'with duration' do
60
+ subject { Duranged::Occurrence.new(5, 36.hours, 20.minutes) }
61
+
62
+ it 'sets duration' do
63
+ expect(subject.duration).to eq 20.minutes
64
+ end
65
+ end
66
+
67
+ context 'without duration' do
68
+ subject { Duranged::Occurrence.new }
69
+
70
+ it 'sets duration to 0' do
71
+ expect(subject.duration).to eq nil
72
+ end
73
+ end
74
+ end
75
+
76
+ describe '#occurrences_string' do
77
+ context 'with zero occurrences' do
78
+ subject { Duranged::Occurrence.new(0, 1.minute) }
79
+
80
+ it "returns 'never'" do
81
+ expect(subject.occurrences_string).to eq 'never'
82
+ end
83
+ end
84
+
85
+ context 'with a single occurrence' do
86
+ subject { Duranged::Occurrence.new(1, 1.minute) }
87
+
88
+ it "returns 'once'" do
89
+ expect(subject.occurrences_string).to eq 'once'
90
+ end
91
+ end
92
+
93
+ context 'with two occurrences' do
94
+ subject { Duranged::Occurrence.new(2, 1.minute) }
95
+
96
+ it "returns 'twice'" do
97
+ expect(subject.occurrences_string).to eq 'twice'
98
+ end
99
+ end
100
+
101
+ context 'with multiple occurrences' do
102
+ subject { Duranged::Occurrence.new([3,4,5].sample, 1.minute) }
103
+
104
+ it "returns 'X times'" do
105
+ expect(subject.occurrences_string).to eq "#{subject.occurrences} times"
106
+ end
107
+ end
108
+ end
109
+
110
+ describe '#as_json' do
111
+ context 'without a range' do
112
+ it 'returns an occurrences hash' do
113
+ expect(subject.as_json).to eq({occurrences: subject.occurrences, interval: subject.interval.as_json})
114
+ end
115
+ end
116
+
117
+ context 'with a range' do
118
+ subject { Duranged::Occurrence.new(5, 36.hours, 10.minutes, Time.now, Time.now + 3.days) }
119
+
120
+ it 'returns an occurrences hash' do
121
+ expect(subject.as_json).to eq({occurrences: subject.occurrences, duration: subject.duration.as_json, interval: subject.interval.as_json, range: subject.range.as_json})
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#to_s' do
127
+ subject { Duranged::Occurrence.new(3, 1.day, 10.minutes) }
128
+
129
+ it "returns a string matching 'OCCURRENCES for DURATION every INTERVAL'" do
130
+ expect(subject.to_s).to eq "#{subject.occurrences_string} for #{subject.duration.to_s} every #{subject.interval.to_s}"
131
+ end
132
+
133
+ context 'when occurrences is 0' do
134
+ subject { Duranged::Occurrence.new(0, 30.seconds) }
135
+
136
+ it 'returns the occurrences_string' do
137
+ expect(subject.to_s).to eq subject.occurrences_string
138
+ end
139
+
140
+ it "returns 'never'" do
141
+ expect(subject.to_s).to eq 'never'
142
+ end
143
+ end
144
+
145
+ context 'when interval is 0' do
146
+ subject { Duranged::Occurrence.new(3, 0, 30.seconds) }
147
+
148
+ it "returns a string matching 'OCCURRENCES for DURATION'" do
149
+ expect(subject.to_s).to eq "#{subject.occurrences_string} for #{subject.duration.to_s}"
150
+ end
151
+
152
+ context 'when duration is 0' do
153
+ subject { Duranged::Occurrence.new(3) }
154
+
155
+ it 'returns the occurrences string' do
156
+ expect(subject.to_s).to eq subject.occurrences_string
157
+ end
158
+ end
159
+ end
160
+
161
+ context 'when duration is 0' do
162
+ subject { Duranged::Occurrence.new(3, 1.day) }
163
+
164
+ it "returns a string matching 'OCCURRENCES every INTERVAL'" do
165
+ expect(subject.to_s).to eq "#{subject.occurrences_string} every #{subject.interval.to_s}"
166
+ end
167
+
168
+ context 'when interval is 0' do
169
+ subject { Duranged::Occurrence.new(3) }
170
+
171
+ it 'returns the occurrences string' do
172
+ expect(subject.to_s).to eq subject.occurrences_string
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#strfocc' do
179
+ context 'using the :occurence token' do
180
+ it 'returns the formatted string' do
181
+ expect(subject.strfocc(':occurrence')).to eq subject.occurrences.to_s
182
+ end
183
+ end
184
+
185
+ context 'using the :occurences token' do
186
+ it 'returns the formatted string' do
187
+ expect(subject.strfocc(':occurrences')).to eq subject.occurrences.to_s
188
+ end
189
+ end
190
+
191
+ context 'using the :duration token' do
192
+ subject { Duranged::Occurrence.new(5, 10.minutes, 30.seconds) }
193
+
194
+ it 'requires nested formatters' do
195
+ expect(subject.strfocc(':duration')).to eq ':duration'
196
+ end
197
+
198
+ it 'returns the formatted string' do
199
+ expect(subject.strfocc(':duration(%d:%h:%m:%s)')).to eq subject.duration.strfdur('%d:%h:%m:%s')
200
+ end
201
+ end
202
+
203
+ context 'using the :interval token' do
204
+ it 'requires nested formatters' do
205
+ expect(subject.strfocc(':interval')).to eq ':interval'
206
+ end
207
+
208
+ it 'returns the formatted string' do
209
+ expect(subject.strfocc(':interval(%d:%h:%m:%s)')).to eq subject.interval.strfdur('%d:%h:%m:%s')
210
+ end
211
+ end
212
+
213
+ context 'using the :range token' do
214
+ subject { Duranged::Occurrence.new(5, 10.minutes, 30.seconds, Time.now, Time.now + 1.hour) }
215
+
216
+ context 'without nested formatters' do
217
+ it 'returns the default formatted range string' do
218
+ expect(subject.strfocc(':range')).to eq subject.range.to_s
219
+ end
220
+ end
221
+
222
+ context 'with nested formatters' do
223
+ it 'returns the formatted string' do
224
+ expect(subject.strfocc(':range(:start_at(%l:%M%P) :end_at(%l:%M%P) :duration(%D%H%M%S))')).to eq "#{subject.range.start_at('%l:%M%P')} #{subject.range.end_at('%l:%M%P')} #{subject.range.strfdur('%D%H%M%S')}"
225
+ end
226
+ end
227
+ end
228
+
229
+ context "using complex format string ':occurrences times for :duration(%-s) seconds every :interval(%-m) minutes between :range(:start_at(%l:%M%P) and :end_at(%l:%M%P) (:duration(%-m minutes)))'" do
230
+ subject { Duranged::Occurrence.new(3, 5.minutes, 30.seconds, DateTime.parse('2015-01-01T06:00:00-07:00'), DateTime.parse('2015-01-01T06:20:00-07:00')) }
231
+
232
+ it "returns '3 times for 30 seconds every 5 minutes between 6:00am and 6:20am'" do
233
+ expect(subject.strfocc(':occurrences times for :duration(%-s) seconds every :interval(%-m) minutes between :range(:start_at(%l:%M%P) and :end_at(%l:%M%P) (:duration(%-m minutes)))')).to eq '3 times for 30 seconds every 5 minutes between 6:00am and 6:20am (20 minutes)'
234
+ end
235
+ end
236
+
237
+ context "using complex format string 'between :range(:start_at(%l:%M%P) and :end_at(%l:%M%P) (:duration(%-m minutes))), do something :occurrences times for :duration(%-s) seconds every :interval(%-m) minutes'" do
238
+ subject { Duranged::Occurrence.new(3, 5.minutes, 30.seconds, DateTime.parse('2015-01-01T06:00:00-07:00'), DateTime.parse('2015-01-01T06:20:00-07:00')) }
239
+
240
+ it "returns 'between 6:00am and 6:20am (20 minutes), do something 3 times for 30 seconds every 5 minutes'" do
241
+ expect(subject.strfocc('between :range(:start_at(%l:%M%P) and :end_at(%l:%M%P) (:duration(%-m minutes))), do something :occurrences times for :duration(%-s) seconds every :interval(%-m) minutes')).to eq 'between 6:00am and 6:20am (20 minutes), do something 3 times for 30 seconds every 5 minutes'
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,324 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.shared_examples "a format token" do |token, format, meth|
4
+ it 'requires nested formatters' do
5
+ expect(subject.strfrange("#{token}")).to eq "#{token}"
6
+ end
7
+
8
+ it "returns the formatted #{token}" do
9
+ expect(subject.strfrange("#{token}(#{format})")).to eq subject.send(meth, format)
10
+ end
11
+ end
12
+
13
+ RSpec.describe Duranged::Range do
14
+ now = Time.now
15
+ subject { Duranged::Range.new(now, 60.minutes) }
16
+
17
+ describe 'dump' do
18
+ it 'dumps the time range as a JSON hash' do
19
+ expect(Duranged::Range.dump(subject)).to eq subject.to_json
20
+ end
21
+ end
22
+
23
+ describe 'load' do
24
+ it 'creates a new time range from a JSON hash' do
25
+ expect(Duranged::Range.load(subject.to_json)).to be_an_instance_of Duranged::Range
26
+ expect(Duranged::Range.load(subject.to_json).as_json).to eq subject.as_json
27
+ end
28
+ end
29
+
30
+ describe '#initialize' do
31
+ context 'with a start_at and end_at' do
32
+ subject { Duranged::Range.new(now, (now + 60.minutes)) }
33
+
34
+ it 'sets start_at' do
35
+ expect(subject.start_at).to eq now
36
+ end
37
+
38
+ it 'sets end_at' do
39
+ expect(subject.end_at).to eq(now + 60.minutes)
40
+ end
41
+ end
42
+
43
+ context 'with a start_at and duration' do
44
+ subject { Duranged::Range.new(now, 60.minutes) }
45
+
46
+ it 'sets start_at' do
47
+ expect(subject.start_at).to eq now
48
+ end
49
+
50
+ context 'when the duration is an ActiveSupport::Duration' do
51
+ subject { Duranged::Range.new(now, 60.minutes) }
52
+
53
+ it 'sets end_at to start_at + duration' do
54
+ expect(subject.end_at).to eq(now + 60.minutes)
55
+ end
56
+ end
57
+
58
+ context 'when the duration is an integer' do
59
+ subject { Duranged::Range.new(now, 3600) }
60
+
61
+ it 'sets end_at to start_at + duration' do
62
+ expect(subject.end_at).to eq(now + 60.minutes)
63
+ end
64
+ end
65
+
66
+ context 'when the duration is a hash' do
67
+ subject { Duranged::Range.new(now, {minutes: 60}) }
68
+
69
+ it 'sets end_at to start_at + duration' do
70
+ expect(subject.end_at).to eq(now + 60.minutes)
71
+ end
72
+ end
73
+
74
+ context 'when the duration is a string' do
75
+ subject { Duranged::Range.new(now, "1 day, 2 hours and 30 minutes") }
76
+
77
+ it 'sets end_at to start_at + duration' do
78
+ expect(subject.end_at).to eq(now + (26.hours + 30.minutes))
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'with just a duration' do
84
+ subject { Duranged::Range.new(60.minutes) }
85
+
86
+ it 'sets start_at to now' do
87
+ expect(subject.start_at).to be_between((Time.now - 1.seconds), Time.now)
88
+ end
89
+
90
+ context 'when the duration is an ActiveSupport::Duration' do
91
+ subject { Duranged::Range.new(60.minutes) }
92
+
93
+ it 'sets end_at to start_at + duration' do
94
+ expect(subject.end_at).to eq(subject.start_at + 60.minutes)
95
+ end
96
+ end
97
+
98
+ context 'when the duration is an integer' do
99
+ subject { Duranged::Range.new(3600) }
100
+
101
+ it 'sets end_at to start_at + duration' do
102
+ expect(subject.end_at).to eq(subject.start_at + 60.minutes)
103
+ end
104
+ end
105
+
106
+ context 'when the duration is a hash' do
107
+ subject { Duranged::Range.new({minutes: 60}) }
108
+
109
+ it 'sets end_at to start_at + duration' do
110
+ expect(subject.end_at).to eq(subject.start_at + 60.minutes)
111
+ end
112
+ end
113
+ end
114
+
115
+ context 'with just an end_at' do
116
+ subject { Duranged::Range.new(now + 60.minutes) }
117
+
118
+ it 'sets start_at to now' do
119
+ expect(subject.start_at).to be_between((Time.now - 1.seconds), Time.now)
120
+ end
121
+
122
+ it 'sets end_at' do
123
+ expect(subject.end_at).to eq(now + 60.minutes)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#start_at' do
129
+ subject { Duranged::Range.new(now, 60.minutes) }
130
+
131
+ context 'without a format argument' do
132
+ it 'returns the start_at DateTime object' do
133
+ expect(subject.start_at).to eq now
134
+ expect(subject.start_at).to be_an_instance_of DateTime
135
+ end
136
+ end
137
+
138
+ context 'with a format argument' do
139
+ it 'returns a formatted string' do
140
+ expect(subject.start_at('%A, %b %e, %Y %l:%M%P')).to eq now.strftime('%A, %b %e, %Y %l:%M%P')
141
+ end
142
+ end
143
+ end
144
+
145
+ describe '#end_at' do
146
+ subject { Duranged::Range.new(now, 60.minutes) }
147
+
148
+ context 'without a format argument' do
149
+ it 'returns the end_at DateTime object' do
150
+ expect(subject.end_at).to eq(now + 60.minutes)
151
+ expect(subject.end_at).to be_an_instance_of DateTime
152
+ end
153
+ end
154
+
155
+ context 'with a format argument' do
156
+ it 'returns a formatted string' do
157
+ expect(subject.end_at('%A, %b %e, %Y %l:%M%P')).to eq((now + 60.minutes).strftime('%A, %b %e, %Y %l:%M%P'))
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#+' do
163
+ context 'when passed an integer' do
164
+ it 'returns an instance of the same class' do
165
+ expect(subject + 20).to be_an_instance_of subject.class
166
+ end
167
+
168
+ it 'adds the integer to the value' do
169
+ expect((subject + 20).value).to eq (subject.value + 20)
170
+ end
171
+
172
+ it 'recalculates the end_at' do
173
+ end_at = subject.end_at.to_i
174
+ expect((subject + 20).end_at.to_i).to eq (end_at + 20)
175
+ end
176
+ end
177
+
178
+ context 'when passed a duration' do
179
+ it 'returns an instance of the same class' do
180
+ expect(subject + Duranged::Duration.new(20)).to be_an_instance_of subject.class
181
+ end
182
+
183
+ it 'adds the duration to the value' do
184
+ expect((subject + Duranged::Duration.new(20)).value).to eq (subject.value + 20)
185
+ end
186
+
187
+ it 'recalculates the end_at' do
188
+ end_at = subject.end_at.to_i
189
+ expect((subject + Duranged::Duration.new(20)).end_at.to_i).to eq (end_at + 20)
190
+ end
191
+ end
192
+
193
+ context 'when passed an interval' do
194
+ it 'returns an instance of the same class' do
195
+ expect(subject + Duranged::Interval.new(20)).to be_an_instance_of subject.class
196
+ end
197
+
198
+ it 'adds the interval to the value' do
199
+ expect((subject + Duranged::Interval.new(20)).value).to eq (subject.value + 20)
200
+ end
201
+
202
+ it 'recalculates the end_at' do
203
+ end_at = subject.end_at.to_i
204
+ expect((subject + Duranged::Interval.new(20)).end_at.to_i).to eq (end_at + 20)
205
+ end
206
+ end
207
+ end
208
+
209
+ describe '#-' do
210
+ context 'when passed an integer' do
211
+ it 'returns an instance of the same class' do
212
+ expect(subject - 20).to be_an_instance_of subject.class
213
+ end
214
+
215
+ it 'adds the integer to the value' do
216
+ expect((subject - 20).value).to eq (subject.value - 20)
217
+ end
218
+
219
+ it 'recalculates the end_at' do
220
+ end_at = subject.end_at.to_i
221
+ expect((subject - 20).end_at.to_i).to eq (end_at - 20)
222
+ end
223
+ end
224
+
225
+ context 'when passed a duration' do
226
+ it 'returns an instance of the same class' do
227
+ expect(subject - Duranged::Duration.new(20)).to be_an_instance_of subject.class
228
+ end
229
+
230
+ it 'adds the duration to the value' do
231
+ expect((subject - Duranged::Duration.new(20)).value).to eq (subject.value - 20)
232
+ end
233
+
234
+ it 'recalculates the end_at' do
235
+ end_at = subject.end_at.to_i
236
+ expect((subject - Duranged::Duration.new(20)).end_at.to_i).to eq (end_at - 20)
237
+ end
238
+ end
239
+
240
+ context 'when passed an interval' do
241
+ it 'returns an instance of the same class' do
242
+ expect(subject - Duranged::Interval.new(20)).to be_an_instance_of subject.class
243
+ end
244
+
245
+ it 'adds the interval to the value' do
246
+ expect((subject - Duranged::Interval.new(20)).value).to eq (subject.value - 20)
247
+ end
248
+
249
+ it 'recalculates the end_at' do
250
+ end_at = subject.end_at.to_i
251
+ expect((subject - Duranged::Interval.new(20)).end_at.to_i).to eq (end_at - 20)
252
+ end
253
+ end
254
+ end
255
+
256
+ describe '#to_h' do
257
+ it 'returns a hash with start_at and end_at' do
258
+ expect(subject.to_h).to eq({start_at: subject.start_at.as_json, end_at: subject.end_at.as_json})
259
+ end
260
+ end
261
+
262
+ describe '#as_json' do
263
+ it 'returns a hash with start_at and end_at' do
264
+ expect(subject.as_json).to eq({start_at: subject.start_at.as_json, end_at: subject.end_at.as_json})
265
+ end
266
+ end
267
+
268
+ describe '#to_duration' do
269
+ it 'returns a duration object' do
270
+ expect(subject.to_duration).to be_an_instance_of Duranged::Duration
271
+ end
272
+
273
+ it 'returns a duration object that represents the duration value' do
274
+ expect(subject.to_duration.duration).to eq subject.duration
275
+ end
276
+ end
277
+
278
+ describe '#to_s' do
279
+ subject { Duranged::Range.new((now.beginning_of_day), 60.minutes) }
280
+ let(:format) { '%-l:%M%P' }
281
+
282
+ it 'uses the default format' do
283
+ expect(subject.to_s).to eq "#{subject.start_at('%b. %-d %Y ' + format)} to #{subject.end_at(format)}"
284
+ end
285
+
286
+ context 'when start and end fall on the same day' do
287
+ subject { Duranged::Range.new((now.beginning_of_day + 30.minutes), (now.beginning_of_day + 5.hours)) }
288
+
289
+ it 'returns the start and end times' do
290
+ expect(subject.to_s).to eq "#{subject.start_at('%b. %-d %Y ' + format)} to #{subject.end_at(format)}"
291
+ end
292
+ end
293
+
294
+ context 'when start and end fall on different days' do
295
+ subject { Duranged::Range.new((now.utc.end_of_day - 20.minutes), 30.minutes) }
296
+ let(:format) { '%b. %-d %Y %-l:%M%P' }
297
+
298
+ it 'returns the start date/time and duration' do
299
+ expect(subject.to_s).to eq "#{subject.start_at(format)} (#{subject.to_duration.to_s})"
300
+ end
301
+ end
302
+ end
303
+
304
+ describe '#strfrange' do
305
+ context 'when using the :start_at token' do
306
+ it_behaves_like "a format token", ':start_at', '%b. %-d %Y %-l:%M%P', :start_at
307
+ end
308
+
309
+ context 'when using the :end_at token' do
310
+ it_behaves_like "a format token", ':end_at', '%b. %-d %Y %-l:%M%P', :end_at
311
+ end
312
+
313
+ context 'when using the :duration token' do
314
+ it_behaves_like "a format token", ':duration', '%D:%H:%M:%S', :strfdur
315
+ end
316
+
317
+ context "using complex format string ':start_at(%b. %-d %Y) :start_at(%l:%M%P) - :end_at(%l:%M%P) (:duration(%-h hours, %-m minutes))'" do
318
+ subject { Duranged::Range.new(DateTime.parse('2015-01-01T00:00:00-07:00'), DateTime.parse('2015-01-01T06:30:00-07:00')) }
319
+ it "returns 'Jan. 1 2015 12:00am - 6:30am (6 hours, 30 minutes)'" do
320
+ expect(subject.strfrange(':start_at(%b. %-d %Y) :start_at(%l:%M%P) - :end_at(%l:%M%P) (:duration(%-h hours, %-m minutes))')).to eq 'Jan. 1 2015 12:00am - 6:30am (6 hours, 30 minutes)'
321
+ end
322
+ end
323
+ end
324
+ end