ndr_support 5.5.1 → 5.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f5d3aeeaaa13e7221012d0c20948ce96257b4ebb2824e765d0570bc10cec75c
4
- data.tar.gz: 57dc19084dc7ebe85fce29dcd5db48372febf8ee58c9cdd8a2ae275c81d1037c
3
+ metadata.gz: 902f81bfb91abe5145aebc693e0f200b621db63183963e6c1bc0d2c7ef783c7e
4
+ data.tar.gz: 8bb91d0cfa4e64493330d9fa1eeb2765452e45547d751df43daf954da5a06ddd
5
5
  SHA512:
6
- metadata.gz: e9b013478c6a967e6dc2293de8c7bae820382fdb6dc0c8b0bb5aae82da064969620fb8143a6ee9013166f0d652c7ce64a7187279ca5b571c718cd74de5d075b5
7
- data.tar.gz: c4a0b7c8c41c93aacbf53351d7ef517593e7f0612c6b6cb750ab218223e2d494dc9c746c9005c1bf003d2ed9c9e6ed4887813688f79f7eb22cb25e0bec6cff5b
6
+ metadata.gz: b006ab6c6ea53f6ce5b47dc38fd290d1b9679995eacea4cbd269477e79e06bc2a1a752b273d707dce3a127e8c605b08fae8dfa6c1a606bb272f290521f66d971
7
+ data.tar.gz: 193a182ca039ba2113d1701c75511eb00bd8c4ce3e52e5797c7a6846c3963fdca392b3374faa79b9fdf78e25ad3514f87d1bbc2bc6545f68092f698b7ab93a33
data/CHANGELOG.md CHANGED
@@ -1,11 +1,40 @@
1
1
  ## [Unreleased]
2
2
  *no unreleased changes*
3
3
 
