[![CPAN version](https://badge.fury.io/pl/Business-DK-Postalcode.svg)](http://badge.fury.io/pl/Business-DK-Postalcode)
[![Build Status](https://travis-ci.org/jonasbn/bdkpst.svg?branch=master)](https://travis-ci.org/jonasbn/bdkpst)
[![Coverage Status](https://coveralls.io/repos/jonasbn/bdkpst/badge.png?branch=master)](https://coveralls.io/r/jonasbn/bdkpst?branch=master)

# NAME

Business::DK::Postalcode - validation of Danish postal codes

# VERSION

This documentation describes version 0.08

# SYNOPSIS

    # basic validation of string
    use Business::DK::Postalcode qw(validate);

    if (validate($postalcode)) {
        print "We have a valid Danish postalcode\n";
    } else {
        print "Not a valid Danish postalcode\n";
    }


    # basic validation of string, using less intrusive subroutine
    use Business::DK::Postalcode qw(validate_postalcode);

    if (validate_postalcode($postalcode)) {
        print "We have a valid Danish postal code\n";
    } else {
        print "Not a valid Danish postal code\n";
    }


    # using the untainted return value
    use Business::DK::Postalcode qw(validate_postalcode);

    if (my $untainted = validate_postalcode($postalcode)) {
        print "We have a valid Danish postal code: $untainted\n";
    } else {
        print "Not a valid Danish postal code\n";
    }


    # extracting a regex for validation of Danish postal codes
    use Business::DK::Postalcode qw(create_regex);

    my $regex_ref = ${create_regex()};

    if ($postalcode =~ m/$regex/) {
        print "We have a valid Danish postalcode\n";
    } else {
        print "Not a valid Danish postalcode\n";
    }


    # All postal codes for use outside this module
    use Business::DK::Postalcode qw(get_all_postalcodes);

    my @postalcodes = @{get_all_postalcodes()};


    # All postal codes and data for use outside this module
    use Business::DK::Postalcode qw(get_all_data);

    my $postalcodes = get_all_data();

    foreach (@{postalcodes}) {
        printf
            'postalcode: %s city: %s street/desc: %s company: %s province: %d country: %d', split /\t/, $_, 6;
    }

# FEATURES

- Providing list of Danish postal codes and related area names
- Look up methods for Danish postal codes for web applications and the like

# DESCRIPTION

This distribution is not the original resource for the included data, but simply
acts as a simple distribution for Perl use. The central source is monitored so this
distribution can contain the newest data. The monitor script (`postdanmark.pl`) is
included in the distribution.

The data are converted for inclusion in this module. You can use different extraction
subroutines depending on your needs:

- ["get\_all\_data"](#get_all_data), to retrieve all data, data description below in ["Data"](#data).
- ["get\_all\_postalcodes"](#get_all_postalcodes), to retrieve all postal codes
- ["get\_all\_citites"](#get_all_citites), to retieve all cities
- ["get\_postalcode\_from\_city"](#get_postalcode_from_city), to retrieve one or more postal codes from a city name
- ["get\_city\_from\_postalcode"](#get_city_from_postalcode), to retieve a city name from a postal code

## Data

Here follows a description of the included data, based on the description from
the original source and the authors interpretation of the data, including
details on the distribution of the data.

### city name

A non-unique, case-sensite representation of a city name in Danish.

### street/description

This field is either a streetname or g a description, is it only provided for
a few special records.

### company name

This field is only provided for a few special records.

### province

This field is a bit special and it's use is expected to be related to ditribution
all entries inside Copenhagen are marked as 'False' in this column and 'True' for
all entries outside Copenhagen - and this of course with exceptions. The data are
included since they are a part of the original data.

### country

Since the original source contains data on 3 different countries:

- Denmark
- Greenland
- Faroe Islands

Only the data representing Denmark has been included in this distribtion, so this
field is always containing a one.

For access to the data on Greenland or Faroe Islands please refer to: [Business::GL::Postalcode](https://metacpan.org/pod/Business::GL::Postalcode) 
and [Business::FO::Postalcode](https://metacpan.org/pod/Business::FO::Postalcode) respectfully.

## Encoding

The data distributed are in Danish for descriptions and names and these are encoded in UTF-8.

# EXAMPLES

A web application example is included in the examples directory following this distribution
or available at [https://metacpan.org/pod/Business::DK::Postalcode](https://metacpan.org/pod/Business::DK::Postalcode).

# SUBROUTINES AND METHODS

## validate

A simple validator for Danish postal codes.

Takes a string representing a possible Danish postal code and returns either
__1__ or __0__ indicating either validity or invalidity.

    my $rv = validate(2665);

    if ($rv == 1) {
        print "We have a valid Danish postal code\n";
    } ($rv == 0) {
        print "Not a valid Danish postal code\n";
    }

## validate\_postalcode

A less intrusive subroutine for import. Acts as a wrapper of ["validate"](#validate).

    my $rv = validate_postalcode(2300);

    if ($rv) {
        print "We have a valid Danish postal code\n";
    } else {
        print "Not a valid Danish postal code\n";
    }

## get\_all\_data

Returns a reference to a a list of strings, separated by tab characters. See
["Data"](#data) for a description of the fields.

    use Business::DK::Postalcode qw(get_all_data);

    my $postalcodes = get_all_data();

    foreach (@{postalcodes}) {
        printf
            'postalcode: %s city: %s street/desc: %s company: %s province: %d country: %d', split /\t/, $_, 6;
    }

## get\_all\_postalcodes

Takes no parameters.

Returns a reference to an array containing all valid Danish postal codes.

    use Business::DK::Postalcode qw(get_all_postalcodes);

    my $postalcodes = get_all_postalcodes;

    foreach my $postalcode (@{$postalcodes}) { ... }

## get\_all\_cities

Takes no parameters.

Returns a reference to an array containing all Danish city names having a postal code.

    use Business::DK::Postalcode qw(get_all_cities);

    my $cities = get_all_cities;

    foreach my $city (@{$cities}) { ... }

Please note that this data source used in this distribution by no means is authorative 
when it comes to cities located in Denmark, it might have all cities listed, but
unfortunately also other post distribution data.

## get\_city\_from\_postalcode

Takes a string representing a Danish postal code.

Returns a single string representing the related city name or an empty string indicating nothing was found.

    use Business::DK::Postalcode qw(get_city_from_postalcode);

    my $zipcode = '2300';

    my $city = get_city_from_postalcode($zipcode);

    if ($city) {
        print "We found a city for $zipcode\n";
    } else {
        warn "No city found for $zipcode";
    }

## get\_postalcode\_from\_city

Takes a string representing a Danish city name.

Returns a reference to an array containing zero or more postal codes related to that city name. Zero indicates nothing was found.

Please note that city names are not unique, hence the possibility of a list of postal codes.

    use Business::DK::Postalcode qw(get_postalcode_from_city);

    my $city = 'København K';

    my $postalcodes = get_postalcode_from_city($city);

    if (scalar @{$cities} == 1) {
        print "$city is unique\n";
    } elsif (scalar @{$cities} > 1) {
        warn "$city is NOT unique\n";
    } else {
        die "$city not found\n";
    }

## create\_regex

This method returns a generated regular expression for validation of a string
representing a possible Danish postal code.

    use Business::DK::Postalcode qw(create_regex);

    my $regex_ref = ${create_regex()};

    if ($postalcode =~ m/$regex/) {
        print "We have a valid Danish postalcode\n";
    } else {
        print "Not a valid Danish postalcode\n";
    }

# PRIVATE SUBROUTINES AND METHODS

## \_retrieve\_cities

Takes a reference to an array based on the DATA section and return a reference
to an array containing only city names.

### \_retrieve\_postalcode

Takes a reference to an array based on the DATA section and return a reference
to an array containing only postal codes.

### \_build\_tree

Internal method to assist ["create\_regex"](#create_regex) in generating the regular expression.

Takes a [https://metacpan.org/pod/Tree::Simple](https://metacpan.org/pod/Tree::Simple) object and a reference to an array of data elements.

# DIAGNOSTICS

There are not special diagnostics apart from the ones related to the different
subroutines.

# CONFIGURATION AND ENVIRONMENT

This distribution requires no special configuration or environment.

# DEPENDENCIES

- [https://metacpan.org/pod/Carp](https://metacpan.org/pod/Carp) (core)
- [https://metacpan.org/pod/Exporter](https://metacpan.org/pod/Exporter) (core)
- [https://metacpan.org/pod/Tree::Simple](https://metacpan.org/pod/Tree::Simple)
- [https://metacpan.org/pod/Params::Validate](https://metacpan.org/pod/Params::Validate)

## Test

Please note that the above list does not reflect requirements for:

- Additional components in this distribution, see `lib/`. Additional
components list own requirements
- Test and build system, please see: `Build.PL` for details
- Requirements for scripts in the `bin/` directory
- Requirements for examples in the `examples/` directory

# BUGS AND LIMITATIONS

There are no known bugs at this time.

The data source used in this distribution by no means is authorative when it
comes to cities located in Denmark, it might have all cities listed, but
unfortunately also other post distribution data.

# BUG REPORTING

Please report issues via CPAN RT:

- Web (RT): [http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-DK-Postalcode](http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-DK-Postalcode)
- Web (Github): [https://github.com/jonasbn/bdkpst/issues](https://github.com/jonasbn/bdkpst/issues)
- Email (RT): [bug-Business-DK-Postalcode@rt.cpan.org](https://metacpan.org/pod/bug-Business-DK-Postalcode@rt.cpan.org)

# INCOMPATIBILITIES

There are no known incompatibilities at this time.

# TEST AND QUALITY

## Perl::Critic

This version of the code is complying with [https://metacpan.org/pod/Perl::Critic](https://metacpan.org/pod/Perl::Critic) a severity: 1

The following policies have been disabled.

- [https://metacpan.org/pod/Perl::Critic::Policy::Variables::ProhibitPackageVars](https://metacpan.org/pod/Perl::Critic::Policy::Variables::ProhibitPackageVars)

    Disabled locally using 'no critic' pragma.

    The module  uses a package variable as a cache, this might not prove usefull in
    the long term, so when this is adressed and this might address this policy.

- [https://metacpan.org/pod/Perl::Critic::Policy::Subroutines::RequireArgUnpacking](https://metacpan.org/pod/Perl::Critic::Policy::Subroutines::RequireArgUnpacking)

    Disabled locally using 'no critic' pragma.

    This policy is violated when using [https://metacpan.org/pod/Params::Validate](https://metacpan.org/pod/Params::Validate) at some point this will
    be investigated further, this might be an issue due to referral to @\_.

- [https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireLineBoundaryMatching](https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireLineBoundaryMatching)

    Disabled locally using 'no critic' pragma.

    This is disabled for some two basic regular expressions.

- [https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting](https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting)

    Disabled locally using 'no critic' pragma.

    This is disabled for some two basic regular expressions.

- [https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireDotMatchAnything](https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireDotMatchAnything)

    Disabled locally using 'no critic' pragma.

    This is disabled for some two basic regular expressions.

- [https://metacpan.org/pod/Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma](https://metacpan.org/pod/Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma)

    Constants are good, - see the link below.

- [https://logiclab.jira.com/wiki/display/OPEN/Perl-Critic-Policy-ValuesAndExpressions-ProhibitConstantPragma](https://logiclab.jira.com/wiki/display/OPEN/Perl-Critic-Policy-ValuesAndExpressions-ProhibitConstantPragma)
- [https://metacpan.org/pod/Perl::Critic::Policy::Documentation::RequirePodAtEnd](https://metacpan.org/pod/Perl::Critic::Policy::Documentation::RequirePodAtEnd)

    This one interfers with our DATA section, perhaps DATA should go before POD,
    well it is not important so I have disabled the policy.

- [https://metacpan.org/pod/Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops](https://metacpan.org/pod/Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops)

    This would require a re-write of part of the code. Currently I rely on use of the iterator in the `for` loop, so it would require significant
    changes.

- [https://metacpan.org/pod/Perl::Critic::Policy::Documentation::RequirePodLinksIncludeText](https://metacpan.org/pod/Perl::Critic::Policy::Documentation::RequirePodLinksIncludeText)

    Temporarily disabled, marked for follow-up

Please see `t/perlcriticrc` for details.

## TEST COVERAGE

Test coverage report is generated using [https://metacpan.org/pod/Devel::Cover](https://metacpan.org/pod/Devel::Cover) via [https://metacpan.org/pod/Module::Build](https://metacpan.org/pod/Module::Build),
for the version described in this documentation (See [VERSION](https://metacpan.org/pod/VERSION)).

    ---------------------------- ------ ------ ------ ------ ------ ------ ------
    File                           stmt   bran   cond    sub    pod   time  total
    ---------------------------- ------ ------ ------ ------ ------ ------ ------
    ...Business/DK/Postalcode.pm  100.0  100.0    n/a  100.0  100.0   98.7  100.0
    ...Business/DK/Postalcode.pm  100.0  100.0    n/a  100.0  100.0    1.2  100.0
    Total                         100.0  100.0    n/a  100.0  100.0  100.0  100.0
    ---------------------------- ------ ------ ------ ------ ------ ------ ------

    $ ./Build testcover

# SEE ALSO

- Main data source: [http://www.postdanmark.dk/da/Documents/Lister/postnummerfil-excel.xls](http://www.postdanmark.dk/da/Documents/Lister/postnummerfil-excel.xls)
- Information resource on data source: [http://www.postdanmark.dk/cms/da-dk/eposthuset/postservices/aendringer\_postnumre\_1.htm](http://www.postdanmark.dk/cms/da-dk/eposthuset/postservices/aendringer_postnumre_1.htm)
- Alternative implementation: [https://metacpan.org/pod/Geo::Postcodes::DK](https://metacpan.org/pod/Geo::Postcodes::DK)
- Alternative validation: [https://metacpan.org/module/Regexp::Common::zip#RE-zip-Denmark-](https://metacpan.org/module/Regexp::Common::zip#RE-zip-Denmark-)
- Related complementary implementation: [https://metacpan.org/pod/Business::GL::Postalcode](https://metacpan.org/pod/Business::GL::Postalcode)
- Related complementary implementation: [https://metacpan.org/pod/Business::FO::Postalcode](https://metacpan.org/pod/Business::FO::Postalcode)
- Related implementation, same author: [https://metacpan.org/pod/Business::DK::CVR](https://metacpan.org/pod/Business::DK::CVR)
- Related implementation, same author: [https://metacpan.org/pod/Business::DK::CPR](https://metacpan.org/pod/Business::DK::CPR)
- Related implementation, same author: [https://metacpan.org/pod/Business::DK::FI](https://metacpan.org/pod/Business::DK::FI)
- Related implementation, same author: [https://metacpan.org/pod/Business::DK::PO](https://metacpan.org/pod/Business::DK::PO)

# RESOURCES

- MetaCPAN: [https://metacpan.org/pod/Business::DK::Postalcode](https://metacpan.org/pod/Business::DK::Postalcode)
- Website: [http://logicLAB.jira.com/browse/BDKPST](http://logicLAB.jira.com/browse/BDKPST)
- Bugtracker: [http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-DK-Postalcode](http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-DK-Postalcode)
- Git repository: [https://github.com/jonasbn/bdkpst](https://github.com/jonasbn/bdkpst)

# TODO

Please see the project `TODO` file, or the bugtracker (RT), website or issues resource at Github.

# AUTHOR

Jonas B. Nielsen, (jonasbn) - `<jonasbn@cpan.org>`

# MOTIVATION

Back in 2006 I was working on a project where I needed to do some presentation
and validation of Danish postal codes. I looked at [https://metacpan.org/pod/Regex::Common::Zip](https://metacpan.org/pod/Regex::Common::Zip)

The implementation at the time of writing looked as follows:

    Denmark     =>  "(?k:(?k:[1-9])(?k:[0-9])(?k:[0-9]{2}))",
    # Postal codes of the form: 'DDDD', with the first
    # digit representing the distribution region, the
    # second digit the distribution district. Postal
    # codes do not start with a zero. Postal codes
    # starting with '39' are in Greenland.

This pattern holds some issues:

- Doing some fast math you can see that you will allow 9000 valid postal
codes where the number should be about 1254
- 0 is actually allowed for a set of postal codes used by the postal service
in Denmark, in some situations these should perhaps be allowed as valid data
- Greenland specified as starting with '39' is not a part of Denmark, but
should be under Greenland and the ISO code 'GL', see also:
    - [https://metacpan.org/pod/Business::GL::Postalcode](https://metacpan.org/pod/Business::GL::Postalcode)

So I decided to write a regular expression, which would be better than the one
above, but I did not want to maintain it I wanted to write a piece of software,
which could generate the pattern for me based on a finite data set.

# COPYRIGHT

Business-DK-Postalcode is (C) by Jonas B. Nielsen, (jonasbn) 2006-2014

# LICENSE

Business-DK-Postalcode and related is released under the Artistic License 2.0

- [http://www.opensource.org/licenses/Artistic-2.0](http://www.opensource.org/licenses/Artistic-2.0)
