module TimeBoss describe Calendars::Broadcast do let(:subject) { described_class.new } context 'quarters' do describe '#quarter' do it 'knows 2017Q2' do quarter = subject.quarter(2017, 2) expect(quarter.name).to eq '2017Q2' expect(quarter.title).to eq 'Q2 2017' expect(quarter.year_index).to eq 2017 expect(quarter.index).to eq 2 expect(quarter.start_date).to eq Date.parse('2017-03-27') expect(quarter.end_date).to eq Date.parse('2017-06-25') expect(quarter.to_range).to eq quarter.start_date..quarter.end_date end it 'knows 2018Q3' do quarter = subject.quarter(2018, 3) expect(quarter.name).to eq '2018Q3' expect(quarter.title).to eq 'Q3 2018' expect(quarter.year_index).to eq 2018 expect(quarter.index).to eq 3 expect(quarter.start_date).to eq Date.parse('2018-06-25') expect(quarter.end_date).to eq Date.parse('2018-09-30') expect(quarter.to_range).to eq quarter.start_date..quarter.end_date end it 'knows 2019Q4' do quarter = subject.quarter(2019, 4) expect(quarter.year_index).to eq 2019 expect(quarter.index).to eq 4 expect(quarter.name).to eq '2019Q4' expect(quarter.title).to eq 'Q4 2019' expect(quarter.start_date).to eq Date.parse('2019-09-30') expect(quarter.end_date).to eq Date.parse('2019-12-29') expect(quarter.to_range).to eq quarter.start_date..quarter.end_date end end describe '#quarter_for' do it 'knows what quarter 2018-06-27 is in' do quarter = subject.quarter_for(Date.parse('2018-06-27')) expect(quarter.name).to eq '2018Q3' end it 'knows what quarter 2018-06-22 is in' do quarter = subject.quarter_for(Date.parse('2018-06-22')) expect(quarter.name).to eq '2018Q2' end end describe '#quarters_for' do it 'knows what quarters are in 2020' do basis = subject.year(2020) periods = subject.quarters_for(basis) expect(periods.map(&:name)).to eq %w[2020Q1 2020Q2 2020Q3 2020Q4] end it 'knows what quarter 2018M7 is in' do basis = subject.month(2018, 7) periods = subject.quarters_for(basis) expect(periods.map(&:name)).to eq %w[2018Q3] end end describe '#this_quarter' do let(:today) { double } let(:quarter) { double } it 'gets the quarter for today' do allow(Date).to receive(:today).and_return today expect(subject).to receive(:quarter_for).with(today).and_return quarter expect(subject.this_quarter).to eq quarter end end describe '#format' do let(:entry) { subject.quarter(2015, 3) } it 'can do a default format' do expect(entry.format).to eq '2015H2Q1' end it 'can format with only the quarter' do expect(entry.format(:quarter)).to eq '2015Q3' end it 'ignores stupidity' do expect(entry.format(:day, :banana)).to eq '2015Q3' end end context 'relative' do let(:this_quarter) { subject.quarter(2015, 3) } let(:quarter) { double } before(:each) { allow(subject).to receive(:this_quarter).and_return this_quarter } it 'can get the last quarter' do allow(this_quarter).to receive(:previous).and_return quarter expect(subject.last_quarter).to eq quarter end it 'can get the next quarter' do allow(this_quarter).to receive(:next).and_return quarter expect(subject.next_quarter).to eq quarter end it 'can get some number of quarters' do quarters = subject.quarters(5) expect(quarters.length).to eq 5 quarters.each { |q| expect(q).to be_a TimeBoss::Calendar::Quarter } expect(quarters.map(&:name)).to eq ['2015Q3', '2015Q4', '2016Q1', '2016Q2', '2016Q3'] end it 'can get a quarter hence' do quarter = subject.quarters_hence(4) expect(quarter).to be_a TimeBoss::Calendar::Quarter expect(quarter.name).to eq '2016Q3' end it 'can get some number of quarters back' do quarters = subject.quarters_back(5) expect(quarters.length).to eq 5 quarters.each { |q| expect(q).to be_a TimeBoss::Calendar::Quarter } expect(quarters.map(&:name)).to eq ['2014Q3', '2014Q4', '2015Q1', '2015Q2', '2015Q3'] end it 'can get a quarter ago' do quarter = subject.quarters_ago(4) expect(quarter).to be_a TimeBoss::Calendar::Quarter expect(quarter.name).to eq '2014Q3' end end end context 'months' do describe '#month' do it 'knows 2017M2' do month = subject.month(2017, 2) expect(month.name).to eq '2017M2' expect(month.title).to eq 'February 2017' expect(month.year_index).to eq 2017 expect(month.index).to eq 2 expect(month.start_date).to eq Date.parse('2017-01-30') expect(month.end_date).to eq Date.parse('2017-02-26') expect(month.to_range).to eq month.start_date..month.end_date end it 'knows 2018M3' do month = subject.month(2018, 3) expect(month.name).to eq '2018M3' expect(month.title).to eq 'March 2018' expect(month.year_index).to eq 2018 expect(month.index).to eq 3 expect(month.start_date).to eq Date.parse('2018-02-26') expect(month.end_date).to eq Date.parse('2018-03-25') expect(month.to_range).to eq month.start_date..month.end_date end it 'knows 2019M11' do month = subject.month(2019, 11) expect(month.year_index).to eq 2019 expect(month.index).to eq 11 expect(month.name).to eq '2019M11' expect(month.title).to eq 'November 2019' expect(month.start_date).to eq Date.parse('2019-10-28') expect(month.end_date).to eq Date.parse('2019-11-24') expect(month.to_range).to eq month.start_date..month.end_date end end describe '#month_for' do it 'knows what month 2018-06-27 is in' do month = subject.month_for(Date.parse('2018-06-27')) expect(month.name).to eq '2018M7' end it 'knows what month 2018-06-22 is in' do month = subject.month_for(Date.parse('2018-06-22')) expect(month.name).to eq '2018M6' end end describe '#months_for' do it 'knows what months are in 2020' do basis = subject.year(2020) periods = subject.months_for(basis) expect(periods.map(&:name)).to eq %w[2020M1 2020M2 2020M3 2020M4 2020M5 2020M6 2020M7 2020M8 2020M9 2020M10 2020M11 2020M12] end it 'knows what months are in 2018Q2' do basis = subject.parse('2018Q2') periods = subject.months_for(basis) expect(periods.map(&:name)).to eq %w[2018M4 2018M5 2018M6] end it 'knows what month 2019-12-12 is in' do basis = subject.parse('2019-12-12') periods = subject.months_for(basis) expect(periods.map(&:name)).to eq %w[2019M12] end end describe '#this_month' do let(:today) { double } let(:month) { double } it 'gets the month for today' do allow(Date).to receive(:today).and_return today expect(subject).to receive(:month_for).with(today).and_return month expect(subject.this_month).to eq month end end describe '#format' do let(:entry) { subject.month(2015, 8) } it 'can do a default format' do expect(entry.format).to eq '2015H2Q1M2' end it 'can format with only the quarter' do expect(entry.format(:quarter)).to eq '2015Q3M2' end it 'ignores stupidity' do expect(entry.format(:banana, :half, :week)).to eq '2015H2M2' end end context 'relative' do let(:this_month) { subject.month(2015, 3) } let(:month) { double } before(:each) { allow(subject).to receive(:this_month).and_return this_month } it 'can get the last month' do allow(this_month).to receive(:previous).and_return month expect(subject.last_month).to eq month end it 'can get the next month' do allow(this_month).to receive(:next).and_return month expect(subject.next_month).to eq month end it 'can get some number of months' do months = subject.months(5) expect(months.length).to eq 5 months.each { |m| expect(m).to be_a TimeBoss::Calendar::Month } expect(months.map(&:name)).to eq ['2015M3', '2015M4', '2015M5', '2015M6', '2015M7'] end it 'can get a month hence' do month = subject.months_hence(4) expect(month).to be_a TimeBoss::Calendar::Month expect(month.name).to eq '2015M7' end it 'can get some number of months back' do months = subject.months_back(5) expect(months.length).to eq 5 months.each { |m| expect(m).to be_a TimeBoss::Calendar::Month } expect(months.map(&:name)).to eq ['2014M11', '2014M12', '2015M1', '2015M2', '2015M3'] end it 'can get a month ago' do month = subject.months_ago(4) expect(month).to be_a TimeBoss::Calendar::Month expect(month.name).to eq '2014M11' end end end context 'weeks' do before(:each) { allow(Date).to receive(:today).and_return Date.parse('2019-08-23') } it 'knows this week 'do expect(subject.this_week.name).to eq '2019W34' expect(subject.this_week.title).to eq 'Week of August 19, 2019' end it 'knows last week' do expect(subject.last_week.name).to eq '2019W33' expect(subject.last_week.title).to eq 'Week of August 12, 2019' end it 'knows next week' do expect(subject.next_week.name).to eq '2019W35' expect(subject.next_week.title).to eq 'Week of August 26, 2019' end end context 'years' do describe '#year' do it 'knows 2016' do year = subject.year(2016) expect(year.name).to eq '2016' expect(year.title).to eq '2016' expect(year.year_index).to eq 2016 expect(year.index).to eq 1 expect(year.start_date).to eq Date.parse('2015-12-28') expect(year.end_date).to eq Date.parse('2016-12-25') expect(year.to_range).to eq year.start_date..year.end_date end it 'knows 2017' do year = subject.year(2017) expect(year.name).to eq '2017' expect(year.title).to eq '2017' expect(year.year_index).to eq 2017 expect(year.index).to eq 1 expect(year.start_date).to eq Date.parse('2016-12-26') expect(year.end_date).to eq Date.parse('2017-12-31') expect(year.to_range).to eq year.start_date..year.end_date end it 'knows 2018' do year = subject.year(2018) expect(year.name).to eq '2018' expect(year.title).to eq '2018' expect(year.year_index).to eq 2018 expect(year.index).to eq 1 expect(year.start_date).to eq Date.parse('2018-01-01') expect(year.end_date).to eq Date.parse('2018-12-30') expect(year.to_range).to eq year.start_date..year.end_date end end describe '#year_for' do it 'knows what year 2018-04-07 is in' do year = subject.year_for(Date.parse('2018-04-07')) expect(year.name).to eq '2018' end it 'knows what year 2016-12-27 is in' do year = subject.year_for(Date.parse('2016-12-27')) expect(year.name).to eq '2017' end end describe '#years_for' do it 'knows what years are in 2020 (duh)' do basis = subject.year(2020) periods = subject.years_for(basis) expect(periods.map(&:name)).to eq %w[2020] end it 'knows what year 2018Q2 is in' do basis = subject.parse('2018Q2') periods = subject.years_for(basis) expect(periods.map(&:name)).to eq %w[2018] end it 'knows what years 2019-12-12 is in' do basis = subject.parse('2019-12-12') periods = subject.years_for(basis) expect(periods.map(&:name)).to eq %w[2019] end end describe '#this_year' do let(:today) { double } let(:year) { double } it 'gets the year for today' do allow(Date).to receive(:today).and_return today expect(subject).to receive(:year_for).with(today).and_return year expect(subject.this_year).to eq year end end describe '#format' do let(:entry) { subject.parse('2020W24') } it 'can do a default format' do expect(entry.format).to eq '2020H1Q2M3W2' end it 'can format with only the quarter' do expect(entry.format(:quarter)).to eq '2020Q2W11' end it 'can format with only the quarter + month' do expect(entry.format(:quarter, :month)).to eq '2020Q2M3W2' expect(entry.format(:month, :quarter)).to eq '2020Q2M3W2' end it 'ignores stupidity' do expect(entry.format(:day, :month, :banana)).to eq '2020M6W2' end end context 'relative' do let(:this_year) { subject.year(2015) } let(:year) { double } before(:each) { allow(subject).to receive(:this_year).and_return this_year } it 'can get the last year' do allow(this_year).to receive(:previous).and_return year expect(subject.last_year).to eq year end it 'can get the next year' do allow(this_year).to receive(:next).and_return year expect(subject.next_year).to eq year end it 'can get some number of years' do years = subject.years(5) expect(years.length).to eq 5 years.each { |y| expect(y).to be_a TimeBoss::Calendar::Year } expect(years.map(&:name)).to eq ['2015', '2016', '2017', '2018', '2019'] end it 'can get some number of years back' do years = subject.years_back(5) expect(years.length).to eq 5 years.each { |y| expect(y).to be_a TimeBoss::Calendar::Year } expect(years.map(&:name)).to eq ['2011', '2012', '2013', '2014', '2015'] end end end describe '#parse' do it 'can parse a year' do date = subject.parse('2018') expect(date).to be_a TimeBoss::Calendar::Year expect(date.name).to eq '2018' end it 'can parse a quarter identifier' do date = subject.parse('2017Q2') expect(date).to be_a TimeBoss::Calendar::Quarter expect(date.name).to eq '2017Q2' end it 'can parse a month identifier' do date = subject.parse('2017M4') expect(date).to be_a TimeBoss::Calendar::Month expect(date.name).to eq '2017M4' end it 'can parse a week within a year' do date = subject.parse('2018W37') expect(date).to be_a TimeBoss::Calendar::Week expect(date.name).to eq '2018W37' end it 'can parse a week within a quarter' do date = subject.parse('2017Q2W2') expect(date).to be_a TimeBoss::Calendar::Week expect(date.name).to eq '2017W15' end it 'can parse a week within a month' do date = subject.parse('2017M4W1') expect(date).to be_a TimeBoss::Calendar::Week expect(date.name).to eq '2017W14' end it 'can parse a date' do date = subject.parse('2017-04-08') expect(date).to be_a TimeBoss::Calendar::Day expect(date.start_date).to eq Date.parse('2017-04-08') expect(date.end_date).to eq Date.parse('2017-04-08') end it 'can parse an aesthetically displeasing date' do date = subject.parse('20170408') expect(date).to be_a TimeBoss::Calendar::Day expect(date.start_date).to eq Date.parse('2017-04-08') expect(date.end_date).to eq Date.parse('2017-04-08') end it 'gives you this year if you give it nothing' do year = subject.this_year expect(subject.parse(nil)).to eq year expect(subject.parse('')).to eq year end end context 'expressions' do it 'can parse waypoints' do result = subject.parse('this_year') expect(result).to be_a TimeBoss::Calendar::Year expect(result).to be_current end it 'can parse mathematic expressions' do result = subject.parse('this_month + 2') expect(result).to be_a TimeBoss::Calendar::Month expect(result).to eq subject.months_hence(2) end context 'ranges' do before(:each) { allow(subject).to receive(:this_year).and_return subject.year(2018) } let(:result) { subject.parse('this_year-2 .. this_year') } it 'can parse range expressions' do expect(result).to be_a TimeBoss::Calendar::Period expect(result.to_s).to eq "2016: 2015-12-28 thru 2016-12-25 .. 2018: 2018-01-01 thru 2018-12-30" end it 'can get an overall start date for a range' do expect(result.start_date).to eq Date.parse('2015-12-28') end it 'can get an overall end date for a range' do expect(result.end_date).to eq Date.parse('2018-12-30') end context 'sub-periods' do it 'can get the months included in a range' do entries = result.months entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Month } expect(entries.map(&:name)).to include('2016M1', '2016M9', '2017M3', '2018M12') end it 'can get the weeks included in a range' do entries = result.weeks entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Week } expect(entries.map(&:name)).to include('2016W1', '2016W38', '2017W15', '2017W53', '2018W52') end it 'can get the days included in a range' do entries = result.days entries.each { |e| expect(e).to be_a TimeBoss::Calendar::Day } expect(entries.map(&:name)).to include('2015-12-28', '2016-04-30', '2017-09-22', '2018-12-30') end end end end context 'shifting' do context 'from day' do let(:basis) { subject.parse('2020-04-21') } it 'can shift to a different week' do allow(subject).to receive(:this_week).and_return subject.parse('2020W23') result = basis.last_week expect(result).to be_a TimeBoss::Calendar::Day expect(result.to_s).to eq '2020-05-26' expect(basis.in_week).to eq 2 end it 'can shift to a different quarter' do allow(subject).to receive(:this_quarter).and_return subject.parse('2020Q3') result = basis.quarters_ago(2) expect(result).to be_a TimeBoss::Calendar::Day expect(result.to_s).to eq '2020-01-21' expect(basis.in_quarter).to eq 23 end it 'can shift to a different year' do allow(subject).to receive(:this_year).and_return subject.parse('2019') result = basis.years_hence(3) expect(result).to be_a TimeBoss::Calendar::Day expect(result.to_s).to eq '2022-04-19' expect(basis.in_year).to eq 114 end end context 'from week' do let(:basis) { subject.parse('2017W8') } it 'cannot shift to a different day' do expect(basis.last_day).to be nil expect(basis.in_day).to be nil end it 'can shift to a different month' do allow(subject).to receive(:this_month).and_return subject.parse('2020M4') result = basis.next_month expect(result).to be_a TimeBoss::Calendar::Week expect(result.to_s).to eq '2020W20: 2020-05-11 thru 2020-05-17' expect(basis.in_month).to eq 3 end it 'can shift to a different half' do allow(subject).to receive(:this_half).and_return subject.parse('2019H1') result = basis.last_half expect(result).to be_a TimeBoss::Calendar::Week expect(result.to_s).to eq '2018W33: 2018-08-13 thru 2018-08-19' expect(basis.in_half).to eq 8 end end context 'from month' do let(:basis) { subject.parse('2017M4') } it 'cannot shift to a different week' do expect(basis.last_week).to be nil expect(basis.in_week).to be nil end it 'can shift to a different year' do allow(subject).to receive(:this_year).and_return subject.parse('2020') result = basis.years_hence(4) expect(result).to be_a TimeBoss::Calendar::Month expect(result.name).to eq '2024M4' expect(basis.in_year).to eq 4 end end context 'from quarter' do let(:basis) { subject.parse('2018Q2') } it 'cannot shift to a different month' do expect(basis.months_ago(4)).to be nil expect(basis.in_month).to be nil end it 'can shift to a different half' do allow(subject).to receive(:this_half).and_return subject.parse('2020H1') result = basis.last_half expect(result).to be_a TimeBoss::Calendar::Quarter expect(result.name).to eq '2019Q4' expect(basis.in_half).to eq 2 end end context 'from year' do let(:basis) { subject.parse('2014') } it 'cannot shift to a different half' do expect(basis.next_half).to be nil expect(basis.in_half).to be nil end it 'shifts to a different year, but knows how useless that is' do allow(subject).to receive(:this_year).and_return subject.parse('2020') result = basis.years_ago(2) expect(result).to be_a TimeBoss::Calendar::Year expect(result.name).to eq '2018' expect(basis.in_year).to eq 1 end end end context 'units' do let(:calendar) { described_class.new } context 'day' do let(:start_date) { Date.parse('2019-09-30') } let(:subject) { TimeBoss::Calendar::Day.new(calendar, start_date) } context 'links' do it 'can get its previous' do expect(subject.previous.name).to eq '2019-09-29' end it 'can get its next' do expect(subject.next.name).to eq '2019-10-01' end it 'can offset backwards' do expect(subject.offset(-3).name).to eq '2019-09-27' expect((subject - 3).name).to eq '2019-09-27' end it 'can offset forwards' do expect(subject.offset(4).name).to eq '2019-10-04' expect((subject + 4).name).to eq '2019-10-04' end end end context 'week' do context 'links' do context 'within year' do let(:parent) { calendar.parse('2020') } let(:week) { parent.weeks.first } it 'knows itself first' do expect(week.to_s).to include('2020W1', '2019-12-30', '2020-01-05') end it 'can get its next week' do subject = week.next expect(subject).to be_a TimeBoss::Calendar::Week expect(subject.to_s).to include('2020W2', '2020-01-06', '2020-01-12') end it 'can get its previous week' do subject = week.previous expect(subject).to be_a TimeBoss::Calendar::Week expect(subject.to_s).to include('2019W52', '2019-12-23', '2019-12-29') end it 'can offset backwards' do expect(week.offset(-4).name).to eq '2019W49' expect((week - 4).name).to eq '2019W49' end it 'can offset forwards' do expect((week + 2).name).to eq '2020W3' end end context 'within quarter' do let(:parent) { calendar.parse('2019Q3') } let(:week) { parent.weeks.last } it 'knows itself first' do expect(week.to_s).to include('2019W39', '2019-09-23', '2019-09-29') end it 'can get its next week' do subject = week.next expect(subject).to be_a TimeBoss::Calendar::Week expect(subject.to_s).to include('2019W40', '2019-09-30', '2019-10-06') end it 'can get its previous week' do subject = week.previous expect(subject).to be_a TimeBoss::Calendar::Week expect(subject.to_s).to include('2019W38', '2019-09-16', '2019-09-22') end it 'can offset backwards' do expect(week.offset(-4).name).to eq '2019W35' expect((week - 4).name).to eq '2019W35' end it 'can offset forwards' do expect((week + 2).name).to eq '2019W41' end end end end context 'quarter' do let(:start_date) { Date.parse('2019-09-30') } let(:end_date) { Date.parse('2019-12-29') } let(:subject) { TimeBoss::Calendar::Quarter.new(calendar, 2019, 4, start_date, end_date) } context 'links' do it 'can get the next quarter' do quarter = subject.next expect(quarter.to_s).to include('2020Q1', '2019-12-30', '2020-03-29') end it 'can get the next next quarter' do quarter = subject.next.next expect(quarter.to_s).to include('2020Q2', '2020-03-30', '2020-06-28') end it 'can get the next next previous quarter' do quarter = subject.next.next.previous expect(quarter.to_s).to include('2020Q1', '2019-12-30', '2020-03-29') end it 'can get the next previous quarter' do quarter = subject.next.previous expect(quarter.to_s).to eq subject.to_s end it 'can get the previous quarter' do quarter = subject.previous expect(quarter.to_s).to include('2019Q3', '2019-07-01', '2019-09-29') end it 'can offset backwards' do expect(subject.offset(-4).name).to eq '2018Q4' expect((subject - 4).name).to eq '2018Q4' end it 'can offset forwards' do expect(subject.offset(2).name).to eq '2020Q2' expect((subject + 2).name).to eq '2020Q2' end end end end end end