4
+ ## 5.8.0 / 2021-04-19
5
+ ## Added
6
+
7
+ * Add ability to disable date reversing in Daterange
8
+
9
+ ## Fixed
10
+ * Support Ruby 2.6-3.0.
11
+ * Fix ruby warnings
12
+
13
+ ## 5.7.1 / 2021-01-03
14
+ ## Fixed
15
+ * Postcodeize old Newport postcodes
16
+ * Bump rake version
17
+ * Support 2022 public holidays
18
+
19
+ ## 5.7.0 / 2020-06-30
20
+ ## Added
21
+ * Handle three char months in Daterange
22
+
23
+ ## 5.6.1 / 2020-01-02
24
+ ## Fixed
25
+ * Fix issue with 2020 public holiday
26
+ * Support 2021 public holidays
27
+ * Ensure dateranges up to 2030 are supported
28
+
29
+ ## 5.6.0 / 2019-08-29
30
+ ### Added
31
+ * Add `Integer#working_days_since`. (#11)
32
+
4
33
  ## 5.5.1 / 2019-05-15
5
34
  ### Fixed
6
35
  * Support Ruby 2.6, Rails 6.0. Minimum Ruby/Rails versions are now 2.5/5.2
7
36
  * Warn when WorkingDays lookup is getting stale
8
- * Improved date parsing in String#to_date
37
+ * Improved date parsing in `String#to_date`
9
38
 
10
39
  ## 5.5.0 / 2018-11-16
11
40
  ### Added
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # NdrSupport [![Build Status](https://travis-ci.org/PublicHealthEngland/ndr_support.svg?branch=master)](https://travis-ci.org/PublicHealthEngland/ndr_support) [![Gem Version](https://badge.fury.io/rb/ndr_support.svg)](https://badge.fury.io/rb/ndr_support)
1
+ # NdrSupport [![Build Status](https://github.com/publichealthengland/ndr_support/workflows/Test/badge.svg)](https://github.com/publichealthengland/ndr_support/actions?query=workflow%3Atest) [![Gem Version](https://badge.fury.io/rb/ndr_support.svg)](https://rubygems.org/gems/ndr_support)
2
2
 
3
3
  This is the Public Health England (PHE) National Disease Registers (NDR) Support ruby gem, providing:
4
4
 
data/code_safety.yml CHANGED
@@ -1,5 +1,17 @@
1
1
  ---
2
2
  file safety:
3
+ ".github/CODEOWNERS":
4
+ comments:
5
+ reviewed_by: ollietulloch
6
+ safe_revision: 993f7719b57eb6ddd8dffa7230ed9ffc69f48749
7
+ ".github/workflows/lint.yml":
8
+ comments:
9
+ reviewed_by: ollietulloch
10
+ safe_revision: 993f7719b57eb6ddd8dffa7230ed9ffc69f48749
11
+ ".github/workflows/test.yml":
12
+ comments:
13
+ reviewed_by: ollietulloch
14
+ safe_revision: 993f7719b57eb6ddd8dffa7230ed9ffc69f48749
3
15
  ".gitignore":
4
16
  comments:
5
17
  reviewed_by: josh.pencheon
@@ -8,16 +20,10 @@ file safety:
8
20
  comments:
9
21
  reviewed_by: josh.pencheon
10
22
  safe_revision: a02f14cbf1fc36fbaf820365fdedfdc9af326db1
11
- ".travis.yml":
12
- comments: 'diff failed; reviewed whole file. Revision c88f5ac8c12fd657bb142144e0d3afaa7f9bc361:
13
- Slack notification secret is public/private key encrypted, so not a disclosure
14
- risk. As a secondary protection, it''s excluded from the .gem file by ndr_support.gemspec'
15
- reviewed_by: josh.pencheon
16
- safe_revision: 0cfcfac5ddcd721a67dcd23f04cbf6bc2278b922
17
23
  CHANGELOG.md:
18
24
  comments:
19
- reviewed_by: josh.pencheon
20
- safe_revision: 1a3b9f3403a81404b0165c0d4ff7dd9a728eb0b5
25
+ reviewed_by: ollietulloch
26
+ safe_revision: b0dc20ab24516ac5c3ecb6c7078b56a20dd2fbd5
21
27
  CODE_OF_CONDUCT.md:
22
28
  comments:
23
29
  reviewed_by: timgentry
@@ -36,8 +42,8 @@ file safety:
36
42
  safe_revision: 90328cca8494539257e192a63b240a91c89f0616
37
43
  README.md:
38
44
  comments:
39
- reviewed_by: josh.pencheon
40
- safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
45
+ reviewed_by: ollietulloch
46
+ safe_revision: 993f7719b57eb6ddd8dffa7230ed9ffc69f48749
41
47
  Rakefile:
42
48
  comments:
43
49
  reviewed_by: timgentry
@@ -60,16 +66,16 @@ file safety:
60
66
  safe_revision: 4a4ed24d2cfe7e1736baadf4cf6e0fece6823be1
61
67
  lib/ndr_support/concerns/working_days.rb:
62
68
  comments:
63
- reviewed_by: brian.shand
64
- safe_revision: a64c2c4616317240faec2db7a84761947ae844a3
69
+ reviewed_by: brianshand
70
+ safe_revision: b72baa322dc58820ccd615a178371c2e5dab113c
65
71
  lib/ndr_support/date_and_time_extensions.rb:
66
72
  comments:
67
73
  reviewed_by: josh.pencheon
68
- safe_revision: 3553509e52b3697d361cfa4c6f8a67754ed942d3
74
+ safe_revision: c1158eccb843b637b4dd82815dc7c081f12b93e4
69
75
  lib/ndr_support/daterange.rb:
70
76
  comments:
71
- reviewed_by: brian.shand
72
- safe_revision: c7ed8bc16be8ec803dfaeb3473fbc538b54587ec
77
+ reviewed_by: ollietulloch
78
+ safe_revision: 7b9c9525a5d9d319ddec75d5106c85a53f77e3f4
73
79
  lib/ndr_support/hash.rb:
74
80
  comments:
75
81
  reviewed_by: pauleves
@@ -80,12 +86,16 @@ file safety:
80
86
  safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
81
87
  lib/ndr_support/integer/julian_date_conversions.rb:
82
88
  comments:
83
- reviewed_by: josh.pencheon
84
- safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
89
+ reviewed_by: ollietulloch
90
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
85
91
  lib/ndr_support/integer/rounding.rb:
86
92
  comments:
87
93
  reviewed_by: josh.pencheon
88
94
  safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
95
+ lib/ndr_support/integer/working_days.rb:
96
+ comments:
97
+ reviewed_by: josh.pencheon
98
+ safe_revision: c1158eccb843b637b4dd82815dc7c081f12b93e4
89
99
  lib/ndr_support/nil.rb:
90
100
  comments:
91
101
  reviewed_by: pauleves
@@ -96,12 +106,12 @@ file safety:
96
106
  safe_revision: 50a4293953f9ae227823c390b069a3ed4825074c
97
107
  lib/ndr_support/ourdate.rb:
98
108
  comments:
99
- reviewed_by: pauleves
100
- safe_revision: a18fb5c6c360806a177a9c5584458586e90b3530
109
+ reviewed_by: ollietulloch
110
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
101
111
  lib/ndr_support/ourtime.rb:
102
112
  comments:
103
- reviewed_by: brian.shand
104
- safe_revision: 73527378c53b7ac00d1037155572fe3f01324992
113
+ reviewed_by: ollietulloch
114
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
105
115
  lib/ndr_support/password.rb:
106
116
  comments: with input from brian.shand
107
117
  reviewed_by: josh.pencheon
@@ -120,64 +130,68 @@ file safety:
120
130
  safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
121
131
  lib/ndr_support/safe_path.rb:
122
132
  comments:
123
- reviewed_by: timgentry
124
- safe_revision: 4492da77f6a8b678c40dd571fdd46fd936052cca
133
+ reviewed_by: ollietulloch
134
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
125
135
  lib/ndr_support/string/cleaning.rb:
126
136
  comments:
127
- reviewed_by: brian.shand
128
- safe_revision: 60cc69774d5aff92252558feae9604544def7583
137
+ reviewed_by: brianshand
138
+ safe_revision: b7b033ad9773cc9037c408a7763b113d31cbf191
129
139
  lib/ndr_support/string/conversions.rb:
130
140
  comments:
131
- reviewed_by: brian.shand
132
- safe_revision: 1692170e1d07e3b6ba95e88d6c15510a7b3c2fe6
141
+ reviewed_by: ollietulloch
142
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
133
143
  lib/ndr_support/tasks.rb:
134
144
  comments:
135
145
  reviewed_by: timgentry
136
146
  safe_revision: 62337584a32e5c30c2e9af7cd998a9df684885cc
137
147
  lib/ndr_support/threat_scanner.rb:
138
148
  comments:
139
- reviewed_by: josh.pencheon
140
- safe_revision: ed8ff421cc9353456af13a37049a07ce2545aac1
149
+ reviewed_by: brianshand
150
+ safe_revision: befa1d4f27448d5be4e133f442c2b5fbac7d8f25
141
151
  lib/ndr_support/utf8_encoding.rb:
142
152
  comments:
143
153
  reviewed_by: josh.pencheon
144
154
  safe_revision: 8e024a4bacfd36e172aad4b800f1960faae0b4b6
145
155
  lib/ndr_support/utf8_encoding/control_characters.rb:
146
156
  comments:
147
- reviewed_by: josh.pencheon
148
- safe_revision: 8e024a4bacfd36e172aad4b800f1960faae0b4b6
157
+ reviewed_by: ollietulloch
158
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
149
159
  lib/ndr_support/utf8_encoding/force_binary.rb:
150
160
  comments:
151
- reviewed_by: timgentry
152
- safe_revision: 29595e6431587ff9b7db6e3ad3abbb3577bff99c
161
+ reviewed_by: ollietulloch
162
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
153
163
  lib/ndr_support/utf8_encoding/object_support.rb:
154
164
  comments:
155
- reviewed_by: timgentry
156
- safe_revision: f7adf44fc2772e1926df37abfd9041d41c303328
165
+ reviewed_by: ollietulloch
166
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
157
167
  lib/ndr_support/version.rb:
168
+ comments:
169
+ reviewed_by: ollietulloch
170
+ safe_revision: b0dc20ab24516ac5c3ecb6c7078b56a20dd2fbd5
171
+ lib/ndr_support/working_days.rb:
158
172
  comments:
159
173
  reviewed_by: josh.pencheon
160
- safe_revision: 1a3b9f3403a81404b0165c0d4ff7dd9a728eb0b5
174
+ safe_revision: c1158eccb843b637b4dd82815dc7c081f12b93e4
161
175
  lib/ndr_support/yaml/serialization_migration.rb:
162
176
  comments:
163
177
  reviewed_by: timgentry
164
178
  safe_revision: 29595e6431587ff9b7db6e3ad3abbb3577bff99c
165
179
  ndr_support.gemspec:
166
180
  comments:
167
- reviewed_by: josh.pencheon
168
- safe_revision: 0cfcfac5ddcd721a67dcd23f04cbf6bc2278b922
181
+ reviewed_by: ollietulloch
182
+ safe_revision: 993f7719b57eb6ddd8dffa7230ed9ffc69f48749
169
183
  test/array_test.rb:
170
184
  comments:
171
185
  reviewed_by: timgentry
172
186
  safe_revision: 2a5d30674dc9dde336e1dbbbf3c8a98905647432
173
187
  test/concerns/working_days_test.rb:
174
188
  comments:
175
- reviewed_by: timgentry
176
- safe_revision: 506a317e59d0ccb5b32d813b74d822f35a55cea9
189
+ reviewed_by: josh.pencheon
190
+ safe_revision: fe6c3beff445e3c84ed0c89f08e1ab10bfb00587
177
191
  test/daterange_test.rb:
178
192
  comments:
179
- reviewed_by: brian.shand
180
- safe_revision: c7ed8bc16be8ec803dfaeb3473fbc538b54587ec
193
+ reviewed_by: ollietulloch
194
+ safe_revision: 7b9c9525a5d9d319ddec75d5106c85a53f77e3f4
181
195
  test/hash_test.rb:
182
196
  comments:
183
197
  reviewed_by: timgentry
@@ -190,6 +204,10 @@ file safety:
190
204
  comments:
191
205
  reviewed_by: josh.pencheon
192
206
  safe_revision: 2685f35c907af6968a69eabb5ab9424b490d0f40
207
+ test/integer/working_days_test.rb:
208
+ comments:
209
+ reviewed_by: josh.pencheon
210
+ safe_revision: c1158eccb843b637b4dd82815dc7c081f12b93e4
193
211
  test/nil_test.rb:
194
212
  comments:
195
213
  reviewed_by: timgentry
@@ -228,12 +246,12 @@ file safety:
228
246
  safe_revision: b562d0c15ff1b1d565522a47e6bae47ea09706f0
229
247
  test/string/cleaning_test.rb:
230
248
  comments:
231
- reviewed_by: brian.shand
232
- safe_revision: 60cc69774d5aff92252558feae9604544def7583
249
+ reviewed_by: brianshand
250
+ safe_revision: b7b033ad9773cc9037c408a7763b113d31cbf191
233
251
  test/string/conversions_test.rb:
234
252
  comments:
235
- reviewed_by: brian.shand
236
- safe_revision: 1692170e1d07e3b6ba95e88d6c15510a7b3c2fe6
253
+ reviewed_by: ollietulloch
254
+ safe_revision: '09b3f97fb4c1afc88d0d070c39796b1f60c39d25'
237
255
  test/test_helper.rb:
238
256
  comments:
239
257
  reviewed_by: josh.pencheon
@@ -3,6 +3,8 @@ require 'active_support/time'
3
3
  # This module contains logic for #working_days_until, #weekday?, and #public_holiday?.
4
4
  module WorkingDays
5
5
  WEEK_DAYS = 1..5
6
+
7
+ # TODO: could we use https://github.com/alphagov/gds-api-adapters ?
6
8
  HOLIDAYS = [ # Sourced from https://www.gov.uk/bank-holidays
7
9
  # 2012
8
10
  '2012-01-02', # Monday - New Year's Day (substitute day)
@@ -81,11 +83,30 @@ module WorkingDays
81
83
  '2020-01-01', # Wednesday - New Year's Day
82
84
  '2020-04-10', # Friday - Good Friday
83
85
  '2020-04-13', # Monday - Easter Monday
84
- '2020-05-04', # Monday - Early May bank holiday
86
+ '2020-05-08', # Friday - Early May bank holiday (moved from Monday)
85
87
  '2020-05-25', # Monday - Spring bank holiday
86
88
  '2020-08-31', # Monday - Summer bank holiday
87
89
  '2020-12-25', # Friday - Christmas Day
88
- '2020-12-26', # Monday - Boxing Day
90
+ '2020-12-28', # Monday - Boxing Day (substitute day)
91
+ # 2021
92
+ '2021-01-01', # Friday - New Year’s Day
93
+ '2021-04-02', # Friday - Good Friday
94
+ '2021-04-05', # Monday - Easter Monday
95
+ '2021-05-03', # Monday - Early May bank holiday
96
+ '2021-05-31', # Monday - Spring bank holiday
97
+ '2021-08-30', # Monday - Summer bank holiday
98
+ '2021-12-27', # Monday - Christmas Day
99
+ '2021-12-28', # Tuesday - Boxing Day
100
+ # 2022
101
+ '2022-01-03', # Monday - New Year’s Day (substitute day)
102
+ '2022-04-15', # Friday - Good Friday
103
+ '2022-04-18', # Monday - Easter Monday
104
+ '2022-05-02', # Monday - Early May bank holiday
105
+ '2022-06-02', # Thursday - Spring bank holiday
106
+ '2022-06-03', # Friday - Platinum Jubilee bank holiday
107
+ '2022-08-29', # Monday - Summer bank holiday
108
+ '2022-12-26', # Monday - Boxing Day
109
+ '2022-12-27', # Tuesday - Christmas Day (substitute day)
89
110
  ].map { |str| Date.parse(str) }
90
111
 
91
112
  def self.check_lookup
@@ -100,7 +121,7 @@ module WorkingDays
100
121
  def working_days_until(other)
101
122
  return -other.working_days_until(self) if other < self
102
123
 
103
- whole_days_to(other).count do |day|
124
+ count_whole_days_to(other) do |day|
104
125
  day.weekday? && !day.public_holiday?
105
126
  end
106
127
  end
@@ -109,7 +130,7 @@ module WorkingDays
109
130
  # `other`. Returns negative number if `other` is earlier.
110
131
  def weekdays_until(other)
111
132
  return -other.weekdays_until(self) if other < self
112
- whole_days_to(other).count(&:weekday?)
133
+ count_whole_days_to(other, &:weekday?)
113
134
  end
114
135
 
115
136
  # Is this a weekday?
@@ -124,14 +145,15 @@ module WorkingDays
124
145
 
125
146
  private
126
147
 
127
- def whole_days_to(other)
128
- [self].tap do |days|
129
- loop do
130
- next_day = days.last + 1.day
131
- next_day <= other ? days.push(next_day) : break
132
- end
148
+ def count_whole_days_to(other, &block)
149
+ day = self + 1.day
150
+ count = 0
133
151
 
134
- days.shift # Drop `self` off the front
152
+ while day <= other
153
+ count += 1 if block.call(day)
154
+ day += 1.day
135
155
  end
156
+
157
+ count
136
158
  end
137
159
  end
@@ -1,8 +1,6 @@
1
1
  require 'yaml'
2
2
  require 'date'
3
-
4
- require 'ndr_support/concerns/working_days'
5
- [Time, Date, DateTime].each { |klass| klass.send(:include, WorkingDays) }
3
+ require 'ndr_support/working_days'
6
4
 
7
5
  class Date
8
6
  # to_iso output must be SQL safe for security reasons
@@ -6,7 +6,10 @@ require 'ndr_support/integer/julian_date_conversions'
6
6
  class Daterange
7
7
  attr_reader :date1, :date2, :source
8
8
 
9
- OKYEARS = 1880..2020
9
+ OKYEARS = 1880..2030
10
+
11
+ # Raised when dates are not supplied in the correct order if do_not_flip_dates is passed as true:
12
+ class WrongDateOrderError < StandardError; end
10
13
 
11
14
  def self.extract(dates_string)
12
15
  dates_string.to_s.split(',').map { |str| new(str) }
@@ -17,15 +20,15 @@ class Daterange
17
20
  new(ranges.map(&:date1).compact.min, ranges.map(&:date2).compact.max)
18
21
  end
19
22
 
20
- def initialize(x1 = nil, x2 = nil)
23
+ def initialize(x1 = nil, x2 = nil, do_not_sort_dates: false)
21
24
  x1 = x1.to_datetime if x1.is_a?(Date) || x1.is_a?(Time)
22
25
  x2 = x2.to_datetime if x2.is_a?(Date) || x2.is_a?(Time)
23
26
 
24
27
  if x1.is_a?(DateTime) && x2.is_a?(DateTime)
25
- @date1 = [x1, x2].min
26
- @date2 = [x1, x2].max
28
+ @date1 = do_not_sort_dates ? x1 : [x1, x2].min
29
+ @date2 = do_not_sort_dates ? x2 : [x1, x2].max
27
30
  @source = nil
28
- elsif x1.is_a?(Daterange) && x2.nil? # Patient model line 645
31
+ elsif x1.is_a?(Daterange) && x2.nil? # Patient model line 645
29
32
  @date1 = x1.date1
30
33
  @date2 = x1.date2
31
34
  @source = x1.source
@@ -34,12 +37,14 @@ class Daterange
34
37
  @date2 = x1
35
38
  @source = nil
36
39
  elsif x1.is_a?(String) && x2.nil?
37
- self.source = (x1)
40
+ self.send(:source=, x1, do_not_sort_dates)
38
41
  else
39
42
  @date1 = nil
40
43
  @date2 = nil
41
44
  @source = nil
42
45
  end
46
+ raise WrongDateOrderError, 'Invalid date range order' if do_not_sort_dates && @date1 > @date2
47
+
43
48
  self.freeze
44
49
  end
45
50
 
@@ -135,24 +140,25 @@ class Daterange
135
140
 
136
141
  # Update our attribute values using a string representation of the date(s).
137
142
  # +s+ consists of one or more dates separated with spaces.
138
- # Each date can be in various formats, e.g. d/m/yyyy, ddmmyyyy, yyyy-mm-dd.
139
- # Each date can omit days or months, e.g. yyyy, dd/yyyy, yyyy-mm
140
- def source=(s)
143
+ # Each date can be in various formats, e.g. d/m/yyyy, ddmmyyyy, yyyy-mm-dd, dd-mon-yyyy
144
+ # Each date can omit days or months, e.g. yyyy, dd/yyyy, yyyy-mm, mon-yyyy
145
+ def source=(s, do_not_sort_dates)
141
146
  @source = s
142
147
  ss = s.upcase.sub(/TO/, ' ') # accept default _to_s format
143
- if ss =~ /[^0-9\-\/\. ]/ # only allow digits, hyphen, slash, dot, space
148
+ if ss =~ %r{[^A-Z0-9\-/\. ]}i # only allow letters, digits, hyphen, slash, dot, space
144
149
  @date1 = @date2 = nil
145
150
  else
146
151
  da = [] # temporary array of arrays of dates
147
- ss.split(' ').each do |vaguedate|
152
+ ss.split.each do |vaguedate|
148
153
  da << str_to_date_array(vaguedate)
149
154
  end
150
155
  da.flatten!
151
156
  if da.include?(nil)
152
- @date1 = @date2 = nil
157
+ @date1 = @date2 = nil
153
158
  else
154
- da.sort!
155
- @date1, @date2 = da.first, da.last
159
+ da.sort! unless do_not_sort_dates
160
+ @date1 = da.first
161
+ @date2 = da.last
156
162
  end
157
163
  end
158
164
  end
@@ -184,7 +190,9 @@ class Daterange
184
190
  # e.g year only or year/month only) and return an array of 1..3 integers
185
191
  # representing the year, month and day
186
192
  def date_string_parts(ds)
187
- if ds =~ /([\/\.\-])/ # find a slash or dot or hyphen
193
+ if ds =~ /\A(\d{1,2}[\/\.\-])?\w{3}[\/\.\-]\d{4}\z/i # dd[-/.]mon[-/.]yyyy or mon[-/.]yyyy
194
+ result = handle_three_char_months(ds)
195
+ elsif ds =~ /([\/\.\-])/ # find a slash or dot or hyphen
188
196
  delimiter = $1
189
197
  result = ds.split(delimiter)
190
198
  elsif ds.length == 8 # ddmmyyyy
@@ -200,4 +208,21 @@ class Daterange
200
208
  result.reverse! unless delimiter == '-' # change to YMD if not ISO format
201
209
  result.collect(&:to_i)
202
210
  end
211
+
212
+ def handle_three_char_months(datestring)
213
+ delimiter = datestring.match(%r{[\/\.\-]})[0]
214
+ components = datestring.split(delimiter)
215
+
216
+ if datestring =~ /\A\d{1,2}#{delimiter}\w{3}#{delimiter}\d{4}\z/i
217
+ month = abbreviated_month_index_for(components[1])
218
+ month.nil? ? [] : [components.first, month, components.last]
219
+ elsif datestring =~ /\A\w{3}#{delimiter}\d{4}\z/i
220
+ month = abbreviated_month_index_for(components.first)
221
+ month.nil? ? [] : [month, components.last]
222
+ end
223
+ end
224
+
225
+ def abbreviated_month_index_for(string)
226
+ Date::ABBR_MONTHNAMES.index(string.capitalize)
227
+ end
203
228
  end
@@ -1,5 +1,3 @@
1
- require 'ndr_support/ourdate'
2
-
3
1
  # Extend Integer for use in our Daterange class
4
2
  class Integer
5
3
  # Julian date number to Ruby Date
@@ -0,0 +1,11 @@
1
+ # Mixin for working_days
2
+ class Integer
3
+ # Returns a date of the number of working days since a given date
4
+ def working_days_since(date)
5
+ times do
6
+ date = date.next
7
+ date = date.next while date.public_holiday? || !date.weekday?
8
+ end
9
+ date
10
+ end
11
+ end
@@ -31,6 +31,8 @@ class Ourdate
31
31
  # with strings arguments only after the 1970 epoch; before, it returns nil.
32
32
  #++
33
33
  def self.build_datetime(year, month = 1, day = 1, hour = 0, min = 0, sec = 0, usec = 0)
34
+ return nil if year.nil?
35
+
34
36
  if ActiveRecord::Base.default_timezone == :local
35
37
  # Time.local_time(year, month, day, hour, min, sec, usec).to_datetime
36
38
  # Behave like oracle_adapter.rb
@@ -72,8 +74,8 @@ class Ourdate
72
74
  end
73
75
 
74
76
  def empty?
75
- # An unspecified date will be empty. A valid or invalid date will not.
76
- @thedate.nil? && @source.blank?
77
+ # An unspecified date will be empty. A valid date will not.
78
+ @thedate.nil?
77
79
  end
78
80
 
79
81
  def source=(s)
@@ -28,8 +28,8 @@ class Ourtime
28
28
  end
29
29
 
30
30
  def empty?
31
- # An unspecified time will be empty. A valid or invalid time will not.
32
- @thetime.nil? && @source.blank?
31
+ # An unspecified time will be empty. A valid time will not.
32
+ @thetime.nil?
33
33
  end
34
34
 
35
35
  private
@@ -118,6 +118,7 @@ class SafePath
118
118
 
119
119
  @path_space = path_space
120
120
  @maximum_prms = fs_paths[platform][path_space]['prms']
121
+ @prm = nil
121
122
 
122
123
  self.path = @root
123
124
  end
@@ -10,6 +10,7 @@ class String
10
10
  [A-Z][0-9][0-9] |
11
11
  [A-Z][0-9][A-Z] |
12
12
  [A-Z][A-Z][0-9] |
13
+ NPT |
13
14
  [A-Z][A-Z][0-9][0-9] |
14
15
  [A-Z][A-Z][0-9][A-Z]
15
16
  )
