HEX
Server: Apache
System: Linux s198.coreserver.jp 5.15.0-151-generic #161-Ubuntu SMP Tue Jul 22 14:25:40 UTC 2025 x86_64
User: nagasaki (10062)
PHP: 7.1.33
Disabled: NONE
Upload Files
File: //usr/local/share/man/man3/DateTime::Format::Builder::Tutorial.3pm
.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings.  \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
.    ds -- \(*W-
.    ds PI pi
.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
.    ds L" ""
.    ds R" ""
.    ds C` ""
.    ds C' ""
'br\}
.el\{\
.    ds -- \|\(em\|
.    ds PI \(*p
.    ds L" ``
.    ds R" ''
.    ds C`
.    ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el       .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD.  Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
.    if \nF \{\
.        de IX
.        tm Index:\\$1\t\\n%\t"\\$2"
..
.        if !\nF==2 \{\
.            nr % 0
.            nr F 2
.        \}
.    \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "DateTime::Format::Builder::Tutorial 3"
.TH DateTime::Format::Builder::Tutorial 3 "2020-08-09" "perl v5.26.3" "User Contributed Perl Documentation"
.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
DateTime::Format::Builder::Tutorial \- Quick class on using Builder
.SH "VERSION"
.IX Header "VERSION"
version 0.83
.SH "CREATING A CLASS"
.IX Header "CREATING A CLASS"
As most people who are writing modules know, you start a package with a
package declaration and some indication of module version:
.PP
.Vb 1
\&    package DateTime::Format::ICal; our $VERSION = \*(Aq0.04\*(Aq;
.Ve
.PP
After that, you call Builder with some options. There are
only a few (detailed later). Right now, we're only interested
in \fIparsers\fR.
.PP
.Vb 1
\&    use DateTime::Format::Builder ( parsers => {...} );
.Ve
.PP
The \fIparsers\fR option takes a reference to a hash of method names and
specifications:
.PP
.Vb 5
\&    parsers => {
\&            parse_datetime => ... ,
\&            parse_datetime_with_timezone => ... ,
\&            ...
\&        }
.Ve
.PP
Builder will create methods in your class, each method being a parser that
follows the given specifications. It is \fBstrongly\fR recommended that one
method is called \fIparse_datetime\fR, be it a Builder created method or one of
your own.
.PP
In addition to creating any of the parser methods it also creates a \f(CW\*(C`new\*(C'\fR
method that can instantiate (or clone) objects of this class. This behaviour
can be modified with the \fIconstructor\fR option, but we don't need to know that
yet.
.PP
Each value corresponding to a method name in the parsers list is either a
single specification, or a list of specifications. We'll start with the simple
case.
.PP
.Vb 2
\&    parse_briefdate => { params => [qw( year month day )], regex =>
\&        qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)$/, },
.Ve
.PP
This will result in a method named \fIparse_briefdate\fR which
will take strings in the form \f(CW20040716\fR and return
DateTime objects representing that date. A user of the class
might write:
.PP
.Vb 4
\&    use DateTime::Format::ICal;
\&    my $date = \*(Aq19790716\*(Aq;
\&    my $dt   = DateTime::Format::ICal\->parse_briefdate($date);
\&    print "My birth month is ", $dt\->month_name, "\en";
.Ve
.PP
The \f(CW\*(C`regex\*(C'\fR is applied to the input string, and if it matches, then \f(CW$1\fR,
\&\f(CW$2\fR, ... are mapped to the \fIparams\fR given and handed to \f(CW\*(C`DateTime\->new\*(C'\fR. Essentially:
.PP
.Vb 1
\&    my $rv = DateTime\->new( year => $1, month => $2, day => $3 );
.Ve
.PP
There are more complicated things one can do within a single specification,
but we'll cover those later.
.PP
Often, you'll want a method to be able to take one string, and run it against
multiple parser specifications. It would be very irritating if the user had to
work out what format the datetime string was in and then which method was most
appropriate.
.PP
So, Builder lets you specify multiple specifications:
.PP
.Vb 10
\&    parse_datetime => [
\&        {
\&            params => [qw( year month day hour minute second )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day hour minute )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day hour )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&    ],
.Ve
.PP
It's an arrayref of specifications. A parser will be created that will try
each of these specifications sequentially, in the order you specified.
.PP
There's a flaw with this though. In this example, we're building a parser for
ICal datetimes. One can place a timezone id at the start of an ICal
datetime. You might extract such an id with the following code:
.PP
.Vb 3
\&    if ( $date =~ s/^TZID=([^:]+):// ) {
\&        $time_zone = $1;
\&    }
\&
\&    # Z at end means UTC
\&    elsif ( $date =~ s/Z$// ) {
\&        $time_zone = \*(AqUTC\*(Aq;
\&    }
\&    else {
\&        $time_zone = \*(Aqfloating\*(Aq;
\&    }
.Ve
.PP
\&\f(CW$date\fR would end up without the id, and \f(CW$time_zone\fR would contain
something appropriate to give to DateTime's \fIset_time_zone\fR method, or
\&\fItime_zone\fR argument.
.PP
But how to get this scrap of code into your parser? You might be tempted to
call the parser something else and build a small wrapper. There's no need
though because an option is provided for preprocessing dates:
.PP
.Vb 10
\&    parse_datetime => [
\&        [ preprocess => \e&_parse_tz ],    # Only changed line!
\&        {
\&            params => [qw( year month day hour minute second )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day hour minute )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day hour )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)$/,
\&        },
\&        {
\&            params => [qw( year month day )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&    ],
.Ve
.PP
It will necessitate \fI_parse_tz\fR to be written, and that routine looks like
this:
.PP
.Vb 6
\&    sub _parse_tz {
\&        my %args = @_;
\&        my ( $date, $p ) = @args{qw( input parsed )};
\&        if ( $date =~ s/^TZID=([^:]+):// ) {
\&            $p\->{time_zone} = $1;
\&        }
\&
\&        # Z at end means UTC
\&        elsif ( $date =~ s/Z$// ) {
\&            $p\->{time_zone} = \*(AqUTC\*(Aq;
\&        }
\&        else {
\&            $p\->{time_zone} = \*(Aqfloating\*(Aq;
\&        }
\&        return $date;
\&    }
.Ve
.PP
On input it is given a hash containing two items: the input date and a hashref
that will be used in the parsing. The return value from the routine is what
the parser specifications will run against, and anything in the \fIparsed\fR hash
(\f(CW$p\fR in the example) will be put in the call to \f(CW\*(C`DateTime\->new(...)\*(C'\fR.
.PP
So, we now have a happily working ICal parser. It parses the assorted formats,
and can also handle timezones. Is there anything else it needs to do? No. But
we can make it work more efficiently.
.PP
At present, the specifications are tested sequentially.  However, each one
applies to strings of particular lengths.  Thus we could be efficient and have
the parser only test the given strings against a parser that handles that
string length. Again, Builder makes it easy:
.PP
.Vb 10
\&    parse_datetime => [
\&        [ preprocess => \e&_parse_tz ],
\&        {
\&            length => 15,    # We handle strings of exactly 15 chars
\&            params => [qw( year month day hour minute second )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            length => 13,                                # exactly 13 chars...
\&            params => [qw( year month day hour minute )],
\&            regex => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)$/,
\&        },
\&        {
\&            length => 11,                                    # 11..
\&            params => [qw( year month day hour )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)$/,
\&        },
\&        {
\&            length => 8,                                     # yes.
\&            params => [qw( year month day )],
\&            regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)$/,
\&        },
\&    ],
.Ve
.PP
Now the created parser will create a parser that only runs specifications
against appropriate strings.
.PP
So our complete code looks like:
.PP
.Vb 3
\&    package DateTime::Format::ICal;
\&    use strict;
\&    our $VERSION = \*(Aq0.04\*(Aq;
\&
\&    use DateTime::Format::Builder (
\&        parsers => {
\&            parse_datetime => [
\&                [ preprocess => \e&_parse_tz ],
\&                {
\&                    length => 15,
\&                    params => [qw( year month day hour minute second )],
\&                    regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)(\ed\ed)$/,
\&                },
\&                {
\&                    length => 13,
\&                    params => [qw( year month day hour minute )],
\&                    regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)(\ed\ed)$/,
\&                },
\&                {
\&                    length => 11,
\&                    params => [qw( year month day hour )],
\&                    regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)T(\ed\ed)$/,
\&                },
\&                {
\&                    length => 8,
\&                    params => [qw( year month day )],
\&                    regex  => qr/^(\ed\ed\ed\ed)(\ed\ed)(\ed\ed)$/,
\&                },
\&            ],
\&        },
\&    );
\&
\&    sub _parse_tz {
\&        my %args = @_;
\&        my ( $date, $p ) = @args{qw( input parsed )};
\&        if ( $date =~ s/^TZID=([^:]+):// ) {
\&            $p\->{time_zone} = $1;
\&        }
\&
\&        # Z at end means UTC
\&        elsif ( $date =~ s/Z$// ) {
\&            $p\->{time_zone} = \*(AqUTC\*(Aq;
\&        }
\&        else {
\&            $p\->{time_zone} = \*(Aqfloating\*(Aq;
\&        }
\&        return $date;
\&    }
\&
\&    1;
.Ve
.PP
And that's an ICal parser. The actual DateTime::Format::ICal module also
includes formatting methods and parsing for durations, but Builder doesn't
support those yet. A drop in replacement (at the time of writing the
replacement) can be found in the \fIexamples\fR directory of the Builder
distribution, along with similar variants of other common modules.
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\f(CW\*(C`datetime@perl.org\*(C'\fR mailing list.
.PP
http://datetime.perl.org/
.PP
perl, DateTime, DateTime::Format::Builder
.SH "SUPPORT"
.IX Header "SUPPORT"
Bugs may be submitted at <https://github.com/houseabsolute/DateTime\-Format\-Builder/issues>.
.PP
I am also usually active on \s-1IRC\s0 as 'autarch' on \f(CW\*(C`irc://irc.perl.org\*(C'\fR.
.SH "SOURCE"
.IX Header "SOURCE"
The source code repository for DateTime-Format-Builder can be found at <https://github.com/houseabsolute/DateTime\-Format\-Builder>.
.SH "AUTHORS"
.IX Header "AUTHORS"
.IP "\(bu" 4
Dave Rolsky <autarch@urth.org>
.IP "\(bu" 4
Iain Truskett <spoon@cpan.org>
.SH "COPYRIGHT AND LICENSE"
.IX Header "COPYRIGHT AND LICENSE"
This software is Copyright (c) 2020 by Dave Rolsky.
.PP
This is free software, licensed under:
.PP
.Vb 1
\&  The Artistic License 2.0 (GPL Compatible)
.Ve
.PP
The full text of the license can be found in the
\&\fI\s-1LICENSE\s0\fR file included with this distribution.