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 version 0.82
10
12 As most people who are writing modules know, you start a package with a
13 package declaration and some indication of module version:
14
15 package DateTime::Format::ICal;
16 our $VERSION = '0.04';
17
18 After that, you call Builder with some options. There are only a few
19 (detailed later). Right now, we're only interested in parsers.
20
21 use DateTime::Format::Builder
22 (
23 parsers => {
24 ...
25 }
26 );
27
28 The parsers option takes a reference to a hash of method names and
29 specifications:
30
31 parsers => {
32 parse_datetime => ... ,
33 parse_datetime_with_timezone => ... ,
34 ...
35 }
36
37 Builder will create methods in your class, each method being a parser
38 that follows the given specifications. It is strongly recommended that
39 one method is called parse_datetime, be it a Builder created method or
40 one of your own.
41
42 In addition to creating any of the parser methods it also creates a
43 "new()" method that can instantiate (or clone) objects of this class.
44 This behaviour can be modified with the constructor option, but we
45 don't need to know that yet.
46
47 Each value corresponding to a method name in the parsers list is either
48 a single specification, or a list of specifications. We'll start with
49 the simple case.
50
51 parse_briefdate => {
52 params => [ qw( year month day ) ],
53 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
54 },
55
56 This will result in a method named parse_briefdate which will take
57 strings in the form 20040716 and return DateTime objects representing
58 that date. A user of the class might write:
59
60 use DateTime::Format::ICal;
61 my $date = "19790716";
62 my $dt = DateTime::Format::ICal->parse_briefdate( $date );
63 print "My birth month is ", $dt->month_name, "\n";
64
65 The "regex" is applied to the input string, and if it matches, then $1,
66 $2, ... are mapped to the params given and handed to "DateTime->new()".
67 Essentially:
68
69 my $rv = DateTime->new( year => $1, month => $2, day => $3 );
70
71 There are more complicated things one can do within a single
72 specification, but we'll cover those later.
73
74 Often, you'll want a method to be able to take one string, and run it
75 against multiple parser specifications. It would be very irritating if
76 the user had to work out what format the datetime string was in and
77 then which method was most appropriate.
78
79 So, Builder lets you specify multiple specifications:
80
81 parse_datetime => [
82 {
83 params => [ qw( year month day hour minute second ) ],
84 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
85 },
86 {
87 params => [ qw( year month day hour minute ) ],
88 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
89 },
90 {
91 params => [ qw( year month day hour ) ],
92 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
93 },
94 {
95 params => [ qw( year month day ) ],
96 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
97 },
98 ],
99
100 It's an arrayref of specifications. A parser will be created that will
101 try each of these specifications sequentially, in the order you
102 specified.
103
104 There's a flaw with this though. In this example, we're building a
105 parser for ICal datetimes. One can place a timezone id at the start of
106 an ICal datetime. You might extract such an id with the following code:
107
108 if ( $date =~ s/^TZID=([^:]+):// )
109 {
110 $time_zone = $1;
111 }
112 # Z at end means UTC
113 elsif ( $date =~ s/Z$// )
114 {
115 $time_zone = 'UTC';
116 }
117 else
118 {
119 $time_zone = 'floating';
120 }
121
122 $date would end up without the id, and $time_zone would contain
123 something appropriate to give to DateTime's set_time_zone method, or
124 time_zone argument.
125
126 But how to get this scrap of code into your parser? You might be
127 tempted to call the parser something else and build a small wrapper.
128 There's no need though because an option is provided for preprocessing
129 dates:
130
131 parse_datetime => [
132 [ preprocess => \&_parse_tz ], # Only changed line!
133 {
134 params => [ qw( year month day hour minute second ) ],
135 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
136 },
137 {
138 params => [ qw( year month day hour minute ) ],
139 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
140 },
141 {
142 params => [ qw( year month day hour ) ],
143 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
144 },
145 {
146 params => [ qw( year month day ) ],
147 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
148 },
149 ],
150
151 It will necessitate _parse_tz to be written, and that routine looks
152 like this:
153
154 sub _parse_tz
155 {
156 my %args = @_;
157 my ($date, $p) = @args{qw( input parsed )};
158 if ( $date =~ s/^TZID=([^:]+):// )
159 {
160 $p->{time_zone} = $1;
161 }
162 # Z at end means UTC
163 elsif ( $date =~ s/Z$// )
164 {
165 $p->{time_zone} = 'UTC';
166 }
167 else
168 {
169 $p->{time_zone} = 'floating';
170 }
171 return $date;
172 }
173
174 On input it is given a hash containing two items: the input date and a
175 hashref that will be used in the parsing. The return value from the
176 routine is what the parser specifications will run against, and
177 anything in the parsed hash ($p in the example) will be put in the call
178 to "DateTime->new(...)".
179
180 So, we now have a happily working ICal parser. It parses the assorted
181 formats, and can also handle timezones. Is there anything else it needs
182 to do? No. But we can make it work more efficiently.
183
184 At present, the specifications are tested sequentially. However, each
185 one applies to strings of particular lengths. Thus we could be
186 efficient and have the parser only test the given strings against a
187 parser that handles that string length. Again, Builder makes it easy:
188
189 parse_datetime => [
190 [ preprocess => \&_parse_tz ],
191 {
192 length => 15, # We handle strings of exactly 15 chars
193 params => [ qw( year month day hour minute second ) ],
194 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
195 },
196 {
197 length => 13, # exactly 13 chars...
198 params => [ qw( year month day hour minute ) ],
199 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
200 },
201 {
202 length => 11, # 11..
203 params => [ qw( year month day hour ) ],
204 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
205 },
206 {
207 length => 8, # yes.
208 params => [ qw( year month day ) ],
209 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
210 },
211 ],
212
213 Now the created parser will create a parser that only runs
214 specifications against appropriate strings.
215
216 So our complete code looks like:
217
218 package DateTime::Format::ICal;
219 use strict;
220 our $VERSION = '0.04';
221
222 use DateTime::Format::Builder
223 (
224 parsers => {
225 parse_datetime => [
226 [ preprocess => \&_parse_tz ],
227 {
228 length => 15,
229 params => [ qw( year month day hour minute second ) ],
230 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
231 },
232 {
233 length => 13,
234 params => [ qw( year month day hour minute ) ],
235 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
236 },
237 {
238 length => 11,
239 params => [ qw( year month day hour ) ],
240 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
241 },
242 {
243 length => 8,
244 params => [ qw( year month day ) ],
245 regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
246 },
247 ],
248 },
249 );
250
251 sub _parse_tz
252 {
253 my %args = @_;
254 my ($date, $p) = @args{qw( input parsed )};
255 if ( $date =~ s/^TZID=([^:]+):// )
256 {
257 $p->{time_zone} = $1;
258 }
259 # Z at end means UTC
260 elsif ( $date =~ s/Z$// )
261 {
262 $p->{time_zone} = 'UTC';
263 }
264 else
265 {
266 $p->{time_zone} = 'floating';
267 }
268 return $date;
269 }
270
271 1;
272
273 And that's an ICal parser. The actual DateTime::Format::ICal module
274 also includes formatting methods and parsing for durations, but Builder
275 doesn't support those yet. A drop in replacement (at the time of
276 writing the replacement) can be found in the examples directory of the
277 Builder distribution, along with similar variants of other common
278 modules.
279
281 Copyright (C) Iain Truskett, 2003. All rights reserved.
282
283 You can redistribute this document and/or modify it under the same
284 terms as Perl itself.
285
286 The full text of the licenses can be found in the Artistic and COPYING
287 files included with this document.
288
290 "datetime@perl.org" mailing list.
291
292 http://datetime.perl.org/
293
294 perl, DateTime, DateTime::Format::Builder
295
297 Any errors you see in this document, please log them with CPAN RT
298 system via the web or email:
299
300 http://perl.dellah.org/rt/dtbuilder
301 bug-datetime-format-builder@rt.cpan.org
302
303 This makes it much easier for me to track things and thus means your
304 problem is less likely to be neglected.
305
306 Bugs may be submitted at
307 <http://rt.cpan.org/Public/Dist/Display.html?Name=DateTime-Format-Builder>
308 or via email to bug-datetime-format-builder@rt.cpan.org <mailto:bug-
309 datetime-format-builder@rt.cpan.org>.
310
311 I am also usually active on IRC as 'autarch' on "irc://irc.perl.org".
312
314 The source code repository for DateTime-Format-Builder can be found at
315 <https://github.com/houseabsolute/DateTime-Format-Builder>.
316
318 · Dave Rolsky <autarch@urth.org>
319
320 · Iain Truskett
321
323 This software is Copyright (c) 2019 by Dave Rolsky.
324
325 This is free software, licensed under:
326
327 The Artistic License 2.0 (GPL Compatible)
328
329 The full text of the license can be found in the LICENSE file included
330 with this distribution.
331
332
333
334perl v5.30.0 2019-07D-a2t6eTime::Format::Builder::Tutorial(3)