@@ -84,8 +84,7 @@ class String
84
84
 
85
85
  # Try to convert the string value into a date.
86
86
  # If given a pattern, use it to parse date, otherwise use default setting to parse it
87
- def to_date(pattern = nil)
88
- return '' if empty? # TODO: check if this is used... :/
87
+ redefine_method :to_date do |pattern = nil|
89
88
  return nil if blank?
90
89
 
91
90
  pattern = '%d%m%Y' if 'ddmmyyyy' == pattern
@@ -118,7 +117,7 @@ class String
118
117
  year += 100 if year <= Ourdate.today.year % 100
119
118
  year += 1900
120
119
  elsif 9 == length && self =~ /\A\d{2}[^A-Z0-9][A-Z]{3}[^A-Z0-9]\d{2}\z/i
121
- # dd/mmm/yy, rare case.
120
+ # dd/mon/yy, rare case.
122
121
  year += 100 if year <= Ourdate.today.year % 100
123
122
  year += 1900
124
123
  end
@@ -1,4 +1,5 @@
1
1
  require 'English'
2
+ require 'shellwords'
2
3
 
3
4
  # Runs a virus/malware check against the given path, using ClamAV.
4
5
  #
@@ -1,5 +1,3 @@
1
- require 'ndr_support/utf8_encoding'
2
-
3
1
  module UTF8Encoding
