banking_calendar 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +42 -5
- data/lib/banking_calendar/calendar.rb +110 -1
- data/lib/banking_calendar/data/bsp.yml +30 -0
- data/lib/banking_calendar/version.rb +1 -1
- data/spec/calendar_spec.rb +386 -16
- data/spec/fixtures/calendars/bsp.yml +12 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fe25c53a1e7a771f43de415ee1195c06ff985a47ed297cd1e438688d64dbe06
|
4
|
+
data.tar.gz: ba475f817892c882ff8372c83e54c2df30a1df1c691cd1fec9ad3ed90c9eb0bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 492881d3f120a304143e02a8cf491a74440470956944019e6bd4273196b33172cdfc348d10c8738ec3df0af1232625d7fb8e6cab9c59aa3fbab52353ddda6c00
|
7
|
+
data.tar.gz: be84c832399d0806fd4a4af9831eb30f2300eae20d79815dcca2cf9914d72e014089f6fc5bd8de41be03b2fd7570b896ee1a23b877d22b87a148a492ceb18625
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
# v0.0.1 - May 8, 2020
|
2
|
+
|
3
|
+
- Initial release
|
4
|
+
|
5
|
+
# v0.1.0 - May 19, 2020
|
6
|
+
|
7
|
+
- Adds support for banking time and normalization of banking days
|
8
|
+
- Includes banking time in rollover calculations, if provided
|
9
|
+
- Extended default calendar library to include 2021 bank holidays
|
data/README.md
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
# Banking Calendar
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/banking_calendar)
|
3
4
|
[](https://circleci.com/gh/paymongo/banking_calendar)
|
4
5
|
|
5
|
-
This Banking Calendar library provides a way to calculate days based on the banking calendar.
|
6
|
+
This Banking Calendar library provides a way to calculate days based on the banking calendar. This library supports dates with or without the time component. If the time component is provided, e.g. using `Time` or `DateTime` objects, the returned calculated date will normalize to the end of banking day based on the provided banking hours.
|
6
7
|
|
7
8
|
# How to use
|
8
9
|
|
10
|
+
You may use the package by running:
|
11
|
+
|
12
|
+
```sh
|
13
|
+
gem install banking_calendar
|
14
|
+
```
|
15
|
+
|
16
|
+
If you are installing via the bundler, make sure to use the https rubygems source:
|
17
|
+
|
18
|
+
```sh
|
19
|
+
source 'https://rubygems.org'
|
20
|
+
|
21
|
+
gem 'banking_calendar'
|
22
|
+
```
|
9
23
|
## Basic usage
|
10
24
|
|
11
25
|
You can create a banking calendar by creating an instance of the `Calendar` class and specifying
|
@@ -14,10 +28,13 @@ the `banking_days` and `bank_holidays`.
|
|
14
28
|
```ruby
|
15
29
|
calendar = BankingCalendar::Calendar.new(
|
16
30
|
banking_days: %w(monday tuesday wednesday thursday friday),
|
17
|
-
banking_holidays: %w(2020-01-01 2020-01-25)
|
31
|
+
banking_holidays: %w(2020-01-01 2020-01-25),
|
32
|
+
banking_hours: (9..16).to_a
|
18
33
|
)
|
19
34
|
```
|
20
|
-
If `banking_days`
|
35
|
+
If `banking_days` is not provided, then the default used is Monday to Friday.
|
36
|
+
|
37
|
+
Note that `banking_hours` is a list of hours, in 24-hours time integers, from opening to closing. It is exclusive of the banking hour at bank closing time. For example, if the banking hours are from 9 a.m. to 5 p.m., the list of banking hours provided must be equivalent to `[9, 10, 11, 12, 13, 14, 15, 16]`. If `banking_hours` is not provided, the default used is from 9 a.m. to 5 p.m. on a regular banking day.
|
21
38
|
|
22
39
|
## Using default calendars
|
23
40
|
|
@@ -25,7 +42,7 @@ Some few calendar configurations are provided. You may use them by calling `load
|
|
25
42
|
the name of the calendar you want to load. You may refer to the list of available calendars below.
|
26
43
|
|
27
44
|
```ruby
|
28
|
-
|
45
|
+
calendar = BankingCalendar::Calendar.load_calendar('bsp')
|
29
46
|
```
|
30
47
|
|
31
48
|
## Useful methods
|
@@ -44,6 +61,8 @@ calendar.banking_day?(Date.parse('15 April 2020'))
|
|
44
61
|
Given a `date`, this method returns the date after `interval` number of business days. If the given
|
45
62
|
`date` falls on a non-banking day, the calculation starts at the next possible banking day.
|
46
63
|
|
64
|
+
If banking hours are provided in the calendar configuration, the returned date and time will be normalized to the end of banking day. If given time falls after banking hours, counting starts from the next banking day.
|
65
|
+
|
47
66
|
```ruby
|
48
67
|
# May 4, Monday is a banking day
|
49
68
|
date = Date.parse('2020-05-04')
|
@@ -55,12 +74,22 @@ date = Date.parse('2020-05-01')
|
|
55
74
|
# Next banking day is May 4, Monday
|
56
75
|
calendar.banking_days_after(date, 2).strftime("%A, %B %d, %Y")
|
57
76
|
# => Wednesday, May 06, 2020
|
77
|
+
|
78
|
+
date = DateTime.parse('2020-05-04 11:00')
|
79
|
+
calendar.banking_days_after(date, 2)
|
80
|
+
# => May 6, 2020 at 5 p.m.
|
81
|
+
|
82
|
+
date = DateTime.parse('2020-05-04 19:00')
|
83
|
+
calendar.banking_days_after(date, 2)
|
84
|
+
# => May 7, 2020 at 5 p.m.
|
58
85
|
```
|
59
86
|
|
60
87
|
### banking_days_before(date, interval)
|
61
88
|
Given a `date`, this method returns the prior `interval` number of business days. If the given
|
62
89
|
`date` falls on a non-banking day, the calculation starts at the first previous possible banking day.
|
63
90
|
|
91
|
+
If banking hours are provided in the calendar configuration, the returned date and time will be normalized to the end of banking day. If given time does not falls before banking hours, counting starts from the previous banking day.
|
92
|
+
|
64
93
|
```ruby
|
65
94
|
# May 22, 2020 Friday is a banking day
|
66
95
|
date = Date.parse('2020-05-22')
|
@@ -70,8 +99,16 @@ calendar.banking_days_before(date, 4).strftime("%A, %B %d, %Y")
|
|
70
99
|
# May 1, 2020 Friday is a bank holiday
|
71
100
|
date = Date.parse('2020-05-01')
|
72
101
|
# Previous banking day is April 30, 2020 Thursday
|
73
|
-
calendar.
|
102
|
+
calendar.banking_days_before(date, 2).strftime("%A, %B %d, %Y")
|
74
103
|
# => Tuesday, April 28, 2020
|
104
|
+
|
105
|
+
date = DateTime.parse('2020-05-06 11:00')
|
106
|
+
calendar.banking_days_before(date, 2)
|
107
|
+
# => May 4, 2020 at 5 p.m.
|
108
|
+
|
109
|
+
date = DateTime.parse('2020-05-08 06:00')
|
110
|
+
calendar.banking_days_before(date, 2)
|
111
|
+
# => May 5, 2020 at 5 p.m.
|
75
112
|
```
|
76
113
|
|
77
114
|
### next_banking_day(date)
|
@@ -41,7 +41,13 @@ module BankingCalendar
|
|
41
41
|
end
|
42
42
|
|
43
43
|
DEFAULT_BANKING_DAYS = %w[mon tue wed thu fri].freeze
|
44
|
-
|
44
|
+
DEFAULT_BANKING_HOURS = (9..16).to_a.freeze
|
45
|
+
VALID_CALENDAR_KEYS = %i[
|
46
|
+
banking_days
|
47
|
+
bank_holidays
|
48
|
+
banking_hours
|
49
|
+
].freeze
|
50
|
+
VALID_BANKING_HOURS_KEYS = %i[start end].freeze
|
45
51
|
VALID_DAYS = %w[sun mon tue wed thu fri sat].freeze
|
46
52
|
|
47
53
|
@semaphore = Mutex.new
|
@@ -75,8 +81,18 @@ module BankingCalendar
|
|
75
81
|
date
|
76
82
|
end
|
77
83
|
|
84
|
+
# Given a date, add interval number of banking days.
|
85
|
+
#
|
86
|
+
# If the given date is not a banking day, counting starts from the
|
87
|
+
# next banking day.
|
88
|
+
#
|
89
|
+
# If banking hours are provided, returned date and time will be
|
90
|
+
# normalized to the end of banking day. If given date falls after
|
91
|
+
# banking hours, counting starts from the next banking day.
|
78
92
|
def banking_days_after(date, interval)
|
93
|
+
date = normalize_date(date, :after) if with_banking_hours?
|
79
94
|
date = next_banking_day(date) unless banking_day?(date)
|
95
|
+
|
80
96
|
interval.times do
|
81
97
|
date = next_banking_day(date)
|
82
98
|
end
|
@@ -84,8 +100,18 @@ module BankingCalendar
|
|
84
100
|
date
|
85
101
|
end
|
86
102
|
|
103
|
+
# Given a date, subtract interval number of banking days.
|
104
|
+
#
|
105
|
+
# If the given date is not a banking day, counting starts from the
|
106
|
+
# previous banking day.
|
107
|
+
#
|
108
|
+
# If banking hours are provided, returned date and time will be
|
109
|
+
# normalized to the end of banking day. If given date falls before
|
110
|
+
# banking hours, counting starts from the prior banking day.
|
87
111
|
def banking_days_before(date, interval)
|
112
|
+
date = normalize_date(date, :before) if with_banking_hours?
|
88
113
|
date = previous_banking_day(date) unless banking_day?(date)
|
114
|
+
|
89
115
|
interval.times do
|
90
116
|
date = previous_banking_day(date)
|
91
117
|
end
|
@@ -103,6 +129,41 @@ module BankingCalendar
|
|
103
129
|
true
|
104
130
|
end
|
105
131
|
|
132
|
+
def banking_hour?(date)
|
133
|
+
time_or_datetime?(date)
|
134
|
+
|
135
|
+
hour = date.hour
|
136
|
+
|
137
|
+
return false unless banking_day?(date)
|
138
|
+
return false unless banking_hours.include?(hour)
|
139
|
+
|
140
|
+
true
|
141
|
+
end
|
142
|
+
|
143
|
+
def before_banking_hours?(date)
|
144
|
+
time_or_datetime? date
|
145
|
+
return false unless banking_day?(date)
|
146
|
+
|
147
|
+
date.hour < banking_hours.min
|
148
|
+
end
|
149
|
+
|
150
|
+
def after_banking_hours?(date)
|
151
|
+
time_or_datetime? date
|
152
|
+
return true unless banking_day?(date)
|
153
|
+
|
154
|
+
date.hour > banking_hours.max
|
155
|
+
end
|
156
|
+
|
157
|
+
def end_of_banking_day(date)
|
158
|
+
date.class.new(
|
159
|
+
date.year,
|
160
|
+
date.month,
|
161
|
+
date.day,
|
162
|
+
banking_hours.max + 1,
|
163
|
+
0
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
106
167
|
private
|
107
168
|
|
108
169
|
def duration_for(date, interval = 1)
|
@@ -115,6 +176,54 @@ module BankingCalendar
|
|
115
176
|
end
|
116
177
|
end
|
117
178
|
|
179
|
+
def time_or_datetime?(date)
|
180
|
+
unless date.is_a?(Time) || date.is_a?(DateTime)
|
181
|
+
raise "#{date} is #{date.class}. " \
|
182
|
+
'Must be Time or DateTime if accounting for banking hours.'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def roll_forward(date)
|
187
|
+
if banking_day?(date) && after_banking_hours?(date)
|
188
|
+
date = next_banking_day(date)
|
189
|
+
end
|
190
|
+
|
191
|
+
date
|
192
|
+
end
|
193
|
+
|
194
|
+
def roll_backward(date)
|
195
|
+
if banking_day?(date) && before_banking_hours?(date)
|
196
|
+
date = previous_banking_day(date)
|
197
|
+
end
|
198
|
+
|
199
|
+
date
|
200
|
+
end
|
201
|
+
|
202
|
+
def normalize_date(date, rollover)
|
203
|
+
time_or_datetime? date
|
204
|
+
|
205
|
+
if rollover == :after
|
206
|
+
date = roll_forward(date)
|
207
|
+
elsif rollover == :before
|
208
|
+
date = roll_backward(date)
|
209
|
+
end
|
210
|
+
date = end_of_banking_day(date)
|
211
|
+
|
212
|
+
date
|
213
|
+
end
|
214
|
+
|
215
|
+
def banking_hours
|
216
|
+
@banking_hours ||= (@config[:banking_hours] || DEFAULT_BANKING_HOURS).map do |hour|
|
217
|
+
hour.tap do |h|
|
218
|
+
raise "#{h} is an invalid hour." if h > 24 || h.negative?
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def with_banking_hours?
|
224
|
+
@config.key?(:banking_hours)
|
225
|
+
end
|
226
|
+
|
118
227
|
def banking_days
|
119
228
|
@banking_days ||= (@config[:banking_days] || DEFAULT_BANKING_DAYS).map do |day|
|
120
229
|
day.downcase.strip[0..2].tap do |shortened_day|
|
@@ -5,6 +5,16 @@ banking_days:
|
|
5
5
|
- thursday
|
6
6
|
- friday
|
7
7
|
|
8
|
+
banking_hours:
|
9
|
+
- 9
|
10
|
+
- 10
|
11
|
+
- 11
|
12
|
+
- 12
|
13
|
+
- 13
|
14
|
+
- 14
|
15
|
+
- 15
|
16
|
+
- 16
|
17
|
+
|
8
18
|
bank_holidays:
|
9
19
|
- 2020-01-01
|
10
20
|
- 2020-01-25
|
@@ -24,3 +34,23 @@ bank_holidays:
|
|
24
34
|
- 2020-12-25
|
25
35
|
- 2020-12-30
|
26
36
|
- 2020-12-31
|
37
|
+
- 2021-01-01
|
38
|
+
- 2021-02-12
|
39
|
+
- 2021-02-25
|
40
|
+
- 2021-04-01
|
41
|
+
- 2021-04-02
|
42
|
+
- 2021-04-09
|
43
|
+
- 2021-05-01
|
44
|
+
- 2021-05-13
|
45
|
+
- 2021-06-12
|
46
|
+
- 2021-07-20
|
47
|
+
- 2021-08-21
|
48
|
+
- 2021-08-30
|
49
|
+
- 2021-11-01
|
50
|
+
- 2021-11-02
|
51
|
+
- 2021-11-30
|
52
|
+
- 2021-12-08
|
53
|
+
- 2021-12-24
|
54
|
+
- 2021-12-25
|
55
|
+
- 2021-12-30
|
56
|
+
- 2021-12-31
|
data/spec/calendar_spec.rb
CHANGED
@@ -25,7 +25,7 @@ describe BankingCalendar::Calendar do
|
|
25
25
|
subject { BankingCalendar::Calendar.load_calendar('invalid_calendar') }
|
26
26
|
it 'raises an error' do
|
27
27
|
expect { subject }.to raise_error(
|
28
|
-
'Only the following keys are valid: banking_days, bank_holidays'
|
28
|
+
'Only the following keys are valid: banking_days, bank_holidays, banking_hours'
|
29
29
|
)
|
30
30
|
end
|
31
31
|
end
|
@@ -39,21 +39,6 @@ describe BankingCalendar::Calendar do
|
|
39
39
|
)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
43
|
-
context 'when calendar is from an additional directory' do
|
44
|
-
after { BankingCalendar::Calendar.additional_load_paths = nil }
|
45
|
-
subject { BankingCalendar::Calendar.load_calendar('valid_calendar') }
|
46
|
-
|
47
|
-
it { is_expected.to be_a BankingCalendar::Calendar }
|
48
|
-
|
49
|
-
context 'when also a default calendar' do
|
50
|
-
subject { BankingCalendar::Calendar.load_calendar('bsp') }
|
51
|
-
|
52
|
-
it 'uses the custom calendar' do
|
53
|
-
expect(subject.banking_day?(Date.parse('2020-01-01'))).to eq(true)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
42
|
end
|
58
43
|
|
59
44
|
shared_examples 'shared' do
|
@@ -202,6 +187,382 @@ describe BankingCalendar::Calendar do
|
|
202
187
|
end
|
203
188
|
end
|
204
189
|
|
190
|
+
shared_examples 'with_time' do
|
191
|
+
context 'when providing date objects without time' do
|
192
|
+
let(:cal) do
|
193
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
194
|
+
end
|
195
|
+
subject { cal.banking_hour?(date) }
|
196
|
+
|
197
|
+
context 'when provided a valid date' do
|
198
|
+
let(:date) { date_class.parse('2020-01-10 10:00') }
|
199
|
+
it { is_expected.to be_truthy }
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'when provided an invalid date' do
|
203
|
+
let(:date) { Date.parse('2020-01-10') }
|
204
|
+
|
205
|
+
it 'raises an error' do
|
206
|
+
expect { subject }.to raise_error(
|
207
|
+
'2020-01-10 is Date. ' \
|
208
|
+
'Must be Time or DateTime if accounting for banking hours.'
|
209
|
+
)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#banking_hour?' do
|
215
|
+
let(:cal) do
|
216
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
217
|
+
end
|
218
|
+
subject { cal.banking_hour?(date) }
|
219
|
+
|
220
|
+
context 'when it is a banking hour' do
|
221
|
+
context 'when it is a banking day' do
|
222
|
+
let(:date) { date_class.parse('2020-01-10 10:00') }
|
223
|
+
it { is_expected.to be_truthy }
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'when it is a non-banking day' do
|
227
|
+
let(:date) { date_class.parse('2020-01-01 13:12') }
|
228
|
+
it { is_expected.to be_falsey }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'when it is not a banking hour' do
|
233
|
+
context 'when it is a banking day' do
|
234
|
+
let(:date) { date_class.parse('2020-01-10 06:22') }
|
235
|
+
it { is_expected.to be_falsey }
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'when it is a non-banking day' do
|
239
|
+
let(:date) { date_class.parse('2020-01-01 18:55') }
|
240
|
+
it { is_expected.to be_falsey }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe '#before_banking_hours?' do
|
246
|
+
let(:cal) do
|
247
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
248
|
+
end
|
249
|
+
subject { cal.before_banking_hours?(date) }
|
250
|
+
|
251
|
+
context 'when it is a banking day' do
|
252
|
+
context 'given before banking hours' do
|
253
|
+
let(:date) { date_class.parse('2020-01-10 06:22') }
|
254
|
+
it { is_expected.to be_truthy }
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'given during banking hours' do
|
258
|
+
let(:date) { date_class.parse('2020-01-10 14:14') }
|
259
|
+
it { is_expected.to be_falsey }
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'given after banking hours' do
|
263
|
+
let(:date) { date_class.parse('2020-01-10 17:15') }
|
264
|
+
it { is_expected.to be_falsey }
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'when it is a non-banking day' do
|
269
|
+
context 'given before banking hours' do
|
270
|
+
let(:date) { date_class.parse('2020-05-02 06:22') }
|
271
|
+
it { is_expected.to be_falsey }
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'given during banking hours' do
|
275
|
+
let(:date) { date_class.parse('2020-05-02 14:14') }
|
276
|
+
it { is_expected.to be_falsey }
|
277
|
+
end
|
278
|
+
|
279
|
+
context 'given after banking hours' do
|
280
|
+
let(:date) { date_class.parse('2020-05-02 17:15') }
|
281
|
+
it { is_expected.to be_falsey }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context 'when it is a holiday' do
|
286
|
+
context 'given before banking hours' do
|
287
|
+
let(:date) { date_class.parse('2020-01-01 06:22') }
|
288
|
+
it { is_expected.to be_falsey }
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'given during banking hours' do
|
292
|
+
let(:date) { date_class.parse('2020-01-01 14:14') }
|
293
|
+
it { is_expected.to be_falsey }
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'given after banking hours' do
|
297
|
+
let(:date) { date_class.parse('2020-01-01 17:15') }
|
298
|
+
it { is_expected.to be_falsey }
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe '#after_banking_hours?' do
|
304
|
+
let(:cal) do
|
305
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
306
|
+
end
|
307
|
+
subject { cal.after_banking_hours?(date) }
|
308
|
+
|
309
|
+
context 'when it is a banking day' do
|
310
|
+
context 'given before banking hours' do
|
311
|
+
let(:date) { date_class.parse('2020-01-10 06:22') }
|
312
|
+
it { is_expected.to be_falsey }
|
313
|
+
end
|
314
|
+
|
315
|
+
context 'given during banking hours' do
|
316
|
+
let(:date) { date_class.parse('2020-01-10 14:14') }
|
317
|
+
it { is_expected.to be_falsey }
|
318
|
+
end
|
319
|
+
|
320
|
+
context 'given before banking hours' do
|
321
|
+
let(:date) { date_class.parse('2020-01-10 17:15') }
|
322
|
+
it { is_expected.to be_truthy }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context 'when it is a non-banking day' do
|
327
|
+
context 'given before banking hours' do
|
328
|
+
let(:date) { date_class.parse('2020-05-02 06:22') }
|
329
|
+
it { is_expected.to be_truthy }
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'given during banking hours' do
|
333
|
+
let(:date) { date_class.parse('2020-05-02 14:14') }
|
334
|
+
it { is_expected.to be_truthy }
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'given before banking hours' do
|
338
|
+
let(:date) { date_class.parse('2020-05-02 17:15') }
|
339
|
+
it { is_expected.to be_truthy }
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context 'when it is a holiday' do
|
344
|
+
context 'given before banking hours' do
|
345
|
+
let(:date) { date_class.parse('2020-01-01 06:22') }
|
346
|
+
it { is_expected.to be_truthy }
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'given during banking hours' do
|
350
|
+
let(:date) { date_class.parse('2020-01-01 14:14') }
|
351
|
+
it { is_expected.to be_truthy }
|
352
|
+
end
|
353
|
+
|
354
|
+
context 'given before banking hours' do
|
355
|
+
let(:date) { date_class.parse('2020-01-01 17:15') }
|
356
|
+
it { is_expected.to be_truthy }
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe '#banking_days_after' do
|
362
|
+
let(:cal) do
|
363
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
364
|
+
end
|
365
|
+
subject { cal.banking_days_after(date, delta) }
|
366
|
+
|
367
|
+
context 'when it is a banking day' do
|
368
|
+
context 'followed only by banking days' do
|
369
|
+
context 'when it is before banking hours' do
|
370
|
+
let(:date) { date_class.parse('2020-01-13 04:00') }
|
371
|
+
let(:delta) { 3 }
|
372
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + delta * interval)) }
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'when it is during banking hours' do
|
376
|
+
let(:date) { date_class.parse('2020-01-13 11:00') }
|
377
|
+
let(:delta) { 3 }
|
378
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + delta * interval)) }
|
379
|
+
end
|
380
|
+
|
381
|
+
context 'when it is after banking hours' do
|
382
|
+
let(:date) { date_class.parse('2020-01-13 18:00') }
|
383
|
+
let(:delta) { 3 }
|
384
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
context 'followed by a weekend' do
|
389
|
+
context 'when it is before banking hours' do
|
390
|
+
let(:date) { date_class.parse('2020-05-15 04:00') }
|
391
|
+
let(:delta) { 3 }
|
392
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
393
|
+
end
|
394
|
+
|
395
|
+
context 'when it is during banking hours' do
|
396
|
+
let(:date) { date_class.parse('2020-05-15 13:00') }
|
397
|
+
let(:delta) { 3 }
|
398
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
399
|
+
end
|
400
|
+
|
401
|
+
context 'when it is after banking hours' do
|
402
|
+
let(:date) { date_class.parse('2020-05-15 18:15') }
|
403
|
+
let(:delta) { 3 }
|
404
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 3) * interval)) }
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context 'followed by a holiday' do
|
409
|
+
context 'when it is before banking hours' do
|
410
|
+
let(:date) { date_class.parse('2020-02-24 04:00') }
|
411
|
+
let(:delta) { 3 }
|
412
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
413
|
+
end
|
414
|
+
|
415
|
+
context 'when it is during banking hours' do
|
416
|
+
let(:date) { date_class.parse('2020-02-24 13:00') }
|
417
|
+
let(:delta) { 3 }
|
418
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
419
|
+
end
|
420
|
+
|
421
|
+
context 'when it is after banking hours' do
|
422
|
+
let(:date) { date_class.parse('2020-02-24 18:15') }
|
423
|
+
let(:delta) { 3 }
|
424
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 4) * interval)) }
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context 'when it is a non-banking day' do
|
430
|
+
context 'followed only by banking days' do
|
431
|
+
context 'when time is before banking hours' do
|
432
|
+
let(:date) { date_class.parse('2020-05-10 04:00') }
|
433
|
+
let(:delta) { 3 }
|
434
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
435
|
+
end
|
436
|
+
|
437
|
+
context 'when time is during banking hours' do
|
438
|
+
let(:date) { date_class.parse('2020-05-10 11:00') }
|
439
|
+
let(:delta) { 3 }
|
440
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
441
|
+
end
|
442
|
+
|
443
|
+
context 'when time is after banking hours' do
|
444
|
+
let(:date) { date_class.parse('2020-05-10 19:00') }
|
445
|
+
let(:delta) { 3 }
|
446
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 1) * interval)) }
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'followed by another non-banking day' do
|
451
|
+
context 'when time is before banking hours' do
|
452
|
+
let(:date) { date_class.parse('2020-05-16 04:00') }
|
453
|
+
let(:delta) { 3 }
|
454
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
455
|
+
end
|
456
|
+
|
457
|
+
context 'when time is during banking hours' do
|
458
|
+
let(:date) { date_class.parse('2020-05-16 11:00') }
|
459
|
+
let(:delta) { 3 }
|
460
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
461
|
+
end
|
462
|
+
|
463
|
+
context 'when time is after banking hours' do
|
464
|
+
let(:date) { date_class.parse('2020-05-16 19:00') }
|
465
|
+
let(:delta) { 3 }
|
466
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context 'followed by a holiday' do
|
471
|
+
context 'when time is before banking hours' do
|
472
|
+
let(:date) { date_class.parse('2020-08-30 04:00') }
|
473
|
+
let(:delta) { 3 }
|
474
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
475
|
+
end
|
476
|
+
|
477
|
+
context 'when time is during banking hours' do
|
478
|
+
let(:date) { date_class.parse('2020-08-30 11:00') }
|
479
|
+
let(:delta) { 3 }
|
480
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
481
|
+
end
|
482
|
+
|
483
|
+
context 'when time is after banking hours' do
|
484
|
+
let(:date) { date_class.parse('2020-08-30 19:00') }
|
485
|
+
let(:delta) { 3 }
|
486
|
+
it { is_expected.to eq(cal.end_of_banking_day(date + (delta + 2) * interval)) }
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
describe '#banking_days_before' do
|
493
|
+
let(:cal) do
|
494
|
+
BankingCalendar::Calendar.load_calendar('bsp')
|
495
|
+
end
|
496
|
+
subject { cal.banking_days_before(date, delta) }
|
497
|
+
|
498
|
+
context 'when it is a banking day' do
|
499
|
+
context 'preceded only by banking days' do
|
500
|
+
context 'when time is before banking hours' do
|
501
|
+
let(:date) { date_class.parse('2020-01-16 08:00') }
|
502
|
+
let(:delta) { 2 }
|
503
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 1) * interval)) }
|
504
|
+
end
|
505
|
+
|
506
|
+
context 'when time is during banking hours' do
|
507
|
+
let(:date) { date_class.parse('2020-01-16 09:00') }
|
508
|
+
let(:delta) { 2 }
|
509
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - delta * interval)) }
|
510
|
+
end
|
511
|
+
|
512
|
+
context 'when time is after banking hours' do
|
513
|
+
let(:date) { date_class.parse('2020-01-16 18:00') }
|
514
|
+
let(:delta) { 2 }
|
515
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - delta * interval)) }
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
context 'preceded by a weekend' do
|
520
|
+
context 'when time is before banking hours' do
|
521
|
+
let(:date) { date_class.parse('2020-05-11 08:00') }
|
522
|
+
let(:delta) { 2 }
|
523
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 3) * interval)) }
|
524
|
+
end
|
525
|
+
|
526
|
+
context 'when time is during banking hours' do
|
527
|
+
let(:date) { date_class.parse('2020-05-11 09:00') }
|
528
|
+
let(:delta) { 2 }
|
529
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 2) * interval)) }
|
530
|
+
end
|
531
|
+
|
532
|
+
context 'when time is after banking hours' do
|
533
|
+
let(:date) { date_class.parse('2020-05-11 18:00') }
|
534
|
+
let(:delta) { 2 }
|
535
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 2) * interval)) }
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
context 'preceded by banking days and non-banking days' do
|
540
|
+
context 'when time is before banking hours' do
|
541
|
+
let(:date) { date_class.parse('2020-05-19 08:00') }
|
542
|
+
let(:delta) { 2 }
|
543
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 3) * interval)) }
|
544
|
+
end
|
545
|
+
|
546
|
+
context 'when time is during banking hours' do
|
547
|
+
let(:date) { date_class.parse('2020-05-19 09:00') }
|
548
|
+
let(:delta) { 2 }
|
549
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 2) * interval)) }
|
550
|
+
end
|
551
|
+
|
552
|
+
context 'when time is after banking hours' do
|
553
|
+
let(:date) { date_class.parse('2020-05-19 18:00') }
|
554
|
+
let(:delta) { 2 }
|
555
|
+
it { is_expected.to eq(cal.end_of_banking_day(date - (delta + 2) * interval)) }
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
context 'when it is a non-banking day' do
|
561
|
+
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
205
566
|
context 'when using Date objects' do
|
206
567
|
let(:date_class) { Date }
|
207
568
|
let(:interval) { 1 }
|
@@ -209,10 +570,19 @@ describe BankingCalendar::Calendar do
|
|
209
570
|
it_behaves_like 'shared'
|
210
571
|
end
|
211
572
|
|
573
|
+
context 'when using DateTime objects' do
|
574
|
+
let(:date_class) { DateTime }
|
575
|
+
let(:interval) { 1 }
|
576
|
+
|
577
|
+
it_behaves_like 'shared'
|
578
|
+
it_behaves_like 'with_time'
|
579
|
+
end
|
580
|
+
|
212
581
|
context 'when using Time objects' do
|
213
582
|
let(:date_class) { Time }
|
214
583
|
let(:interval) { 3_600 * 24 }
|
215
584
|
|
216
585
|
it_behaves_like 'shared'
|
586
|
+
it_behaves_like 'with_time'
|
217
587
|
end
|
218
588
|
end
|
@@ -5,7 +5,18 @@ banking_days:
|
|
5
5
|
- thursday
|
6
6
|
- friday
|
7
7
|
|
8
|
+
banking_hours:
|
9
|
+
- 9
|
10
|
+
- 10
|
11
|
+
- 11
|
12
|
+
- 12
|
13
|
+
- 13
|
14
|
+
- 14
|
15
|
+
- 15
|
16
|
+
- 16
|
17
|
+
|
8
18
|
bank_holidays:
|
19
|
+
- 2020-01-01
|
9
20
|
- 2020-01-25
|
10
21
|
- 2020-02-25
|
11
22
|
- 2020-04-09
|
@@ -17,6 +28,7 @@ bank_holidays:
|
|
17
28
|
- 2020-08-31
|
18
29
|
- 2020-11-01
|
19
30
|
- 2020-11-02
|
31
|
+
- 2020-11-03
|
20
32
|
- 2020-11-30
|
21
33
|
- 2020-12-08
|
22
34
|
- 2020-12-24
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: banking_calendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- PayMongo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|