DateTime-Format-Natural

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

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.

MANIFEST  view on Meta::CPAN

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

README  view on Meta::CPAN

    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.

README  view on Meta::CPAN

      * 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();



( run in 1.033 second using v1.01-cache-2.11-cpan-f52f0507bed )