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