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   341088 use strict;
  14         74  
  14         396  
4              
5 15     15   122 use Scalar::Util 'blessed';
  15         63  
  15         541  
6              
7 15     15   112 use base 'Number::Phone';
  12         39  
  12         1110  
8 12     12   161 use Number::Phone::NANP::Data;
  11         2059  
  11         9116  
9              
10 12     12   177 use Number::Phone::Country qw(noexport);
  11         571  
  11         586  
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 1031 my $class = shift;
48 528         883 my $number = shift;
49              
50 528 100       1383 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         1813 my $country = Number::Phone::Country::phone2country($number);
55            
56             # try to load country class
57 12     12   201 eval "use Number::Phone::NANP::$country;";
  11     2   100  
  11     2   158  
  497     2   39418  
  2     2   5  
  2     2   26  
  2     2   23  
  1     2   3  
  1     2   12  
  2     2   24  
  1     2   2  
  1     2   19  
  2     2   21  
  1     2   3  
  1     2   8  
  2     2   19  
  1     2   9  
  1     2   10  
  2     2   20  
  1     2   3  
  1     2   8  
  2     2   21  
  1     2   2  
  1     2   7  
  2     2   19  
  1     2   3  
  1     2   11  
  2     2   19  
  1     2   3  
  1     2   7  
  2     2   21  
  1     2   8  
  1     2   12  
  2     2   18  
  2     2   10  
  2     2   30  
  2     2   15  
  2     2   6  
  2     2   15  
  2     2   15  
  2     2   6  
  2     2   16  
  2     2   18  
  2     2   8  
  2     2   19  
  2     2   15  
  2     2   7  
  2     2   20  
  2     2   15  
  2     2   7  
  2     2   16  
  2     2   15  
  2     2   7  
  2     2   17  
  2     2   21  
  2     2   5  
  2     2   19  
  2     2   17  
  2     2   5  
  2     2   16  
  2     2   19  
  1     2   2  
  1     2   12  
  2     2   20  
  1     2   3  
  1     2   7  
  2     2   19  
  1     2   3  
  1     2   50  
  2     2   20  
  1     2   8  
  1     1   9  
  2     1   25  
  1     1   2  
  1     1   11  
  2     1   28  
  1     1   3  
  1     1   12  
  2     1   24  
  1     1   3  
  1     1   8  
  2     1   20  
  1     1   2  
  1     1   8  
  2     1   30  
  1     1   3  
  1     1   14  
  2     1   21  
  2     1   7  
  2     1   21  
  2     1   16  
  2     1   7  
  2     1   16  
  2     1   15  
  2     1   11  
  2     1   28  
  2     1   17  
  2     1   7  
  2     1   21  
  2     1   18  
  2     1   7  
  2     1   18  
  2     1   31  
  2     1   9  
  2     1   29  
  2     1   26  
  0     1   0  
  0     1   0  
  2     1   19  
  1     1   12  
  1     1   12  
  2     1   28  
  1     1   3  
  1     1   8  
  2     1   32  
  1     1   7  
  1     1   18  
  2     1   19  
  1     1   2  
  1     1   7  
  2     1   29  
  1     1   5  
  1     1   9  
  2     1   21  
  1     1   3  
  1     1   13  
  2     1   24  
  1     1   3  
  1     1   9  
  2     1   23  
  1     1   3  
  1     1   9  
  2     1   20  
  2     1   13  
  2     1   19  
  2     1   20  
  1     1   5  
  1     1   7  
  2     1   18  
  2     1   9  
  2     1   19  
  2     1   24  
  1     1   4  
  1     1   10  
  2     1   28  
  1     1   4  
  1     1   10  
  2     1   17  
  2     1   4  
  2     1   14  
  2     1   21  
  1     1   2  
  1     1   6  
  2     1   29  
  1     1   3  
  1     1   10  
  2     1   26  
  1     1   4  
  1     1   9  
  2     1   25  
  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   28  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   14  
  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   14  
  1     1   3  
  1     1   10  
  1     1   28  
  1     1   3  
  1     1   9  
  1     1   9  
  1     1   5  
  1     1   11  
  1     1   19  
  1     1   2  
  1     1   9  
  1     1   11  
  1     1   3  
  1     1   8  
  1     1   9  
  1     1   4  
  1     1   9  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   14  
  1     1   4  
  1     1   11  
  1     1   8  
  1     1   2  
  1     1   9  
  1     1   8  
  1     1   3  
  1     1   6  
  1     1   10  
  1     1   3  
  1     1   9  
  1     1   8  
  1     1   6  
  1     1   8  
  1     1   9  
  1     1   3  
  1     1   7  
  1     1   12  
  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   18  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   19  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   17  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   15  
  1     1   3  
  1     1   11  
  1     1   13  
  1     1   6  
  1     1   8  
  1     1   7  
  1     1   3  
  1     1   7  
  1     1   14  
  1     1   3  
  1     1   12  
  1     1   8  
  1     1   5  
  1     1   9  
  1     1   15  
  1     1   5  
  1     1   9  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   14  
  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   13  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   23  
  1     1   6  
  1     1   11  
  1     1   7  
  1     1   3  
  1     1   10  
  1     1   18  
  1     1   6  
  1     1   10  
  1     1   9  
  1     1   8  
  1     1   8  
  1     1   8  
  1     1   4  
  1     1   10  
  1     1   8  
  1     1   11  
  1     1   7  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   14  
  0     1   0  
  0     1   0  
  1     1   12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         19  
  1         5  
  1         11  
  1         13  
  1         6  
  1         10  
  1         9  
  1         8  
  1         9  
  1         8  
  1         5  
  1         13  
  1         13  
  1         5  
  1         13  
  1         7  
  1         5  
  1         12  
  1         19  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         18  
  1         5  
  1         13  
  1         8  
  1         4  
  1         9  
  1         10  
  1         6  
  1         8  
  1         27  
  1         6  
  1         9  
  1         15  
  1         3  
  1         8  
  1         9  
  1         5  
  1         15  
  1         14  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  1         3  
  1         12  
  1         12  
  1         4  
  1         10  
  1         7  
  1         8  
  1         9  
  1         8  
  1         3  
  1         9  
  1         9  
  1         2  
  1         9  
  1         10  
  1         3  
  1         9  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         17  
  1         4  
  1         11  
  1         11  
  1         3  
  1         13  
  1         8  
  1         5  
  1         11  
  1         11  
  1         3  
  1         12  
  1         7  
  1         10  
  1         10  
  1         17  
  1         9  
  1         8  
  1         18  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         20  
  1         13  
  1         62  
  1         8  
  1         4  
  1         9  
  1         20  
  1         4  
  1         16  
  1         12  
  1         3  
  1         8  
  1         7  
  1         4  
  1         8  
  1         13  
  1         5  
  1         11  
  1         19  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         21  
  1         11  
  1         11  
  1         15  
  1         8  
  1         11  
  1         9  
  1         3  
  1         8  
  1         10  
  1         4  
  1         13  
  1         10  
  1         4  
  1         11  
  1         29  
  1         3  
  1         10  
  1         18  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         30  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         32  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         15  
  1         3  
  1         13  
  1         8  
  1         4  
  1         7  
  1         10  
  1         10  
  1         10  
  1         8  
  1         3  
  1         12  
  1         12  
  1         11  
  1         10  
  1         11  
  1         5  
  1         10  
  1         15  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         16  
  1         3  
  1         11  
  1         12  
  1         5  
  1         10  
  1         8  
  1         4  
  1         9  
  1         18  
  1         6  
  1         9  
  1         10  
  1         9  
  1         10  
  1         11  
  1         4  
  1         11  
  1         15  
  0         0  
  0         0  
  1         24  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         46  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         21  
  1         10  
  1         11  
  1         8  
  1         4  
  1         16  
  1         15  
  1         4  
  1         14  
  1         17  
  1         3  
  1         12  
  1         9  
  1         3  
  1         9  
  1         8  
  1         7  
  1         3665  
  1         17  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         17  
  1         5  
  1         11  
  1         13  
  1         3  
  1         9  
  1         11  
  1         6  
  1         10  
  1         8  
  1         3  
  1         8  
  1         7  
  1         11  
  1         9  
  1         12  
  1         6  
  1         12  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         12  
  1         6  
  1         16  
  1         10  
  1         3  
  1         8  
  1         8  
  1         3  
  1         9  
  1         11  
  1         4  
  1         9  
  1         9  
  1         3  
  1         10  
  1         11  
  1         4  
  1         10  
  1         16  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         54  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         27  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         14  
  1         6  
  1         11  
  1         11  
  1         2  
  1         12  
  1         26  
  1         3  
  1         14  
  1         8  
  1         3  
  1         18  
  1         9  
  1         3  
  1         8  
  1         8  
  1         3  
  1         7  
  1         19  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         12  
  1         6  
  1         14  
  1         11  
  1         5  
  1         10  
  1         7  
  1         4  
  1         9  
  1         10  
  1         4  
  1         9  
  1         10  
  1         3  
  1         9  
  1         7  
  1         3  
  1         8  
  1         16  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         18  
  1         4  
  1         10  
  1         10  
  1         4  
  1         9  
  1         12  
  1         3  
  1         7  
  1         9  
  1         8  
  1         10  
  1         8  
  1         3  
  1         9  
  1         8  
  1         3  
  1         8  
  1         12  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         14  
  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       45768 return bless(\$number, $class) if($@);