4
2
  # Allows any supported object to have control characters
5
3
  # escaped, using standard replacement scheme.
@@ -1,5 +1,3 @@
1
- require 'ndr_support/utf8_encoding'
2
-
3
1
  module UTF8Encoding
4
2
  # Allows any supported object to have any high-ascii string
5
3
  # content to be force-encoded from UTF-8 to BINARY (/ASCII-8BIT).
@@ -1,5 +1,3 @@
1
- require 'ndr_support/utf8_encoding'
2
-
3
1
  # Allows any object (if supported) to have all related
4
2
  # strings encoded in place to UTF-8.
5
3
  module UTF8Encoding
@@ -3,5 +3,5 @@
3
3
  # This defines the NdrSupport version. If you change it, rebuild and commit the gem.
4
4
  # Use "rake build" to build the gem, see rake -T for all bundler rake tasks.
5
5
  module NdrSupport
6
- VERSION = '5.5.1'
6
+ VERSION = '5.8.0'
7
7
  end
@@ -0,0 +1,5 @@
1
+ require 'date'
2
+ require 'ndr_support/concerns/working_days'
3
+ require 'ndr_support/integer/working_days'
4
+
5
+ [Time, Date, DateTime].each { |klass| klass.send(:include, WorkingDays) }
data/ndr_support.gemspec CHANGED
@@ -1,4 +1,4 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require 'ndr_support/version'
4
4
 
