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   332510 use strict;
  14         71  
  14         399  
4              
5 15     15   125 use Scalar::Util 'blessed';
  15         39  
  15         550  
6              
7 15     15   121 use base 'Number::Phone';
  12         20  
  12         1110  
8 12     12   134 use Number::Phone::NANP::Data;
  11         1871  
  11         8395  
9              
10 12     12   708 use Number::Phone::Country qw(noexport);
  11         191  
  11         778  
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 1043 my $class = shift;
48 528         868 my $number = shift;
49              
50 528 100       1254 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         1625 my $country = Number::Phone::Country::phone2country($number);
55            
56             # try to load country class
57 12     12   195 eval "use Number::Phone::NANP::$country;";
  11     2   420  
  11     2   97  
  497     2   39265  
  2     2   15  
  2     2   23  
  2     2   27  
  1     2   4  
  1     2   10  
  2     2   18  
  1     2   2  
  1     2   7  
  2     2   18  
  1     2   2  
  1     2   8  
  2     2   19  
  1     2   2  
  1     2   8  
  2     2   20  
  1     2   3  
  1     2   9  
  2     2   30  
  1     2   2  
  1     2   9  
  2     2   24  
  1     2   7  
  1     2   7  
  2     2   20  
  1     2   3  
  1     2   11  
  2     2   17  
  1     2   7  
  1     2   7  
  2     2   15  
  2     2   7  
  2     2   15  
  2     2   28  
  2     2   5  
  2     2   19  
  2     2   14  
  2     2   5  
  2     2   14  
  2     2   22  
  2     2   7  
  2     2   18  
  2     2   20  
  2     2   7  
  2     2   24  
  2     2   16  
  2     2   7  
  2     2   16  
  2     2   15  
  2     2   7  
  2     2   18  
  2     2   19  
  2     2   11  
  2     2   18  
  2     2   44  
  2     2   7  
  2     2   26  
  2     2   23  
  1     2   3  
  1     2   7  
  2     2   38  
  1     2   4  
  1     2   8  
  2     2   39  
  1     2   7  
  1     2   55  
  2     2   47  
  1     2   5  
  1     1   17  
  2     1   19  
  1     1   3  
  1     1   8  
  2     1   42  
  1     1   4  
  1     1   10  
  2     1   24  
  1     1   4  
  1     1   10  
  2     1   33  
  1     1   4  
  1     1   12  
  2     1   32  
  1     1   3  
  1     1   17  
  2     1   23  
  2     1   7  
  2     1   17  
  2     1   17  
  2     1   6  
  2     1   17  
  2     1   21  
  2     1   10  
  2     1   21  
  2     1   23  
  2     1   9  
  2     1   18  
  2     1   19  
  2     1   6  
  2     1   17  
  2     1   15  
  2     1   5  
  2     1   24  
  2     1   56  
  0     1   0  
  0     1   0  
  2     1   25  
  1     1   3  
  1     1   10  
  2     1   23  
  1     1   5  
  1     1   8  
  2     1   28  
  1     1   10  
  1     1   11  
  2     1   19  
  1     1   2  
  1     1   8  
  2     1   20  
  1     1   2  
  1     1   10  
  2     1   19  
  1     1   2  
  1     1   9  
  2     1   27  
  1     1   12  
  1     1   9  
  2     1   23  
  1     1   4  
  1     1   7  
  2     1   18  
  2     1   4  
  2     1   22  
  2     1   20  
  1     1   2  
  1     1   8  
  2     1   17  
  2     1   9  
  2     1   17  
  2     1   29  
  1     1   5  
  1     1   16  
  2     1   27  
  1     1   3  
  1     1   10  
  2     1   15  
  2     1   13  
  2     1   76  
  2     1   26  
  1     1   6  
  1     1   14  
  2     1   27  
  1     1   14  
  1     1   7  
  2     1   26  
  1     1   2  
  1     1   9  
  2     1   26  
  0     1   0  
  0     1   0  
  1     1   13  
  0     1   0  
  0     1   0  
  1     1   28  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   21  
  0     1   0  
  0     1   0  
  1     1   18  
  0     1   0  
  0     1   0  
  1     1   11  
  0     1   0  
  0     1   0  
  1     1   18  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   14  
  1     1   4  
  1     1   10  
  1     1   9  
  1     1   3  
  1     1   9  
  1     1   44  
  1     1   6  
  1     1   9  
  1     1   11  
  1     1   4  
  1     1   8  
  1     1   11  
  1     1   5  
  1     1   11  
  1     1   10  
  1     1   3  
  1     1   9  
  1     1   16  
  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   20  
  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   18  
  0     1   0  
  0     1   0  
  1     1   16  
  0     1   0  
  0     1   0  
  1     1   16  
  1     1   8  
  1     1   13  
  1     1   11  
  1     1   5  
  1     1   11  
  1     1   15  
  1     1   14  
  1     1   12  
  1     1   8  
  1     1   3  
  1     1   8  
  1     1   9  
  1     1   3  
  1     1   9  
  1     1   12  
  1     1   5  
  1     1   8  
  1     1   13  
  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   17  
  0     1   0  
  0     1   0  
  1     1   11  
  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   30  
  0     1   0  
  0     1   0  
  1     1   28  
  1     1   8  
  1     1   12  
  1     1   10  
  1     1   5  
  1     1   8  
  1     1   15  
  1     1   3  
  1     1   10  
  1     1   15  
  1     1   5  
  1     1   7  
  1     1   16  
  1     1   4  
  1     1   9  
  1     1   13  
  1     1   3  
  1     1   7  
  1     1   24  
  0     1   0  
  0     1   0  
  1     1   13  
  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   20  
  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   16  
  0     1   0  
  0     1   0  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   12  
  1     1   6  
  1     1   11  
  1     1   8  
  1     1   3  
  1     1   9  
  1     1   12  
  1     1   3  
  1     1   39  
  1     1   8  
  1     1   4  
  1     1   9  
  1     1   8  
  1     1   4  
  1     1   10  
  1     1   9  
  1     1   3  
  1     1   8  
  1     1   15  
  0     1   0  
  0     1   0  
  1     1   12  
  0     1   0  
  0     1   0  
  1     1   17  
  0     1   0  
  0     1   0  
  1     1   13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         21  
  1         5  
  1         9  
  1         10  
  1         5  
  1         8  
  1         7  
  1         4  
  1         9  
  1         12  
  1         3  
  1         11  
  1         7  
  1         4  
  1         10  
  1         7  
  1         3  
  1         8  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         13  
  1         3  
  1         11  
  1         9  
  1         2  
  1         10  
  1         7  
  1         3  
  1         8  
  1         20  
  1         3  
  1         19  
  1         18  
  1         5  
  1         15  
  1         11  
  1         2  
  1         24  
  1         17  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  1         4  
  1         9  
  1         9  
  1         4  
  1         8  
  1         9  
  1         3  
  1         7  
  1         10  
  1         3  
  1         9  
  1         11  
  1         2  
  1         9  
  1         8  
  1         3  
  1         9  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         16  
  1         4  
  1         10  
  1         10  
  1         3  
  1         8  
  1         8  
  1         3  
  1         7  
  1         10  
  1         2  
  1         11  
  1         9  
  1         6  
  1         7  
  1         8  
  1         2  
  1         9  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         13  
  1         4  
  1         10  
  1         8  
  1         2  
  1         8  
  1         9  
  1         4  
  1         7  
  1         8  
  1         3  
  1         9  
  1         16  
  1         4  
  1         8  
  1         14  
  1         4  
  1         9  
  1         13  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         30  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         13  
  1         6  
  1         10  
  1         15  
  1         6  
  1         9  
  1         12  
  1         3  
  1         8  
  1         8  
  1         4  
  1         9  
  1         8  
  1         3  
  1         6  
  1         9  
  1         3  
  1         8  
  1         11  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         29  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         45  
  1         8  
  1         14  
  1         15  
  1         7  
  1         7  
  1         11  
  1         5  
  1         8  
  1         7  
  1         3  
  1         8  
  1         8  
  1         6  
  1         10  
  1         15  
  1         7  
  1         14  
  1         16  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         27  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         15  
  1         6  
  1         11  
  1         13  
  1         4  
  1         10  
  1         13  
  1         8  
  1         12  
  1         16  
  1         4  
  1         9  
  1         8  
  1         3  
  1         8  
  1         8  
  1         15  
  1         13  
  1         18  
  0         0  
  0         0  
  1         79  
  0         0  
  0         0  
  1         29  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         21  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         18  
  0         0  
  0         0  
  1         20  
  0         0  
  0         0  
  1         25  
  1         7  
  1         13  
  1         11  
  1         3  
  1         7  
  1         16  
  1         12  
  1         16  
  1         11  
  1         3  
  1         7  
  1         12  
  1         4  
  1         12  
  1         8  
  1         12  
  1         3824  
  1         13  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         45  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         13  
  1         8  
  1         10  
  1         8  
  1         2  
  1         10  
  1         9  
  1         4  
  1         7  
  1         8  
  1         4  
  1         53  
  1         29  
  1         6  
  1         9  
  1         9  
  1         3  
  1         8  
  1         20  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         25  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         22  
  1         11  
  1         8  
  1         16  
  1         9  
  1         19  
  1         7  
  1         4  
  1         13  
  1         8  
  1         3  
  1         9  
  1         9  
  1         3  
  1         13  
  1         8  
  1         2  
  1         8  
  1         20  
  0         0  
  0         0  
  1         23  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         30  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  1         16  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         13  
  1         4  
  1         9  
  1         8  
  1         3  
  1         10  
  1         10  
  1         5  
  1         8  
  1         12  
  1         3  
  1         14  
  1         11  
  1         19  
  1         9  
  1         10  
  1         2  
  1         54  
  1         26  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         15  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         13  
  1         15  
  1         17  
  1         7  
  1         2  
  1         16  
  1         9  
  1         5  
  1         12  
  1         8  
  1         4  
  1         9  
  1         18  
  1         12  
  1         13  
  1         7  
  1         4  
  1         8  
  1         16  
  0         0  
  0         0  
  1         13  
  0         0  
  0         0  
  1         36  
  0         0  
  0         0  
  1         16  
  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         15  
  0         0  
  0         0  
  1         24  
  1         4  
  1         14  
  1         14  
  1         3  
  1         11  
  1         40  
  1         4  
  1         9  
  1         13  
  1         3  
  1         19  
  1         10  
  1         3  
  1         10  
  1         7  
  1         3  
  1         10  
  1         19  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         28  
  0         0  
  0         0  
  1         12  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  1         14  
  0         0  
  0         0  
  1         11  
  0         0  
  0         0  
  1         12  
  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       44068 return bless(\$number, $class) if($@);
