timeboss 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +31 -0
  3. data/.github/workflows/ruby.yml +35 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +2 -1
  6. data/README.md +17 -2
  7. data/Rakefile +3 -1
  8. data/bin/tbsh +2 -2
  9. data/lib/tasks/calendars.rake +5 -5
  10. data/lib/tasks/timeboss.rake +2 -2
  11. data/lib/timeboss/calendar/day.rb +3 -2
  12. data/lib/timeboss/calendar/half.rb +2 -1
  13. data/lib/timeboss/calendar/month.rb +2 -1
  14. data/lib/timeboss/calendar/parser.rb +9 -8
  15. data/lib/timeboss/calendar/period.rb +9 -6
  16. data/lib/timeboss/calendar/quarter.rb +2 -1
  17. data/lib/timeboss/calendar/support/formatter.rb +5 -4
  18. data/lib/timeboss/calendar/support/has_fiscal_weeks.rb +17 -0
  19. data/lib/timeboss/calendar/support/has_iso_weeks.rb +30 -0
  20. data/lib/timeboss/calendar/support/month_basis.rb +1 -1
  21. data/lib/timeboss/calendar/support/monthly_unit.rb +8 -8
  22. data/lib/timeboss/calendar/support/navigable.rb +2 -1
  23. data/lib/timeboss/calendar/support/shiftable.rb +12 -11
  24. data/lib/timeboss/calendar/support/translatable.rb +3 -2
  25. data/lib/timeboss/calendar/support/unit.rb +20 -13
  26. data/lib/timeboss/calendar/waypoints/absolute.rb +4 -3
  27. data/lib/timeboss/calendar/waypoints/relative.rb +14 -13
  28. data/lib/timeboss/calendar/week.rb +3 -2
  29. data/lib/timeboss/calendar/year.rb +2 -1
  30. data/lib/timeboss/calendar.rb +8 -7
  31. data/lib/timeboss/calendars/broadcast.rb +10 -7
  32. data/lib/timeboss/calendars/gregorian.rb +4 -5
  33. data/lib/timeboss/calendars.rb +3 -2
  34. data/lib/timeboss/version.rb +2 -1
  35. data/lib/timeboss.rb +2 -0
  36. data/spec/{calendar → lib/timeboss/calendar}/day_spec.rb +14 -14
  37. data/spec/{calendar → lib/timeboss/calendar}/quarter_spec.rb +9 -9
  38. data/spec/lib/timeboss/calendar/support/monthly_unit_spec.rb +91 -0
  39. data/spec/{calendar → lib/timeboss/calendar}/support/unit_spec.rb +23 -22
  40. data/spec/{calendar → lib/timeboss/calendar}/week_spec.rb +20 -20
  41. data/spec/lib/timeboss/calendars/broadcast_spec.rb +796 -0
  42. data/spec/lib/timeboss/calendars/gregorian_spec.rb +793 -0
  43. data/spec/{calendars_spec.rb → lib/timeboss/calendars_spec.rb} +19 -19
  44. data/spec/spec_helper.rb +2 -2
  45. data/timeboss.gemspec +16 -14
  46. metadata +51 -20
  47. data/lib/timeboss/support/shellable.rb +0 -17
  48. data/spec/calendar/support/monthly_unit_spec.rb +0 -85
  49. data/spec/calendars/broadcast_spec.rb +0 -796
  50. data/spec/calendars/gregorian_spec.rb +0 -684