@@ -13,9 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.license = 'MIT'
14
14
 
15
15
  # Exclude older versions of this gem from the package.
16
- spec.files = `git ls-files -z`.split("\x0").reject { |s| s =~ /^pkg\// }
17
- # SECURE BNS 2018-08-06: Minimise sharing of (public-key encrypted) slack secrets in .travis.yml
18
- spec.files -= %w[.travis.yml] # Not needed in the gem
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |s| s =~ %r{^(\.github|pkg/)} }
19
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
19
  spec.require_paths = ['lib']
@@ -24,15 +22,15 @@ Gem::Specification.new do |spec|
24
22
  spec.add_dependency 'activesupport', '>= 5.2.0', '< 7'
25
23
 
26
24
  spec.add_development_dependency 'bundler'
27
- spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rake', '>= 12.3.3'
28
26
 
29
- spec.required_ruby_version = '>= 2.5.0'
27
+ spec.required_ruby_version = '>= 2.6.0'
30
28
 
31
29
  # Avoid std-lib minitest (which has different namespace)
32
30
  spec.add_development_dependency 'minitest', '>= 5.0.0'
33
31
  spec.add_development_dependency 'mocha', '~> 1.1'
34
32
 
35
- spec.add_development_dependency 'ndr_dev_support', '~> 3.1', '>= 3.1.3'
33
+ spec.add_development_dependency 'ndr_dev_support', '~> 5.4', '>= 5.4.5'
36
34
  spec.add_development_dependency 'guard'
