1DateTime::Format::BuildUesre:r:TCuotnotrriiablu(t3e)d PeDralteDToicmuem:e:nFtoartmiaotn::Builder::Tutorial(3)
2
3
4
6 DateTime::Format::Builder::Tutorial - Quick class on using Builder
7
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
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
297 Iain Truskett <spoon@cpan.org>
298
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)