61 261         1785 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   10077 if(!$datafh || $pid != $$) {
95 7         92 my $file = Number::Phone::_find_data_file('Number-Phone-NANP-Data.db');
96 8 50       419 open($datafh, '<:bytes', $file) || die("Can't read $file: $!");
97 7         153 read($datafh, my $header, 8);
98 7 50       46 die("$file isn't the right format\n") unless($header eq 'NANPOP'.chr(0).chr(0));
99 8         57 read($datafh, $WORDLENGTH, 1);
100 8         34 $WORDLENGTH = ord($WORDLENGTH);
101 8         54 $pid = $$;
102             }
103 472         4690 return $datafh;
104             }
105              
106             sub operator {
107 56     56 1 125 my $self = shift;
108              
109             # file needs to be open so we have a $WORDLENGTH
110 56         257 $self->_datafh();
111              
112 56         105 (my $number = ${$self}) =~ s/\D//g;
  56         428  
113 56         179 my $ten_thousands = substr($number, 1, 6);
114              
115 56         154 $ten_thousands -= 200000; # area codes below 200 are invalid
116 56         302 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   194 my($self, $offset) = @_;
121              
122 78         248 my $pointer = $self->_get_pointer_at_offset($offset);
123 78 100       251 return undef unless($pointer);
124              
125 67         329 my $block_type = $self->_get_block_type_at_offset($pointer);
126 67         128 $pointer += 1;
127              
128 67 100       188 if($block_type == 0) {
    50          
129             # $pointer points at a string
130 45         287 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         135  
134 23         67 my $thousands = substr($number, 7, 1); # the seventh digit
135 24         107 return $self->_get_data_starting_from_pointer_at_offset($pointer + $WORDLENGTH * $thousands);
136             } else {
137 1         3 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   262 my($self, $offset) = @_;
143              
144 67         159 seek($self->_datafh(), $offset, 0);
145 66         306 read($self->_datafh(), my $block_type, 1);
146 66         265 return ord($block_type);
147             }
148              
149             sub _get_string_at_offset {
150 45     45   128 my($self, $offset) = @_;
151              
152 44         89 seek($self->_datafh(), $offset, 0);
153 44         183 read($self->_datafh(), my $chars, 1);
154 45         213 $chars = unpack('C', $chars);
155 44         131 read($self->_datafh(), my $string, $chars);
156 44         279 return $string;
157             }
158              
159             sub _get_pointer_at_offset {
160 78     78   160 my($self, $offset) = @_;
161              
162 77         139 seek($self->_datafh(), $offset, 0);
163 77         340 read($self->_datafh(), my $pointer, $WORDLENGTH);
164 78         420 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 970 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       1845 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       2965 return 1 if($cache->{$number}->{is_valid});
206              
207 191         395 my $parsed_number = $number;
208 191         335 my %digits;
209 192         544 $parsed_number =~ s/[^\d+]//g; # strip non-digits/plusses
210 191         890 $parsed_number =~ s/^\+1//; # remove leading +1
211              
212 191         1358 @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     2886 $digits{A}.$digits{B} ne '96'
235             ) ? 1 : 0;
236              
237 192         646 $cache->{$number}->{areacode} = substr($parsed_number, 0, 3);
238 192         629 $cache->{$number}->{subscriber} = substr($parsed_number, 3);
239 192         1027 return $cache->{$number}->{is_valid};
240             }
241              
242             # define the other methods
243              
244             foreach my $method (qw(areacode subscriber)) {
245 12     12   119 no strict 'refs';
  11         28  
  11         11731  
246             *{__PACKAGE__."::$method"} = sub {
247 116     116   197 my $self = shift;
248 116         184 return $cache->{${$self}}->{$method};
  116         742  
249             }
250             }
251              
252             sub _is_canadian_600 {
253 159     159   355 my $self = shift;
254 159         281 ${$self} =~ /^(\+1)?6([0234578])\2/;
  159         767  
255             }
256              
257             sub is_geographic {
258 159     159 1 1038 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       683 return 0 if($self->_is_canadian_600());
262             # NANP-globals like 1-800 aren't geographic, the rest are
263 158 100       816 return ref($self) eq __PACKAGE__ ? 0 : 1;
264             }
265              
266             sub is_mobile {
267 83     83 1 29910 my $self = shift;
268             # NANP-globals like 1-800 aren't mobile
269 83 100       341 if(ref($self) eq __PACKAGE__) { return 0; }
  3         22  
270 82         679 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
271 82 100       371 return undef if(!exists($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code}));
272 55 100       147 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1643  
273             }
274              
275             sub is_fixed_line {
276 82     83 1 31573 my $self = shift;
277             # NANP-globals like 1-800 are fixed
278 83 100       351 if(ref($self) eq __PACKAGE__) { return 1; }
  2         7  
279 81         693 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
280 82 100       379 return undef if(!exists($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code}));
281 54 100       125 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1663  
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 32 my $self = shift;
296 7 100       9 if(${$self} =~ /555(\d{4})$/) {
  7         39  
297 7 100 100     82 return ($1 gt '0099' && $1 lt '0200') ? 1 : 0;
298             }
299 2         11 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 14 my $self = shift;
311 6 100       27 if(${$self} =~ /^(\+1)?710/) { return 1; }
  5         21  
  2         15  
312 5         34 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 31460 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       220 if(${$self} =~ /^(\+1)?8(00|33|44|55|66|77|88)/) { return 1; }
  91         1204  
  84         399  
330 7         52 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 34052 my $self = shift;
342 87 100       243 if(${$self} =~ /
  87         709  
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         415 /x) { return 1; }
350 5         22 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 32490 my $self = shift;
361 81 100       182 if(${$self} =~ /^(\+1)?5[03467]{2}/) { return 1; }
  81         665  
  78         428  
362 5         28 else { return 0; }
363             }
364              
365             sub areaname {
366 8     8 1 24 my $self = shift;
367 8         42 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 2425 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 26 my $class = shift;
389 10 100       53 if(blessed($class) eq __PACKAGE__) {
390 3         40 return 'NANPA, http://www.nanpa.com/';
391             } else {
392 9         47 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 97 my $self = shift;
417 35         100 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;