37
35
  spec.add_development_dependency 'listen', '< 3.1' # Bundle 1.12 should be (but isn't) resolving Ruby 2.1 issue
38
36
  spec.add_development_dependency 'guard-rubocop'
@@ -119,4 +119,22 @@ class WorkingDaysTest < Minitest::Test
119
119
  assert_equal 253, @normal_time.working_days_until(@normal_time + 1.year)
120
120
  assert_equal 253, @normal_date_time.working_days_until(@normal_date_time + 1.year)
121
121
  end
122
+
123
+ test 'against GOV.UK holidays' do
124
+ require 'net/http'
125
+ require 'json'
126
+
127
+ url = 'https://www.gov.uk/bank-holidays/england-and-wales.json'
128
+ response = Net::HTTP.get(URI(url))
129
+
130
+ events = JSON.parse(response)['events']
131
+ events.each do |event|
132
+ event_date = event['date']
133
+ parsed_date = Date.parse(event_date)
134
+
135
+ assert parsed_date.public_holiday?, "#{event_date} should be a public holiday"
136
+ # next if parsed_date.public_holiday?
137
+ # puts "'#{event_date}', # #{parsed_date.strftime('%A')} - #{event['title']}"
138
+ end
139
+ end
122
140
  end
@@ -24,19 +24,47 @@ class DaterangeTest < Minitest::Test
24
24
  # assert_match(/\d\d\.\d\d\.\d\d\d\d to \d\d.\d\d.\d\d\d\d/, dr.to_s)
25
25
  end
26
26
 
27
- def test_dates_in_reverse_order
28
- d = Date.today
29
- dr = Daterange.new d, d + 1
30
- dr2 = Daterange.new d + 1, d
27
+ def test_date_time_dates_in_reverse_order_with_do_not_sort_dates_false
28
+ d = Time.zone.today
29
+ dr = Daterange.new(d, d + 1)
30
+ dr2 = Daterange.new(d + 1, d)
31
31
  assert_equal(dr.to_s, dr2.to_s)
