ndr_support 5.5.1 → 5.8.0

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