File Coverage

blib/lib/Number/Phone/NANP.pm
Criterion Covered Total %
statement 787 1131 69.5
branch 49 52 94.2
condition 9 9 100.0
subroutine 377 377 100.0
pod 15 15 100.0
total 1237 1584 78.0


line stmt bran cond sub pod time code
1             package Number::Phone::NANP;
2              
3 16     16   351934 use strict;
  14         68  
  14         364  
4              
5 15     15   103 use Scalar::Util 'blessed';
  15         49  
  15         597  
6              
7 15     15   106 use base 'Number::Phone';
  12         23  
  12         1051  
8 12     12   133 use Number::Phone::NANP::Data;
  11         454  
  11         10728  
9              
10 12     12   263 use Number::Phone::Country qw(noexport);
  11         623  
  11         1681  
11              
12             our $VERSION = '1.7001';
13              
14             my $cache = {};
15              
16             =head1 NAME
17              
18             Number::Phone::NANP - NANP-specific methods for Number::Phone
19              
20             =head1 DESCRIPTION
21              
22             This is a base class which encapsulates that information about phone
23             numbers in the North American Numbering Plan (NANP) which are
24             common to all NANP countries - that is, those whose international
25             dialling code is +1.
26              
27             Country-specific modules should inherit from this module and provide
28             their own versions of methods as necessary. However, they should not
29             provide an C method or a constructor.
30              
31             =head1 SYNOPSIS
32              
33             This module should not be used directly. It will be loaded as necessary
34             by Number::Phone:
35              
36             use Number::Phone;
37              
38             my $phone_number = Number::Phone->new('+1 202 418 1440');
39             # $phone_number is now a Number::Phone::NANP::US
40              
41             my $other_phone_number = Number::Phone->new('+1 866 623 2282');
42             # $phone_number is non-geographic so is a Number::Phone::NANP
43              
44             =cut
45              
46             sub new {
47 529     529 1 1141 my $class = shift;
48 528         999 my $number = shift;
49              
50 528 100       1364 return undef if(!is_valid($number));
51            
52             # cunningly, N::P::C::p2c supports local NANPish number formats
53             # as well as +1XXXXXXXXXX format. Yay!
54 498         1738 my $country = Number::Phone::Country::phone2country($number);
55            
56             # try to load country class
57 12     12   178 eval "use Number::Phone::NANP::$country;";
  11     2   49  
  11     2   115  
  497     2   41525  
  2     2   7  
  2     2   19  
  2     2   25  
  1     2   4  
  1     2   7  
  2     2   20  
  1     2   5  
  1     2   14  
  2     2   24  
  1     2   18  
  1     2   8  
  2     2   27  
  1     2   2  
  1     2   7  
  2     2   25  
  1     2   3  
  1     2   9  
  2     2   19  
  1     2   3  
  1     2   12  
  2     2   19  
  1     2   2  
  1     2   8  
  2     2   22  
  1     2   2  
  1     2   7  
  2     2   27  
  1     2   3  
  1     2   8  
  2     2   16  
  2     2   5  
  2     2   16  
  2     2   16  
  2     2   4  
  2     2   18  
  2     2   14  
  2     2   9  
  2     2   15  
  2     2   20  
  2     2   8  
  2     2   20  
  2     2   26  
  2     2   11  
  2     2   17  
  2     2   14  
  2     2   7  
  2     2   24  
  2     2   17  
  2     2   20  
  2     2   15  
  2     2   22  
  2     2   6  
  2     2   15  
  2     2   33  
  2     2   6  
  2     2   21  
  2     2   26  
  1     2   2  
  1     2   15  
  2     2   41  
  1     2   3  
  1     2   13  
  2     2   25  
  1     2   12  
  1     2   54  
  2     2   19  
  1     2   7  
  1     1   11  
  2     1   25  
  1     1   4  
  1     1   8  
  2     1   25  
  1     1   9  
  1     1   20  
  2     1   34  
  1     1   3  
  1     1   9  
  2     1   23  
  1     1   6  
  1     1   28  
  2     1   21  
  1     1   14  
  1     1   17  
  2     1   32  
  2     1   14  
  2     1   23  
  2     1   19  
  2     1   6  
  2     1   21  
  2     1   25  
  2     1   23  
  2     1   22  
  2     1   16  
  2     1   7  
  2     1   17  
  2     1   26  
  2     1   6  
  2     1   19  
  2     1   16  
  2     1   5  
  2     1   19  
  2     1   33  
  0     1   0  
  0     1   0  
  2     1   41  
  1     1   2  
  1     1   10  
  2     1   22  
  1     1   3  
  1     1   7  
  2     1   30  
  1     1   5  
  1     1   10  
  2     1   20  
  1     1   3  
  1     1   9  
  2     1   29  
  1     1   2  
  1     1   10  
  2     1   24  
  1     1   3  
  1     1   7  
  2     1   20  
  1     1   3  
  1     1   14  
  2     1   25  
  1     1   5  
  1     1   10  
  2     1   20  
  2     1   7  
  2     1   17  
  2     1   25  
  1     1   2  
  1     1   11  
  2     1   23  
  2     1   19  
  2     1   18  
  2     1   26  
  1     1   5  
  1     1   12  
  2     1   22  
  1     1   3  
  1     1   8  
  2     1   14  
  2     1   16  
  2     1   22  
  2     1   18  
  1     1   3  
  1     1   6  
  2     1   23  
  1     1   2  
  1     1   8  
  2     1   39  
  1     1   3  
  1     1   7  
  2     1   33  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   18  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   23  
  1     1   5  
  1     1   14  
  1     1   30  
  1     1   4  
  1     1   12  
  1     1   8  
  1     1   3  
  1     1   10  
  1     1   17  
  1     1   6  
  1     1   8  
  1     1   12  
  1     1   4  
  1     1   7  
  1     1   27  
  1     1   4  
  1     1   8  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   21  
  0     1   0  
  0     1   0  
  1     1   23  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   21  
  0     1   0  
  0     1   0  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   21  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   23  
  1     1   7  
  1     1   12  
  1     1   25  
  1     1   5  
  1     1   11  
  1     1   12  
  1     1   3  
  1     1   9  
  1     1   16  
  1     1   9  
  1     1   13  
  1     1   14  
  1     1   5  
  1     1   11  
  1     1   20  
  1     1   11  
  1     1   17  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   27  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   31  
  0     1   0  
  0     1   0  
  1     1   25  
  0     1   0  
  0     1   0  
  1     1   23  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   34  
  1     1   5  
  1     1   13  
  1     1   10  
  1     1   3  
  1     1   8  
  1     1   8  
  1     1   3  
  1     1   11  
  1     1   12  
  1     1   2  
  1     1   8  
  1     1   11  
  1     1   3  
  1     1   7  
  1     1   10  
  1     1   2  
  1     1   7  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   17  
  1     1   7  
  1     1   11  
  1     1   8  
  1     1   3  
  1     1   8  
  1     1   7  
  1     1   2  
  1     1   8  
  1     1   11  
  1     1   5  
  1     1   10  
  1     1   9  
  1     1   3  
  1     1   10  
  1     1   8  
  1     1   2  
  1     1   8  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         14  
  1         4  
  1         10  
  1         7  
  1         3  
  1         10  
  1         8  
  1         3  
  1         8  
  1         9  
  1         3  
  1         11  
  1         12  
  1         3  
  1         11  
  1         30  
  1         6  
  1         11  
  1         28  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         12  
  1         6  
  1         11  
  1         8  
  1         4  
  1         8  
  1         8  
  1         3  
  1         13  
  1         11  
  1         3  
  1         8  
  1         13  
  1         3  
  1         10  
  1         7  
  1         4  
  1         11  
  1         18  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         45  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         13  
  1         6  
  1         14  
  1         8  
  1         3  
  1         18  
  1         16  
  1         12  
  1         12  
  1         9  
  1         2  
  1         10  
  1         9  
  1         14  
  1         10  
  1         6  
  1         8  
  1         9  
  1         13  
  0         0  
  0         0  
  1         30  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         14  
  1         9  
  1         13  
  1         8  
  1         4  
  1         12  
  1         8  
  1         19  
  1         9  
  1         10  
  1         8  
  1         9  
  1         7  
  1         2  
  1         16  
  1         15  
  1         3  
  1         10  
  1         15  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         13  
  1         5  
  1         11  
  1         8  
  1         2  
  1         9  
  1         8  
  1         3  
  1         8  
  1         8  
  1         3  
  1         7  
  1         9  
  1         4  
  1         10  
  1         8  
  1         4  
  1         8  
  1         14  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         49  
  0         0  
  0         0  
  1         12  
  1         5  
  1         12  
  1         11  
  1         4  
  1         12  
  1         11  
  1         6  
  1         10  
  1         11  
  1         3  
  1         8  
  1         25  
  1         11  
  1         10  
  1         13  
  1         6  
  1         8  
  1         23  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         46  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         13  
  1         10  
  1         13  
  1         13  
  1         4  
  1         8  
  1         7  
  1         2  
  1         8  
  1         7  
  1         3  
  1         8  
  1         8  
  1         4  
  1         10  
  1         6  
  1         4  
  1         8  
  1         16  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         29  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         13  
  1         4  
  1         17  
  1         15  
  1         5  
  1         9  
  1         8  
  1         6  
  1         11  
  1         18  
  1         6  
  1         9  
  1         11  
  1         3  
  1         10  
  1         11  
  1         3  
  1         11  
  1         17  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         29  
  1         6  
  1         14  
  1         7  
  1         4  
  1         12  
  1         13  
  1         12  
  1         13  
  1         13  
  1         3  
  1         7  
  1         11  
  1         15  
  1         9  
  1         15  
  1         3  
  1         9  
  1         35  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         26  
  1         6  
  1         11  
  1         12  
  1         11  
  1         13  
  1         8  
  1         10  
  1         8  
  1         15  
  1         3  
  1         7  
  1         9  
  1         10  
  1         12  
  1         17  
  1         3  
  1         9  
  1         14  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         35  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         34  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         16  
  1         9  
  1         19  
  1         15  
  1         4  
  1         12  
  1         19  
  1         4  
  1         11  
  1         13  
  1         2  
  1         9  
  1         17  
  1         8  
  1         8  
  1         7  
  1         3  
  1         9  
  1         17  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         38  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         17  
  1         7  
  1         13  
  1         15  
  1         3  
  1         9  
  1         14  
  1         7  
  1         14  
  1         10  
  1         11  
  1         12  
  1         8  
  1         3  
  1         7  
  1         29  
  1         9  
  1         8  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         15  
  1         12  
  1         15  
  1         16  
  1         4  
  1         10  
  1         15  
  1         3  
  1         10  
  1         13  
  1         10  
  1         7  
  1         9  
  1         3  
  1         10  
  1         27  
  1         9  
  1         8  
  1         18  
  0         0  
  0         0  
  1         47  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         26  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         16  
  1         11  
  1         22  
  1         9  
  1         4  
  1         10  
  1         38  
  1         3  
  1         9  
  1         16  
  1         4  
  1         11  
  1         16  
  1         4  
  1         8  
  1         8  
  1         4  
  1         7  
  1         19  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         15  
  0            
  0            
58             # if we fail, return a generic NANP object, which just happens to
59             # also be the right thing to do for pan-NANP numbers like 800
60 497 100       45866 return bless(\$number, $class) if($@);
61 261         1760 return bless(\$number, $class."::$country");
62             }
63              
64             =head1 METHODS
65              
66             The following methods from Number::Phone are overridden:
67              
68             =over 4
69              
70             =item new
71              
72             The constructor, you should never have to call this yourself. To create an
73             object the canonical incantation is Cnew('+1 ...')>.
74              
75             =item operator
76              
77             For some countries operator data is available.
78              
79             =item data_source
80              
81             Returns a string telling where and when the data for operators was last updated, looking something like:
82              
83             "localcallingguide.com at Wed Sep 30 10:37:39 2020 UTC"
84              
85             The current value of this is also documented in L.
86              
87             =cut
88              
89             # some faffing about to re-open the database if we fork
90             my $WORDLENGTH;
91             my $datafh;
92             my $pid = -1;
93             sub _datafh {
94 471 100 100 472   8355 if(!$datafh || $pid != $$) {
95 7         94 my $file = Number::Phone::_find_data_file('Number-Phone-NANP-Data.db');
96 8 50       409 open($datafh, '<:bytes', $file) || die("Can't read $file: $!");
97 7         192 read($datafh, my $header, 8);
98 7 50       65 die("$file isn't the right format\n") unless($header eq 'NANPOP'.chr(0).chr(0));
99 8         71 read($datafh, $WORDLENGTH, 1);
100 8         35 $WORDLENGTH = ord($WORDLENGTH);
101 8         62 $pid = $$;
102             }
103 472         4677 return $datafh;
104             }
105              
106             sub operator {
107 56     56 1 142 my $self = shift;
108              
109             # file needs to be open so we have a $WORDLENGTH
110 56         267 $self->_datafh();
111              
112 56         110 (my $number = ${$self}) =~ s/\D//g;
  56         444  
113 56         183 my $ten_thousands = substr($number, 1, 6);
114              
115 56         164 $ten_thousands -= 200000; # area codes below 200 are invalid
116 56         269 return $self->_get_data_starting_from_pointer_at_offset($WORDLENGTH * $ten_thousands);
117             }
118              
119             sub _get_data_starting_from_pointer_at_offset {
120 78     78   186 my($self, $offset) = @_;
121              
122 78         267 my $pointer = $self->_get_pointer_at_offset($offset);
123 78 100       266 return undef unless($pointer);
124              
125 67         360 my $block_type = $self->_get_block_type_at_offset($pointer);
126 67         135 $pointer += 1;
127              
128 67 100       219 if($block_type == 0) {
    50          
129             # $pointer points at a string
130 45         281 return $self->_get_string_at_offset($pointer);
131             } elsif($block_type == 1) {
132             # $pointer points at a block of pointers
133 24         57 (my $number = ${$self}) =~ s/\D//g;
  23         137  
134 23         69 my $thousands = substr($number, 7, 1); # the seventh digit
135 24         109 return $self->_get_data_starting_from_pointer_at_offset($pointer + $WORDLENGTH * $thousands);
136             } else {
137 1         12 die("Don't know how to handle a block of type $block_type at ".($pointer - 1)."\n");
138             }
139             }
140              
141             sub _get_block_type_at_offset {
142 66     67   146 my($self, $offset) = @_;
143              
144 67         163 seek($self->_datafh(), $offset, 0);
145 66         251 read($self->_datafh(), my $block_type, 1);
146 66         246 return ord($block_type);
147             }
148              
149             sub _get_string_at_offset {
150 45     45   122 my($self, $offset) = @_;
151              
152 44         130 seek($self->_datafh(), $offset, 0);
153 44         262 read($self->_datafh(), my $chars, 1);
154 45         204 $chars = unpack('C', $chars);
155 44         117 read($self->_datafh(), my $string, $chars);
156 44         324 return $string;
157             }
158              
159             sub _get_pointer_at_offset {
160 78     78   172 my($self, $offset) = @_;
161              
162 77         163 seek($self->_datafh(), $offset, 0);
163 77         366 read($self->_datafh(), my $pointer, $WORDLENGTH);
164 78         440 return unpack('N', $pointer);
165             }
166              
167             =item is_valid
168              
169             The number is valid within the numbering scheme. It may or may
170             not yet be allocated, or it may be reserved.
171              
172             =item is_geographic
173              
174             NANP-globals like 1-800 aren't geographic, the rest are.
175              
176             As a special case, 1-600 is non-geographic. So too will be
177             1-622/633/644/655/677/688 when they come in to service.
178              
179             =item is_mobile
180              
181             NANP-globals like 1-800 aren't mobile. For most others we just don't know because
182             the data isn't published. libphonenumber has data for *some* countries, so we use
183             that if we can.
184              
185             =item is_fixed_line
186              
187             NANP-globals are fixed lines, for the rest we generally don't know with some
188             exceptions as per is_mobile above.
189              
190             =cut
191              
192             # NB the EF digits being 11 *is* legal in at least some area codes.
193             # Obviously you can't dial, eg, 911-1234
194             sub is_valid {
195 532     533 1 916 my $number = shift;
196              
197             # If called as an object method, it *must* be valid otherwise the
198             # object would never have been instantiated.
199             # If called as a sub, then it's the constructor that's calling.
200 532 100       1687 return 1 if(blessed($number));
201              
202             # otherwise we have to validate
203              
204             # if we've seen this number before, use cached result
205 529 100       3111 return 1 if($cache->{$number}->{is_valid});
206              
207 191         401 my $parsed_number = $number;
208 191         341 my %digits;
209 192         550 $parsed_number =~ s/[^\d+]//g; # strip non-digits/plusses
210 191         805 $parsed_number =~ s/^\+1//; # remove leading +1
211              
212 191         1545 @digits{qw(A B C D)} = split(//, $parsed_number, 5);
213              
214             # this is checked in N::P::C::phone2country_and_idd waaaay before we
215             # ever get here. NB leave this here in case a refactor makes that go
216             # away. There are tests for this!
217             #
218             # # and quickly check length
219             # if(length($parsed_number) != 10) {
220             # $cache->{$number}->{is_valid} = 0;
221             # return 0;
222             # }
223            
224             $cache->{$number}->{is_valid} = (
225             $digits{A} ne '1' &&
226             $digits{D} ne '1' &&
227             $digits{B}.$digits{C} ne '11' &&
228              
229             # checked on 2023-02-23
230             # next check due 2024-01-01 (annually)
231             # https://en.wikipedia.org/wiki/List_of_North_American_Numbering_Plan_area_codes#Summary_table
232             $digits{B} ne '9' &&
233             $digits{A}.$digits{B} ne '37' &&
234 192 100 100     2750 $digits{A}.$digits{B} ne '96'
235             ) ? 1 : 0;
236              
237 192         713 $cache->{$number}->{areacode} = substr($parsed_number, 0, 3);
238 192         537 $cache->{$number}->{subscriber} = substr($parsed_number, 3);
239 192         1008 return $cache->{$number}->{is_valid};
240             }
241              
242             # define the other methods
243              
244             foreach my $method (qw(areacode subscriber)) {
245 12     12   133 no strict 'refs';
  11         30  
  11         11469  
246             *{__PACKAGE__."::$method"} = sub {
247 116     116   201 my $self = shift;
248 116         179 return $cache->{${$self}}->{$method};
  116         727  
249             }
250             }
251              
252             sub _is_canadian_600 {
253 159     159   253 my $self = shift;
254 159         253 ${$self} =~ /^(\+1)?6([0234578])\2/;
  159         857  
255             }
256              
257             sub is_geographic {
258 159     159 1 991 my $self = shift;
259             # 600 is non-geographic. 6(22|33|44|55|77|88) are reserved
260             # for non-geographic use but not yet in use
261 159 100       559 return 0 if($self->_is_canadian_600());
262             # NANP-globals like 1-800 aren't geographic, the rest are
263 158 100       893 return ref($self) eq __PACKAGE__ ? 0 : 1;
264             }
265              
266             sub is_mobile {
267 83     83 1 26280 my $self = shift;
268             # NANP-globals like 1-800 aren't mobile
269 83 100       391 if(ref($self) eq __PACKAGE__) { return 0; }
  3         24  
270 82         604 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
271 82 100       416 return undef if(!exists($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code}));
272 55 100       137 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1711  
273             }
274              
275             sub is_fixed_line {
276 82     83 1 27192 my $self = shift;
277             # NANP-globals like 1-800 are fixed
278 83 100       370 if(ref($self) eq __PACKAGE__) { return 1; }
  2         7  
279 81         643 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
280 82 100       469 return undef if(!exists($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code}));
281 54 100       128 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1739  
282             }
283              
284             =item is_drama
285              
286             The number is a '555' number. Numbers with the D, E, and F digits set to 555
287             are not allocated to real customers, and are intended for use in fiction. eg
288             212 555 2368 for Ghostbusters.
289              
290             NB, despite Ghostbusters above, only 555-0100 to 555-0199 are actually reserved.
291              
292             =cut
293              
294             sub is_drama {
295 8     8 1 33 my $self = shift;
296 7 100       15 if(${$self} =~ /555(\d{4})$/) {
  7         48  
297 7 100 100     69 return ($1 gt '0099' && $1 lt '0200') ? 1 : 0;
298             }
299 2         10 return 0;
300             }
301              
302             =item is_government
303              
304             Area code 710 is reserved for the US Feds, but apparently only one number
305             in the whole area code works.
306              
307             =cut
308              
309             sub is_government {
310 5     6 1 25 my $self = shift;
311 6 100       26 if(${$self} =~ /^(\+1)?710/) { return 1; }
  5         19  
  2         15  
312 5         38 else { return 0; }
313             }
314              
315             =item is_tollfree
316              
317             The number is free to the caller. 800, 833, 844, 855, 866, 877 and 888 "area codes"
318              
319             =cut
320              
321             sub is_tollfree {
322 90     91 1 27832 my $self = shift;
323              
324             # FIXME this really should be data-driven, based on US data in libphonenumber
325             # https://en.wikipedia.org/wiki/Toll-free_telephone_numbers_in_the_North_American_Numbering_Plan
326             # see also tests in t/nanp.t if anything changes
327             # checked on 2022-12-08
328             # next check due 2023-12-01 (annually)
329 90 100       217 if(${$self} =~ /^(\+1)?8(00|33|44|55|66|77|88)/) { return 1; }
  91         894  
  84         440  
330 7         36 else { return 0; }
331             }
332              
333             =item is_specialrate
334              
335             The number is charged at a higher rate than normal. The 900 "area code"
336             and some parts of 242 and 246 (Bahamas and Barbados).
337              
338             =cut
339              
340             sub is_specialrate {
341 88     88 1 30603 my $self = shift;
342 87 100       199 if(${$self} =~ /
  87         737  
343             ^(\+1)?
344             (
345             900 | # NANP-global
346             242225[0-46-9] | # BS-specific
347             246 ( 292 | 367 | 41[7-9] | 43[01] | 444 | 467 | 736 ) # BB-specific, apparently
348             )
349 85         421 /x) { return 1; }
350 5         25 else { return 0; }
351             }
352              
353             =item is_personal
354              
355             The number is a "personal" number. The 500, 533, 544, 566 and 577 "area codes".
356              
357             =cut
358              
359             sub is_personal {
360 81     81 1 28052 my $self = shift;
361 81 100       186 if(${$self} =~ /^(\+1)?5[03467]{2}/) { return 1; }
  81         673  
  78         386  
362 5         33 else { return 0; }
363             }
364              
365             sub areaname {
366 8     8 1 22 my $self = shift;
367 8         43 return Number::Phone::NANP::Data::_areaname('1'.$self->areacode().$self->subscriber());
368             }
369              
370             =item country_code
371              
372             Returns 1.
373              
374             =cut
375              
376 72     72 1 2410 sub country_code { 1; }
377              
378             =item regulator
379              
380             Returns informational text relevant to the whole NANP. Note that when
381             this method is inherited by a subclass it returns undef meaning "not
382             known", but returns information about the NANPA when called on an object
383             of class Number::Phone::NANP.
384              
385             =cut
386              
387             sub regulator {
388 10     10 1 29 my $class = shift;
389 10 100       53 if(blessed($class) eq __PACKAGE__) {
390 3         39 return 'NANPA, http://www.nanpa.com/';
391             } else {
392 9         45 return undef;
393             }
394             }
395              
396             =item areacode
397              
398             Return the area code for the number.
399              
400             =item areaname
401              
402             Return the name for the area code, if applicable, otherwise returns undef.
403             For instance, for a number beginning with +1 201 200 it would return "Jersey City, NJ".
404              
405             =item subscriber
406              
407             Return the subscriber part of the number.
408              
409             =item format
410              
411             Return a sanely formatted version of the number, complete with IDD code.
412              
413             =cut
414              
415             sub format {
416 35     35 1 99 my $self = shift;
417 35         101 return '+'.country_code().' '.
418             $self->areacode().' '.
419             substr($self->subscriber(), 0, 3).' '.
420             substr($self->subscriber(), 3);
421             }
422              
423             =back
424              
425             =head1 BUGS/FEEDBACK
426              
427             Please report bugs at L, including, if possible, a test case.
428              
429             I welcome feedback from users.
430              
431             =head1 LICENCE
432              
433             You may use, modify and distribute this software under the same terms as
434             perl itself.
435              
436             =head1 AUTHOR
437              
438             David Cantrell Edavid@cantrell.org.ukE
439              
440             Copyright 2023
441              
442             =cut
443              
444             1;