view release on metacpan or search on metacpan
1.26 2026-05-28 <schubiger@cpan.org>
- Merged development version to stable.
1.25_04 2026-05-27 <schubiger@cpan.org>
- ::Calendar: make 'type' part of the object.
1.25_03 2026-05-26 <schubiger@cpan.org>
- Guard against non-existent calendar module when testing.
1.25_02 2026-05-25 <schubiger@cpan.org>
- Introduce ::Calendar which converts between calendar systems.
1.25_01 2026-05-17 <schubiger@cpan.org>
- New supported formats: christmas/new year's eve/day.
- Test and document the new formats.
1.25 2026-02-13 <schubiger@cpan.org>
- Merged development version to stable.
t/05-parse_time_zone.t
t/06-parse_demand_future.t
t/06-parse_demand_future_hires.t
t/06-parse_prefer_future.t
t/06-parse_prefer_future_hires.t
t/07-parse_datetime.t
t/07-parse_datetime_future.t
t/08-parse_aliases.t
t/08-parse_aliases_hires.t
t/09-parse_iso8601.t
t/10-parse_calendar.t
t/11-parse_expand.t
t/12-parse_success.t
t/13-parse_failure.t
t/14-parse_assert.t
t/15-extract_expression.t
t/16-regression.t
t/17-state.t
t/18-trace.t
t/19-validate.t
t/pod-coverage.t
Creates a new DateTime::Format::Natural object. Arguments to new() are
options and not necessarily required.
$parser = DateTime::Format::Natural->new(
datetime => DateTime->new(...),
lang => 'en',
format => 'mm/dd/yy',
prefer_future => [0|1],
demand_future => [0|1],
time_zone => 'floating',
calendar_class => 'DateTime::Calendar::Julian',
daytime => { morning => 06,
afternoon => 13,
evening => 20,
},
);
* datetime
Overrides the present now with a DateTime object provided.
* demand_future
Demands future time and dates. Similar to prefer_future, but
stronger. Accepts a boolean, defaults to false.
* time_zone
The time zone to use when parsing and for output. Accepts any time
zone recognized by DateTime. Defaults to 'floating'.
* calendar_class
The calendar class used for fixed-date holidays such as christmas
day. Accepts DateTime::Calendar::Julian. Defaults to 'gregorian'.
* daytime
A hash reference consisting of customized daytime hours, which may be
selectively changed.
METHODS
parse_datetime
lib/DateTime/Format/Natural.pm view on Meta::CPAN
{
my $self = shift;
my %opts = @_;
my %presets = (
lang => 'en',
format => 'd/m/y',
demand_future => false,
prefer_future => false,
time_zone => 'floating',
calendar_class => undef,
);
foreach my $opt (keys %presets) {
$self->{ucfirst $opt} = $presets{$opt};
}
foreach my $opt (keys %opts) {
if (defined $opts{$opt}) {
$self->{ucfirst $opt} = $opts{$opt};
}
}
$self->{Daytime} = $opts{daytime} || {};
lib/DateTime/Format/Natural.pm view on Meta::CPAN
$self->{grammar_class} = $mod;
$self->{mode} = '';
}
sub _init_check
{
my $self = shift;
validate(@_, {
calendar_class => {
type => SCALAR | UNDEF,
optional => true,
callbacks => {
'calendar class exists' => sub
{
my $class = shift;
return true unless defined $class;
return $class eq 'DateTime::Calendar::Julian' && eval "require $class; 1";
},
},
},
demand_future => {
# SCALARREF due to boolean.pm's implementation
type => BOOLEAN | SCALARREF,
lib/DateTime/Format/Natural.pm view on Meta::CPAN
Creates a new C<DateTime::Format::Natural> object. Arguments to C<new()> are options and
not necessarily required.
$parser = DateTime::Format::Natural->new(
datetime => DateTime->new(...),
lang => 'en',
format => 'mm/dd/yy',
prefer_future => [0|1],
demand_future => [0|1],
time_zone => 'floating',
calendar_class => 'DateTime::Calendar::Julian',
daytime => { morning => 06,
afternoon => 13,
evening => 20,
},
);
=over 4
=item * C<datetime>
lib/DateTime/Format/Natural.pm view on Meta::CPAN
=item * C<demand_future>
Demands future time and dates. Similar to C<prefer_future>, but stronger.
Accepts a boolean, defaults to false.
=item * C<time_zone>
The time zone to use when parsing and for output. Accepts any time zone
recognized by L<DateTime>. Defaults to 'floating'.
=item * C<calendar_class>
The calendar class used for fixed-date holidays such as C<christmas day>.
Accepts C<DateTime::Calendar::Julian>. Defaults to 'C<gregorian>'.
=item * C<daytime>
A hash reference consisting of customized daytime hours,
which may be selectively changed.
=back
=head1 METHODS
lib/DateTime/Format/Natural/Calc.pm view on Meta::CPAN
$self->_set(day => $day);
}
sub _christmas_new_year
{
my $self = shift;
$self->_register_trace;
my $opts = pop;
my ($when) = @_;
my %holidays = %{$self->{data}->{holidays}};
my $calendar = DateTime::Format::Natural::Calendar::_init($self->{Calendar_class});
my $year = $self->{datetime}->year;
my ($month, $day) = @{$holidays{$calendar->{type}}{$opts->{type}}}{qw(month day)};
$self->{datetime}->set(hour => 0, minute => 0, second => 0, nanosecond => 0);
my $code = sub
{
my ($year, $month, $day, $opts) = @_;
$$year++ if $opts->{type} eq 'new_year';
};
if ($calendar->_can_convert) {
($year, $month, $day) = $calendar->_to_gregorian($year, $month, $day, $opts, $code);
}
else {
$code->(\$year, \$month, \$day, $opts);
}
$self->_set(
year => $year,
month => $month,
day => $day,
);
$self->_add_or_subtract({
lib/DateTime/Format/Natural/Calendar.pm view on Meta::CPAN
use DateTime ();
our $VERSION = '0.02';
sub _init
{
my ($class) = @_;
my $type = _type($class);
return "DateTime::Format::Natural::Calendar::${type}"->_new(calendar_class => $class, type => $type);
}
sub _type
{
my ($class) = @_;
if (defined $class) {
return 'julian' if $class eq 'DateTime::Calendar::Julian';
}
else {
lib/DateTime/Format/Natural/Calendar.pm view on Meta::CPAN
use base 'DateTime::Format::Natural::Calendar';
sub _to_gregorian
{
my $self = shift;
my ($year, $month, $day, $opts, $code) = @_;
$code->(\$year, \$month, \$day, $opts);
my $dt = DateTime->from_object(object => $self->{calendar_class}->new(year => $year, month => $month, day => $day));
return map $dt->$_, qw(year month day);
}
1;
__END__
=head1 NAME
DateTime::Format::Natural::Calendar - Convert between calendar systems
=head1 SYNOPSIS
Please see the DateTime::Format::Natural documentation.
=head1 DESCRIPTION
C<DateTime::Format::Natural::Calendar> converts between calendar systems.
=head1 SEE ALSO
L<DateTime::Format::Natural>
=head1 AUTHOR
Steven Schubiger <schubiger@cpan.org>
=head1 LICENSE
lib/DateTime/Format/Natural/Lang/EN.pm view on Meta::CPAN
);
%data_rewrite = (
at => {
match => qr/\S+? \s+? at \s+? (\S+)/ix,
subst => qr/\s+? at \b/ix,
daytime => qr/^(?:noon|midnight)$/i,
},
);
# month/day are expressed in the respective calendar system
%data_holidays = (
gregorian => {
christmas => { month => 12, day => 25 },
new_year => { month => 1, day => 1 },
},
julian => {
christmas => { month => 12, day => 25 },
new_year => { month => 1, day => 1 },
},
);
scripts/dateparse view on Meta::CPAN
{
my $opts = {};
$opts = parse_switches() if @ARGV;
set_values($opts);
process();
}
sub parse_switches
{
my %opts;
GetOptions(\%opts, qw(c|calendar_class=s
d|datetime=s
e|extract
f|format=s
h|help
l|lang=s
p|prefer_future
P|demand_future
s|supported
t|time_zone=s
T|trace
scripts/dateparse view on Meta::CPAN
else {
$ignore = true;
}
if ($ignore) {
warn "ignoring invalid datetime string '$opts->{d}'\n";
delete $opts->{d};
}
}
my %table = (
c => 'calendar_class',
d => 'datetime',
l => 'lang',
f => 'format',
p => 'prefer_future',
P => 'demand_future',
t => 'time_zone',
);
foreach my $opt (keys %$opts) {
if (exists $table{$opt}) {
$args{$table{$opt}} = $opts->{$opt};
}
}
}
sub usage
{
print <<"USAGE";
Usage: $0 [switches]
-c, --calendar_class=<class> calendar class
-d, --datetime=<string> datetime string
-e, --extract extract expressions
-f, --format=<format> format of numeric dates
-h, --help this help screen
-l, --lang=<code> language code
-p, --prefer_future prefer future dates
-P, --demand_future demand future dates
-s, --supported list of supported languages
-t, --time_zone=<string> time zone string
-T, --trace print trace after processing
scripts/dateparse view on Meta::CPAN
}
}
=head1 NAME
dateparse - frontend to DateTime::Format::Natural
=head1 SYNOPSIS
Usage: dateparse [switches]
-c, --calendar_class=<class> calendar class
-d, --datetime=<string> datetime string *
-e, --extract extract expressions
-f, --format=<format> format of numeric dates
-h, --help this help screen
-l, --lang=<code> language code
-p, --prefer_future prefer future dates
-P, --demand_future demand future dates
-s, --supported list of supported languages
-t, --time_zone=<string> time zone string
-T, --trace print trace after processing
t/10-parse_calendar.t view on Meta::CPAN
#!/usr/bin/perl
use strict;
use warnings;
use DateTime::Format::Natural;
use DateTime::Format::Natural::Test ':set';
use Test::More;
my @calendar_gregorian = (
{ 'christmas eve' => '24.12.2006 20:00:00' },
{ 'christmas day' => '25.12.2006 00:00:00' },
{ 'new years eve' => '31.12.2006 20:00:00' },
{ 'new years day' => '01.01.2007 00:00:00' },
{ 'new year\'s eve' => '31.12.2006 20:00:00' },
{ 'new year\'s day' => '01.01.2007 00:00:00' },
);
# these tests will break at the transition to 2100...
my @calendar_julian = (
{ 'christmas eve' => '06.01.2007 20:00:00' },
{ 'christmas day' => '07.01.2007 00:00:00' },
{ 'new years eve' => '13.01.2007 20:00:00' },
{ 'new years day' => '14.01.2007 00:00:00' },
{ 'new year\'s eve' => '13.01.2007 20:00:00' },
{ 'new year\'s day' => '14.01.2007 00:00:00' },
);
my @tests = ([ \@calendar_gregorian, undef ]);
push @tests, [ \@calendar_julian, 'DateTime::Calendar::Julian' ] if eval { require DateTime::Calendar::Julian; 1 };
my $tests = 0;
$tests += scalar @{$_->[0]} for @tests;
_run_tests($tests, [ @tests ], \&compare);
sub compare
{
my ($aref, $calendar_class) = @_;
foreach my $href (@$aref) {
my $key = (keys %$href)[0];
foreach my $string ($case_strings->($key)) {
compare_strings($string, $href->{$key}, $calendar_class);
}
}
}
sub compare_strings
{
my ($string, $result, $calendar_class) = @_;
my $parser = DateTime::Format::Natural->new(calendar_class => $calendar_class);
$parser->_set_datetime(\%time);
my $dt = $parser->parse_datetime($string);
if ($parser->success) {
is(_result_string($dt), $result, _message($string));
}
else {
fail(_message($string));
}
t/19-validate.t view on Meta::CPAN
eval { DateTime::Format::Natural->new(daytime => { morning => 8, afternoon => 14, evening => 20 }) };
ok(!$@, 'daytime hours');
eval { DateTime::Format::Natural->new(datetime => DateTime->now) };
ok(!$@, 'datetime with DateTime object');
eval { DateTime::Format::Natural->new(datetime => DateTime::HiRes->now) };
ok(!$@, 'datetime with DateTime::HiRes object');
eval { DateTime::Format::Natural->new(calendar_class => undef) };
ok(!$@, 'calendar_class undef');
if (eval { require DateTime::Calendar::Julian; 1 }) {
eval { DateTime::Format::Natural->new(calendar_class => 'DateTime::Calendar::Julian') };
ok(!$@, 'calendar_class class');
}
done_testing();