32
32
  end
33
33
 
34
+ def test_date_time_dates_in_reverse_order_with_do_not_sort_dates_true
35
+ d = Time.zone.today
36
+ Daterange.new(d, d + 1, do_not_sort_dates: true)
37
+ assert_raises(Daterange::WrongDateOrderError) do
38
+ Daterange.new(d + 1, d, do_not_sort_dates: true)
39
+ end
40
+ end
41
+
42
+ def test_string_dates_in_reverse_order_with_do_not_sort_dates_false
43
+ dr = Daterange.new('01/01/2019 to 01/01/2020')
44
+ dr2 = Daterange.new('01/01/2020 to 01/01/2019')
45
+ assert_equal(dr.to_s, dr2.to_s)
46
+ end
47
+
48
+ def test_string_dates_in_reverse_order_with_do_not_sort_dates_true
49
+ Daterange.new('01/01/2019 to 01/01/2020', do_not_sort_dates: true)
50
+ assert_raises(Daterange::WrongDateOrderError) do
51
+ Daterange.new('01/01/2020 to 01/01/2019', do_not_sort_dates: true)
52
+ end
53
+ end
54
+
34
55
  def test_illegal_strings
35
56
  dr = Daterange.new('01/o1/2000')
36
57
  assert_equal '', dr.to_s
37
58
  assert_nil dr.date1
38
59
  assert_nil dr.date2
39
- refute dr.empty? # Illegal dates do not count as empty / blank,
60
+ refute dr.empty? # Illegal dates do not count as empty / blank,
61
+ refute_nil dr.source # but the illegal string is preserved
62
+
63
+ dr = Daterange.new('01_01_2000')
64
+ assert_equal '', dr.to_s
65
+ assert_nil dr.date1
66
+ assert_nil dr.date2
67
+ refute dr.empty? # Illegal dates do not count as empty / blank,
40
68
  refute_nil dr.source # but the illegal string is preserved
41
69
  end
42
70
 
@@ -63,6 +91,12 @@ class DaterangeTest < Minitest::Test
63
91
  assert_equal '1880 2020', dr.source
64
92
  end
65
93
 
94
+ def test_year_range_future
95
+ s = 2.years.from_now.strftime('%Y')
96
+ dr = Daterange.new(s)
97
+ assert_equal s, dr.to_s, "Daterange should support future years up to #{s}"
98
+ end
99
+
66
100
  def test_hyphen_month_input_style
67
101
  dr = Daterange.new('2000-05')
68
102
  assert_equal '05.2000', dr.to_s
@@ -220,4 +254,42 @@ class DaterangeTest < Minitest::Test
220
254
  refute_equal dr1, nil
221
255
  refute_equal dr1, 0
222
256
  end
257
+
258
+ def test_three_char_months
259
+ dr1 = Daterange.new('01-APR-2020')
260
+ assert_equal '01.04.2020', dr1.date1.to_s
261
+ assert_equal '01.04.2020', dr1.date2.to_s
262
+
263
+ dr2 = Daterange.new('APR-2020')
264
+ assert_equal '01.04.2020', dr2.date1.to_s
265
+ assert_equal '30.04.2020', dr2.date2.to_s
266
+
267
+ dr3 = Daterange.new('JAN-2020 TO apr-2020')
268
+ assert_equal '01.01.2020', dr3.date1.to_s
269
+ assert_equal '30.04.2020', dr3.date2.to_s
270
+
271
+ dr4 = Daterange.new('20-JAN-2020 TO 12-Apr-2020')
272
+ assert_equal '20.01.2020', dr4.date1.to_s
273
+ assert_equal '12.04.2020', dr4.date2.to_s
274
+
275
+ dr5 = Daterange.new('01-BOB-2020')
276
+ assert_nil dr5.date1
277
+ assert_nil dr5.date2
278
+
279
+ dr6 = Daterange.new('01/APR/2020')
280
+ assert_equal '01.04.2020', dr6.date1.to_s
281
+ assert_equal '01.04.2020', dr6.date2.to_s
282
+
283
+ dr7 = Daterange.new('APR/2020')
284
+ assert_equal '01.04.2020', dr7.date1.to_s
285
+ assert_equal '30.04.2020', dr7.date2.to_s
286
+
287
+ dr8 = Daterange.new('JAN/2020 TO apr.2020')
288
+ assert_equal '01.01.2020', dr8.date1.to_s
289
+ assert_equal '30.04.2020', dr8.date2.to_s
290
+
291
+ dr9 = Daterange.new('20.JAN.2020 TO 12/Apr/2020')
292
+ assert_equal '20.01.2020', dr9.date1.to_s
293
+ assert_equal '12.04.2020', dr9.date2.to_s
294
+ end
223
295
  end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ # This tests our Integer working days extension
4
+ class Integer::WorkingDaysTest < Minitest::Test
5
+ test 'Integer should be extended with #working_days_since' do
6
+ assert 1.respond_to?(:working_days_since)
7
+ end
8
+
9
+ test 'Integer#working_days_since should behave correctly' do
10
+ assert_equal Date.new(2019, 12, 23), 1.working_days_since(Date.new(2019, 12, 20))
11
+ assert_equal Date.new(2019, 12, 27), 3.working_days_since(Date.new(2019, 12, 20))
12
+ assert_equal Date.new(2019, 12, 30), 4.working_days_since(Date.new(2019, 12, 20))
13
+ end
14
+ end
@@ -23,6 +23,9 @@ class String
23
23
  assert_equal 'AA9 9AA', 'AA9 9AA'.postcodeize(:db)
24
24
  assert_equal 'AA999AA', 'AA99 9AA'.postcodeize(:db)