61 261         1703 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   6443 if(!$datafh || $pid != $$) {
95 7         73 my $file = Number::Phone::_find_data_file('Number-Phone-NANP-Data.db');
96 8 50       404 open($datafh, '<:bytes', $file) || die("Can't read $file: $!");
97 7         192 read($datafh, my $header, 8);
98 7 50       58 die("$file isn't the right format\n") unless($header eq 'NANPOP'.chr(0).chr(0));
99 8         78 read($datafh, $WORDLENGTH, 1);
100 8         39 $WORDLENGTH = ord($WORDLENGTH);
101 8         55 $pid = $$;
102             }
103 472         4516 return $datafh;
104             }
105              
106             sub operator {
107 56     56 1 114 my $self = shift;
108              
109             # file needs to be open so we have a $WORDLENGTH
110 56         240 $self->_datafh();
111              
112 56         101 (my $number = ${$self}) =~ s/\D//g;
  56         395  
113 56         216 my $ten_thousands = substr($number, 1, 6);
114              
115 56         170 $ten_thousands -= 200000; # area codes below 200 are invalid
116 56         273 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   214 my($self, $offset) = @_;
121              
122 78         283 my $pointer = $self->_get_pointer_at_offset($offset);
123 78 100       237 return undef unless($pointer);
124              
125 67         320 my $block_type = $self->_get_block_type_at_offset($pointer);
126 67         124 $pointer += 1;
127              
128 67 100       210 if($block_type == 0) {
    50          
129             # $pointer points at a string
130 45         237 return $self->_get_string_at_offset($pointer);
131             } elsif($block_type == 1) {
132             # $pointer points at a block of pointers
133 24         60 (my $number = ${$self}) =~ s/\D//g;
  23         140  
134 23         65 my $thousands = substr($number, 7, 1); # the seventh digit
135 24         111 return $self->_get_data_starting_from_pointer_at_offset($pointer + $WORDLENGTH * $thousands);
136             } else {
137 1         5 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   129 my($self, $offset) = @_;
143              
144 67         146 seek($self->_datafh(), $offset, 0);
145 66         285 read($self->_datafh(), my $block_type, 1);
146 66         247 return ord($block_type);
147             }
148              
149             sub _get_string_at_offset {
150 45     45   121 my($self, $offset) = @_;
151              
152 44         113 seek($self->_datafh(), $offset, 0);
153 44         174 read($self->_datafh(), my $chars, 1);
154 45         208 $chars = unpack('C', $chars);
155 44         119 read($self->_datafh(), my $string, $chars);
156 44         288 return $string;
157             }
158              
159             sub _get_pointer_at_offset {
160 78     78   177 my($self, $offset) = @_;
161              
162 77         158 seek($self->_datafh(), $offset, 0);
163 77         340 read($self->_datafh(), my $pointer, $WORDLENGTH);
164 78         414 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 897 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       1668 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       2946 return 1 if($cache->{$number}->{is_valid});
206              
207 191         376 my $parsed_number = $number;
208 191         351 my %digits;
209 192         544 $parsed_number =~ s/[^\d+]//g; # strip non-digits/plusses
210 191         865 $parsed_number =~ s/^\+1//; # remove leading +1
211              
212 191         1443 @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     2720 $digits{A}.$digits{B} ne '96'
235             ) ? 1 : 0;
236              
237 192         722 $cache->{$number}->{areacode} = substr($parsed_number, 0, 3);
238 192         556 $cache->{$number}->{subscriber} = substr($parsed_number, 3);
239 192         1011 return $cache->{$number}->{is_valid};
240             }
241              
242             # define the other methods
243              
244             foreach my $method (qw(areacode subscriber)) {
245 12     12   127 no strict 'refs';
  11         35  
  11         10779  
246             *{__PACKAGE__."::$method"} = sub {
247 116     116   202 my $self = shift;
248 116         171 return $cache->{${$self}}->{$method};
  116         686  
249             }
250             }
251              
252             sub _is_canadian_600 {
253 159     159   353 my $self = shift;
254 159         323 ${$self} =~ /^(\+1)?6([0234578])\2/;
  159         795  
255             }
256              
257             sub is_geographic {
258 159     159 1 1013 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       635 return 0 if($self->_is_canadian_600());
262             # NANP-globals like 1-800 aren't geographic, the rest are
263 158 100       884 return ref($self) eq __PACKAGE__ ? 0 : 1;
264             }
265              
266             sub is_mobile {
267 83     83 1 30630 my $self = shift;
268             # NANP-globals like 1-800 aren't mobile
269 83 100       370 if(ref($self) eq __PACKAGE__) { return 0; }
  3         24  
270 82         654 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
271 82 100       406 return undef if(!exists($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code}));
272 55 100       156 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::mobile_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1632  
273             }
274              
275             sub is_fixed_line {
276 82     83 1 32548 my $self = shift;
277             # NANP-globals like 1-800 are fixed
278 83 100       365 if(ref($self) eq __PACKAGE__) { return 1; }
  2         7  
279 81         701 (my $ISO_country_code = ref($self)) =~ s/.*(..)$/$1/;
280 82 100       413 return undef if(!exists($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code}));
281 54 100       111 return ${$self} =~ /^\+1($Number::Phone::NANP::Data::fixed_line_regexes{$ISO_country_code})$/ ? 1 : 0;
  54         1644  
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 38 my $self = shift;
296 7 100       12 if(${$self} =~ /555(\d{4})$/) {
  7         36  
297 7 100 100     79 return ($1 gt '0099' && $1 lt '0200') ? 1 : 0;
298             }
299 2         9 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 20 my $self = shift;
311 6 100       29 if(${$self} =~ /^(\+1)?710/) { return 1; }
  5         18  
  2         15  
312 5         36 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 32183 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       191 if(${$self} =~ /^(\+1)?8(00|33|44|55|66|77|88)/) { return 1; }
  91         880  
  84         400  
330 7         43 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 34119 my $self = shift;
342 87 100       163 if(${$self} =~ /
  87         671  
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         23 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 32202 my $self = shift;
361 81 100       198 if(${$self} =~ /^(\+1)?5[03467]{2}/) { return 1; }
  81         709  
  78         386  
362 5         36 else { return 0; }
363             }
364              
365             sub areaname {
366 8     8 1 29 my $self = shift;
367 8         51 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 2413 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 27 my $class = shift;
389 10 100       57 if(blessed($class) eq __PACKAGE__) {
390 3         36 return 'NANPA, http://www.nanpa.com/';
391             } else {
392 9         71 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 94 my $self = shift;
417 35         92 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;