spok 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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