Skip to content

Commit 9044327

Browse files
committed
formatPhoneNumber
1 parent cd1dbba commit 9044327

File tree

5 files changed

+111
-9
lines changed

5 files changed

+111
-9
lines changed

packages/format/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ formatOrganizationNumber('0000000000') // => '000000-0000'
3636

3737
## Methods
3838

39+
* formatPhoneNumber
3940
* formatOrganizationNumber
4041
* formatObosMembershipNumber

packages/format/src/format.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@ import { describe, expect, test } from 'vitest';
22
import {
33
formatObosMembershipNumber as formatObosMembershipNumberNo,
44
formatOrganizationNumber as formatOrganizationNumberNo,
5+
formatPhoneNumber as formatPhoneNumberNo,
56
} from './no';
67
import {
78
formatObosMembershipNumber as formatObosMembershipNumberSe,
89
formatOrganizationNumber as formatOrganizationNumberSe,
10+
formatPhoneNumber as formatPhoneNumberSe,
911
} from './se';
1012

1113
describe('no', () => {
14+
test.each([
15+
['22865500', '22 86 55 00'],
16+
['80000000', '800 00 000'],
17+
])('formatPhoneNumber(%s) -> %s', (input, expected) => {
18+
expect(formatPhoneNumberNo(input)).toBe(expected);
19+
});
20+
1221
test.each([
1322
['000000000', '000 000 000'],
1423
['000 000 000', '000 000 000'],
@@ -20,6 +29,31 @@ describe('no', () => {
2029
});
2130

2231
describe('se', () => {
32+
test.each([
33+
// mobile phone numbers
34+
['0701234567', '070-123 45 67'],
35+
['070 12 345 67', '070-123 45 67'],
36+
// 2 digit area code
37+
['0812345', '08-123 45'],
38+
['08123456', '08-12 34 56'],
39+
['081234567', '08-123 45 67'],
40+
['0812345678', '08-123 456 78'],
41+
// 3 digit area code
42+
['03112345', '031-123 45'],
43+
['031123456', '031-12 34 56'],
44+
['0311234567', '031-123 45 67'],
45+
['03112345678', '031-123 456 78'],
46+
// 4 digit area code
47+
['030312345', '0303-123 45'],
48+
['0303123456', '0303-12 34 56'],
49+
['03031234567', '0303-123 45 67'],
50+
['030312345678', '0303-123 456 78'],
51+
// invalid, too long a number
52+
['0303123456789', '0303123456789'],
53+
])('formatPhoneNumber(%s) -> %s', (input, expected) => {
54+
expect(formatPhoneNumberSe(input)).toBe(expected);
55+
});
56+
2357
test.each([
2458
['0000000000', '000000-0000'],
2559
['000000-0000', '000000-0000'],

packages/format/src/no.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { replaceIfMatch } from './utils';
22

3+
// Regular phone number format is: 00 00 00 00
4+
// if the number starts with 8, it's an 800-series number, with the format: 800 00 000
35
// See https://sprakradet.no/godt-og-korrekt-sprak/rettskriving-og-grammatikk/tall-tid-dato/
46
const REGULAR_PHONE_NUMBER_FORMAT = /^(\d{2})(\d{2})(\d{2})(\d{2})$/;
57
const EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT = /^(\d{3})(\d{2})(\d{3})$/;
@@ -12,12 +14,21 @@ const EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT = /^(\d{3})(\d{2})(\d{3})$/;
1214
* formatPhoneNumber('80000000') // => '800 00 000'
1315
* ```
1416
*/
15-
export function formatPhoneNumber(number: string): string {
16-
if (number.startsWith('8')) {
17-
return number.replace(EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT, '$1 $2 $3');
18-
}
17+
export function formatPhoneNumber(input: string): string {
18+
const number = replaceIfMatch(
19+
input,
20+
REGULAR_PHONE_NUMBER_FORMAT,
21+
'$1 $2 $3 $4',
22+
);
1923

20-
return number.replace(DEFAULT_PHONE_NUMBER_FORMAT, '$1 $2 $3 $4');
24+
// if the number starts with 8, it's an 800-series number, so we'll format it differently
25+
return number.startsWith('8')
26+
? replaceIfMatch(
27+
number,
28+
EIGHT_HUNDRED_SERIES_PHONE_NUMBER_FORMAT,
29+
'$1 $2 $3',
30+
)
31+
: number;
2132
}
2233

2334
const ORG_NUMBER_FORMAT = /^(\d{3})(\d{3})(\d{3})$/;

packages/format/src/se.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,56 @@
1-
import { replaceIfMatch } from './utils';
1+
import { cleanInput, replaceIfMatch } from './utils';
2+
3+
const MOBILE_PHONE_NUMBER_FORMAT = /^(07[02369]{1})(\d{3})(\d{2})(\d{2})$/;
4+
// subscriber numbers, without the area codes, can be 5, 6, 7 or 8 digits long
5+
const SUBSCRIBER_NUMBER_FORMATS = {
6+
5: /^(\d{3})(\d{2})$/,
7+
6: /^(\d{2})(\d{2})(\d{2})$/,
8+
7: /^(\d{3})(\d{2})(\d{2})$/,
9+
8: /^(\d{3})(\d{3})(\d{2})$/,
10+
};
11+
// SE numbers have area codes of 2, 3 or 4 digits
12+
const TWO_DIGIT_AREA_CODE = /^08/;
13+
const THREE_DIGIT_AREA_CODE =
14+
/^0(11|13|16|18|19|21|23|26|31|33|35|36|40|42|44|46|54|60|63|90)/;
15+
16+
/**
17+
* Format a phone number
18+
* @example
19+
* ```
20+
* formatPhoneNumber('07012345678') // => '070-123 45 678'
21+
* formatPhoneNumber('0812345') // => '08-123 45'
22+
* formatPhoneNumber('0311234567') // => '031-123 45 67'
23+
* formatPhoneNumber('0303123456') // => '0303-12 34 56'
24+
* ```
25+
*/
26+
export function formatPhoneNumber(input: string): string {
27+
const normalizedInput = cleanInput(input);
28+
29+
if (MOBILE_PHONE_NUMBER_FORMAT.test(normalizedInput)) {
30+
return normalizedInput.replace(MOBILE_PHONE_NUMBER_FORMAT, '$1-$2 $3 $4');
31+
}
32+
33+
const areaCodeLength = TWO_DIGIT_AREA_CODE.test(normalizedInput)
34+
? 2
35+
: THREE_DIGIT_AREA_CODE.test(normalizedInput)
36+
? 3
37+
: 4;
38+
39+
const areaCode = normalizedInput.substring(0, areaCodeLength);
40+
const subscriberNumber = normalizedInput.substring(areaCodeLength);
41+
42+
// if the subscriber number length is not in the formats, return the input as is
43+
if (!(subscriberNumber.length in SUBSCRIBER_NUMBER_FORMATS)) {
44+
return normalizedInput;
45+
}
46+
47+
const subscriberNumberFormat =
48+
SUBSCRIBER_NUMBER_FORMATS[subscriberNumber.length];
49+
50+
const replacePattern = subscriberNumber.length === 5 ? '$1 $2' : '$1 $2 $3';
51+
52+
return `${areaCode}-${subscriberNumber.replace(subscriberNumberFormat, replacePattern)}`;
53+
}
254

355
const ORG_NUMBER_FORMAT = /^(\d{6})(\d{4})$/;
456

packages/format/src/utils.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ export function replaceIfMatch(
33
regex: RegExp,
44
replacerPattern: string,
55
): string {
6+
const normalizedInput = cleanInput(input);
7+
8+
return normalizedInput.replace(regex, replacerPattern);
9+
}
10+
11+
export function cleanInput(input: string): string {
612
// We're extremely lenient when attemtping to format the input.
713
// We remove everything that isn't a letter or a number, that way we can get rid of any
814
// formatting that might already be present in the input, eg spaces, hyphens or dots
9-
const cleaned = input.replace(/[^a-zA-Z0-9]/g, '');
10-
11-
return cleaned.replace(regex, replacerPattern);
15+
return input.replace(/[^a-zA-Z0-9]/g, '');
1216
}

0 commit comments

Comments
 (0)