5. Add Quick Reference section after NAME
6. Preserve all technical info
7. No XSS - escape bare < and >
Let me identify the sections:
- NAME
- VERSION
- SYNOPSIS
- DESCRIPTION
- TUTORIAL
- ERROR HANDLING AND BAD PARSES
- SINGLE SPECIFICATIONS (with sub-sections: length, label, on_match and on_fail (with input, label, self, args sub-sub-sections), preprocess (with input, parsed, self, args, label), postprocess, parsers list)
- Subroutines/coderefs as specifications (h3)
- Callbacks (h3)
- MULTIPLE SPECIFICATIONS (with preprocess, on_fail)
- EXECUTION FLOW (with For Single Specifications, For Multiple Specifications)
- METHODS (with import, create_class (with parsers, constructor, verbose, class, version))
- SUBCLASSING (with create_method, on_fail)
- USING BUILDER OBJECTS (with new, clone, parser, set_parser, get_parser, parse_datetime, format_datetime)
- LONGER EXAMPLES
- THANKS
- SEE ALSO
- SUPPORT
- SOURCE
- DONATIONS
- AUTHORS
- CONTRIBUTORS
- COPYRIGHT AND LICENSE
Let me draft the HTML now. I need to be careful with the nested sub-sub-sections like "input", "label", "self", "args" inside "on_match and on_fail" — these are within a bullet list, so they should be sub-bullets, not h3.
Actually looking again, the original uses bullet items like:
• length
• label
• on_match and on_fail
• input
• label
• self
• args
These are nested bullets. I'll keep them as nested .
For the Quick Reference table, let me create common use cases:
- Basic parser creation
- Single specification
- Multiple specification
- Subclass with custom new
- Create one-off parser
Let me start writing:
```html
🚀 Quick Reference
Use Case Command Description
Define parser class use DateTime::Format::Builder(parsers => { ... })Create a parsing class at compile time
Create parser runtime DateTime::Format::Builder->create_class(...)Build parser class at runtime
One-off parser object DateTime::Format::Builder->new->parser(...)Get a parser object for single use
Parse a date $class->parse_datetime($string)Run the configured parser
Subclass our @ISA = 'DateTime::Format::Builder'Override on_fail/create_method
```
Now let me build the full HTML document carefully.
For the synopsis section - all code should be wrapped in pre>code.
For the postprocess example, the original has:
```
{
regex => qr/^(\d\d) (\d\d) (\d\d)$/,
params => [qw( year month day )],
postprocess => \&_fix_year,
}
```
and
```
sub _fix_year {
my %args = @_;
my ( $date, $p ) = @args{qw( input parsed )};
$p->{year} += $p->{year} > 69 ? 1900 : 2000;
return 1;
}
```
These are code blocks.
For execution flow code:
```
my $dt = $class->parse_datetime($string);
```
and
```
my $dt = $class->complex_parse($string);
```
These are code blocks.
For methods:
```
use DateTime::Format::Builder ( ... )
```
and
```
use DateTime::Format::Builder;
DateTime::Format::Builder->create_class( ... );
```
and
```
my $parser = DateTime::Format::Builder->new;
```
etc.
The license section has "The Artistic License 2.0 (GPL Compatible)" - that's plain text within the structure.
Let me think about the `<` and `>` escaping. Looking at the original I see:
- `datetime AT perl.org` - this has AT replacing @ — but that's the rendered display.
- I see `...` and `...` which are allowed tags.
For AUTHORS section:
- `autarch AT urth.org` - the AT is shown but the actual text after mailto: would be `autarch@urth.org`. In the display text they show `autarch AT urth.org` (with non-breaking space).
I'll preserve these as-is.
Let me also check: "Bugs may be submitted at <https://github.com/...>" - these are already escaped.
Let me make sure I escape any unescaped < and > in the original. Let me look at AUTHORS:
"Dave Rolsky <autarch AT urth.org>"
OK, the < and > around the mailto link are already escaped. Good.
Now let me also check the QUICK REFERENCE I want to add. The Quick Reference section is required per the rules.
Let me write the final HTML. I'll be careful about:
- Only allowed tags
- All < and > escaped except in allowed tags
- Code wrapped properly
- Em-dashes for separators
- Maintain structure
- Add emoji prefixes
Let me also think about the THEME — Perl module docs are reference-y. I'll use moderate emoji density.
Heading emojis:
- NAME → (already H1 area)
- VERSION: 📦
- SYNOPSIS: 📋
- DESCRIPTION: 📖
- TUTORIAL: 🎓
- ERROR HANDLING AND BAD PARSES: ⚠️
- SINGLE SPECIFICATIONS: 🎯
- length: 📏
- label: 🏷️
- on_match and on_fail: 🔔
- preprocess: 🧹
- postprocess: ✨
- MULTIPLE SPECIFICATIONS: 🎯 (already used)... maybe 📚 or 🔢
- EXECUTION FLOW: 🔄
- METHODS: 🔧
- SUBCLASSING: 🏛️
- USING BUILDER OBJECTS: 🛠️
- LONGER EXAMPLES: 📚
- THANKS: 🙏
- SEE ALSO: 🔗
- SUPPORT: 💬
- SOURCE: 📂
- DONATIONS: 💸
- AUTHORS: 👤
- CONTRIBUTORS: 🤝
- COPYRIGHT AND LICENSE: ⚖️
Quick Reference: 🚀
Let me write the document now. I need to be very careful about code blocks - they must contain EXACT original code with no changes, no links inside.
Wait, in the synopsis the original has:
```
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 )],
},
],
}
);
```
The original has `DateTime::Format::Brief` and similar links inside the code. But per rule 8: "NEVER put inside a block." So I should strip these links when inside code blocks. I'll keep the module names as plain text in code blocks.
Similarly for other code blocks.
OK let me write this out now.
Actually let me re-check the original to see what links are inside code:
- SYNOPSIS: package DateTime::Format::Brief; / use DateTime::Format::Builder (parsers =>...)
- METHODS > create_class: "use DateTime::Format::Builder ( ... )" / "use DateTime::Format::Builder;" / "DateTime::Format::Builder->create_class( ... );"
- USING BUILDER OBJECTS > new: "my $parser = DateTime::Format::Builder->new;"
- USING BUILDER OBJECTS > parser: "regex => qr/^ (\d{4}) (\d\d) (\d\d) $/x;" / "params => [qw( year month day )],"
All these have links inside code. I'll strip them.
Now let me write the output. I'll be careful to keep this under 128,000 chars (it should be fine - the original is well under that).
Let me draft:
```html
🚀 Quick Reference
Use Case Code Description
📦 Define class at compile-time use DateTime::Format::Builder(parsers => { ... })Build a parser class with methods
🔧 Create class at runtime DateTime::Format::Builder->create_class(...)Equivalent of import but runtime
🛠️ One-off parser object DateTime::Format::Builder->new->parser(...)Get a parser for single-use scripts
🪄 Run the parser $class->parse_datetime($string)Returns a DateTime object
🏛️ Subclass our @ISA = ('DateTime::Format::Builder');Override on_fail / create_method
```
Now the main content. Let me be careful and structured.
I need to also remember: original has `Module::Name` - I should preserve these links outside of code blocks (rule 17 says preserve tags). But the link destination might not be useful in the rendered version. Let me keep the relative phpMan paths as in the original.
Actually wait - rule 17: "Preserve , , tags from the original — they carry semantic meaning." So I keep them.
OK let me write the full thing now.
One more check - I need to look at the multi-level nesting of bullets. The original has:
```
• on_match and on_fail
• input
• label
• self
• args
```
These are nested bullets within a single bullet. I should use nested .
Let me finalize the structure. I want to make sure I escape any bare < or > in text. The SUPPORT section has `<https://...>` already escaped. Good.
The mailto links look like `autarch AT urth.org` - those are valid tags.
Alright, writing the final HTML now. I'll keep it tight and well-formatted.
For the quick reference table, I'll use simple entries with emojis in the description column.
For method descriptions in the methods section, I'll use wrapping method names. The original format is like:
"import is a wrapper for "create_class"."
I'll convert to: `import is a wrapper for create_class.`
Let me draft the complete document now.
Actually one consideration - the multiple specifications might have parsers listed. The original is:
"Parsers at the time of writing are:
• DateTime::Format::Builder::Parser::Regex - provides regular expression based parsing.
• DateTime::Format::Builder::Parser::Strptime - provides strptime based parsing."
These are a list. I'll keep them as a list with proper module links.
OK let me write the final HTML now. I'll be focused on structure preservation and proper code wrapping.
Let me check the verbose option - it's a paragraph with multiple sentences. I'll keep it as one paragraph.
Final structure:
1. Title (already present, H1 not needed)
2. 🚀 Quick Reference
3. 📦 VERSION
4. 📋 SYNOPSIS
5. 📖 DESCRIPTION
6. 🎓 TUTORIAL
7. ⚠️ ERROR HANDLING AND BAD PARSES
8. 🎯 SINGLE SPECIFICATIONS
- list of keys
9. 🔢 MULTIPLE SPECIFICATIONS
10. 🔄 EXECUTION FLOW
11. 🔧 METHODS
12. 🏛️ SUBCLASSING
13. 🛠️ USING BUILDER OBJECTS
14. 📚 LONGER EXAMPLES
15. 🙏 THANKS
16. 🔗 SEE ALSO
17. 💬 SUPPORT
18. 📂 SOURCE
19. 💸 DONATIONS
20. 👤 AUTHORS
21. 🤝 CONTRIBUTORS
22. ⚖️ COPYRIGHT AND LICENSE
OK let me write it.
Wait, I need to check about postprocess's Note - it has bold inside:
"Note: It is very important to return..."
I'll preserve these tags.
OK final output below.
🚀 Quick Reference
Use Case Code Description
📦 Define class at compile-time use DateTime::Format::Builder(parsers => { ... })Build a parser class with methods
🔧 Create class at runtime DateTime::Format::Builder->create_class(...)Runtime equivalent of import
🛠️ One-off parser object DateTime::Format::Builder->new->parser(...)Get a parser for single-use scripts
🪄 Run the parser $class->parse_datetime($string)Returns a DateTime object
🏛️ Subclass our @ISA = ('DateTime::Format::Builder');Override on_fail / create_method
🔁 Clone parser object $parser->new or $parser->cloneRe-use a configured parser
📦 VERSION
version 0.83
📋 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 )],
},
],
}
);
📖 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 the author expected.
🎓 TUTORIAL
📘 See DateTime::Format::Builder::Tutorial.
⚠️ ERROR HANDLING AND BAD PARSES
🌀 Often, the docs speak of "undef" 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 on_fail if the single parser returns undef. The single parser must return undef 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 on_fail right at the end when it's tried all it could.
🚨 on_fail (see later) is defined, by default, to throw an error.
Multiple parser specifications can also specify on_fail with a coderef as an argument in the options block. This will take precedence over the inheritable and overrideable 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 method will result in either a DateTime object being returned or an error being thrown (unless you've overridden on_fail or create_method, or you've specified a on_fail key to a multiple parser specification).
Individual parsers (be they multiple parsers or single parsers) will return either the DateTime object or undef.
🎯 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:
- 📏 length
length is an optional parameter that can be used to specify that this particular regex 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 lengths are given and the particular length parser fails, then the non-length parsers are tried.
This parameter is ignored unless the specification is part of a multiple parser specification.
- 🏷️ label
label provides a name for the specification and is passed to some of the callbacks about to be mentioned.
- 🔔 on_match and on_fail
on_match and on_fail are callbacks. Both routines will be called with parameters of:
- 📥 input — the input to the parser (after any preprocessing callbacks).
- 🏷️ label — the label of the parser if there is one.
- 🪞 self — the object on which the method has been invoked (which may just be a class name). Naturally, you can then invoke your own methods on it to get information you want.
- 📦 args — an arrayref of any passed arguments, if any. If there were no arguments, then this parameter is not given.
These routines will be called depending on whether the regex match succeeded or failed.
- 🧹 preprocess
preprocess is a callback provided for cleaning up input prior to parsing. It's given a hash as arguments with the following keys:
- 📥 input — the datetime string the parser was given (if using multiple specifications and an overall preprocess then this is the date after it's been through that preprocessor).
- 📦 parsed — the state of parsing so far. Usually empty at this point unless an overall preprocess was given. Items may be placed in it and will be given to any postprocessor and
DateTime->new (unless the postprocessor deletes it).
- 🪞 self, 📦 args, 🏷️ label — as per on_match and on_fail.
The return value from the routine is what is given to the regex. Note that this is last code stop before the match.
Note: mixing length and a preprocess that modifies the length of the input string is probably not what you meant to do. You probably meant to use the multiple parser variant of preprocess which is done before any length calculations. This "single parser" variant of preprocess is performed after any length calculations.
- ✨ postprocess
postprocess is the last code stop before DateTime->new is called. It's given the same arguments as preprocess. This allows it to modify the parsed parameters after the parse and before the creation of the object. For example, you might use:
{
regex => qr/^(\d\d) (\d\d) (\d\d)$/,
params => [qw( year month day )],
postprocess => \&_fix_year,
}
where _fix_year is defined as:
sub _fix_year {
my %args = @_;
my ( $date, $p ) = @args{qw( input parsed )};
$p->{year} += $p->{year} > 69 ? 1900 : 2000;
return 1;
}
This will cause the two digit years to be corrected according to the cut off. If the year was '69' or lower, then it is made into 2069 (or 2045, or whatever the year was parsed as). Otherwise it is assumed to be 19xx. The DateTime::Format::Mail module uses code similar to this (only it allows the cut off to be configured and it doesn't use Builder).
Note: It is very important to return an explicit value from the postprocess callback. If the return value is false then the parse is taken to have failed. If the return value is true, then the parse is taken to have succeeded and DateTime->new is called.
See the documentation for the individual parsers for their valid keys.
Parsers at the time of writing are:
- DateTime::Format::Builder::Parser::Regex — provides regular expression based parsing.
- DateTime::Format::Builder::Parser::Strptime — provides strptime based parsing.
🧩 Subroutines / coderefs as specifications
A single parser specification can be a coderef. This was added mostly because it could be and because the author knew someone, somewhere, would want to use it.
If the specification is a reference to a piece of code, be it a subroutine, anonymous, or whatever, then it's passed more or less straight through. The code should return undef in event of failure (or any false value, but undef is strongly preferred), or a true value in the event of success (ideally a DateTime object or some object that has the same interface).
💡 This all said, it is generally not recommended to use this feature unless you have to.
🔔 Callbacks
Any time you see a callback being mentioned, you can, if you like, substitute an arrayref of coderefs rather than having the straight coderef.
🔢 MULTIPLE SPECIFICATIONS
📚 These are very easily described as an array of single specifications.
Note that if the first element of the array is an arrayref, then you're specifying options.
- 🧹 preprocess
preprocess lets you specify a preprocessor that is called before any of the parsers are tried. This lets you do things like strip off timezones or any unnecessary data. The most common use people have for it at present is to get the input date to a particular length so that the length is usable (DateTime::Format::ICal would use it to strip off the variable length timezone).
Arguments are as for the single parser preprocess variant with the exception that label is never given.
- 🚨 on_fail
on_fail should be a reference to a subroutine that is called if the parser fails. If this is not provided, the default action is to call DateTime::Format::Builder::on_fail, or the on_fail method of the subclass of DTFB that was used to create the parser.
🔄 EXECUTION FLOW
🧭 Builder allows you to plug in a fair few callbacks, which can make following how a parse failed (or succeeded unexpectedly) somewhat tricky.
➡️ For Single Specifications
A single specification will do the following:
User calls parser:
my $dt = $class->parse_datetime($string);
- 🧹 preprocess is called. It's given
$string and a reference to the parsing workspace hash, which we'll call $p. At this point, $p is empty. The return value is used as $date for the rest of this single parser. Anything put in $p is also used for the rest of this single parser.
- 🔍 regex is applied.
- ❌ If regex did not match, then on_fail is called (and is given
$date and also label if it was defined). Any return value is ignored and the next thing is for the single parser to return undef.
✅ If regex did match, then on_match is called with the same arguments as would be given to on_fail. The return value is similarly ignored, but we then move to step 4 rather than exiting the parser.
- ✨ postprocess is called with
$date and a filled out $p. The return value is taken as an indication of whether the parse was a success or not. If it wasn't a success then the single parser will exit at this point, returning undef.
- 🏗️
DateTime->new is called and the user is given the resultant DateTime object.
See the section on error handling regarding the undefs mentioned above.
➡️ For Multiple Specifications
With multiple specifications:
User calls parser:
my $dt = $class->complex_parse($string);
- 🧹 The overall preprocessor is called and is given
$string and the hashref $p (identically to the per parser preprocess mentioned in the previous flow).
If the callback modifies $p then a copy of $p is given to each of the individual parsers. This is so parsers won't accidentally pollute each other's workspace.
- 📏 If an appropriate length specific parser is found, then it is called and the single parser flow (see the previous section) is followed, and the parser is given a copy of
$p and the return value of the overall preprocessor as $date.
If a DateTime object was returned so we go straight back to the user.
If no appropriate parser was found, or the parser returned undef, then we progress to step 3!
- 🌀 Any non-length based parsers are tried in the order they were specified.
For each of those the single specification flow above is performed, and is given a copy of the output from the overall preprocessor.
If a real DateTime object is returned then we exit back to the user.
If no parser could parse, then an error is thrown.
See the section on error handling regarding the undefs mentioned above.
🔧 METHODS
🧰 In the general course of things you won't need any of the methods. Life often throws unexpected things at us so the methods are all available for use.
📥 import
import is a wrapper for create_class. If you specify the class option (see documentation for create_class) it will be ignored.
🏗️ create_class
This method can be used as the runtime equivalent of import. That is, it takes the exact same parameters as when one does:
use DateTime::Format::Builder ( ... )
That can be (almost) equivalently written as:
use DateTime::Format::Builder;
DateTime::Format::Builder->create_class( ... );
The difference being that the first is done at compile time while the second is done at run time.
In the tutorial the author said there were only two parameters at present. That was a lie. There are actually three of them.
- 📚 parsers
parsers takes a hashref of methods and their parser specifications. See the DateTime::Format::Builder::Tutorial for details.
Note that if you define a subroutine of the same name as one of the methods you define here, an error will be thrown.
- 🏗️ constructor
constructor determines whether and how to create a new function in the new class. If given a true value, a constructor is created. If given a false value, one isn't.
If given an anonymous sub or a reference to a sub then that is used as new.
The default is 1 (that is, create a constructor using the default code which simply creates a hashref and blesses it).
If your class defines its own new method it will not be overwritten. If you define your own new and also tell Builder to define one an error will be thrown.
- 🗣️ verbose
verbose takes a value. If the value is undef, then logging is disabled. If the value is a filehandle then that's where logging will go. If it's a true value, then output will go to STDERR.
Alternatively, call $DateTime::Format::Builder::verbose with the relevant value. Whichever value is given more recently is adhered to.
⚠️ Be aware that verbosity is a global setting.
- 📦 class
class is optional and specifies the name of the class in which to create the specified methods.
If using this method in the guise of import then this field will cause an error so it is only of use when calling as create_class.
- 🔖 version
version is also optional and specifies the value to give $VERSION in the class. It's generally not recommended unless you're combining with the class option. An ExtUtils::MakeMaker / CPAN compliant version specification is much better.
In addition to creating any of the methods it also creates a new method that can instantiate (or clone) objects.
🏛️ SUBCLASSING
📘 In the rest of the documentation there's been occasional simplification in order to get some of the ideas across more easily. The thing is, this module's very flexible. You can get markedly different behaviour from simply subclassing it and overriding some methods.
🛠️ create_method
Given a parser coderef, returns a coderef that is suitable to be a method.
The default action is to call on_fail in the event of a non-parse, but you can make it do whatever you want.
🚨 on_fail
This is called in the event of a non-parse (unless you've overridden create_method to do something else).
The single argument is the input string. The default action is to call croak. Above, where the docs say parsers or methods throw errors, this is the method that is doing the error throwing.
You could conceivably override this method to, say, return undef.
🛠️ USING BUILDER OBJECTS aka USERS USING BUILDER
📖 The methods listed in the METHODS section are all you generally need when creating your own class. Sometimes you may not want a full blown class to parse something just for this one program. Some methods are provided to make that task easier.
🆕 new
The basic constructor. It takes no arguments, merely returns a new DateTime::Format::Builder object.
my $parser = DateTime::Format::Builder->new;
If called as a method on an object (rather than as a class method), then it clones the object.
my $clone = $parser->new;
📋 clone
Provided for those who prefer an explicit clone method rather than using new as an object method.
my $clone_of_clone = $clone->clone;
⚙️ parser
Given either a single or multiple parser specification, sets the object to have a parser based on that specification.
$parser->parser(
regex => qr/^ (\d{4}) (\d\d) (\d\d) $/x;
params => [qw( year month day )],
);
The arguments given to parser are handed directly to create_parser. The resultant parser is passed to set_parser.
If called as an object method, it returns the object.
If called as a class method, it creates a new object, sets its parser and returns that object.
🔧 set_parser
Sets the parser of the object to the given parser.
$parser->set_parser($coderef);
Note: this method does not take specifications. It also does not take anything except coderefs. Luckily, coderefs are what most of the other methods produce.
The method return value is the object itself.
🔍 get_parser
Returns the parser the object is using.
my $code = $parser->get_parser;
🪄 parse_datetime
Given a string, it calls the parser and returns the DateTime object that results.
my $dt = $parser->parse_datetime('1979 07 16');
The return value, if not a DateTime object, is whatever the parser wants to return. Generally this means that if the parse failed an error will be thrown.
💥 format_datetime
If you call this function, it will throw an error.
📚 LONGER EXAMPLES
📁 Some longer examples are provided in the distribution. These implement some of the common parsing DateTime modules using Builder. Each of them are, or were, drop in replacements for the modules at the time of writing them.
🙏 THANKS
- 🎉 Dave Rolsky (DROLSKY) for kickstarting the DateTime project, writing DateTime::Format::ICal and DateTime::Format::MySQL, and some much needed review.
- 👏 Joshua Hoblitt (JHOBLITT) for the concept, some of the API, impetus for writing the multi-length code (both one length with multiple parsers and single parser with multiple lengths), blame for the Regex custom constructor code, spotting a bug in Dispatch, and more much needed review.
- 💡 Kellan Elliott-McCrea (KELLAN) for even more review, suggestions, DateTime::Format::W3CDTF and the encouragement to rewrite these docs almost 100%!
- 🛠️ Claus Färber (CFAERBER) for having the author get around to fixing the auto-constructor writing, providing the 'args'/'self' patch, and suggesting the multi-callbacks.
- 📅 Rick Measham (RICKM) for DateTime::Format::Strptime which Builder now supports.
- 🐛 Matthew McGillis for pointing out that
on_fail overriding should be simpler.
- 😎 Simon Cozens (SIMON) for saying it was cool.
🔗 SEE ALSO
- ✉️ "datetime AT perl.org" mailing list.
- 🌐 http://datetime.perl.org/
- 📖 perl, DateTime, DateTime::Format::Builder::Tutorial, DateTime::Format::Builder::Parser
💬 SUPPORT
🐞 Bugs may be submitted at <https://github.com/houseabsolute/DateTime-Format-Builder/issues>.
💭 I am also usually active on IRC as 'autarch' on "irc://irc.perl.org".
📂 SOURCE
🔗 The source code repository for DateTime-Format-Builder can be found at <https://github.com/houseabsolute/DateTime-Format-Builder>.
💸 DONATIONS
💝 If you'd like to thank the author for the work done on this module, please consider making a "donation" via PayPal. A lot of free time creating free software was spent, and any support would be appreciated.
Please note that I am not suggesting that you must do this in order for work to continue on this particular software. It will continue to be done, inasmuch as it has been in the past, for as long as it interests the author.
Similarly, a donation made in this way will probably not make work on this software much more, unless so many donations come in that working on free software full time can be considered (let's all have a chuckle at that together).
🪙 To donate, log into PayPal and send money to autarch AT urth.org, or use the button at <https://www.urth.org/fs-donation.html>.
👤 AUTHORS
- Dave Rolsky <autarch AT urth.org>
- Iain Truskett <spoon AT cpan.org>
🤝 CONTRIBUTORS
- Daisuke Maki <daisuke AT endeworks.jp>
- James Raspass <jraspass AT gmail.com>
⚖️ COPYRIGHT AND LICENSE
📜 This software is Copyright (c) 2020 by Dave Rolsky.
🆓 This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the LICENSE file included with this distribution.
🐪 perl v5.30.3 — 2020-08-11 — DateTime::Format::Builder(3pm)
Generated by phpman v4.9.26-1-g511901d · Markdown · JSON · MCP Author: Che Dong Under GNU General Public License
2026-08-04 07:40 @216.73.216.183
CrawledBy Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
Enhanced by LLM: deepseek-v4-flash / taotoken.net / www.chedong.com - original format