spok 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ class Spok
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,117 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+ require 'set'
4
+ require 'yaml'
5
+
6
+ class Spok
7
+ # Public: Various methods useful for checking for restdays and workdays.
8
+ # All methods are module methods and should be called on the Spok::Workday
9
+ # module.
10
+ module Workday
11
+ # Public: Array of available calendars.
12
+ CALENDARS = %i(brasil bovespa)
13
+
14
+ # Public: Hash containing all holidays for each available calendar.
15
+ HOLIDAYS = CALENDARS.map do |calendar|
16
+ holidays_file = File.open(File.join(File.dirname(__FILE__), "config/#{calendar}.yml"))
17
+ holidays = YAML.safe_load(holidays_file.read, [Date])
18
+ [calendar, Set.new(holidays[calendar.to_s])]
19
+ end.to_h
20
+
21
+ # Public: Checks if a given day is a restday.
22
+ #
23
+ # date - The Date to be checked.
24
+ # calendar - Symbol informing in which calendar the date will be checked
25
+ # (default: :brasil).
26
+ #
27
+ # Examples
28
+ #
29
+ # Spok::Workday.restday?(Date.new(2012, 8, 6))
30
+ # # => false
31
+ #
32
+ # Returns a boolean.
33
+ def self.restday?(date, calendar: :brasil)
34
+ self.weekend?(date) || self.holiday?(date, calendar: calendar)
35
+ end
36
+
37
+ # Public: Checks if a given day is a workday.
38
+ #
39
+ # date - The Date to be checked.
40
+ # calendar - Symbol informing in which calendar the date will be checked
41
+ # (default: :brasil).
42
+ #
43
+ # Examples
44
+ #
45
+ # Spok::Workday.workday?(Date.new(2012, 8, 6))
46
+ # # => true
47
+ #
48
+ # Returns a boolean.
49
+ def self.workday?(date, calendar: :brasil)
50
+ !restday?(date, calendar: calendar)
51
+ end
52
+
53
+ # Public: Checks if a given Date is on a weekend.
54
+ #
55
+ # date - The Date to be checked.
56
+ #
57
+ # Examples
58
+ #
59
+ # Spok::Workday.weekend?(Date.new(2012, 8, 6))
60
+ # # => false
61
+ #
62
+ # Returns a boolean.
63
+ def self.weekend?(date)
64
+ weekday = date.wday
65
+
66
+ weekday == 0 || weekday == 6
67
+ end
68
+
69
+ # Public: Checks if a given Date is on a holiday.
70
+ #
71
+ # date - The Date to be checked.
72
+ # calendar - Symbol informing in which calendar the date will be checked
73
+ # (default: :brasil).
74
+ #
75
+ # Examples
76
+ #
77
+ # Spok::Workday.holiday?(Date.new(2012, 5, 1))
78
+ # # => true
79
+ #
80
+ # Returns a boolean.
81
+ def self.holiday?(date, calendar: :brasil)
82
+ HOLIDAYS[calendar].include?(date.to_date)
83
+ end
84
+
85
+ # Public: Returns the last workday until the informed date.
86
+ # It returns the informed date in case it is a workday.
87
+ #
88
+ # date - End Date to check for workdays.
89
+ # calendar - Symbol informing in which calendar to check for workdays
90
+ # (default: :brasil).
91
+ #
92
+ # Examples
93
+ # Spok::Workday.last_workday(Date.new(2012, 10, 21))
94
+ # # => #<Date: 2012-10-19 ((2456220j,0s,0n),+0s,2299161j)>
95
+ def self.last_workday(date, calendar: :brasil)
96
+ return date if workday?(date, calendar: calendar)
97
+
98
+ last_workday((date - 1.day), calendar: calendar)
99
+ end
100
+
101
+ # Public: Returns the next workday starting from the informed date.
102
+ # It returns the informed date in case it is a workday.
103
+ #
104
+ # date - Start Date to check for workdays.
105
+ # calendar - Symbol informing in which calendar to check for workdays
106
+ # (default: :brasil).
107
+ #
108
+ # Examples
109
+ # Spok::Workday.next_workday(Date.new(2012, 10, 21))
110
+ # # => #<Date: 2012-10-19 ((2456220j,0s,0n),+0s,2299161j)>
111
+ def self.next_workday(date, calendar: :brasil)
112
+ return date if workday?(date, calendar: calendar)
113
+
114
+ next_workday((date + 1.day), calendar: calendar)
115
+ end
116
+ end
117
+ end
Binary file
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+ require 'spok/workday'
3
+
4
+ describe Spok::Workday do
5
+ describe '#workday?' do
6
+ context 'monday' do
7
+ it 'is a workday' do
8
+ expect(described_class.workday?(Date.new(2012, 8, 6))).to eq(true)
9
+ end
10
+ end
11
+
12
+ context 'saturday' do
13
+ it 'is not a workday' do
14
+ expect(described_class.workday?(Date.new(2012, 8, 4))).to eq(false)
15
+ end
16
+ end
17
+
18
+ context 'sunday' do
19
+ it 'is not a workday' do
20
+ expect(described_class.workday?(Date.new(2012, 8, 5))).to eq(false)
21
+ end
22
+ end
23
+
24
+ context 'holidays using brasil calendar' do
25
+ it 'is not a workday' do
26
+ ['2012-06-07', '2012-09-07', '2012-10-12',
27
+ '2012-11-02', '2012-11-15', '2012-12-25'].each do |holiday|
28
+ expect(described_class.workday?(Date.parse(holiday))).to eq(false)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'holidays using bovespa calendar' do
34
+ it 'is not a workday' do
35
+ ['2012-07-09', '2012-11-20', '2012-12-24'].each do |holiday|
36
+ expect(described_class.workday?(Date.parse(holiday), calendar: :bovespa)).to eq(false)
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ describe '#restday?' do
44
+ context 'Using Date objects' do
45
+ context 'monday' do
46
+ it 'is not a restday' do
47
+ expect(described_class.restday?(Date.new(2012, 8, 6))).to eq(false)
48
+ end
49
+ end
50
+
51
+ context 'saturday' do
52
+ it 'is a restday' do
53
+ expect(described_class.restday?(Date.new(2012, 8, 4))).to eq(true)
54
+ end
55
+ end
56
+
57
+ context 'sunday' do
58
+ it 'is a restday' do
59
+ expect(described_class.restday?(Date.new(2012, 8, 5))).to eq(true)
60
+ end
61
+ end
62
+
63
+ context 'holiday' do
64
+ [
65
+ Date.new(2012, 6, 07),
66
+ Date.new(2012, 9, 07),
67
+ Date.new(2012, 10, 12),
68
+ Date.new(2012, 11, 02),
69
+ Date.new(2012, 11, 15),
70
+ Date.new(2012, 12, 25)
71
+ ].each do |holiday|
72
+ it 'is a restday' do
73
+ expect(described_class.restday?(holiday)).to eq(true)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'using DateTime objects' do
80
+ context 'monday' do
81
+ it 'is not a restday' do
82
+ expect(described_class.restday?(DateTime.new(2012, 8, 6, 12))).to eq(false)
83
+ end
84
+ end
85
+
86
+ context 'saturday' do
87
+ it 'is a restday' do
88
+ expect(described_class.restday?(DateTime.new(2012, 8, 4, 12))).to eq(true)
89
+ end
90
+ end
91
+
92
+ context 'sunday' do
93
+ it 'is a restday' do
94
+ expect(described_class.restday?(DateTime.new(2012, 8, 5, 13))).to eq(true)
95
+ end
96
+ end
97
+
98
+ context 'holiday' do
99
+ [
100
+ DateTime.new(2017, 01, 01, 00),
101
+ DateTime.new(2017, 10, 12, 01),
102
+ DateTime.new(2017, 11, 02, 02),
103
+ DateTime.new(2017, 11, 15, 03),
104
+ DateTime.new(2017, 12, 25, 04)
105
+ ].each do |holiday|
106
+ it 'is a restday' do
107
+ expect(described_class.restday?(holiday)).to eq(true)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ describe '#weekend?' do
115
+ it 'returns true when date is a Saturday' do
116
+ expect(described_class.weekend?(Date.new(2018, 8, 11))).to eq(true)
117
+ end
118
+
119
+ it 'returns true when date is a Sunday' do
120
+ expect(described_class.weekend?(Date.new(2018, 8, 12))).to eq(true)
121
+ end
122
+
123
+ it 'returns false when date is not a Saturday or Sunday' do
124
+ expect(described_class.weekend?(Date.new(2018, 8, 13))).to eq(false)
125
+ expect(described_class.weekend?(Date.new(2018, 8, 14))).to eq(false)
126
+ expect(described_class.weekend?(Date.new(2018, 8, 15))).to eq(false)
127
+ expect(described_class.weekend?(Date.new(2018, 8, 16))).to eq(false)
128
+ expect(described_class.weekend?(Date.new(2018, 8, 17))).to eq(false)
129
+ end
130
+ end
131
+
132
+ describe '#holiday?' do
133
+ it 'returns false when date is not a holiday on the given calendar' do
134
+ expect(described_class.holiday?(Date.new(2018, 05, 02), calendar: :brasil)).to eq(false)
135
+ expect(described_class.holiday?(Date.new(2018, 12, 15), calendar: :bovespa)).to eq(false)
136
+ end
137
+
138
+ it 'returns true when date is a holiday on the given calendar' do
139
+ expect(described_class.holiday?(Date.new(2018, 05, 01), calendar: :brasil)).to eq(true)
140
+ expect(described_class.holiday?(Date.new(2018, 12, 25), calendar: :bovespa)).to eq(true)
141
+ end
142
+ end
143
+
144
+ describe '#last_workday' do
145
+ context 'when date is 2012-10-25 (Thursday)' do
146
+ it 'returns the same date' do
147
+ expect(described_class.last_workday(Date.new(2012, 10, 25))).to eq(Date.new(2012, 10, 25))
148
+ end
149
+ end
150
+
151
+ context 'when date is 2012-10-21 (Sunday)' do
152
+ it 'returns 2012-10-19 (Friday)' do
153
+ expect(described_class.last_workday(Date.new(2012, 10, 21))).to eq(Date.new(2012, 10, 19))
154
+ end
155
+ end
156
+
157
+ context 'when date is 2012-10-20 (Saturday)' do
158
+ it 'returns 2012-10-19 (Friday)' do
159
+ expect(described_class.last_workday(Date.new(2012, 10, 20))).to eq(Date.new(2012, 10, 19))
160
+ end
161
+ end
162
+
163
+ context 'when bovespa calendar' do
164
+ context 'when date is 2013-01-01 (bovespa holiday)' do
165
+ it 'returns 2012-12-30, because 2012-12-31 is also a bovespa holiday and 2012-01-30|29 is a weekend' do
166
+ expect(described_class.last_workday(Date.new(2013, 1, 1), calendar: :bovespa)).to eq(Date.new(2012, 12, 28))
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#next_workday' do
173
+ context 'when date is 2012-10-26 (Friday)' do
174
+ it 'returns the same date' do
175
+ expect(described_class.next_workday(Date.new(2012, 10, 26))).to eq(Date.new(2012, 10, 26))
176
+ end
177
+ end
178
+
179
+ context 'when date is 2012-10-21 (Sunday)' do
180
+ it 'returns 2012-10-22 (Monday)' do
181
+ expect(described_class.next_workday(Date.new(2012, 10, 21))).to eq(Date.new(2012, 10, 22))
182
+ end
183
+ end
184
+
185
+ context 'when date is 2012-10-20 (Saturday)' do
186
+ it 'returns 2012-10-22 (Monday)' do
187
+ expect(described_class.next_workday(Date.new(2012, 10, 20))).to eq(Date.new(2012, 10, 22))
188
+ end
189
+ end
190
+
191
+ context 'when bovespa calendar' do
192
+ context 'when date is 2012-12-30 (sunday)' do
193
+ it 'returns 2013-01-02, because 2012, 12, 31 and 2013-01-01 are bovespa holidays' do
194
+ expect(described_class.next_workday(Date.new(2012, 12, 30), calendar: :bovespa)).to eq(Date.new(2013, 1, 2))
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,209 @@
1
+ require 'spec_helper'
2
+ require 'spok'
3
+
4
+ describe Spok do
5
+ let(:sunday) { Date.new(2012, 1, 1) }
6
+ let(:monday) { Date.new(2012, 1, 2) }
7
+ let(:tuesday) { Date.new(2012, 1, 3) }
8
+
9
+ subject(:spok) { described_class.new(sunday, tuesday) }
10
+
11
+ describe '.parse' do
12
+ it { expect(described_class.parse('20120101-20120103')).to eq(spok) }
13
+
14
+ context 'when the input string is wrong' do
15
+ it { expect(described_class.parse('20120101')).to be_nil }
16
+ it { expect(described_class.parse(nil)).to be_nil }
17
+ it { expect(described_class.parse('')).to be_nil }
18
+ end
19
+ end
20
+
21
+ describe '#initialize' do
22
+ it 'does not raise error when start date is before or equals to the end date' do
23
+ expect { subject }.not_to raise_error
24
+ expect { described_class.new(sunday, sunday) }.not_to raise_error
25
+ end
26
+
27
+ it 'raises exception when the start date is nil' do
28
+ expect { described_class.new(nil, sunday) }.to raise_error(ArgumentError)
29
+ end
30
+
31
+ it 'raises exception when the end date is nil' do
32
+ expect { described_class.new(tuesday, nil) }.to raise_error(ArgumentError)
33
+ end
34
+
35
+ it 'raises exception when the start date is greater than the end date' do
36
+ expect { described_class.new(tuesday, sunday) }.to raise_error(ArgumentError)
37
+ end
38
+ end
39
+
40
+ describe '#date_as_integer' do
41
+ it 'represents the start date as integer' do
42
+ expect(subject.start_date_as_integer).to eq(20120101)
43
+ end
44
+
45
+ it 'represents the end date as integer' do
46
+ expect(subject.end_date_as_integer).to eq(20120103)
47
+ end
48
+ end
49
+
50
+ describe '#date_as_string' do
51
+ it 'represents the start date as string' do
52
+ expect(subject.start_date_as_string).to eq('20120101')
53
+ end
54
+
55
+ it 'represents the end date as string' do
56
+ expect(subject.end_date_as_string).to eq('20120103')
57
+ end
58
+ end
59
+
60
+ describe '#to_s' do
61
+ it 'successfully converts Spok to String' do
62
+ expect(spok.to_s).to eq('20120101-20120103')
63
+ end
64
+ end
65
+
66
+ describe '#workdays' do
67
+ subject(:spok) { described_class.new(sunday, wednesday) }
68
+
69
+ let(:sunday) { Date.new(2013, 7, 7) }
70
+ let(:monday) { Date.new(2013, 7, 8) }
71
+ let(:bovespa_holiday) { Date.new(2013, 7, 9) }
72
+ let(:wednesday) { Date.new(2013, 7, 10) }
73
+
74
+ it 'returns all workdays when no calendar is selected' do
75
+ expect(spok.workdays).to eq([monday, bovespa_holiday, wednesday])
76
+ end
77
+
78
+ it 'returns only bovespa workdays when bovespa calendar is selected' do
79
+ expect(subject.workdays(:bovespa)).to eq([monday, wednesday])
80
+ end
81
+ end
82
+
83
+ describe '#to_calendar' do
84
+ context 'when bovespa is the calendar' do
85
+ subject { described_class.new(sunday, tuesday).to_calendar(:bovespa) }
86
+ context 'and start date on a bovespa holiday' do
87
+ let(:start_date) { Date.new(2013, 7, 8) }
88
+ let(:sunday) { Date.new(2013, 7, 9) } # bovespa holiday
89
+ let(:tuesday) { Date.new(2013, 7, 11) }
90
+ it 'returns the last bovespa workday as the start date' do
91
+ expect(subject.start_date).to eq(start_date)
92
+ end
93
+ end
94
+
95
+ context 'and end date on a bovespa holiday' do
96
+ let(:sunday) { Date.new(2013, 7, 7) }
97
+ let(:tuesday) { Date.new(2013, 7, 9) } # bovespa holiday
98
+ let(:end_date) { Date.new(2013, 7, 8) }
99
+ it 'returns the last bovespa workday as the end date' do
100
+ expect(subject.end_date).to eq(end_date)
101
+ end
102
+ end
103
+
104
+ context 'and start and end date on a bovespa holiday' do
105
+ let(:sunday) { Date.new(2012, 12, 31) } #bovespa holiday
106
+ let(:tuesday) { Date.new(2013, 1, 1) } # bovespa holiday
107
+ it 'start and end date are going to be the same' do
108
+ expect(subject.start_date).to eq(subject.end_date)
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'when brasil is the calendar' do
114
+ subject { described_class.new(sunday, tuesday).to_calendar(:brasil) }
115
+
116
+ context 'and the start date is holiday' do
117
+ let(:start_date) { Date.new(2013, 9, 6) }
118
+ let(:sunday) { Date.new(2013, 9, 7) } # brasil holiday
119
+ let(:tuesday) { Date.new(2013, 9, 10) }
120
+ it 'returns the last workday as the start date' do
121
+ expect(subject.start_date).to eq(start_date)
122
+ end
123
+ end
124
+
125
+ context 'end date on a bovespa holiday' do
126
+ let(:sunday) { Date.new(2013, 9, 5) }
127
+ let(:tuesday) { Date.new(2013, 9, 7) } # brasil holiday
128
+ let(:end_date) { Date.new(2013, 9, 6) }
129
+ it 'returns the last bovespa workday as the end date' do
130
+ expect(subject.end_date).to eq(end_date)
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#one_day?' do
137
+ context 'when the start and end date are the same' do
138
+ let(:tuesday) { sunday }
139
+ it { expect(subject).to be_one_day }
140
+ end
141
+
142
+ context 'when the start and end date are different' do
143
+ it { expect(subject).not_to be_one_day }
144
+ end
145
+ end
146
+
147
+ describe '#days_count' do
148
+ it 'counts number of days on spok' do
149
+ spok = described_class.new(sunday, tuesday)
150
+
151
+ expect(spok.days_count).to eq(2)
152
+ end
153
+
154
+ it 'returns 0 when start date is equal to the end date' do
155
+ spok = described_class.new(sunday, sunday)
156
+
157
+ expect(spok.days_count).to eq(0)
158
+ end
159
+ end
160
+
161
+ describe '#years_count' do
162
+ let(:start_date) { Date.new(2013, 1, 1) }
163
+
164
+ it 'calculates integer result' do
165
+ end_date = Date.new(2014, 1, 1)
166
+
167
+ expect(described_class.new(start_date, end_date).years_count).to eq(1)
168
+ end
169
+
170
+ it 'calculates fraction result' do
171
+ spok = described_class.new(start_date, Date.new(2014, 7, 1))
172
+ greater_spok = described_class.new(start_date, Date.new(2014, 9, 1))
173
+
174
+ expect(spok.years_count).to eq(1.5)
175
+ expect(greater_spok.years_count).to eq(1.7)
176
+ end
177
+ end
178
+
179
+ describe '#==' do
180
+ let(:spok) { Spok.new(sunday, tuesday) }
181
+
182
+ context 'when spoks are equal' do
183
+ let(:other_spok) { Spok.new(sunday, tuesday) }
184
+
185
+ it { expect(spok == other_spok).to eq(true) }
186
+ end
187
+
188
+ context 'when spoks are not equal' do
189
+ let(:other_spok) { Spok.new(sunday, monday) }
190
+
191
+ it { expect(spok == other_spok).to eq(false) }
192
+ end
193
+
194
+ context 'when the comparison does respond to start date' do
195
+ let(:other_spok) { double(:other_spok, :end_date => double(:end_date)) }
196
+ it { expect(spok == other_spok).to eq(false) }
197
+ end
198
+
199
+ context 'when the comparison does respond to end date' do
200
+ let(:other_spok) { double(:other_spok, :start_date => sunday) }
201
+ it { expect(spok == other_spok).to eq(false) }
202
+ end
203
+ end
204
+
205
+ describe '#to_range' do
206
+ subject { Spok.new(sunday, tuesday).to_range }
207
+ it { is_expected.to eq((sunday..tuesday)) }
208
+ end
209
+ end