The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package MooseX::Types::Date::Piece;

use strict;
use warnings;
use namespace::autoclean;

our $VERSION = '0.03';

use Carp 'croak';
use Date::Piece qw( date years months weeks days );
use MooseX::Types::Moose qw( ArrayRef Int Str );

my %DATE_DURATION = (
    days   => \&days,
    weeks  => \&weeks,
    months => \&months,
    years  => \&years,
);

use MooseX::Types -declare => [qw( Date Duration )];

class_type 'Date::Piece';
class_type 'Date::Piece::Duration', { class => 'Date::Piece::unit_base' };

subtype Date,     as 'Date::Piece';
subtype Duration, as 'Date::Piece::Duration';

for my $type ('Date::Piece', Date) {
    coerce $type,
        from Str,      via { date($_) },
        from ArrayRef, via { date($_) };
}

for my $type ('Date::Piece::Duration', Duration) {
    coerce $type,
        from Int, via { $_ * days },
        from Str, via {
            my $str = lc $_;
            $str =~ s/([^s])$/$1s/; # ensure there is an 's' at the end

            my ( $val, $unit ) = $str =~ m/^([+-]*\d+)\s*(\w+)$/;

            ( defined $val && defined $unit && defined $DATE_DURATION{$unit} )
                || croak "invalid duration '$str'";

            return $val * $DATE_DURATION{$unit}->();
        };
}

1;

__END__

=head1 NAME

MooseX::Types::Date::Piece - Date::Piece type and coercions for Moose.

=head1 SYNOPSIS

    package Foo;

    use Moose;
    use MooseX::Types::Date::Piece qw( Date Duration );

    has 'date' => (
        is      => 'ro',
        isa     => Date,
        coerce  => 1,
    );

    has 'duration' => (
        is      => 'ro',
        isa     => Duration,
        coerce  => 1,
    );

    # ...

    my $f = Foo->new(
        date     => '2012-07-09',
        duration => '1day',
    );

=head1 DESCRIPTION

This module provides L<Moose> type constraints and coercions for using
L<Date::Piece> objects as Moose attributes.

=head1 EXPORTS

The following type constants provided by L<MooseX::Types> must be explicitly
imported. The full class name may also be used (as strings with quotes) without
importing the constant declarations.

=over

=item Date

A class type for L<Date::Piece>.

=over

=item coerce from C<Str>

Uses L<Date::Piece/date>, where the string is formatted as C<'2012-12-31'> or C<'20121231'>.

=item coerce from C<ArrayRef>

Uses L<Date::Piece/date>, where the array is formatted as C<[2012, 12, 31]>.

=back

An exception is thrown if the value to be coerced is not in a valid format
or if the date is invalid.

=item Duration

A class type for C<Date::Piece::Duration>. Subtypes include C<day_unit>,
C<week_unit>, C<month_unit> and C<year_unit>.
These objects are normally created using the C<days>, C<weeks>, C<months>
and C<years> constants and may be multiplied by an integer. They may also be
used for date math by adding (or subtracting) them to C<Date::Piece> objects.
See L<Date::Piece/Year-Month-and-etc-units> for more information.

=over

=item coerce from C<Str>

The string must specify the number and unit,
e.g. C<'1day'>, C<'2weeks'>, C<'3 months'>, C<'4 YEARS'>.

=item coerce from C<Int>

The integer value will be interpreted as the number of C<days>.

=back

=back

=head1 SEE ALSO

L<Date::Piece>, L<Moose::Util::TypeConstraints>, L<MooseX::Types>

=head1 AUTHOR

Steven Lee, C<< <stevenl at cpan.org> >>

=head1 LICENSE AND COPYRIGHT

Copyright E<copy> 2012 Steven Lee. All rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut