—use
5.008;
use
strict;
use
warnings;
package
Types::DateTime;
our
$AUTHORITY
=
'cpan:TOBYINK'
;
our
$VERSION
=
'0.002'
;
use
DateTime;
use
DateTime::Duration;
use
DateTime::Locale;
use
DateTime::TimeZone;
DateTime Duration TimeZone Locale Now
DateTimeWithZone DateTimeUTC
)
;
use
Type::Utils;
# This stuff for compat with MooseX::Types::DateTime
class_type(DateTime, {
class
=>
'DateTime'
});
class_type(Duration, {
class
=>
'DateTime::Duration'
});
class_type(TimeZone, {
class
=>
'DateTime::TimeZone'
});
declare Locale,
as InstanceOf[
'DateTime::Locale::root'
,
'DateTime::Locale::FromData'
];
enum(Now, [
'now'
]);
coerce DateTime,
from Num,
q{ 'DateTime'->from_epoch(epoch => $_) }
,
from HashRef,
q{ exists($_->{epoch}
) ?
'DateTime'
->from_epoch(
%$_
) :
'DateTime'
->new(%{
$_
}) },
from Now,
q{ 'DateTime'->now }
,
from InstanceOf[
'DateTime::Tiny'
],
q{ $_->DateTime }
;
coerce Duration,
from Num,
q{ 'DateTime::Duration'->new(seconds => $_) }
,
from HashRef,
q{ 'DateTime::Duration'->new(%{$_}
) };
coerce TimeZone,
from Str,
q{ 'DateTime::TimeZone'->new(name => $_) }
;
coerce Locale,
from InstanceOf[
'Locale::Maketext'
],
q{ 'DateTime::Locale'->load($_->language_tag) }
,
from Str,
q{ 'DateTime::Locale'->load($_) }
;
# Time zone stuff
declare DateTimeWithZone,
as DateTime,
coercion
=> 1,
# inherit coercions
where { not(
$_
->time_zone->is_floating) },
inline_as { (
undef
,
"not($_\->time_zone->is_floating)"
) },
constraint_generator
=>
sub
{
my
$zone
= TimeZone->assert_coerce(
shift
);
sub
{
$_
[0]->time_zone eq
$zone
};
},
coercion_generator
=>
sub
{
my
$parent
=
shift
;
my
$child
=
shift
;
my
$zone
= TimeZone->assert_coerce(
shift
);
my
$c
=
'Type::Coercion'
->new(
type_constraint
=>
$child
);
$c
->add_type_coercions(
$parent
->coercibles,
sub
{
my
$dt
= DateTime->coerce(
$_
);
return
$_
unless
DateTime->check(
$dt
);
$dt
->set_time_zone(
$zone
);
return
$dt
;
},
);
$c
;
};
declare DateTimeUTC, as DateTimeWithZone[
'UTC'
],
coercion
=> 1;
# Stringy coercions. No sugar for this stuff ;-)
__PACKAGE__->meta->add_coercion({
name
=>
'Format'
,
type_constraint
=> DateTime,
coercion_generator
=>
sub
{
my
(
$self
,
$target
,
$format
) =
@_
;
$format
= use_module(
"DateTime::Format::$format"
)->new
unless
ref
(
$format
);
my
$timezone
;
if
(
$target
->is_a_type_of(DateTimeWithZone))
{
my
(
$paramd_type
) =
grep
{
$_
->is_parameterized and
$_
->parent==DateTimeWithZone
} (
$target
,
$target
->parents);
$timezone
= TimeZone->assert_coerce(
$paramd_type
->type_parameter)
if
$paramd_type
;
}
return
(
Str,
sub
{
my
$dt
=
eval
{
$format
->parse_datetime(
$_
) };
return
$_
unless
$dt
;
$dt
->set_time_zone(
$timezone
)
if
$timezone
;
$dt
;
},
);
},
});
__PACKAGE__->meta->add_coercion({
name
=>
'Strftime'
,
type_constraint
=> Str,
coercion_generator
=>
sub
{
my
(
$self
,
$target
,
$format
) =
@_
;
my
$format_quoted
= B::perlstring(
$format
);
return
(
DateTime->coercibles,
qq{\$_->strftime($format_quoted)}
,
);
},
});
__PACKAGE__->meta->add_coercion({
name
=>
'ToISO8601'
,
type_constraint
=> Str,
type_coercion_map
=> [
DateTime->coercibles,
q{$_->iso8601}
,
],
});
1;
__END__
=pod
=encoding utf-8
=for stopwords datetime timezone
=head1 NAME
Types::DateTime - type constraints and coercions for datetime objects
=head1 SYNOPSIS
package FroobleGala;
use Moose;
use Types::DateTime -all;
has start_date => (
is => 'ro',
isa => DateTimeUTC->plus_coercions( Format['ISO8601'] ),
coerce => 1,
);
=head1 DESCRIPTION
L<Types::DateTime> is a type constraint library suitable for use with
L<Moo>/L<Moose> attributes, L<Kavorka> sub signatures, and so forth.
=head2 Types
This module provides some type constraints broadly compatible with
those provided by L<MooseX::Types::DateTime>, plus a couple of extra
type constraints.
=over
=item C<DateTime>
A class type for L<DateTime>. Coercions from:
=over
=item from C<Num>
Uses L<DateTime/from_epoch>. Floating values will be used for sub-second
precision, see L<DateTime> for details.
=item from C<HashRef>
Calls L<DateTime/new> or L<DateTime/from_epoch> as appropriate, passing
the hash as arguments.
=item from C<Now>
Uses L<DateTime/now>.
=item from C<< InstanceOf['DateTime::Tiny'] >>
Inflated using L<DateTime::Tiny/DateTime>.
=back
=item C<Duration>
A class type for L<DateTime::Duration>. Coercions from:
=over
=item from C<Num>
Uses L<DateTime::Duration/new> and passes the number as the C<seconds>
argument.
=item from C<HashRef>
Calls L<DateTime::Duration/new> with the hash entries as arguments.
=back
=item C<Locale>
A class type for L<DateTime::Locale>. Coercions from:
=over
=item from C<Str>
The string is treated as a language tag (e.g. C<en> or C<he_IL>) and
given to L<DateTime::Locale/load>.
=item from C<< InstanceOf['Locale::Maketext'] >>
The C<Locale::Maketext/language_tag> attribute will be used with
L<DateTime::Locale/load>.
=back
=item C<TimeZone>
A class type for L<DateTime::TimeZone>. Coercions from:
=over
=item from C<Str>
Treated as a time zone name or offset. See L<DateTime::TimeZone/USAGE>
for more details on the allowed values.
Delegates to L<DateTime::TimeZone/new> with the string as the C<name>
argument.
=back
=item C<Now>
Type constraint with only one allowed value, the string "now".
This is exported for compatibility with L<MooseX::Types::DateTime>, which
exports such a constraint, even though it is not documented.
=item C<DateTimeWithZone>
A subtype of C<DateTime> for objects with a defined (non-floating) time
zone.
This type constraint inherits its coercions from C<DateTime>.
=item C<< DateTimeWithZone[`a] >>
The C<DateTimeWithZone> type constraint may be parameterized with a
L<DateTime::TimeZone> object, or a string that can be coerced into one.
has start_date => (
is => 'ro',
isa => DateTimeWithZone['Europe/London'],
coerce => 1,
);
This type constraint inherits its coercions from C<DateTime>, and will
additionally call L<DateTime/set_time_zone> to shift objects into the
correct timezone.
=item C<DateTimeUTC>
Shortcut for C<< DateTimeWithZone["UTC"] >>.
=back
=head2 Named Coercions
It is hoped that Type::Tiny will help avoid the proliferation of
modules like L<MooseX::Types::DateTimeX>,
L<MooseX::Types::DateTime::ButMaintained>, and
L<MooseX::Types::DateTime::MoreCoercions>. It makes it very easy to add
coercions to a type constraint at the point of use:
has start_date => (
is => 'ro',
isa => DateTime->plus_coercions(
InstanceOf['MyApp::DT'] => sub { $_->to_DateTime }
),
coerce => 1,
);
Even easier, this module exports some named coercions.
=over
=item C<< Format[`a] >>
May be passed an object providing a C<parse_datetime> method, or a
class name from the C<< DateTime::Format:: >> namespace (upon which
C<new> will be called).
For example:
DateTime->plus_coercions( Format['ISO8601'] )
Or:
DateTimeUTC->plus_coercions(
Format[
DateTime::Format::Natural->new(lang => 'en')
]
)
=item C<< Strftime[`a] >>
A pattern for serializing a DateTime object into a string using
L<DateTime/strftime>.
Str->plus_coercions( Strftime['%a %e %b %Y'] );
=item C<ToISO8601>
A coercion for serializing a DateTime object into a string using
L<DateTime/iso8601>.
Str->plus_coercions( ToISO8601 );
=back
=head1 BUGS
Please report any bugs to
=head1 SEE ALSO
L<MooseX::Types::DateTime>,
L<Type::Tiny::Manual>,
L<DateTime>,
L<DateTime::Duration>,
L<DateTime::Locale>,
L<DateTime::TimeZone>.
=head1 AUTHOR
Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
=head1 COPYRIGHT AND LICENCE
This software is copyright (c) 2014, 2017 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=head1 DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.