1DateTime::Format::BuildUesre:r:TCuotnotrriiablu(t3e)d PeDralteDToicmuem:e:nFtoartmiaotn::Builder::Tutorial(3)
2
3
4

NAME

6       DateTime::Format::Builder::Tutorial - Quick class on using Builder
7

CREATING A CLASS

9       As most people who are writing modules know, you start a package with a
10       package declaration and some indication of module version:
11
12           package DateTime::Format::ICal;
13           our $VERSION = '0.04';
14
15       After that, you call Builder with some options. There are only a few
16       (detailed later). Right now, we're only interested in parsers.
17
18           use DateTime::Format::Builder
19           (
20               parsers => {
21               ...
22               }
23           );
24
25       The parsers option takes a reference to a hash of method names and
26       specifications:
27
28               parsers => {
29                   parse_datetime => ... ,
30                   parse_datetime_with_timezone => ... ,
31                   ...
32               }
33
34       Builder will create methods in your class, each method being a parser
35       that follows the given specifications. It is strongly recommended that
36       one method is called parse_datetime, be it a Builder created method or
37       one of your own.
38
39       In addition to creating any of the parser methods it also creates a
40       "new()" method that can instantiate (or clone) objects of this class.
41       This behaviour can be modified with the constructor option, but we
42       don't need to know that yet.
43
44       Each value corresponding to a method name in the parsers list is either
45       a single specification, or a list of specifications. We'll start with
46       the simple case.
47
48               parse_briefdate => {
49                   params => [ qw( year month day ) ],
50                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
51               },
52
53       This will result in a method named parse_briefdate which will take
54       strings in the form 20040716 and return DateTime objects representing
55       that date. A user of the class might write:
56
57           use DateTime::Format::ICal;
58           my $date = "19790716";
59           my $dt = DateTime::Format::ICal->parse_briefdate( $date );
60           print "My birth month is ", $dt->month_name, "\n";
61
62       The "regex" is applied to the input string, and if it matches, then $1,
63       $2, ... are mapped to the params given and handed to "DateTime->new()".
64       Essentially:
65
66           my $rv = DateTime->new( year => $1, month => $2, day => $3 );
67
68       There are more complicated things one can do within a single specifica‐
69       tion, but we'll cover those later.
70
71       Often, you'll want a method to be able to take one string, and run it
72       against multiple parser specifications. It would be very irritating if
73       the user had to work out what format the datetime string was in and
74       then which method was most appropriate.
75
76       So, Builder lets you specify multiple specifications:
77
78           parse_datetime => [
79               {
80                   params => [ qw( year month day hour minute second ) ],
81                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
82               },
83               {
84                   params => [ qw( year month day hour minute ) ],
85                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
86               },
87               {
88                   params => [ qw( year month day hour ) ],
89                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
90               },
91               {
92                   params => [ qw( year month day ) ],
93                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
94               },
95           ],
96
97       It's an arrayref of specifications. A parser will be created that will
98       try each of these specifications sequentially, in the order you speci‐
99       fied.
100
101       There's a flaw with this though. In this example, we're building a
102       parser for ICal datetimes. One can place a timezone id at the start of
103       an ICal datetime. You might extract such an id with the following code:
104
105           if ( $date =~ s/^TZID=([^:]+):// )
106           {
107               $time_zone = $1;
108           }
109           # Z at end means UTC
110           elsif ( $date =~ s/Z$// )
111           {
112               $time_zone = 'UTC';
113           }
114           else
115           {
116               $time_zone = 'floating';
117           }
118
119       $date would end up without the id, and $time_zone would contain some‐
120       thing appropriate to give to DateTime's set_time_zone method, or
121       time_zone argument.
122
123       But how to get this scrap of code into your parser? You might be
124       tempted to call the parser something else and build a small wrapper.
125       There's no need though because an option is provided for preprocesing
126       dates:
127
128           parse_datetime => [
129               [ preprocess => \&_parse_tz ], # Only changed line!
130               {
131                   params => [ qw( year month day hour minute second ) ],
132                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
133               },
134               {
135                   params => [ qw( year month day hour minute ) ],
136                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
137               },
138               {
139                   params => [ qw( year month day hour ) ],
140                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
141               },
142               {
143                   params => [ qw( year month day ) ],
144                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
145               },
146           ],
147
148       It will necessitate _parse_tz to be written, and that routine looks
149       like this:
150
151           sub _parse_tz
152           {
153               my %args = @_;
154               my ($date, $p) = @args{qw( input parsed )};
155               if ( $date =~ s/^TZID=([^:]+):// )
156               {
157                   $p->{time_zone} = $1;
158               }
159               # Z at end means UTC
160               elsif ( $date =~ s/Z$// )
161               {
162                   $p->{time_zone} = 'UTC';
163               }
164               else
165               {
166                   $p->{time_zone} = 'floating';
167               }
168               return $date;
169           }
170
171       On input it is given a hash containing two items: the input date and a
172       hashref that will be used in the parsing. The return value from the
173       routine is what the parser specifications will run against, and any‐
174       thing in the parsed hash ($p in the example) will be put in the call to
175       "DateTime->new(...)".
176
177       So, we now have a happily working ICal parser. It parses the assorted
178       formats, and can also handle timezones. Is there anything else it needs
179       to do? No. But we can make it work more efficiently.
180
181       At present, the specifications are tested sequentially.  However, each
182       one applies to strings of particular lengths.  Thus we could be effi‐
183       cient and have the parser only test the given strings against a parser
184       that handles that string length. Again, Builder makes it easy:
185
186           parse_datetime => [
187               [ preprocess => \&_parse_tz ],
188               {
189                   length => 15, # We handle strings of exactly 15 chars
190                   params => [ qw( year month day hour minute second ) ],
191                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
192               },
193               {
194                   length => 13, # exactly 13 chars...
195                   params => [ qw( year month day hour minute ) ],
196                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
197               },
198               {
199                   length => 11, # 11..
200                   params => [ qw( year month day hour ) ],
201                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
202               },
203               {
204                   length => 8, # yes.
205                   params => [ qw( year month day ) ],
206                   regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
207               },
208               ],
209
210       Now the created parser will create a parser that only runs specifica‐
211       tions against appropriate strings.
212
213       So our complete code looks like:
214
215           package DateTime::Format::ICal;
216           use strict;
217           our $VERSION = '0.04';
218
219           use DateTime::Format::Builder
220           (
221               parsers => {
222                   parse_datetime => [
223                   [ preprocess => \&_parse_tz ],
224                   {
225                       length => 15,
226                       params => [ qw( year month day hour minute second ) ],
227                       regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
228                   },
229                   {
230                       length => 13,
231                       params => [ qw( year month day hour minute ) ],
232                       regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
233                   },
234                   {
235                       length => 11,
236                       params => [ qw( year month day hour ) ],
237                       regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
238                   },
239                   {
240                       length => 8,
241                       params => [ qw( year month day ) ],
242                       regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
243                   },
244                   ],
245               },
246           );
247
248           sub _parse_tz
249           {
250               my %args = @_;
251               my ($date, $p) = @args{qw( input parsed )};
252               if ( $date =~ s/^TZID=([^:]+):// )
253               {
254                   $p->{time_zone} = $1;
255               }
256               # Z at end means UTC
257               elsif ( $date =~ s/Z$// )
258               {
259                   $p->{time_zone} = 'UTC';
260               }
261               else
262               {
263                   $p->{time_zone} = 'floating';
264               }
265               return $date;
266           }
267
268           1;
269
270       And that's an ICal parser. The actual DateTime::Format::ICal module
271       also includes formatting methods and parsing for durations, but Builder
272       doesn't support those yet. A drop in replacement (at the time of writ‐
273       ing the replacement) can be found in the examples directory of the
274       Builder distribution, along with similar variants of other common mod‐
275       ules.
276

SUPPORT

278       Any errors you see in this document, please log them with CPAN RT sys‐
279       tem via the web or email:
280
281           http://perl.dellah.org/rt/dtbuilder
282           bug-datetime-format-builder@rt.cpan.org
283
284       This makes it much easier for me to track things and thus means your
285       problem is less likely to be neglected.
286
288       Copyright (C) Iain Truskett, 2003. All rights reserved.
289
290       You can redistribute this document and/or modify it under the same
291       terms as Perl itself.
292
293       The full text of the licenses can be found in the Artistic and COPYING
294       files included with this document.
295

AUTHOR

297       Iain Truskett <spoon@cpan.org>
298

SEE ALSO

300       "datetime@perl.org" mailing list.
301
302       http://datetime.perl.org/
303
304       perl, DateTime, DateTime::Format::Builder
305
306
307
308perl v5.8.8                       2008-02D-a0t1eTime::Format::Builder::Tutorial(3)
Impressum