@@ -0,0 +1,796 @@
1
+ module TimeBoss
2
+ describe Calendars::Broadcast do
3
+ let(:subject) { described_class.new }
4
+
5
+ context "days" do
6
+ it "can get today" do
7
+ day = subject.today
8
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
9
+ expect(day.start_date).to eq Date.today
10
+ end
11
+
12
+ it "can get yesterday" do
13
+ day = subject.yesterday
14
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
15
+ expect(day.start_date).to eq Date.yesterday
16
+ end
17
+
18
+ it "can get tomorrow" do
19
+ day = subject.tomorrow
20
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
21
+ expect(day.start_date).to eq Date.tomorrow
22
+ end
23
+ end
24
+
25
+ context "quarters" do
26
+ describe "#quarter" do
27
+ it "knows 2017Q2" do
28
+ quarter = subject.quarter(2017, 2)
29
+ expect(quarter.name).to eq "2017Q2"
30
+ expect(quarter.title).to eq "Q2 2017"
31
+ expect(quarter.year_index).to eq 2017
32
+ expect(quarter.index).to eq 2
33
+ expect(quarter.start_date).to eq Date.parse("2017-03-27")
34
+ expect(quarter.end_date).to eq Date.parse("2017-06-25")
35
+ expect(quarter.to_range).to eq quarter.start_date..quarter.end_date
36
+ end
37
+
38
+ it "knows 2018Q3" do
39
+ quarter = subject.quarter(2018, 3)
40
+ expect(quarter.name).to eq "2018Q3"
41
+ expect(quarter.title).to eq "Q3 2018"
42
+ expect(quarter.year_index).to eq 2018
43
+ expect(quarter.index).to eq 3
44
+ expect(quarter.start_date).to eq Date.parse("2018-06-25")
45
+ expect(quarter.end_date).to eq Date.parse("2018-09-30")
46
+ expect(quarter.to_range).to eq quarter.start_date..quarter.end_date
47
+ end
48
+
49
+ it "knows 2019Q4" do
50
+ quarter = subject.quarter(2019, 4)
51
+ expect(quarter.year_index).to eq 2019
52
+ expect(quarter.index).to eq 4
53
+ expect(quarter.name).to eq "2019Q4"
54
+ expect(quarter.title).to eq "Q4 2019"
55
+ expect(quarter.start_date).to eq Date.parse("2019-09-30")
56
+ expect(quarter.end_date).to eq Date.parse("2019-12-29")
57
+ expect(quarter.to_range).to eq quarter.start_date..quarter.end_date
58
+ end
59
+ end
60
+
61
+ describe "#quarter_for" do
62
+ it "knows what quarter 2018-06-27 is in" do
63
+ quarter = subject.quarter_for(Date.parse("2018-06-27"))
64
+ expect(quarter.name).to eq "2018Q3"
65
+ end
66
+
67
+ it "knows what quarter 2018-06-22 is in" do
68
+ quarter = subject.quarter_for(Date.parse("2018-06-22"))
69
+ expect(quarter.name).to eq "2018Q2"
70
+ end
71
+ end
72
+
73
+ describe "#quarters_for" do
74
+ it "knows what quarters are in 2020" do
75
+ basis = subject.year(2020)
76
+ periods = subject.quarters_for(basis)
77
+ expect(periods.map(&:name)).to eq %w[2020Q1 2020Q2 2020Q3 2020Q4]
78
+ end
79
+
80
+ it "knows what quarter 2018M7 is in" do
81
+ basis = subject.month(2018, 7)
82
+ periods = subject.quarters_for(basis)
83
+ expect(periods.map(&:name)).to eq %w[2018Q3]
84
+ end
85
+ end
86
+
87
+ describe "#this_quarter" do
88
+ let(:today) { double }
89
+ let(:quarter) { double }
90
+
91
+ it "gets the quarter for today" do
92
+ allow(Date).to receive(:today).and_return today
93
+ expect(subject).to receive(:quarter_for).with(today).and_return quarter
94
+ expect(subject.this_quarter).to eq quarter
95
+ end
96
+ end
97
+
98
+ describe "#format" do
99
+ let(:entry) { subject.quarter(2015, 3) }
100
+
101
+ it "can do a default format" do
102
+ expect(entry.format).to eq "2015H2Q1"
103
+ end
104
+
105
+ it "can format with only the quarter" do
106
+ expect(entry.format(:quarter)).to eq "2015Q3"
107
+ end
108
+
109
+ it "ignores stupidity" do
110
+ expect(entry.format(:day, :banana)).to eq "2015Q3"
111
+ end
112
+ end
113
+
114
+ context "relative" do
115
+ let(:this_quarter) { subject.quarter(2015, 3) }
116
+ let(:quarter) { double }
117
+ before(:each) { allow(subject).to receive(:this_quarter).and_return this_quarter }
118
+
119
+ it "can get the last quarter" do
120
+ allow(this_quarter).to receive(:previous).and_return quarter
121
+ expect(subject.last_quarter).to eq quarter
122
+ end
123
+
124
+ it "can get the next quarter" do
125
+ allow(this_quarter).to receive(:next).and_return quarter
126
+ expect(subject.next_quarter).to eq quarter
127
+ end
128
+
129
+ it "can get some number of quarters" do
130
+ quarters = subject.quarters(5)
131
+ expect(quarters.length).to eq 5
132
+ quarters.each { |q| expect(q).to be_a TimeBoss::Calendar::Quarter }
133
+ expect(quarters.map(&:name)).to eq ["2015Q3", "2015Q4", "2016Q1", "2016Q2", "2016Q3"]
134
+ end
135
+
136
+ it "can get a quarter ahead" do
137
+ quarter = subject.quarters_ahead(4)
138
+ expect(quarter).to be_a TimeBoss::Calendar::Quarter
139
+ expect(quarter.name).to eq "2016Q3"
140
+ end
141
+
142
+ it "can get some number of quarters back" do
143
+ quarters = subject.quarters_back(5)
144
+ expect(quarters.length).to eq 5
145
+ quarters.each { |q| expect(q).to be_a TimeBoss::Calendar::Quarter }
146
+ expect(quarters.map(&:name)).to eq ["2014Q3", "2014Q4", "2015Q1", "2015Q2", "2015Q3"]
147
+ end
148
+
149
+ it "can get a quarter ago" do
150
+ quarter = subject.quarters_ago(4)
151
+ expect(quarter).to be_a TimeBoss::Calendar::Quarter
152
+ expect(quarter.name).to eq "2014Q3"
153
+ end
154
+ end
155
+ end
156
+
157
+ context "months" do
158
+ describe "#month" do
159
+ it "knows 2017M2" do
160
+ month = subject.month(2017, 2)
161
+ expect(month.name).to eq "2017M2"
162
+ expect(month.title).to eq "February 2017"
163
+ expect(month.year_index).to eq 2017
164
+ expect(month.index).to eq 2
165
+ expect(month.start_date).to eq Date.parse("2017-01-30")
166
+ expect(month.end_date).to eq Date.parse("2017-02-26")
167
+ expect(month.to_range).to eq month.start_date..month.end_date
168
+ end
169
+
170
+ it "knows 2018M3" do
171
+ month = subject.month(2018, 3)
172
+ expect(month.name).to eq "2018M3"
173
+ expect(month.title).to eq "March 2018"
174
+ expect(month.year_index).to eq 2018
175
+ expect(month.index).to eq 3
176
+ expect(month.start_date).to eq Date.parse("2018-02-26")
177
+ expect(month.end_date).to eq Date.parse("2018-03-25")
178
+ expect(month.to_range).to eq month.start_date..month.end_date
179
+ end
180
+
181
+ it "knows 2019M11" do
182
+ month = subject.month(2019, 11)
183
+ expect(month.year_index).to eq 2019
184
+ expect(month.index).to eq 11
185
+ expect(month.name).to eq "2019M11"
186
+ expect(month.title).to eq "November 2019"
187
+ expect(month.start_date).to eq Date.parse("2019-10-28")
188
+ expect(month.end_date).to eq Date.parse("2019-11-24")
189
+ expect(month.to_range).to eq month.start_date..month.end_date
190
+ end
191
+ end
192
+
193
+ describe "#month_for" do
194
+ it "knows what month 2018-06-27 is in" do
195
+ month = subject.month_for(Date.parse("2018-06-27"))
196
+ expect(month.name).to eq "2018M7"
197
+ end
198
+
199
+ it "knows what month 2018-06-22 is in" do
200
+ month = subject.month_for(Date.parse("2018-06-22"))
201
+ expect(month.name).to eq "2018M6"
202
+ end
203
+ end
204
+
205
+ describe "#months_for" do
206
+ it "knows what months are in 2020" do
207
+ basis = subject.year(2020)
208
+ periods = subject.months_for(basis)
209
+ expect(periods.map(&:name)).to eq %w[2020M1 2020M2 2020M3 2020M4 2020M5 2020M6 2020M7 2020M8 2020M9 2020M10 2020M11 2020M12]
210
+ end
211
+
212
+ it "knows what months are in 2018Q2" do
213
+ basis = subject.parse("2018Q2")
214
+ periods = subject.months_for(basis)
215
+ expect(periods.map(&:name)).to eq %w[2018M4 2018M5 2018M6]
216
+ end
217
+
218
+ it "knows what month 2019-12-12 is in" do
219
+ basis = subject.parse("2019-12-12")
220
+ periods = subject.months_for(basis)
221
+ expect(periods.map(&:name)).to eq %w[2019M12]
222
+ end
223
+ end
224
+
225
+ describe "#this_month" do
226
+ let(:today) { double }
227
+ let(:month) { double }
228
+
229
+ it "gets the month for today" do
230
+ allow(Date).to receive(:today).and_return today
231
+ expect(subject).to receive(:month_for).with(today).and_return month
232
+ expect(subject.this_month).to eq month
233
+ end
234
+ end
235
+
236
+ describe "#format" do
237
+ let(:entry) { subject.month(2015, 8) }
238
+
239
+ it "can do a default format" do
240
+ expect(entry.format).to eq "2015H2Q1M2"
241
+ end
242
+
243
+ it "can format with only the quarter" do
244
+ expect(entry.format(:quarter)).to eq "2015Q3M2"
245
+ end
246
+
247
+ it "ignores stupidity" do
248
+ expect(entry.format(:banana, :half, :week)).to eq "2015H2M2"
249
+ end
250
+ end
251
+
252
+ context "relative" do
253
+ let(:this_month) { subject.month(2015, 3) }
254
+ let(:month) { double }
255
+ before(:each) { allow(subject).to receive(:this_month).and_return this_month }
256
+
257
+ it "can get the last month" do
258
+ allow(this_month).to receive(:previous).and_return month
259
+ expect(subject.last_month).to eq month
260
+ end
261
+
262
+ it "can get the next month" do
263
+ allow(this_month).to receive(:next).and_return month
264
+ expect(subject.next_month).to eq month
265
+ end
266
+
267
+ it "can get some number of months" do
268
+ months = subject.months(5)
269
+ expect(months.length).to eq 5
270
+ months.each { |m| expect(m).to be_a TimeBoss::Calendar::Month }
271
+ expect(months.map(&:name)).to eq ["2015M3", "2015M4", "2015M5", "2015M6", "2015M7"]
272
+ end
273
+
274
+ it "can get a month ahead" do
275
+ month = subject.months_ahead(4)
276
+ expect(month).to be_a TimeBoss::Calendar::Month
277
+ expect(month.name).to eq "2015M7"
278
+ end
279
+
280
+ it "can get some number of months back" do
281
+ months = subject.months_back(5)
282
+ expect(months.length).to eq 5
283
+ months.each { |m| expect(m).to be_a TimeBoss::Calendar::Month }
284
+ expect(months.map(&:name)).to eq ["2014M11", "2014M12", "2015M1", "2015M2", "2015M3"]
285
+ end
286
+
287
+ it "can get a month ago" do
288
+ month = subject.months_ago(4)
289
+ expect(month).to be_a TimeBoss::Calendar::Month
290
+ expect(month.name).to eq "2014M11"
291
+ end
292
+ end
293
+ end
294
+
295
+ context "weeks" do
296
+ before(:each) { allow(Date).to receive(:today).and_return Date.parse("2019-08-23") }
297
+
298
+ it "knows this week " do
299
+ expect(subject.this_week.name).to eq "2019W34"
300
+ expect(subject.this_week.title).to eq "Week of August 19, 2019"
301
+ end
302
+
303
+ it "knows last week" do
304
+ expect(subject.last_week.name).to eq "2019W33"
305
+ expect(subject.last_week.title).to eq "Week of August 12, 2019"
306
+ end
307
+
308
+ it "knows next week" do
309
+ expect(subject.next_week.name).to eq "2019W35"
310
+ expect(subject.next_week.title).to eq "Week of August 26, 2019"
311
+ end
312
+ end
313
+
314
+ context "years" do
315
+ describe "#year" do
316
+ it "knows 2016" do
317
+ year = subject.year(2016)
318
+ expect(year.name).to eq "2016"
319
+ expect(year.title).to eq "2016"
320
+ expect(year.year_index).to eq 2016
321
+ expect(year.index).to eq 1
322
+ expect(year.start_date).to eq Date.parse("2015-12-28")
323
+ expect(year.end_date).to eq Date.parse("2016-12-25")
324
+ expect(year.to_range).to eq year.start_date..year.end_date
325
+ end
326
+
327
+ it "knows 2017" do
328
+ year = subject.year(2017)
329
+ expect(year.name).to eq "2017"
330
+ expect(year.title).to eq "2017"
331
+ expect(year.year_index).to eq 2017
332
+ expect(year.index).to eq 1
333
+ expect(year.start_date).to eq Date.parse("2016-12-26")
334
+ expect(year.end_date).to eq Date.parse("2017-12-31")
335
+ expect(year.to_range).to eq year.start_date..year.end_date
336
+ end
337
+
338
+ it "knows 2018" do
339
+ year = subject.year(2018)
340
+ expect(year.name).to eq "2018"
341
+ expect(year.title).to eq "2018"
342
+ expect(year.year_index).to eq 2018
343
+ expect(year.index).to eq 1
344
+ expect(year.start_date).to eq Date.parse("2018-01-01")
345
+ expect(year.end_date).to eq Date.parse("2018-12-30")
346
+ expect(year.to_range).to eq year.start_date..year.end_date
347
+ end
348
+ end
349
+
350
+ describe "#year_for" do
351
+ it "knows what year 2018-04-07 is in" do
352
+ year = subject.year_for(Date.parse("2018-04-07"))
353
+ expect(year.name).to eq "2018"
354
+ end
355
+
356
+ it "knows what year 2016-12-27 is in" do
357
+ year = subject.year_for(Date.parse("2016-12-27"))
358
+ expect(year.name).to eq "2017"
359
+ end
360
+ end
361
+
362
+ describe "#years_for" do
363
+ it "knows what years are in 2020 (duh)" do
364
+ basis = subject.year(2020)
365
+ periods = subject.years_for(basis)
366
+ expect(periods.map(&:name)).to eq %w[2020]
367
+ end
368
+
369
+ it "knows what year 2018Q2 is in" do
370
+ basis = subject.parse("2018Q2")
371
+ periods = subject.years_for(basis)
372
+ expect(periods.map(&:name)).to eq %w[2018]
373
+ end
374
+
375
+ it "knows what years 2019-12-12 is in" do
376
+ basis = subject.parse("2019-12-12")
377
+ periods = subject.years_for(basis)
378
+ expect(periods.map(&:name)).to eq %w[2019]
379
+ end
380
+ end
381
+
382
+ describe "#this_year" do
383
+ let(:today) { double }
384
+ let(:year) { double }
385
+
386
+ it "gets the year for today" do
387
+ allow(Date).to receive(:today).and_return today
388
+ expect(subject).to receive(:year_for).with(today).and_return year
389
+ expect(subject.this_year).to eq year
390
+ end
391
+ end
392
+
393
+ describe "#format" do
394
+ let(:entry) { subject.parse("2020W24") }
395
+
396
+ it "can do a default format" do
397
+ expect(entry.format).to eq "2020H1Q2M3W2"
398
+ end
399
+
400
+ it "can format with only the quarter" do
401
+ expect(entry.format(:quarter)).to eq "2020Q2W11"
402
+ end
403
+
404
+ it "can format with only the quarter + month" do
405
+ expect(entry.format(:quarter, :month)).to eq "2020Q2M3W2"
406
+ expect(entry.format(:month, :quarter)).to eq "2020Q2M3W2"
407
+ end
408
+
409
+ it "ignores stupidity" do
410
+ expect(entry.format(:day, :month, :banana)).to eq "2020M6W2"
411
+ end
412
+ end
413
+
414
+ context "relative" do
415
+ let(:this_year) { subject.year(2015) }
416
+ let(:year) { double }
417
+ before(:each) { allow(subject).to receive(:this_year).and_return this_year }
418
+
419
+ it "can get the last year" do
420
+ allow(this_year).to receive(:previous).and_return year
421
+ expect(subject.last_year).to eq year
422
+ end
423
+
424
+ it "can get the next year" do
425
+ allow(this_year).to receive(:next).and_return year
426
+ expect(subject.next_year).to eq year
427
+ end
428
+
429
+ it "can get some number of years" do
430
+ years = subject.years(5)
431
+ expect(years.length).to eq 5
432
+ years.each { |y| expect(y).to be_a TimeBoss::Calendar::Year }
433
+ expect(years.map(&:name)).to eq ["2015", "2016", "2017", "2018", "2019"]
434
+ end
435
+
436
+ it "can get some number of years back" do
437
+ years = subject.years_back(5)
438
+ expect(years.length).to eq 5
439
+ years.each { |y| expect(y).to be_a TimeBoss::Calendar::Year }
440
+ expect(years.map(&:name)).to eq ["2011", "2012", "2013", "2014", "2015"]
441
+ end
442
+ end
443
+ end
444
+
445
+ describe "#parse" do
446
+ it "can parse a year" do
447
+ date = subject.parse("2018")
448
+ expect(date).to be_a TimeBoss::Calendar::Year
449
+ expect(date.name).to eq "2018"
450
+ end
451
+
452
+ it "can parse a quarter identifier" do
453
+ date = subject.parse("2017Q2")
454
+ expect(date).to be_a TimeBoss::Calendar::Quarter
455
+ expect(date.name).to eq "2017Q2"
456
+ end
457
+
458
+ it "can parse a month identifier" do
459
+ date = subject.parse("2017M4")
460
+ expect(date).to be_a TimeBoss::Calendar::Month
461
+ expect(date.name).to eq "2017M4"
462
+ end
463
+
464
+ it "can parse a week within a year" do
465
+ date = subject.parse("2018W37")
466
+ expect(date).to be_a TimeBoss::Calendar::Week
467
+ expect(date.name).to eq "2018W37"
468
+ end
469
+
470
+ it "can parse a week within a quarter" do
471
+ date = subject.parse("2017Q2W2")
472
+ expect(date).to be_a TimeBoss::Calendar::Week
473
+ expect(date.name).to eq "2017W15"
474
+ end
475
+
476
+ it "can parse a week within a month" do
477
+ date = subject.parse("2017M4W1")
478
+ expect(date).to be_a TimeBoss::Calendar::Week
479
+ expect(date.name).to eq "2017W14"
480
+ end
481
+
482
+ it "can parse a date" do
483
+ date = subject.parse("2017-04-08")
484
+ expect(date).to be_a TimeBoss::Calendar::Day
485
+ expect(date.start_date).to eq Date.parse("2017-04-08")
486
+ expect(date.end_date).to eq Date.parse("2017-04-08")
487
+ end
488
+
489
+ it "can parse an aesthetically displeasing date" do
490
+ date = subject.parse("20170408")
491
+ expect(date).to be_a TimeBoss::Calendar::Day
492
+ expect(date.start_date).to eq Date.parse("2017-04-08")
493
+ expect(date.end_date).to eq Date.parse("2017-04-08")
494
+ end
495
+
496
+ it "gives you nothing if you give it nothing" do
497
+ expect(subject.parse(nil)).to be nil
498
+ expect(subject.parse("")).to be nil
499
+ end
500
+ end
501
+
502
+ context "expressions" do
503
+ it "can parse waypoints" do
504
+ result = subject.parse("this_year")
505
+ expect(result).to be_a TimeBoss::Calendar::Year
506
+ expect(result).to be_current
507
+ end
508
+
509
+ it "can parse mathematic expressions" do
510
+ result = subject.parse("this_month + 2")
511
+ expect(result).to be_a TimeBoss::Calendar::Month
512
+ expect(result).to eq subject.months_ahead(2)
513
+ end
514
+
515
+ context "ranges" do
516
+ before(:each) { allow(subject).to receive(:this_year).and_return subject.year(2018) }
517
+ let(:result) { subject.parse("this_year-2 .. this_year") }
518
+
519
+ it "can parse range expressions" do
520
+ expect(result).to be_a TimeBoss::Calendar::Period
521
+ expect(result.to_s).to eq "2016: 2015-12-28 thru 2016-12-25 .. 2018: 2018-01-01 thru 2018-12-30"
522
+ end
523
+
524
+ it "can get an overall start date for a range" do
525
+ expect(result.start_date).to eq Date.parse("2015-12-28")
526
+ end
527
+
528
+ it "can get an overall end date for a range" do
529
+ expect(result.end_date).to eq Date.parse("2018-12-30")
530
+ end
531
+
532
+ context "sub-periods" do
533
+ it "can get the months included in a range" do
534
+ entries = result.months
535
+ entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Month }
536
+ expect(entries.map(&:name)).to include("2016M1", "2016M9", "2017M3", "2018M12")
537
+ end
538
+
539
+ it "can get the weeks included in a range" do
540
+ entries = result.weeks
541
+ entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Week }
542
+ expect(entries.map(&:name)).to include("2016W1", "2016W38", "2017W15", "2017W53", "2018W52")
543
+ end
544
+
545
+ it "can get the days included in a range" do
546
+ entries = result.days
547
+ entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Day }
548
+ expect(entries.map(&:name)).to include("2015-12-28", "2016-04-30", "2017-09-22", "2018-12-30")
549
+ end
550
+ end
551
+ end
552
+ end
553
+
554
+ context "shifting" do
555
+ context "from day" do
556
+ let(:basis) { subject.parse("2020-04-21") }
557
+
558
+ it "can shift to a different week" do
559
+ allow(subject).to receive(:this_week).and_return subject.parse("2020W23")
560
+ result = basis.last_week
561
+ expect(result).to be_a TimeBoss::Calendar::Day
562
+ expect(result.to_s).to eq "2020-05-26"
563
+ expect(basis.in_week).to eq 2
564
+ end
565
+
566
+ it "can shift to a different quarter" do
567
+ allow(subject).to receive(:this_quarter).and_return subject.parse("2020Q3")
568
+ result = basis.quarters_ago(2)
569
+ expect(result).to be_a TimeBoss::Calendar::Day
570
+ expect(result.to_s).to eq "2020-01-21"
571
+ expect(basis.in_quarter).to eq 23
572
+ end
573
+
574
+ it "can shift to a different year" do
575
+ allow(subject).to receive(:this_year).and_return subject.parse("2019")
576
+ result = basis.years_ahead(3)
577
+ expect(result).to be_a TimeBoss::Calendar::Day
578
+ expect(result.to_s).to eq "2022-04-19"
579
+ expect(basis.in_year).to eq 114
580
+ end
581
+ end
582
+
583
+ context "from week" do
584
+ let(:basis) { subject.parse("2017W8") }
585
+
586
+ it "cannot shift to a different day" do
587
+ expect(basis.last_day).to be nil
588
+ expect(basis.in_day).to be nil
589
+ end
590
+
591
+ it "can shift to a different month" do
592
+ allow(subject).to receive(:this_month).and_return subject.parse("2020M4")
593
+ result = basis.next_month
594
+ expect(result).to be_a TimeBoss::Calendar::Week
595
+ expect(result.to_s).to eq "2020W20: 2020-05-11 thru 2020-05-17"
596
+ expect(basis.in_month).to eq 3
597
+ end
598
+
599
+ it "can shift to a different half" do
600
+ allow(subject).to receive(:this_half).and_return subject.parse("2019H1")
601
+ result = basis.last_half
602
+ expect(result).to be_a TimeBoss::Calendar::Week
603
+ expect(result.to_s).to eq "2018W33: 2018-08-13 thru 2018-08-19"
604
+ expect(basis.in_half).to eq 8
605
+ end
606
+ end
607
+
608
+ context "from month" do
609
+ let(:basis) { subject.parse("2017M4") }
610
+
611
+ it "cannot shift to a different week" do
612
+ expect(basis.last_week).to be nil
613
+ expect(basis.in_week).to be nil
614
+ end
615
+
616
+ it "can shift to a different year" do
617
+ allow(subject).to receive(:this_year).and_return subject.parse("2020")
618
+ result = basis.years_ahead(4)
619
+ expect(result).to be_a TimeBoss::Calendar::Month
620
+ expect(result.name).to eq "2024M4"
621
+ expect(basis.in_year).to eq 4
622
+ end
623
+ end
624
+
625
+ context "from quarter" do
626
+ let(:basis) { subject.parse("2018Q2") }
627
+
628
+ it "cannot shift to a different month" do
629
+ expect(basis.months_ago(4)).to be nil
630
+ expect(basis.in_month).to be nil
631
+ end
632
+
633
+ it "can shift to a different half" do
634
+ allow(subject).to receive(:this_half).and_return subject.parse("2020H1")
635
+ result = basis.last_half
636
+ expect(result).to be_a TimeBoss::Calendar::Quarter
637
+ expect(result.name).to eq "2019Q4"
638
+ expect(basis.in_half).to eq 2
639
+ end
640
+ end
641
+
642
+ context "from year" do
643
+ let(:basis) { subject.parse("2014") }
644
+
645
+ it "cannot shift to a different half" do
646
+ expect(basis.next_half).to be nil
647
+ expect(basis.in_half).to be nil
648
+ end
649
+
650
+ it "shifts to a different year, but knows how useless that is" do
651
+ allow(subject).to receive(:this_year).and_return subject.parse("2020")
652
+ result = basis.years_ago(2)
653
+ expect(result).to be_a TimeBoss::Calendar::Year
654
+ expect(result.name).to eq "2018"
655
+ expect(basis.in_year).to eq 1
656
+ end
657
+ end
658
+ end
659
+
660
+ context "units" do
661
+ let(:calendar) { described_class.new }
662
+
663
+ context "day" do
664
+ let(:start_date) { Date.parse("2019-09-30") }
665
+ let(:subject) { TimeBoss::Calendar::Day.new(calendar, start_date) }
666
+
667
+ context "links" do
668
+ it "can get its previous" do
669
+ expect(subject.previous.name).to eq "2019-09-29"
670
+ end
671
+
672
+ it "can get its next" do
673
+ expect(subject.next.name).to eq "2019-10-01"
674
+ end
675
+
676
+ it "can offset backwards" do
677
+ expect(subject.offset(-3).name).to eq "2019-09-27"
678
+ expect((subject - 3).name).to eq "2019-09-27"
679
+ end
680
+
681
+ it "can offset forwards" do
682
+ expect(subject.offset(4).name).to eq "2019-10-04"
683
+ expect((subject + 4).name).to eq "2019-10-04"
684
+ end
685
+ end
686
+ end
687
+
688
+ context "week" do
689
+ context "links" do
690
+ context "within year" do
691
+ let(:parent) { calendar.parse("2020") }
692
+ let(:week) { parent.weeks.first }
693
+
694
+ it "knows itself first" do
695
+ expect(week.to_s).to include("2020W1", "2019-12-30", "2020-01-05")
696
+ end
697
+
698
+ it "can get its next week" do
699
+ subject = week.next
700
+ expect(subject).to be_a TimeBoss::Calendar::Week
701
+ expect(subject.to_s).to include("2020W2", "2020-01-06", "2020-01-12")
702
+ end
703
+
704
+ it "can get its previous week" do
705
+ subject = week.previous
706
+ expect(subject).to be_a TimeBoss::Calendar::Week
707
+ expect(subject.to_s).to include("2019W52", "2019-12-23", "2019-12-29")
708
+ end
709
+
710
+ it "can offset backwards" do
711
+ expect(week.offset(-4).name).to eq "2019W49"
712
+ expect((week - 4).name).to eq "2019W49"
713
+ end
714
+
715
+ it "can offset forwards" do
716
+ expect((week + 2).name).to eq "2020W3"
717
+ end
718
+ end
719
+
720
+ context "within quarter" do
721
+ let(:parent) { calendar.parse("2019Q3") }
722
+ let(:week) { parent.weeks.last }
723
+
724
+ it "knows itself first" do
725
+ expect(week.to_s).to include("2019W39", "2019-09-23", "2019-09-29")
726
+ end
727
+
728
+ it "can get its next week" do
729
+ subject = week.next
730
+ expect(subject).to be_a TimeBoss::Calendar::Week
731
+ expect(subject.to_s).to include("2019W40", "2019-09-30", "2019-10-06")
732
+ end
733
+
734
+ it "can get its previous week" do
735
+ subject = week.previous
736
+ expect(subject).to be_a TimeBoss::Calendar::Week
737
+ expect(subject.to_s).to include("2019W38", "2019-09-16", "2019-09-22")
738
+ end
739
+
740
+ it "can offset backwards" do
741
+ expect(week.offset(-4).name).to eq "2019W35"
742
+ expect((week - 4).name).to eq "2019W35"
743
+ end
744
+
745
+ it "can offset forwards" do
746
+ expect((week + 2).name).to eq "2019W41"
747
+ end
748
+ end
749
+ end
750
+ end
751
+
752
+ context "quarter" do
753
+ let(:start_date) { Date.parse("2019-09-30") }
754
+ let(:end_date) { Date.parse("2019-12-29") }
755
+ let(:subject) { TimeBoss::Calendar::Quarter.new(calendar, 2019, 4, start_date, end_date) }
756
+
757
+ context "links" do
758
+ it "can get the next quarter" do
759
+ quarter = subject.next
760
+ expect(quarter.to_s).to include("2020Q1", "2019-12-30", "2020-03-29")
761
+ end
762
+
763
+ it "can get the next next quarter" do
764
+ quarter = subject.next.next
765
+ expect(quarter.to_s).to include("2020Q2", "2020-03-30", "2020-06-28")
766
+ end
767
+
768
+ it "can get the next next previous quarter" do
769
+ quarter = subject.next.next.previous
770
+ expect(quarter.to_s).to include("2020Q1", "2019-12-30", "2020-03-29")
771
+ end
772
+
773
+ it "can get the next previous quarter" do
774
+ quarter = subject.next.previous
775
+ expect(quarter.to_s).to eq subject.to_s
776
+ end
777
+
778
+ it "can get the previous quarter" do
779
+ quarter = subject.previous
780
+ expect(quarter.to_s).to include("2019Q3", "2019-07-01", "2019-09-29")
781
+ end
782
+
783
+ it "can offset backwards" do
784
+ expect(subject.offset(-4).name).to eq "2018Q4"
785
+ expect((subject - 4).name).to eq "2018Q4"
786
+ end
787
+
788
+ it "can offset forwards" do
789
+ expect(subject.offset(2).name).to eq "2020Q2"
790
+ expect((subject + 2).name).to eq "2020Q2"
791
+ end
792
+ end
793
+ end
794
+ end
795
+ end
796
+ end