25
25
  assert_equal 'AA9A9AA', 'AA9A 9AA'.postcodeize(:db)
26
+ # Old Newport postcodes until end 1984
27
+ assert_equal 'NPT 1AA' , 'NPT 1AA'.postcodeize(:db)
28
+ assert_equal 'NPT 1AA' , 'NPT 1AA'.postcodeize(:db)
26
29
  # Examples of legacy postcodes, that should be unchanged
27
30
  assert_equal 'IP222', 'IP222'.postcodeize(:db)
28
31
  assert_equal 'IP222E', 'IP222E'.postcodeize(:db)
@@ -244,8 +244,8 @@ class String::ConversionsTest < Minitest::Test
244
244
  assert_equal Ourdate.build_datetime(1998, 02, 13), '1998-02-13'.to_date('%Y-%m-%d')
245
245
  assert_equal Ourdate.build_datetime(1998, 05, 03), '1998-05-03'.to_date('%Y-%m-%d')
246
246
 
247
- assert ''.to_date('%Y-%m-%d').blank?
248
- assert ' '.to_date('%Y-%m-%d').blank?
247
+ assert_nil ''.to_date('%Y-%m-%d') # Should behave like Rails-defined to_date
248
+ assert_nil ' '.to_date('%Y-%m-%d')
249
249
  end
250
250
 
251
251
  test '%d-%m-%Y string to_date' do
@@ -311,7 +311,7 @@ class String::ConversionsTest < Minitest::Test
311
311
  assert_equal Ourdate.build_datetime(2008, 05, 03), '03/05/08'.to_date
312
312
  end
313
313
 
314
- test 'inferred dd/mmm/yy string to_date' do
314
+ test 'inferred dd/mon/yy string to_date' do
315
315
  # pre_epoch
316
316
  assert_equal Ourdate.build_datetime(1945, 02, 13), '13/FEB/45'.to_date
317
317
  assert_equal Ourdate.build_datetime(1945, 06, 03), '03/JUN/45'.to_date
@@ -357,9 +357,9 @@ class String::ConversionsTest < Minitest::Test
357
357
  end
358
358
 
359
359
  def assert_ymd(ymd, date)
360
- assert_equal ymd.first, date.year, 'years were not equal'
361
- assert_equal ymd.second, date.month, 'months were not equal'
362
- assert_equal ymd.third, date.day, 'days were not equal'
360
+ assert_equal ymd[0], date.year, 'years were not equal'
361
+ assert_equal ymd[1], date.month, 'months were not equal'
362
+ assert_equal ymd[2], date.day, 'days were not equal'
363
363
  end
364
364
 
365
365
  def assert_ymd_parsed(ymd, parse_results)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ndr_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.1
4
+ version: 5.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - NCRS Development Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-15 00:00:00.000000000 Z
11
+ date: 2021-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -68,16 +68,16 @@ dependencies:
68
68
  name: rake
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
- - - "~>"
71
+ - - ">="
72
72
  - !ruby/object:Gem::Version
73
- version: '10.0'
73
+ version: 12.3.3
74
74
  type: :development
75
75
  prerelease: false
76
76
  version_requirements: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - "~>"
78
+ - - ">="
79
79
  - !ruby/object:Gem::Version
80
- version: '10.0'
80
+ version: 12.3.3
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: minitest
83
83
  requirement: !ruby/object:Gem::Requirement
@@ -112,20 +112,20 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '3.1'
115
+ version: '5.4'
116
116
  - - ">="
117
117
  - !ruby/object:Gem::Version
118
- version: 3.1.3
118
+ version: 5.4.5
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: '3.1'
125
+ version: '5.4'
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
- version: 3.1.3
128
+ version: 5.4.5
129
129
  - !ruby/object:Gem::Dependency
130
130
  name: guard
131
131
  requirement: !ruby/object:Gem::Requirement
@@ -251,6 +251,7 @@ files:
251
251
  - lib/ndr_support/integer/calculations.rb
252
252
  - lib/ndr_support/integer/julian_date_conversions.rb
253
253
  - lib/ndr_support/integer/rounding.rb
254
+ - lib/ndr_support/integer/working_days.rb
254
255
  - lib/ndr_support/nil.rb
255
256
  - lib/ndr_support/obfuscator.rb
256
257
  - lib/ndr_support/ourdate.rb
@@ -269,6 +270,7 @@ files:
269
270
  - lib/ndr_support/utf8_encoding/force_binary.rb
270
271
  - lib/ndr_support/utf8_encoding/object_support.rb
271
272
  - lib/ndr_support/version.rb
273
+ - lib/ndr_support/working_days.rb
272
274
  - lib/ndr_support/yaml/serialization_migration.rb
273
275
  - ndr_support.gemspec
274
276
  - test/array_test.rb
@@ -277,6 +279,7 @@ files:
277
279
  - test/hash_test.rb
278
280
  - test/integer/calculations_test.rb
279
281
  - test/integer/rounding_test.rb
282
+ - test/integer/working_days_test.rb
280
283
  - test/nil_test.rb
281
284
  - test/obfuscator_test.rb
282
285
  - test/ourdate_test.rb
@@ -306,7 +309,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
306
309
  requirements:
307
310
  - - ">="
308
311
  - !ruby/object:Gem::Version
309
- version: 2.5.0
312
+ version: 2.6.0
310
313
  required_rubygems_version: !ruby/object:Gem::Requirement
311
314
  requirements:
312
315
  - - ">="
@@ -324,6 +327,7 @@ test_files:
324
327
  - test/hash_test.rb
325
328
  - test/integer/calculations_test.rb
326
329
  - test/integer/rounding_test.rb
330
+ - test/integer/working_days_test.rb
327
331
  - test/nil_test.rb
328
332
  - test/obfuscator_test.rb
329
333
  - test/ourdate_test.rb