From 9002b2b47f284cf0769b045164a3a9c87b6625e2 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 24 2020 09:42:08 +0000 Subject: perl-DateTime-Format-Builder-0.8100 base --- diff --git a/Changes b/Changes new file mode 100644 index 0000000..47b0ca7 --- /dev/null +++ b/Changes @@ -0,0 +1,246 @@ +0.81 2013-04-02 + +- Fixes to pass tests with DateTime 1.00. + + +0.80 2010-03-14 + +- Remove the 00sig.t test, which seemed to cause random failures based on CPAN + testers reports. + +- Lots of cleanup in test code. + +- Future releases will all increment by 0.01 (0.81, 0.82, etc). + +- No code changes. + + +0.7901 2007-09-01 + +- The memory-cycle.t test will fail if you have PadWalker and the + current Devel::Cycle (1.07) installed. I forgot I had patched my + local Devel::Cycle to fix this bug. + + +0.79 2007-08-30 + +- Fix several circular references in + DateTime::Format::Builder::Parser. This would be triggered by any + standard usage of DT::F::Builder. Reported by Carl Franks. RT + #29034. + + +0.7807 2006-05-29 + +- Fix a test failure in on_fail_regex.t on Win32 (bug in the test code). + Reported by Ben Thul. + + +0.7806 2004-09-09 + +- When DateTime::Format::Builder::Parser loaded worker classes, it + ignored modules located under relative paths. This was fixed by using + Class::Factory::Util for this functionality instead. + + +0.7805 2004-11-08 + +- DateTime::Format::Build::Parser caused an exception when run in taint + mode. Reported by Curtis Hawthorne. + + +0.7805 2004-11-08 + +- DateTime::Format::Build::Parser caused an exception when run in taint + mode. Reported by Curtis Hawthorne. + + +0.7804 2004-07-07 + +- No code or doc changes, just change the code in examples to ensure + that PAUSE does not index it. + + +0.7803 2004-02-13 + +- Localize $_ before calling File::Find, otherwise it will be written + over for caller. Patch from Leon Brocard. + + +0.7802 2004-02-13 + +- Add dependency on DateTime::Format::Strptime. + + +0.7801 2004-01-26 + +- New maintainer - Dave Rolsky + +- Switched to a different build/install sysstem (Module::Build instead + of Module::Install) + + +0.78 2003-12-01 + +- Fix multigroup bug in Dispatch + +- Add Tivoli example. + + +0.77 2003-08-14 + +- Correct package name for Quick. + +- Augment license conditions. + +- Fixed timezones in fall.t and quick.t tests. + + +0.76 2003-08-10 + +- Fallthrough example and test added. + +- Quick parser added to simplify fallthrough stuff. + +- Rejigged internals to allow for on_fail argument to multi-parsers. + + +0.75 2003-06-29 + +- Silly MANIFEST.SKIP entry caused dispatch stuff to be skipped. + + +0.74 2003-06-28 + +- Dispatch Parser class added, which allows us to make groups of parsers + and hop quickly to them. + +- length parameter can now be an arrayref, hence we can have a parser + belong to more than one length group. + +- Version numbers are all identical now. + +- Regex Parser can call custom constructors. + +- Wrapped method only regards undef as a failure. + + +0.73 2003-06-24 + +- Exit parser more quickly if able. + +- Multiple same length parsers accepted. + +- Fixed some perl 5.005 testing problems. + + +0.72 2003-05-28 + +- Minor tweaks for the 'private' tag in META.yml + + +0.71 2003-05-23 + +- Massive doc (re)writing. + +- Examples tidied up. + +- Tutorial refactored. + +- Users of generic.pm get to subclass now rather than coderef. + + +0.69 2003-04-28 + +- DateTime::Format::Strptime is now supported. + +- To support the previous item, massive refactoring. + +- Minor API changes that shouldn't affect anyone. + + +0.64 2003-04-27 + +- Callbacks are given 'args' and 'self' keys. + +- Callbacks can be arrays of callbacks. + + +0.62 2003-04-20 + +- Fixed erroneous call to on_fail() + +- Added 'verbose' debugging capability. + +- Added 'constructor' option to create_class/import. + +- Avoid overwriting developers' own new() functions. + +- Complain if asked to overwrite methods. + + +0.60 2003-04-12 + +- Fixed missing '\' from a few '\d' in the docs. + +- Added ICal.pm example. + +- Added import() feature, to save lots of typing. + +- Completely revised documentation. + +- croak is same length is given twice. + +- Assorted refactoring, retouching. + + +0.25 2003-03-29 + +- Minor code cleanups. + +- Bug fix; 'extra' params were being ignored. + +- Doc fix; 'extra' params with 0 are not recommended. + + +0.24 2003-03-25 + +- First release! + +- Split POD into separate file. + +- Preprocess option for method building works. + +- Postprocess option per parser works. + +- Specification lists can now have coderefs in there. + + +0.23 2003-03-25 + +- Improved ancillary files. + +- Added basic use case test for new classes. + +- Assorted API changes. + +- create_class infers package name. + +- also creates specified methods rather than assumed ones. + +- Assorted POD updates. Still not complete. + +- Rejigged to only have an array of hashrefs. Simpler. + +- No inference of length/key stuff. + +- zero is now extra and is thus semantically different. + +- Preliminary pre/post processing per parser provided. + + +0.22 2003-03-21 + +- Initial (pre-actual release) version. + + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4250c8b --- /dev/null +++ b/INSTALL @@ -0,0 +1,44 @@ + +This is the Perl distribution DateTime-Format-Builder. + +Installing DateTime-Format-Builder is straightforward. + +## Installation with cpanm + +If you have cpanm, you only need one line: + + % cpanm DateTime::Format::Builder + +If you are installing into a system-wide directory, you may need to pass the +"-S" flag to cpanm, which uses sudo to install the module: + + % cpanm -S DateTime::Format::Builder + +## Installing with the CPAN shell + +Alternatively, if your CPAN shell is set up, you should just be able to do: + + % cpan DateTime::Format::Builder + +## Manual installation + +As a last resort, you can manually install it. Download the tarball, untar it, +then build it: + + % perl Makefile.PL + % make && make test + +Then install it: + + % make install + +If you are installing into a system-wide directory, you may need to run: + + % sudo make install + +## Documentation + +DateTime-Format-Builder documentation is available as POD. +You can run perldoc from a shell to read the documentation: + + % perldoc DateTime::Format::Builder diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6d717b0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,207 @@ +This software is Copyright (c) 2013 by Dave Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) + + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..75b7efd --- /dev/null +++ b/MANIFEST @@ -0,0 +1,54 @@ +Changes +INSTALL +LICENSE +MANIFEST +META.json +META.yml +Makefile.PL +README +dist.ini +examples/Apache.pm +examples/Fall.pm +examples/ICal.pm +examples/MySQL.pm +examples/Simple.pm +examples/Tivoli.pm +examples/W3CDTF.pm +lib/DateTime/Format/Builder.pm +lib/DateTime/Format/Builder/Parser.pm +lib/DateTime/Format/Builder/Parser/Dispatch.pm +lib/DateTime/Format/Builder/Parser/Quick.pm +lib/DateTime/Format/Builder/Parser/Regex.pm +lib/DateTime/Format/Builder/Parser/Strptime.pm +lib/DateTime/Format/Builder/Parser/generic.pm +lib/DateTime/Format/Builder/Tutorial.pod +t/99pod.t +t/altcon.t +t/basic.t +t/clone.t +t/create.t +t/dispatch.t +t/extra.t +t/fall.t +t/import.t +t/lengths.t +t/memory-cycle.t +t/mergecb.t +t/newclass.t +t/nocon.t +t/noredef.t +t/on_fail.t +t/on_fail_regex.t +t/on_fail_sub.t +t/param.t +t/quick.t +t/release-cpan-changes.t +t/release-eol.t +t/release-no-tabs.t +t/release-pod-linkcheck.t +t/release-pod-syntax.t +t/self.t +t/strptime.t +t/taint.t +t/verbose.t +t/wholeclass.t diff --git a/META.json b/META.json new file mode 100644 index 0000000..1114042 --- /dev/null +++ b/META.json @@ -0,0 +1,68 @@ +{ + "abstract" : "Create DateTime parser classes and objects.", + "author" : [ + "Dave Rolsky ", + "Iain Truskett" + ], + "dynamic_config" : 0, + "generated_by" : "Dist::Zilla version 4.300032, CPAN::Meta::Converter version 2.120921", + "license" : [ + "artistic_2" + ], + "meta-spec" : { + "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", + "version" : "2" + }, + "name" : "DateTime-Format-Builder", + "prereqs" : { + "configure" : { + "requires" : { + "ExtUtils::MakeMaker" : "6.30" + } + }, + "develop" : { + "requires" : { + "Test::Pod" : "1.41" + } + }, + "runtime" : { + "requires" : { + "Carp" : "0", + "Class::Factory::Util" : "1.6", + "DateTime" : "1.00", + "DateTime::Format::Strptime" : "1.04", + "Params::Validate" : "0.72", + "Scalar::Util" : "0", + "base" : "0", + "perl" : "5.005", + "strict" : "0", + "vars" : "0", + "warnings" : "0" + } + }, + "test" : { + "requires" : { + "Test::More" : "0.88" + } + } + }, + "release_status" : "stable", + "resources" : { + "bugtracker" : { + "mailto" : "bug-datetime-format-strptime@rt.cpan.org", + "web" : "http://rt.cpan.org/NoAuth/Bugs.html?Dist=DateTime-Format-Builder" + }, + "repository" : { + "type" : "git", + "url" : "git://git.urth.org/DateTime-Format-Builder.git", + "web" : "http://git.urth.org/DateTime-Format-Builder.git" + } + }, + "version" : "0.81", + "x_contributors" : [ + "(no author) <(no author)@49043108-e40d-0410-ab17-85caa8b5b18d>", + "Daisuke Maki ", + "Ian Truskett " + ] +} + diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..4654c0f --- /dev/null +++ b/META.yml @@ -0,0 +1,36 @@ +--- +abstract: 'Create DateTime parser classes and objects.' +author: + - 'Dave Rolsky ' + - 'Iain Truskett' +build_requires: + Test::More: 0.88 +configure_requires: + ExtUtils::MakeMaker: 6.30 +dynamic_config: 0 +generated_by: 'Dist::Zilla version 4.300032, CPAN::Meta::Converter version 2.120921' +license: artistic_2 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.4.html + version: 1.4 +name: DateTime-Format-Builder +requires: + Carp: 0 + Class::Factory::Util: 1.6 + DateTime: 1.00 + DateTime::Format::Strptime: 1.04 + Params::Validate: 0.72 + Scalar::Util: 0 + base: 0 + perl: 5.005 + strict: 0 + vars: 0 + warnings: 0 +resources: + bugtracker: http://rt.cpan.org/NoAuth/Bugs.html?Dist=DateTime-Format-Builder + repository: git://git.urth.org/DateTime-Format-Builder.git +version: 0.81 +x_contributors: + - '(no author) <(no author)@49043108-e40d-0410-ab17-85caa8b5b18d>' + - 'Daisuke Maki ' + - 'Ian Truskett ' diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..7382d90 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,76 @@ + +use strict; +use warnings; + +use 5.005; + +use ExtUtils::MakeMaker 6.30; + + + +my %WriteMakefileArgs = ( + "ABSTRACT" => "Create DateTime parser classes and objects.", + "AUTHOR" => "Dave Rolsky , Iain Truskett", + "BUILD_REQUIRES" => {}, + "CONFIGURE_REQUIRES" => { + "ExtUtils::MakeMaker" => "6.30" + }, + "DISTNAME" => "DateTime-Format-Builder", + "EXE_FILES" => [], + "LICENSE" => "artistic_2", + "NAME" => "DateTime::Format::Builder", + "PREREQ_PM" => { + "Carp" => 0, + "Class::Factory::Util" => "1.6", + "DateTime" => "1.00", + "DateTime::Format::Strptime" => "1.04", + "Params::Validate" => "0.72", + "Scalar::Util" => 0, + "base" => 0, + "strict" => 0, + "vars" => 0, + "warnings" => 0 + }, + "TEST_REQUIRES" => { + "Test::More" => "0.88" + }, + "VERSION" => "0.81", + "test" => { + "TESTS" => "t/*.t" + } +); + + +unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { + my $tr = delete $WriteMakefileArgs{TEST_REQUIRES}; + my $br = $WriteMakefileArgs{BUILD_REQUIRES}; + for my $mod ( keys %$tr ) { + if ( exists $br->{$mod} ) { + $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod}; + } + else { + $br->{$mod} = $tr->{$mod}; + } + } +} + +unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { + my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; + my $pp = $WriteMakefileArgs{PREREQ_PM}; + for my $mod ( keys %$br ) { + if ( exists $pp->{$mod} ) { + $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; + } + else { + $pp->{$mod} = $br->{$mod}; + } + } +} + +delete $WriteMakefileArgs{CONFIGURE_REQUIRES} + unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; + +WriteMakefile(%WriteMakefileArgs); + + + diff --git a/README b/README new file mode 100644 index 0000000..cd97d06 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ + + +This archive contains the distribution DateTime-Format-Builder, +version 0.81: + + Create DateTime parser classes and objects. + +This software is Copyright (c) 2013 by Dave Rolsky. + +This is free software, licensed under: + + The Artistic License 2.0 (GPL Compatible) + + diff --git a/dist.ini b/dist.ini new file mode 100644 index 0000000..83c1f02 --- /dev/null +++ b/dist.ini @@ -0,0 +1,40 @@ +name = DateTime-Format-Builder +author = Dave Rolsky +author = Iain Truskett +copyright_holder = Dave Rolsky + +version = 0.81 + +[NextRelease] +format = %-8v %{yyyy-MM-dd}d + +[@Basic] + +[InstallGuide] +[MetaJSON] + +[MetaResources] +bugtracker.web = http://rt.cpan.org/NoAuth/Bugs.html?Dist=DateTime-Format-Builder +bugtracker.mailto = bug-datetime-format-strptime@rt.cpan.org +repository.url = git://git.urth.org/DateTime-Format-Builder.git +repository.web = http://git.urth.org/DateTime-Format-Builder.git +repository.type = git + +[SurgicalPodWeaver] + +[PkgVersion] + +[ContributorsFromGit] + +[EOLTests] +[NoTabsTests] +[PodSyntaxTests] +[Test::CPAN::Changes] +[Test::Pod::LinkCheck] +;[Test::Pod::No404s] + +[AutoPrereqs] + +[CheckPrereqsIndexed] + +[@Git] diff --git a/examples/Apache.pm b/examples/Apache.pm new file mode 100644 index 0000000..267b9bd --- /dev/null +++ b/examples/Apache.pm @@ -0,0 +1,51 @@ +# we need to comment this out or PAUSE might index it +# pack age DateTime::Format::Apache; + +use DateTime::Format::Builder ( + parsers => { + parse_datetime => { + strptime => '%e/%b/%Y:%H:%M:%S %z', + + # params => [qw( day month year hour minute second time_zone )], + # regex => qr{ ^ + # (\d+)/(\w{3})/(\d{4}) + # : + # (\d\d):(\d\d):(\d\d) + # \s + # ([+-]\d{4}) + # $ }x, + # postprocess => sub { + # my %args = @_; + # $args{parsed}{month} = month_to_num( $args{parsed}{month} ); + # 1; + # }, + }, + }, +); + +sub month_to_num { + my $wanted = shift; + my %months; + my $lang = DateTime::Language->new( language => 'en' ); + my $i; + $months{$_} = ++$i for @{ $lang->month_abbreviations }; + return $months{$wanted}; +} + +sub format_datetime { + my ( $self, $dt ) = @_; + return $dt->strftime("%e/%b/%Y:%H:%M:%S %z"); +} + +package main; + +my $parser = DateTime::Format::Apache->new(); + +my @dates = ( '27/Feb/2003:19:45:11 -0400', '27/Apr/2003:19:45:11 -0400' ); + +for my $date (@dates) { + my $dt + = $parser->parse_datetime($date)->set_time_zone('Australia/Sydney'); + print "$date => ", $dt->datetime, " => ", $parser->format_datetime($dt), + "\n"; +} diff --git a/examples/Fall.pm b/examples/Fall.pm new file mode 100644 index 0000000..829b795 --- /dev/null +++ b/examples/Fall.pm @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w +use strict; + +=pod + +This example shows a simple fall through parser that tries +a few of the other formatting modules, _then_ fails. + +=cut + +package DateTime::Format::Fall; +use DateTime::Format::HTTP; +use DateTime::Format::Mail; +use DateTime::Format::IBeat; + +use DateTime::Format::Builder ( + parsers => { + parse_datetime => [ + sub { + eval { DateTime::Format::HTTP->parse_datetime( $_[1] ) }; + }, + sub { + eval { DateTime::Format::Mail->parse_datetime( $_[1] ) }; + }, + sub { + eval { DateTime::Format::IBeat->parse_datetime( $_[1] ) }; + }, + ] + } +); + +package main; + +for ( '@d19.07.03 @704', '20030719T155345', 'gibberish' ) { + print DateTime::Format::Fall->parse_datetime($_)->datetime, "\n"; +} diff --git a/examples/ICal.pm b/examples/ICal.pm new file mode 100644 index 0000000..e3b63a5 --- /dev/null +++ b/examples/ICal.pm @@ -0,0 +1,148 @@ +# we need to comment this out or PAUSE might index it +# pack age DateTime::Format::ICal; + +use strict; + +use DateTime; + +# Builder relevant stuff starts here. + +use DateTime::Format::Builder parsers => { + parse_datetime => [ + [ preprocess => \&_parse_tz ], + { + length => 15, + params => [qw( year month day hour minute second )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/, + }, + { + length => 13, + params => [qw( year month day hour minute )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/, + }, + { + length => 11, + params => [qw( year month day hour )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/, + }, + { + length => 8, + params => [qw( year month day )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/, + }, + ], +}; + +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} = 'UTC'; + } + else { + $p->{time_zone} = 'floating'; + } + return $date; +} + +# Builder relevant stuff ends here. + +sub parse_duration { + my ( $self, $dur ) = @_; + + my @units = qw( weeks days hours minutes seconds ); + + $dur =~ m{ ([\+\-])? # Sign + P # 'P' for period? This is our magic character) + (?: + (?:(\d+)W)? # Weeks + (?:(\d+)D)? # Days + )? + (?: T # Time prefix + (?:(\d+)H)? # Hours + (?:(\d+)M)? # Minutes + (?:(\d+)S)? # Seconds + )? + }x; + + my $sign = $1; + + my %units; + $units{weeks} = $2 if defined $2; + $units{days} = $3 if defined $3; + $units{hours} = $4 if defined $4; + $units{minutes} = $5 if defined $5; + $units{seconds} = $6 if defined $6; + + die "Invalid ICal duration string ($dur)\n" + unless %units; + + if ( $sign eq '-' ) { + $_ *= -1 foreach values %units; + } + + return DateTime::Duration->new(%units); +} + +sub format_datetime { + my ( $self, $dt ) = @_; + + my $tz = $dt->time_zone; + + unless ( $tz->is_floating || $tz->is_utc || $tz->name ) { + $dt = $dt->clone->set_time_zone('UTC'); + $tz = $dt->time_zone; + } + + my $base = ( + $dt->hour || $dt->min || $dt->sec + ? sprintf( + '%04d%02d%02dT%02d%02d%02d', + $dt->year, $dt->month, $dt->day, + $dt->hour, $dt->minute, $dt->second + ) + : sprintf( '%04d%02d%02d', $dt->year, $dt->month, $dt->day ) + ); + + return $base if $tz->is_floating; + + return $base . 'Z' if $tz->is_utc; + + return 'TZID=' . $tz->name . ':' . $base; +} + +sub format_duration { + my ( $self, $duration ) = @_; + + die "Cannot represent years or months in an iCal duration\n" + if $duration->delta_months; + + # simple string for 0-length durations + return '+PT0S' + unless $duration->delta_days || $duration->delta_seconds; + + my $ical = $duration->is_positive ? '+' : '-'; + $ical .= 'P'; + + if ( $duration->delta_days ) { + $ical .= $duration->weeks . 'W' if $duration->weeks; + $ical .= $duration->days . 'D' if $duration->days; + } + + if ( $duration->delta_seconds ) { + $ical .= 'T'; + + $ical .= $duration->hours . 'H' if $duration->hours; + $ical .= $duration->minutes . 'M' if $duration->minutes; + $ical .= $duration->seconds . 'S' if $duration->seconds; + } + + return $ical; +} + +1; diff --git a/examples/MySQL.pm b/examples/MySQL.pm new file mode 100644 index 0000000..8789a22 --- /dev/null +++ b/examples/MySQL.pm @@ -0,0 +1,180 @@ +# we need to comment this out or PAUSE might index it +# pack age DateTime::Format::MySQL; + +use strict; + +use DateTime; + +# Builder relevant stuff starts here. + +use DateTime::Format::Builder ( + parsers => { + parse_date => { + params => [qw( year month day )], + regex => qr/^(\d{1,4})-(\d\d)-(\d\d)$/, + }, + + parse_datetime => { + params => [qw( year month day hour minute second )], + regex => qr/^(\d{1,4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/, + extra => { time_zone => 'floating' }, + }, + + parse_timestamp => [ + { + length => 14, + params => [qw( year month day hour minute second )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + }, + { + length => 12, + params => [qw( year month day hour minute second )], + regex => qr/^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + postprocess => \&_fix_2_digit_year, + }, + { + length => 10, + params => [qw( year month day hour minute )], + regex => qr/^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + postprocess => \&_fix_2_digit_year, + }, + { + length => 8, + params => [qw( year month day )], + regex => qr/^(\d\d\d\d)(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + }, + { + length => 6, + params => [qw( year month day )], + regex => qr/^(\d\d)(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + postprocess => \&_fix_2_digit_year, + }, + { + length => 4, + params => [qw( year month )], + regex => qr/^(\d\d)(\d\d)$/, + extra => { time_zone => 'floating' }, + postprocess => \&_fix_2_digit_year, + }, + { + length => 2, + params => [qw( year )], + regex => qr/^(\d\d)$/, + extra => { time_zone => 'floating' }, + postprocess => \&_fix_2_digit_year, + }, + ], + }, +); + +sub _fix_2_digit_year { + my %p = @_; + + $p{parsed}{year} += $p{parsed}{year} <= 69 ? 2000 : 1900; +} + +# Builder relevant stuff ends here. + +sub format_date { + my ( $self, $dt ) = @_; + + return $dt->ymd('-'); +} + +sub format_time { + my ( $self, $dt ) = @_; + + return $dt->hms(':'); +} + +sub format_datetime { + my ( $self, $dt ) = @_; + + return $self->format_date($dt) . ' ' . $self->format_time($dt); +} + +1; + +__END__ + +=head1 NAME + +DateTime::Format::MySQL - Parse and format MySQL dates and times + +=head1 SYNOPSIS + + use DateTime::Format::MySQL; + + my $dt = DateTime::Format::MySQL->parse_datetime( '2003-01-16 23:12:01' ); + + # 2003-01-16 23:12:01 + DateTime::Format::MySQL->format_datetime($dt); + +=head1 DESCRIPTION + +This module understands the formats used by MySQL for its DATE, +DATETIME, TIME, and TIMESTAMP data types. It can be used to parse +these formats in order to create DateTime objects, and it can take a +DateTime object and produce a string representing it in the MySQL +format. + +=head1 METHODS + +This class offers the following methods. All of the parsing methods +set the returned DateTime object's time zone to the floating time +zone, because MySQL does not provide time zone information. + +=over 4 + +=item * parse_datetime($string) + +=item * parse_date($string) + +=item * parse_timestamp($string) + +Given a value of the appropriate type, this method will return a new +C object. + +If given an improperly formatted string, this method may die. + +=item * format_date($datetime) + +=item * format_time($datetime) + +=item * format_datetime($datetime) + +Given a C object, this methods returns an appropriately +formatted string. + +=back + +=head1 SUPPORT + +Support for this module is provided via the datetime@perl.org email +list. See http://lists.perl.org/ for more details. + +=head1 AUTHOR + +Dave Rolsky + +=head1 COPYRIGHT + +Copyright (c) 2003 David Rolsky. All rights reserved. This program +is free software; you can redistribute it and/or modify it under the +same terms as Perl itself. + +The full text of the license can be found in the LICENSE file included +with this module. + +=head1 SEE ALSO + +datetime@perl.org mailing list + +http://datetime.perl.org/ + +=cut diff --git a/examples/Simple.pm b/examples/Simple.pm new file mode 100644 index 0000000..aa85a2f --- /dev/null +++ b/examples/Simple.pm @@ -0,0 +1,30 @@ +package DateTime::Format::Simple; + +use DateTime::Format::Builder ( + parsers => { + parse_datetime => [ + { + params => [qw( year month mday hours mins secs fsecs ampm )], + regex => qr[^ + (\d{4}) \s*-?\s* (\d{2}) \s*-?\s* (\d{2}) + \s* + (?:-?\s* (\d{1,2}) :? (\d{2}) (?::? (\d{2}) )? )? + (?:\. (\d+) ) ? # fsecs + (?:\s* ([aApP]\.?[mM]\.?) )? + $ + ]x, + }, + { + # mm/dd/yyyy, mm-dd-yyyy, [hh:mm[:ss[.nnn]]] [am/pm] + params => [qw( month mday year hours mins secs fsecs ampm )], + regex => qr#^ + (\d{1,2})[-/](\d{1,2})[-/](\d{4}) + (?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)? + (?:\.(\d+))? + (?:\s*([aApP]\.?[mM]\.?))? + $ + #x + }, + ] + } +); diff --git a/examples/Tivoli.pm b/examples/Tivoli.pm new file mode 100644 index 0000000..ca6c539 --- /dev/null +++ b/examples/Tivoli.pm @@ -0,0 +1,29 @@ +# we need to comment this out or PAUSE might index it +# pack age DateTime::Format::Tivoli; + +use DateTime::Format::Builder ( + parsers => { + parse_datetime => { + strptime => '%h %e %k:%M:%S %Y', + }, + }, +); + +sub format_datetime { + my ( $self, $dt ) = @_; + my $z = $dt->clone->set_time_zone('GMT'); + return $z->strftime('%h %e %k:%M:%S %Y'); +} + +package main; + +my $parser = DateTime::Format::Tivoli->new(); + +my @dates = ( 'Nov 5 22:49:45 2003', '27/Apr/2003:19:45:11 -0400' ); + +for my $date (@dates) { + my $dt + = $parser->parse_datetime($date)->set_time_zone('Australia/Sydney'); + print "$date => ", $dt->datetime, " => ", $parser->format_datetime($dt), + "\n"; +} diff --git a/examples/W3CDTF.pm b/examples/W3CDTF.pm new file mode 100644 index 0000000..17d05be --- /dev/null +++ b/examples/W3CDTF.pm @@ -0,0 +1,190 @@ +# we need to comment this out or PAUSE might index it +# pack age DateTime::Format::W3CDTF; + +use strict; + +use DateTime::Format::Builder ( + parsers => { + parse_datetime => [ + [ preprocess => \&_parse_tz ], + { + params => [qw( year month day hour minute second)], + regex => + qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.(\d\d)$/, + length => 22, + }, + { + params => [qw( year month day hour minute second)], + regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)$/, + length => 19, + }, + { + params => [qw( year month day hour minute)], + regex => qr/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/, + length => 16, + }, + { + params => [qw( year month day )], + regex => qr/^(\d{4})-(\d\d)-(\d\d)$/, + length => 10, + }, + { + params => [qw( year month )], + regex => qr/^(\d{4})-(\d\d)$/, + length => 7, + extra => { day => 1 }, + }, + { + params => [qw( year )], + regex => qr/^(\d\d\d\d)$/, + length => 4, + extra => { month => 1, day => 1 } + } + ] + } +); + +sub _parse_tz { + my %args = @_; + my ( $date, $p ) = @args{qw( input parsed )}; + if ( $date =~ s/([+-]\d\d:\d\d)$// ) { + $p->{time_zone} = $1; + } + + # Z at end means UTC + elsif ( $date =~ s/Z$// ) { + $p->{time_zone} = 'UTC'; + } + else { + $p->{time_zone} = 'floating'; + } + return $date; +} + +sub format_datetime { + my ( $self, $dt ) = @_; + + my $base = ( + $dt->hour || $dt->min || $dt->sec + ? sprintf( + '%04d-%02d-%02dT%02d:%02d:%02d', + $dt->year, $dt->month, $dt->day, + $dt->hour, $dt->minute, $dt->second + ) + : sprintf( '%04d-%02d-%02d', $dt->year, $dt->month, $dt->day ) + ); + + my $tz = $dt->time_zone; + + return $base if $tz->is_floating; + + # if there is a time component + if ( $dt->hour || $dt->min || $dt->sec ) { + return $base . 'Z' if $tz->is_utc; + + if ( $tz->{'offset'} ) { + return $base . offset_as_string( $tz->{'offset'} ); + } + } + else { + return $base; + } +} + +# minor offset_as_string variant w/ : +# +sub offset_as_string { + my $offset = shift; + + return undef unless defined $offset; + + my $sign = $offset < 0 ? '-' : '+'; + + my $hours = $offset / ( 60 * 60 ); + $hours = abs($hours) % 24; + + my $mins = ( $offset % ( 60 * 60 ) ) / 60; + + my $secs = $offset % 60; + + return ( + $secs + ? sprintf( '%s%02d:%02d:%02d', $sign, $hours, $mins, $secs ) + : sprintf( '%s%02d:%02d', $sign, $hours, $mins ) + ); +} + +1; + +__END__ + +=head1 NAME + +DateTime::Format::W3CDTF - Parse and format W3CDTF datetime strings + +=head1 SYNOPSIS + + use DateTime::Format::W3CDTF; + + my $f = DateTime::Format::W3CDTF->new; + my $dt = $f->parse_datetime( '2003-02-15T13:50:05-05:00' ); + + # 2003-02-15T13:50:05-05:00 + $f->format_datetime($dt); + +=head1 DESCRIPTION + +This module understands the W3CDTF date/time format, an ISO 8601 profile, +defined at http://www.w3.org/TR/NOTE-datetime. This format as the native +date format of RSS 1.0. + +It can be used to parse these formats in order to create the appropriate +objects. + +=head1 METHODS + +This API is currently experimental and may change in the future. + +=over 4 + +=item * parse_datetime($string) + +Given a W3CDTF datetime string, this method will return a new +C object. + +If given an improperly formatted string, this method may die. + +=item * format_datetime($datetime) + +Given a C object, this methods returns a W3CDTF datetime +string. + +=back + +=head1 SUPPORT + +Support for this module is provided via the datetime@perl.org email +list. See http://lists.perl.org/ for more details. + +=head1 AUTHOR + +Kellan Elliott-McCrea + +This module was inspired by C + +=head1 COPYRIGHT + +Copyright (c) 2003 Kellan Elliott-McCrea. All rights reserved. This program +is free software; you can redistribute it and/or modify it under the +same terms as Perl itself. + +The full text of the license can be found in the LICENSE file included +with this module. + +=head1 SEE ALSO + +datetime@perl.org mailing list + +http://datetime.perl.org/ + +=cut diff --git a/lib/DateTime/Format/Builder.pm b/lib/DateTime/Format/Builder.pm new file mode 100644 index 0000000..7d27020 --- /dev/null +++ b/lib/DateTime/Format/Builder.pm @@ -0,0 +1,937 @@ +package DateTime::Format::Builder; +{ + $DateTime::Format::Builder::VERSION = '0.81'; +} + +use strict; +use warnings; + +use 5.005; +use Carp; +use DateTime 1.00; +use Params::Validate 0.72 qw( + validate SCALAR ARRAYREF HASHREF SCALARREF CODEREF GLOB GLOBREF UNDEF +); +use vars qw( %dispatch_data ); + +my $parser = 'DateTime::Format::Builder::Parser'; + +sub verbose { + warn "Use of verbose() deprecated for the interim."; + 1; +} + +sub import { + my $class = shift; + $class->create_class( @_, class => (caller)[0] ) if @_; +} + +sub create_class { + my $class = shift; + my %args = validate( + @_, + { + class => { type => SCALAR, default => (caller)[0] }, + version => { type => SCALAR, optional => 1 }, + verbose => { type => SCALAR | GLOBREF | GLOB, optional => 1 }, + parsers => { type => HASHREF }, + groups => { type => HASHREF, optional => 1 }, + constructor => + { type => UNDEF | SCALAR | CODEREF, optional => 1 }, + } + ); + + verbose( $args{verbose} ) if exists $args{verbose}; + + my $target = $args{class}; # where we're writing our methods and such. + + # Create own lovely new package + { + no strict 'refs'; + + ${"${target}::VERSION"} = $args{version} if exists $args{version}; + + $class->create_constructor( + $target, exists $args{constructor}, + $args{constructor} + ); + + # Turn groups of parser specs in to groups of parsers + { + my $specs = $args{groups}; + my %groups; + + for my $label ( keys %$specs ) { + my $parsers = $specs->{$label}; + my $code = $class->create_parser($parsers); + $groups{$label} = $code; + } + + $dispatch_data{$target} = \%groups; + } + + # Write all our parser methods, creating parsers as we go. + while ( my ( $method, $parsers ) = each %{ $args{parsers} } ) { + my $globname = $target . "::$method"; + croak "Will not override a preexisting method $method()" + if defined &{$globname}; + *$globname = $class->create_end_parser($parsers); + } + } + +} + +sub create_constructor { + my $class = shift; + my ( $target, $intended, $value ) = @_; + + my $new = $target . "::new"; + $value = 1 unless $intended; + + return unless $value; + return if not $intended and defined &$new; + croak "Will not override a preexisting constructor new()" + if defined &$new; + + no strict 'refs'; + + return *$new = $value if ref $value eq 'CODE'; + return *$new = sub { + my $class = shift; + croak "${class}->new takes no parameters." if @_; + + my $self = bless {}, ref($class) || $class; + + # If called on an object, clone, but we've nothing to + # clone + + $self; + }; +} + +sub create_parser { + my $class = shift; + my @common = ( maker => $class ); + if ( @_ == 1 ) { + my $parsers = shift; + my @parsers = ( + ( ref $parsers eq 'HASH' ) + ? %$parsers + : ( ( ref $parsers eq 'ARRAY' ) ? @$parsers : $parsers ) + ); + $parser->create_parser( \@common, @parsers ); + } + else { + $parser->create_parser( \@common, @_ ); + } +} + + +sub create_end_parser { + my ( $class, $parsers ) = @_; + $class->create_method( $class->create_parser($parsers) ); +} + +sub create_method { + my ( $class, $parser ) = @_; + return sub { + my $self = shift; + $parser->parse( $self, @_ ); + } +} + +sub on_fail { + my ( $class, $input ) = @_; + + my $pkg; + my $i = 0; + while ( ($pkg) = caller( $i++ ) ) { + last + if ( !UNIVERSAL::isa( $pkg, 'DateTime::Format::Builder' ) + && !UNIVERSAL::isa( $pkg, 'DateTime::Format::Builder::Parser' ) ); + } + local $Carp::CarpLevel = $i; + croak "Invalid date format: $input"; +} + +sub new { + my $class = shift; + croak "Constructor 'new' takes no parameters" if @_; + my $self = bless { + parser => sub { croak "No parser set." } + }, + ref($class) || $class; + if ( ref $class ) { + + # If called on an object, clone + $self->set_parser( $class->get_parser ); + + # and that's it. we don't store that much info per object + } + return $self; +} + +sub parser { + my $class = shift; + my $parser = $class->create_end_parser( \@_ ); + + # Do we need to instantiate a new object for return, + # or are we modifying an existing object? + my $self; + $self = ref $class ? $class : $class->new(); + + $self->set_parser($parser); + + $self; +} + +sub clone { + my $self = shift; + croak "Calling object method as class method!" unless ref $self; + return $self->new(); +} + +sub set_parser { + my ( $self, $parser ) = @_; + croak "set_parser given something other than a coderef" + unless $parser + and ref $parser eq 'CODE'; + $self->{parser} = $parser; + $self; +} + +sub get_parser { + my ($self) = @_; + return $self->{parser}; +} + +sub parse_datetime { + my $self = shift; + croak "parse_datetime is an object method, not a class method." + unless ref $self and $self->isa(__PACKAGE__); + croak "No date specified." unless @_; + return $self->{parser}->( $self, @_ ); +} + +sub format_datetime { + croak __PACKAGE__ . "::format_datetime not implemented."; +} + +require DateTime::Format::Builder::Parser; + +1; + +# ABSTRACT: Create DateTime parser classes and objects. + +__END__ + +=pod + +=head1 NAME + +DateTime::Format::Builder - Create DateTime parser classes and objects. + +=head1 VERSION + +version 0.81 + +=head1 SYNOPSIS + + package DateTime::Format::Brief; + + use DateTime::Format::Builder + ( + parsers => { + parse_datetime => [ + { + regex => qr/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/, + params => [qw( year month day hour minute second )], + }, + { + regex => qr/^(\d{4})(\d\d)(\d\d)$/, + params => [qw( year month day )], + }, + ], + } + ); + +=head1 DESCRIPTION + +DateTime::Format::Builder creates DateTime parsers. +Many string formats of dates and times are simple and just +require a basic regular expression to extract the relevant +information. Builder provides a simple way to do this +without writing reams of structural code. + +Builder provides a number of methods, most of which you'll +never need, or at least rarely need. They're provided more +for exposing of the module's innards to any subclasses, or +for when you need to do something slightly beyond what I +expected. + +This creates the end methods. Coderefs die on bad parses, +return C objects on good parse. + +=head1 TUTORIAL + +See L. + +=head1 ERROR HANDLING AND BAD PARSES + +Often, I will speak of C being returned, however +that's not strictly true. + +When a simple single specification is given for a method, +the method isn't given a single parser directly. It's given +a wrapper that will call C if the single parser +returns C. The single parser must return C so +that a multiple parser can work nicely and actual errors can +be thrown from any of the callbacks. + +Similarly, any multiple parsers will only call C +right at the end when it's tried all it could. + +C (see L) is defined, by default, +to throw an error. + +Multiple parser specifications can also specify C +with a coderef as an argument in the options block. This +will take precedence over the inheritable and over-ridable +method. + +That said, don't throw real errors from callbacks in +multiple parser specifications unless you really want +parsing to stop right there and not try any other parsers. + +In summary: calling a B will result in either a +C object being returned or an error being thrown +(unless you've overridden C or +C, or you've specified a C key to +a multiple parser specification). + +Individual B (be they multiple parsers or single +parsers) will return either the C object or +C. + +=head1 SINGLE SPECIFICATIONS + +A single specification is a hash ref of instructions +on how to create a parser. + +The precise set of keys and values varies according to parser +type. There are some common ones though: + +=over 4 + +=item * + +B is an optional parameter that can be used to +specify that this particular I is only applicable to +strings of a certain fixed length. This can be used to make +parsers more efficient. It's strongly recommended that any +parser that can use this parameter does. + +You may happily specify the same length twice. The parsers +will be tried in order of specification. + +You can also specify multiple lengths by giving it an +arrayref of numbers rather than just a single scalar. +If doing so, please keep the number of lengths to a minimum. + +If any specifications without Is are given and the +particular I parser fails, then the non-I +parsers are tried. + +This parameter is ignored unless the specification is part +of a multiple parser specification. + +=item * + +B