@yext/phonenumber-util 0.1.1 → 0.2.1
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.
- package/.github/workflows/pull-request.yml +17 -0
- package/.husky/pre-commit +3 -1
- package/README.md +94 -1
- package/package.json +11 -4
- package/src/__tests__/{index.test.js → base.test.js} +12 -3
- package/src/__tests__/geo.test.js +310 -0
- package/src/areaCodeList.js +12 -0
- package/src/{index.js → base.js} +12 -14
- package/src/compliance.js +31 -0
- package/src/daylightSavings.js +21 -0
- package/src/geo.d.ts +31 -0
- package/src/geo.js +258 -0
- package/src/phoneCodes.js +665 -0
- package/src/timezones.js +148 -0
- package/vitest.config.js +3 -0
- /package/src/{index.d.ts → base.d.ts} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Run pull request checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
name: Test
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
- run: npm ci
|
|
16
|
+
- run: npm run lint
|
|
17
|
+
- run: npm test
|
package/.husky/pre-commit
CHANGED
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
Utility for extracting and validating phone numbers. Extracts an array of phone numbers from an inputted string, validates that these numbers appear genuine and provide data about those phone numbers.
|
|
2
2
|
|
|
3
|
+
[](https://opensource.org/licenses/BSD-3-Clause)    
|
|
4
|
+
|
|
3
5
|
### Scripts
|
|
4
6
|
|
|
5
7
|
#### Install dependencies
|
|
@@ -70,7 +72,7 @@ import { getPhoneParts } from '@yext/phonenumber-util';
|
|
|
70
72
|
const validPhoneNumber = '3496333';
|
|
71
73
|
getPhoneParts(validPhoneNumber); // Returns an object, assumed to be US / Canada, region code "1" but no area code can be reliably determined.
|
|
72
74
|
|
|
73
|
-
const validPhoneNumber = '
|
|
75
|
+
const validPhoneNumber = '+923331234567';
|
|
74
76
|
getPhoneParts(validPhoneNumber); // Returns an object, assumed to be Pakistan, region code "92".
|
|
75
77
|
```
|
|
76
78
|
|
|
@@ -151,3 +153,94 @@ Example for US with no area code provided ("Hey there, my number is 349.9999. Pl
|
|
|
151
153
|
```javascript
|
|
152
154
|
[];
|
|
153
155
|
```
|
|
156
|
+
|
|
157
|
+
## Geography / Time Functionality
|
|
158
|
+
|
|
159
|
+
In addition to the above methods, the following methods are also available via a different export.
|
|
160
|
+
|
|
161
|
+
There is additional functionality exposed as `export`, but the primary expected use case is:
|
|
162
|
+
|
|
163
|
+
#### findTimeFromAreaCode
|
|
164
|
+
|
|
165
|
+
Returns an object with geographic and time related information for a given region.
|
|
166
|
+
|
|
167
|
+
_NOTE:_ This is only applicable for United States and Canada.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
import { findTimeFromAreaCode } from '@yext/phonenumber-util/geo';
|
|
171
|
+
findTimeFromAreaCode('928');
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Example output for Arizona:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
{
|
|
178
|
+
areaCodeHasMultipleTimezones: false,
|
|
179
|
+
daylightSavings: true,
|
|
180
|
+
estimatedTime: true,
|
|
181
|
+
isQuietHours: false,
|
|
182
|
+
isTCPAQuietHours: false,
|
|
183
|
+
localTime24Hour: "15:00:00",
|
|
184
|
+
localTimeReadable: "3:00:00 PM",
|
|
185
|
+
region: {
|
|
186
|
+
code: "US",
|
|
187
|
+
flag: "🇺🇸",
|
|
188
|
+
name: "United States"
|
|
189
|
+
},
|
|
190
|
+
state: {
|
|
191
|
+
code: "AZ",
|
|
192
|
+
name: "Arizona"
|
|
193
|
+
},
|
|
194
|
+
stateHasMultipleTimezones: false,
|
|
195
|
+
timezoneOffset: "-07:00"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
import { findTimeFromAreaCode } from '@yext/phonenumber-util/geo';
|
|
201
|
+
findTimeFromAreaCode('250', date); // A date object is optional, defaulting to the current time.
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Example output for British Columbia:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
{
|
|
208
|
+
areaCodeHasMultipleTimezones: true,
|
|
209
|
+
daylightSavings: true,
|
|
210
|
+
estimatedTime: true,
|
|
211
|
+
isCRTCQuietHours: false,
|
|
212
|
+
isQuietHours: false,
|
|
213
|
+
localTime24Hour: "17:00:00",
|
|
214
|
+
localTimeReadable: "5:00:00 PM",
|
|
215
|
+
region: {
|
|
216
|
+
code: "CA",
|
|
217
|
+
flag: "🇨🇦",
|
|
218
|
+
name: "Canada"
|
|
219
|
+
},
|
|
220
|
+
state: {
|
|
221
|
+
code: "BC",
|
|
222
|
+
name: "British Columbia"
|
|
223
|
+
},
|
|
224
|
+
stateHasMultipleTimezones: true,
|
|
225
|
+
timezoneOffset: "-07:00"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### findRegionFromRegionCode
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import { findRegionFromRegionCode } from '@yext/phonenumber-util/geo';
|
|
233
|
+
findRegionFromRegionCode('47');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Returns the string name of a given region name based on region code provided.
|
|
237
|
+
|
|
238
|
+
_NOTE:_ This string is provided in English only (example: "Norway" and not the region-specific name "Norge").
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
{
|
|
242
|
+
code: "NO",
|
|
243
|
+
flag: "🇳🇴",
|
|
244
|
+
name: "Norway"
|
|
245
|
+
}
|
|
246
|
+
```
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yext/phonenumber-util",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"author": "bajohnson@hearsaycorp.com",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"description": "Utility for extracting and validating phone numbers",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "src
|
|
8
|
+
"main": "src",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/base.js",
|
|
11
|
+
"./geo": "./src/geo.js"
|
|
12
|
+
},
|
|
9
13
|
"types": "src/index.d.ts",
|
|
10
14
|
"sideEffects": false,
|
|
11
15
|
"repository": {
|
|
@@ -15,16 +19,19 @@
|
|
|
15
19
|
"scripts": {
|
|
16
20
|
"lint": "eslint **/*.js **/__tests__/*.js",
|
|
17
21
|
"format": "prettier --write **/*.js **/__tests__/*.js",
|
|
18
|
-
"test": "vitest",
|
|
22
|
+
"test": "TZ=America/Los_Angeles vitest",
|
|
19
23
|
"build": "node -e \"console.log('No build script for vanilla JS')\"",
|
|
20
|
-
"prepare": "husky"
|
|
24
|
+
"prepare": "husky",
|
|
25
|
+
"make-badges": "istanbul-badges-readme"
|
|
21
26
|
},
|
|
22
27
|
"devDependencies": {
|
|
23
28
|
"@eslint/js": "^9.7.0",
|
|
24
29
|
"@vitest/coverage-v8": "^2.1.1",
|
|
25
30
|
"eslint": "^9.7.0",
|
|
31
|
+
"generate-license-file": "^3.5.1",
|
|
26
32
|
"globals": "^15.8.0",
|
|
27
33
|
"husky": "^9.1.5",
|
|
34
|
+
"istanbul-badges-readme": "^1.9.0",
|
|
28
35
|
"lint-staged": "^15.2.10",
|
|
29
36
|
"prettier": "^3.3.3",
|
|
30
37
|
"vitest": "^2.1.1"
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
findNumbersInString,
|
|
8
8
|
findPhoneFormat,
|
|
9
9
|
formatPhoneNumber,
|
|
10
|
-
} from '../
|
|
10
|
+
} from '../base.js';
|
|
11
11
|
import { describe, it, expect } from 'vitest';
|
|
12
12
|
|
|
13
13
|
const testNumbers = {
|
|
@@ -220,20 +220,29 @@ describe('Phone number pretty formatting', () => {
|
|
|
220
220
|
format: findPhoneFormat({ regionCode: '57', e164: '+573211234567' }),
|
|
221
221
|
},
|
|
222
222
|
germany: {
|
|
223
|
-
e164: '+
|
|
223
|
+
e164: '+4917087654321',
|
|
224
224
|
regionCode: '49',
|
|
225
225
|
format: findPhoneFormat({ regionCode: '49', e164: '+4917087654321' }),
|
|
226
226
|
},
|
|
227
227
|
germanyAlt: {
|
|
228
|
-
e164: '+
|
|
228
|
+
e164: '+491708765432',
|
|
229
229
|
regionCode: '49',
|
|
230
230
|
format: findPhoneFormat({ regionCode: '49', e164: '+491708765432' }),
|
|
231
231
|
},
|
|
232
|
+
norwayUnexpected: {
|
|
233
|
+
e164: '+47174087654',
|
|
234
|
+
regionCode: '47',
|
|
235
|
+
format: findPhoneFormat({ regionCode: '47', e164: '+471740876543' }),
|
|
236
|
+
},
|
|
232
237
|
};
|
|
233
238
|
|
|
234
239
|
expect(formatPhoneNumber(testNumbers.nullCase)).toBe(null);
|
|
235
240
|
expect(formatPhoneNumber(testNumbers.us)).toBe('(310) 349-6200');
|
|
236
241
|
expect(formatPhoneNumber(testNumbers.colombia)).toBe('+57 321 123 4567');
|
|
237
242
|
expect(formatPhoneNumber(testNumbers.germanyAlt)).toBe('+49 17 08765432');
|
|
243
|
+
// This looks like a Norwegian number, but doesn't match a known format. In this case, we'll return a sanitized generic format.
|
|
244
|
+
expect(formatPhoneNumber(testNumbers.norwayUnexpected)).toBe(
|
|
245
|
+
'+47174087654',
|
|
246
|
+
);
|
|
238
247
|
});
|
|
239
248
|
});
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isDaylightSavingTime,
|
|
3
|
+
formatTimeOffset,
|
|
4
|
+
offsetTieBreaker,
|
|
5
|
+
findTimeDetails,
|
|
6
|
+
findTimeFromAreaCode,
|
|
7
|
+
findRegionFromRegionCode,
|
|
8
|
+
} from '../geo.js';
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
|
|
11
|
+
const invalidPhone = {
|
|
12
|
+
timezoneOffset: null,
|
|
13
|
+
daylightSavings: null,
|
|
14
|
+
stateHasMultipleTimezones: null,
|
|
15
|
+
areaCodeHasMultipleTimezones: null,
|
|
16
|
+
estimatedTime: false,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const seattlePhone = {
|
|
20
|
+
areaCodeHasMultipleTimezones: false,
|
|
21
|
+
daylightSavings: true,
|
|
22
|
+
estimatedTime: false,
|
|
23
|
+
isQuietHours: false,
|
|
24
|
+
isTCPAQuietHours: false,
|
|
25
|
+
localTime24Hour: '08:00:00',
|
|
26
|
+
localTimeReadable: '8:00:00 AM',
|
|
27
|
+
stateHasMultipleTimezones: false,
|
|
28
|
+
timezoneOffset: '-07:00',
|
|
29
|
+
state: {
|
|
30
|
+
name: 'Washington',
|
|
31
|
+
code: 'WA',
|
|
32
|
+
},
|
|
33
|
+
region: {
|
|
34
|
+
name: 'United States',
|
|
35
|
+
code: 'US',
|
|
36
|
+
flag: '🇺🇸',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const portlandPhone = {
|
|
41
|
+
areaCodeHasMultipleTimezones: false,
|
|
42
|
+
daylightSavings: true,
|
|
43
|
+
estimatedTime: false,
|
|
44
|
+
isQuietHours: false,
|
|
45
|
+
isTCPAQuietHours: false,
|
|
46
|
+
localTime24Hour: '08:00:00',
|
|
47
|
+
localTimeReadable: '8:00:00 AM',
|
|
48
|
+
stateHasMultipleTimezones: true,
|
|
49
|
+
timezoneOffset: '-07:00',
|
|
50
|
+
state: {
|
|
51
|
+
name: 'Oregon',
|
|
52
|
+
code: 'OR',
|
|
53
|
+
},
|
|
54
|
+
region: {
|
|
55
|
+
name: 'United States',
|
|
56
|
+
code: 'US',
|
|
57
|
+
flag: '🇺🇸',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const arizonaPhoneJul = {
|
|
62
|
+
areaCodeHasMultipleTimezones: false,
|
|
63
|
+
daylightSavings: true,
|
|
64
|
+
estimatedTime: true,
|
|
65
|
+
isQuietHours: false,
|
|
66
|
+
isTCPAQuietHours: false,
|
|
67
|
+
localTime24Hour: '09:00:00',
|
|
68
|
+
localTimeReadable: '9:00:00 AM',
|
|
69
|
+
stateHasMultipleTimezones: false,
|
|
70
|
+
timezoneOffset: '-06:00',
|
|
71
|
+
state: {
|
|
72
|
+
name: 'Arizona',
|
|
73
|
+
code: 'AZ',
|
|
74
|
+
},
|
|
75
|
+
region: {
|
|
76
|
+
name: 'United States',
|
|
77
|
+
code: 'US',
|
|
78
|
+
flag: '🇺🇸',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const arizonaPhoneDec = {
|
|
83
|
+
areaCodeHasMultipleTimezones: false,
|
|
84
|
+
daylightSavings: false,
|
|
85
|
+
estimatedTime: false,
|
|
86
|
+
isQuietHours: false,
|
|
87
|
+
isTCPAQuietHours: false,
|
|
88
|
+
localTime24Hour: '09:00:00',
|
|
89
|
+
localTimeReadable: '9:00:00 AM',
|
|
90
|
+
stateHasMultipleTimezones: false,
|
|
91
|
+
timezoneOffset: '-07:00',
|
|
92
|
+
state: {
|
|
93
|
+
name: 'Arizona',
|
|
94
|
+
code: 'AZ',
|
|
95
|
+
},
|
|
96
|
+
region: {
|
|
97
|
+
name: 'United States',
|
|
98
|
+
code: 'US',
|
|
99
|
+
flag: '🇺🇸',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const texasPhone = {
|
|
104
|
+
areaCodeHasMultipleTimezones: true,
|
|
105
|
+
daylightSavings: false,
|
|
106
|
+
estimatedTime: true,
|
|
107
|
+
isQuietHours: false,
|
|
108
|
+
isTCPAQuietHours: false,
|
|
109
|
+
localTime24Hour: '10:00:00',
|
|
110
|
+
localTimeReadable: '10:00:00 AM',
|
|
111
|
+
stateHasMultipleTimezones: true,
|
|
112
|
+
timezoneOffset: '-06:00',
|
|
113
|
+
state: {
|
|
114
|
+
name: 'Texas',
|
|
115
|
+
code: 'TX',
|
|
116
|
+
},
|
|
117
|
+
region: {
|
|
118
|
+
name: 'United States',
|
|
119
|
+
code: 'US',
|
|
120
|
+
flag: '🇺🇸',
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const floridaPhone = {
|
|
125
|
+
areaCodeHasMultipleTimezones: false,
|
|
126
|
+
daylightSavings: false,
|
|
127
|
+
estimatedTime: false,
|
|
128
|
+
isQuietHours: false,
|
|
129
|
+
isTCPAQuietHours: false,
|
|
130
|
+
localTime24Hour: '10:00:00',
|
|
131
|
+
localTimeReadable: '10:00:00 AM',
|
|
132
|
+
stateHasMultipleTimezones: true,
|
|
133
|
+
timezoneOffset: '-06:00',
|
|
134
|
+
state: {
|
|
135
|
+
name: 'Florida',
|
|
136
|
+
code: 'FL',
|
|
137
|
+
},
|
|
138
|
+
region: {
|
|
139
|
+
name: 'United States',
|
|
140
|
+
code: 'US',
|
|
141
|
+
flag: '🇺🇸',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const hawaiiPhone = {
|
|
146
|
+
areaCodeHasMultipleTimezones: false,
|
|
147
|
+
daylightSavings: false,
|
|
148
|
+
estimatedTime: false,
|
|
149
|
+
isQuietHours: true,
|
|
150
|
+
isTCPAQuietHours: true,
|
|
151
|
+
localTime24Hour: '06:00:00',
|
|
152
|
+
localTimeReadable: '6:00:00 AM',
|
|
153
|
+
stateHasMultipleTimezones: false,
|
|
154
|
+
timezoneOffset: '-10:00',
|
|
155
|
+
state: {
|
|
156
|
+
name: 'Hawaii',
|
|
157
|
+
code: 'HI',
|
|
158
|
+
},
|
|
159
|
+
region: {
|
|
160
|
+
name: 'United States',
|
|
161
|
+
code: 'US',
|
|
162
|
+
flag: '🇺🇸',
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const canadianPhone = {
|
|
167
|
+
areaCodeHasMultipleTimezones: true,
|
|
168
|
+
daylightSavings: true,
|
|
169
|
+
estimatedTime: true,
|
|
170
|
+
isQuietHours: false,
|
|
171
|
+
isCRTCQuietHours: false,
|
|
172
|
+
localTime24Hour: '10:00:00',
|
|
173
|
+
localTimeReadable: '10:00:00 AM',
|
|
174
|
+
stateHasMultipleTimezones: true,
|
|
175
|
+
timezoneOffset: '-05:00',
|
|
176
|
+
state: {
|
|
177
|
+
name: 'British Columbia',
|
|
178
|
+
code: 'BC',
|
|
179
|
+
},
|
|
180
|
+
region: {
|
|
181
|
+
name: 'Canada',
|
|
182
|
+
code: 'CA',
|
|
183
|
+
flag: '🇨🇦',
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
describe('Daylight Savings', () => {
|
|
188
|
+
it('should correctly be determined if the time given is or is not within daylight savings time', () => {
|
|
189
|
+
const daylightSavings = new Date('2024-07-15T12:00:00');
|
|
190
|
+
const notDaylightSavings = new Date('2024-12-15T12:00:00');
|
|
191
|
+
|
|
192
|
+
expect(isDaylightSavingTime(daylightSavings)).toBe(true);
|
|
193
|
+
expect(isDaylightSavingTime(notDaylightSavings)).toBe(false);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('Formatting time offset', () => {
|
|
198
|
+
it('return a proper ISO 8601 formatted date string, regardless of hour length', () => {
|
|
199
|
+
expect(formatTimeOffset('-8:00')).toBe('-08:00');
|
|
200
|
+
expect(formatTimeOffset('-12:00')).toBe('-12:00');
|
|
201
|
+
expect(formatTimeOffset('+7:00')).toBe('+07:00');
|
|
202
|
+
expect(formatTimeOffset('-3:30')).toBe('-03:30');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('Errs on the side of caution, minimizing the given time options to the more narrow', () => {
|
|
207
|
+
it('Late at night, the available options for morning should be later', () => {
|
|
208
|
+
expect(
|
|
209
|
+
offsetTieBreaker(['-8:00', '-7:00'], new Date('2024-07-15T23:00:00')),
|
|
210
|
+
).toBe('-7:00');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('Early in day, the available options for morning should be earlier', () => {
|
|
214
|
+
expect(
|
|
215
|
+
offsetTieBreaker(['-8:00', '-7:00'], new Date('2024-07-15T08:00:00')),
|
|
216
|
+
).toBe('-8:00');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('Provides compliance quiet hours for any given region', () => {
|
|
221
|
+
it('Returns quiet hours booleans for standard US area', () => {
|
|
222
|
+
// US regions should have TCPA quiet hours but not CRTC quiet hours.
|
|
223
|
+
expect(
|
|
224
|
+
findTimeDetails('-08:00', new Date('2024-07-15T21:00:00'), 'California')
|
|
225
|
+
.isTCPAQuietHours,
|
|
226
|
+
).toEqual(false);
|
|
227
|
+
expect(
|
|
228
|
+
findTimeDetails('-08:00', new Date('2024-07-15T22:00:00'), 'California')
|
|
229
|
+
.isTCPAQuietHours,
|
|
230
|
+
).toEqual(true);
|
|
231
|
+
expect(
|
|
232
|
+
findTimeDetails('-08:00', new Date('2024-07-15T08:00:00'), 'California')
|
|
233
|
+
.isTCPAQuietHours,
|
|
234
|
+
).toEqual(true);
|
|
235
|
+
expect(
|
|
236
|
+
findTimeDetails('-08:00', new Date('2024-07-15T08:00:00'), 'California')
|
|
237
|
+
.isCRTCQuietHours,
|
|
238
|
+
).toEqual(undefined);
|
|
239
|
+
expect(
|
|
240
|
+
findTimeDetails('-08:00', new Date('2024-07-15T09:00:00'), 'California')
|
|
241
|
+
.isTCPAQuietHours,
|
|
242
|
+
).toEqual(false);
|
|
243
|
+
|
|
244
|
+
// Alberta should have CRTC quiet hours but not TCPA quiet hours.
|
|
245
|
+
expect(
|
|
246
|
+
findTimeDetails('-07:00', new Date('2024-07-15T09:00:00'), 'Alberta')
|
|
247
|
+
.isTCPAQuietHours,
|
|
248
|
+
).toEqual(undefined);
|
|
249
|
+
expect(
|
|
250
|
+
findTimeDetails('-07:00', new Date('2024-07-20T07:00:00'), 'Alberta')
|
|
251
|
+
.isCRTCQuietHours,
|
|
252
|
+
).toEqual(true);
|
|
253
|
+
|
|
254
|
+
// Both should have an abstracted general quiet hours value.
|
|
255
|
+
expect(
|
|
256
|
+
findTimeDetails('-08:00', new Date('2024-07-15T08:00:00'), 'California')
|
|
257
|
+
.isQuietHours,
|
|
258
|
+
).toEqual(true);
|
|
259
|
+
expect(
|
|
260
|
+
findTimeDetails('-07:00', new Date('2024-07-15T7:00:00'), 'Alberta')
|
|
261
|
+
.isQuietHours,
|
|
262
|
+
).toEqual(true);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('Provides general time information for the given phone number (US and Canada only)', () => {
|
|
267
|
+
it('Returns general time information for a phone number region', () => {
|
|
268
|
+
expect(findTimeFromAreaCode(null, new Date('2024-07-15T08:00:00'))).toEqual(
|
|
269
|
+
invalidPhone,
|
|
270
|
+
);
|
|
271
|
+
expect(
|
|
272
|
+
findTimeFromAreaCode('206', new Date('2024-07-15T08:00:00')),
|
|
273
|
+
).toEqual(seattlePhone);
|
|
274
|
+
expect(
|
|
275
|
+
findTimeFromAreaCode('503', new Date('2024-07-15T08:00:00')),
|
|
276
|
+
).toEqual(portlandPhone);
|
|
277
|
+
expect(
|
|
278
|
+
findTimeFromAreaCode('928', new Date('2024-07-15T08:00:00')),
|
|
279
|
+
).toEqual(arizonaPhoneJul);
|
|
280
|
+
expect(
|
|
281
|
+
findTimeFromAreaCode('928', new Date('2024-12-15T08:00:00')),
|
|
282
|
+
).toEqual(arizonaPhoneDec);
|
|
283
|
+
expect(
|
|
284
|
+
findTimeFromAreaCode('432', new Date('2024-12-15T08:00:00')),
|
|
285
|
+
).toEqual(texasPhone);
|
|
286
|
+
expect(
|
|
287
|
+
findTimeFromAreaCode('850', new Date('2024-12-15T08:00:00')),
|
|
288
|
+
).toEqual(floridaPhone);
|
|
289
|
+
expect(
|
|
290
|
+
findTimeFromAreaCode('808', new Date('2024-12-15T08:00:00')),
|
|
291
|
+
).toEqual(hawaiiPhone);
|
|
292
|
+
expect(
|
|
293
|
+
findTimeFromAreaCode('236', new Date('2024-07-20T08:00:00')),
|
|
294
|
+
).toEqual(canadianPhone);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('Provides region name for a given region code', () => {
|
|
299
|
+
it('Returns region name', () => {
|
|
300
|
+
expect(findRegionFromRegionCode(1).name).toEqual('United States, Canada');
|
|
301
|
+
expect(findRegionFromRegionCode(1, 206).name).toEqual('United States');
|
|
302
|
+
expect(findRegionFromRegionCode(7).name).toEqual('Russia, Kazakhstan');
|
|
303
|
+
expect(findRegionFromRegionCode(20).name).toEqual('Egypt');
|
|
304
|
+
expect(findRegionFromRegionCode(27).name).toEqual('South Africa');
|
|
305
|
+
expect(findRegionFromRegionCode(30).name).toEqual('Greece');
|
|
306
|
+
expect(findRegionFromRegionCode(31).name).toEqual('Netherlands');
|
|
307
|
+
expect(findRegionFromRegionCode(32).name).toEqual('Belgium');
|
|
308
|
+
expect(findRegionFromRegionCode(33).name).toEqual('France');
|
|
309
|
+
});
|
|
310
|
+
});
|
package/src/areaCodeList.js
CHANGED
|
@@ -21,6 +21,7 @@ export const AREA_CODE_LIST = [
|
|
|
21
21
|
'223',
|
|
22
22
|
'224',
|
|
23
23
|
'225',
|
|
24
|
+
'257',
|
|
24
25
|
'228',
|
|
25
26
|
'229',
|
|
26
27
|
'231',
|
|
@@ -89,6 +90,7 @@ export const AREA_CODE_LIST = [
|
|
|
89
90
|
'350',
|
|
90
91
|
'351',
|
|
91
92
|
'352',
|
|
93
|
+
'357',
|
|
92
94
|
'360',
|
|
93
95
|
'361',
|
|
94
96
|
'363',
|
|
@@ -134,17 +136,20 @@ export const AREA_CODE_LIST = [
|
|
|
134
136
|
'447',
|
|
135
137
|
'448',
|
|
136
138
|
'450',
|
|
139
|
+
'457',
|
|
137
140
|
'458',
|
|
138
141
|
'463',
|
|
139
142
|
'464',
|
|
140
143
|
'469',
|
|
141
144
|
'470',
|
|
145
|
+
'471',
|
|
142
146
|
'472',
|
|
143
147
|
'473',
|
|
144
148
|
'475',
|
|
145
149
|
'478',
|
|
146
150
|
'479',
|
|
147
151
|
'480',
|
|
152
|
+
'483',
|
|
148
153
|
'484',
|
|
149
154
|
'501',
|
|
150
155
|
'502',
|
|
@@ -212,6 +217,7 @@ export const AREA_CODE_LIST = [
|
|
|
212
217
|
'618',
|
|
213
218
|
'619',
|
|
214
219
|
'620',
|
|
220
|
+
'621',
|
|
215
221
|
'623',
|
|
216
222
|
'626',
|
|
217
223
|
'628',
|
|
@@ -241,6 +247,7 @@ export const AREA_CODE_LIST = [
|
|
|
241
247
|
'671',
|
|
242
248
|
'672',
|
|
243
249
|
'678',
|
|
250
|
+
'679',
|
|
244
251
|
'680',
|
|
245
252
|
'681',
|
|
246
253
|
'682',
|
|
@@ -270,14 +277,17 @@ export const AREA_CODE_LIST = [
|
|
|
270
277
|
'726',
|
|
271
278
|
'727',
|
|
272
279
|
'728',
|
|
280
|
+
'729',
|
|
273
281
|
'731',
|
|
274
282
|
'732',
|
|
275
283
|
'734',
|
|
276
284
|
'737',
|
|
285
|
+
'738',
|
|
277
286
|
'740',
|
|
278
287
|
'742',
|
|
279
288
|
'743',
|
|
280
289
|
'747',
|
|
290
|
+
'748',
|
|
281
291
|
'754',
|
|
282
292
|
'757',
|
|
283
293
|
'758',
|
|
@@ -331,6 +341,7 @@ export const AREA_CODE_LIST = [
|
|
|
331
341
|
'832',
|
|
332
342
|
'833',
|
|
333
343
|
'835',
|
|
344
|
+
'837',
|
|
334
345
|
'838',
|
|
335
346
|
'839',
|
|
336
347
|
'840',
|
|
@@ -393,6 +404,7 @@ export const AREA_CODE_LIST = [
|
|
|
393
404
|
'939',
|
|
394
405
|
'940',
|
|
395
406
|
'941',
|
|
407
|
+
'942',
|
|
396
408
|
'943',
|
|
397
409
|
'945',
|
|
398
410
|
'947',
|
package/src/{index.js → base.js}
RENAMED
|
@@ -234,18 +234,6 @@ export const getPhoneParts = (phoneNumber) => {
|
|
|
234
234
|
phoneParts.e164 = formatPhoneNumberForE164(phoneParts);
|
|
235
235
|
phoneParts.format = findPhoneFormat(phoneParts);
|
|
236
236
|
phoneParts.formattedNumber = formatPhoneNumber(phoneParts);
|
|
237
|
-
|
|
238
|
-
// If there are left over x's, the formatting ran into something unexpected.
|
|
239
|
-
// This may be ok depending on the region and their phone number formats.
|
|
240
|
-
// But it does mean we don't want to display this number.
|
|
241
|
-
if (
|
|
242
|
-
phoneParts.formattedNumber &&
|
|
243
|
-
phoneParts.formattedNumber.indexOf('x') !== -1
|
|
244
|
-
) {
|
|
245
|
-
// Since `rawNumber` isn't sanitized, we'll use a simple format that we
|
|
246
|
-
// are assured to be safe.
|
|
247
|
-
phoneParts.formattedNumber = strippedPhoneNumber;
|
|
248
|
-
}
|
|
249
237
|
}
|
|
250
238
|
|
|
251
239
|
return phoneParts;
|
|
@@ -353,16 +341,26 @@ export const findPhoneFormat = ({ regionCode, e164 }) => {
|
|
|
353
341
|
* @param {Object} params - The parameters for formatting the phone number.
|
|
354
342
|
* @param {string} params.format - The desired format for the phone number. Example: `(xxx) xxx-xxxx`.
|
|
355
343
|
* @param {string} params.e164 - The E.164 formatted phone number to format. Example: `+12065551234`.
|
|
344
|
+
* @param {string} params.regionCode - The region code of the phone number. Example, the US would be "1".
|
|
356
345
|
* @returns {string|null} The formatted phone number, or null if the E.164 number or format is not provided.
|
|
357
346
|
*/
|
|
358
|
-
export const formatPhoneNumber = ({ format, e164 }) => {
|
|
347
|
+
export const formatPhoneNumber = ({ format, e164, regionCode }) => {
|
|
359
348
|
let formattedNumber = '';
|
|
360
349
|
|
|
361
350
|
if (e164 && format) {
|
|
362
351
|
// Remove the leading '+' and let the format handle it.
|
|
363
|
-
const strippedPhone = e164.replace(
|
|
352
|
+
const strippedPhone = e164.replace(/\+/g, '');
|
|
364
353
|
let phoneIndex = strippedPhone.length - 1;
|
|
365
354
|
|
|
355
|
+
// The US / NANP rarely includes region code prefix when sharing numbers. Other regions often do, so we'll have a special condition to allow ignoring of the region code for region 1.
|
|
356
|
+
// For other regions, we'll return the stripped number to safely display the presumed good number that may be in a safe but not-ideal format.
|
|
357
|
+
if (
|
|
358
|
+
regionCode !== '1' &&
|
|
359
|
+
strippedPhone.length !== format.split('x').length - 1
|
|
360
|
+
) {
|
|
361
|
+
return e164;
|
|
362
|
+
}
|
|
363
|
+
|
|
366
364
|
// Traverse backward so we can omit country code in some formats (like US).
|
|
367
365
|
for (let i = format.length; i >= 0; i--) {
|
|
368
366
|
if (format[i]) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const TCPA_QUIET_HOURS = {
|
|
2
|
+
start: 8,
|
|
3
|
+
end: 21,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const CRTC_QUIET_HOURS = {
|
|
7
|
+
weekdays: {
|
|
8
|
+
start: 9,
|
|
9
|
+
end: 21.5,
|
|
10
|
+
},
|
|
11
|
+
weekends: {
|
|
12
|
+
start: 10,
|
|
13
|
+
end: 18,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const CRTC_STATES = [
|
|
18
|
+
'Alberta',
|
|
19
|
+
'British Columbia',
|
|
20
|
+
'Manitoba',
|
|
21
|
+
'New Brunswick',
|
|
22
|
+
'Newfoundland and Labrador',
|
|
23
|
+
'Northwest Territories',
|
|
24
|
+
'Nova Scotia',
|
|
25
|
+
'Nunavut',
|
|
26
|
+
'Ontario',
|
|
27
|
+
'Nova Scotia and Prince Edward Island',
|
|
28
|
+
'Quebec',
|
|
29
|
+
'Saskatchewan',
|
|
30
|
+
'Yukon',
|
|
31
|
+
];
|