Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial import of DateTime-Format-Human-Duration 0.0.1 from CPAN

git-cpan-module:   DateTime-Format-Human-Duration
git-cpan-version:  0.0.1
git-cpan-authorid: DMUEY
git-cpan-file:     authors/id/D/DM/DMUEY/DateTime-Format-Human-Duration-0.0.1.tar.gz
  • Loading branch information...
commit 3679ee2984fe4edb47322abd9f3be3a696b6321a 0 parents
@drmuey drmuey authored schwern committed
17 Build.PL
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+use Module::Build;
+
+my $builder = Module::Build->new(
+ module_name => 'DateTime::Format::Human::Duration',
+ license => 'perl',
+ dist_author => 'Daniel Muey <http://drmuey.com/cpan_contact.pl>',
+ dist_version_from => 'lib/DateTime/Format/Human/Duration.pm',
+ requires => {
+ 'Test::More' => 0,
+ 'version' => 0,
+ },
+ add_to_cleanup => [ 'DateTime-Format-Human-Duration-*' ],
+);
+
+$builder->create_build_script();
5 Changes
@@ -0,0 +1,5 @@
+Revision history for DateTime-Format-Human-Duration
+
+0.0.1 Sun Mar 16 10:54:08 2008
+ Initial release.
+
16 MANIFEST
@@ -0,0 +1,16 @@
+Build.PL
+Changes
+MANIFEST
+Makefile.PL
+README
+lib/DateTime/Format/Human/Duration.pm
+lib/DateTime/Format/Human/Duration/Locale.pm
+lib/DateTime/Format/Human/Duration/Locale/es.pm
+lib/DateTime/Format/Human/Duration/Locale/fr.pm
+lib/DateTime/Format/Human/Duration/Locale/pt.pm
+t/00.load.t
+t/01.methods.t
+t/perlcritic.t
+t/pod-coverage.t
+t/pod.t
+META.yml Module meta-data (added by MakeMaker)
12 META.yml
@@ -0,0 +1,12 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
+name: DateTime-Format-Human-Duration
+version: 0.0.1
+version_from: lib/DateTime/Format/Human/Duration.pm
+installdirs: site
+requires:
+ Test::More: 0
+ version: 0
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.30
17 Makefile.PL
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ NAME => 'DateTime::Format::Human::Duration',
+ AUTHOR => 'Daniel Muey <http://drmuey.com/cpan_contact.pl>',
+ VERSION_FROM => 'lib/DateTime/Format/Human/Duration.pm',
+ ABSTRACT_FROM => 'lib/DateTime/Format/Human/Duration.pm',
+ PL_FILES => {},
+ PREREQ_PM => {
+ 'Test::More' => 0,
+ 'version' => 0,
+ },
+ dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+ clean => { FILES => 'DateTime-Format-Human-Duration-*' },
+);
33 README
@@ -0,0 +1,33 @@
+DateTime-Format-Human-Duration version 0.0.1
+
+DOCUMENTATION
+
+See POD for documentation.
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+Alternatively, to install with Module::Build, you can use the following commands:
+
+ perl Build.PL
+ ./Build
+ ./Build test
+ ./Build install
+
+DEPENDENCIES
+
+See DEPENDENCIES section in POD, 'requires' key in Build.PL,
+or 'PREREQ_PM' key in Makefile.PL
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2008, Daniel Muey
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
430 lib/DateTime/Format/Human/Duration.pm
@@ -0,0 +1,430 @@
+package DateTime::Format::Human::Duration;
+
+use warnings;
+use strict;
+require DateTime::Format::Human::Duration::Locale;
+
+use version; our $VERSION = qv('0.0.1');
+
+sub new {
+ bless { 'locale_cache' => {} }, 'DateTime::Format::Human::Duration';
+}
+
+sub format_duration_between {
+ my ($span, $dt, $dtb, %args) = @_;
+ my $dur = $dt - $dtb;
+
+ if (!exists $args{'locale'}) {
+ $args{'locale'} = $dt->{'locale'}{'id'};
+ }
+
+ return $span->format_duration($dur, %args);
+}
+
+sub format_duration {
+ my ($span, $duration, %args) = @_;
+
+ my @raw = $duration->in_units( qw(years months weeks days hours minutes seconds nanoseconds) );
+ my @n = map { abs($_) } @raw; # no negative numbers
+ my $say = '';
+
+ # $dta - $dtb:
+ # if dta < dtb means past -> future (Duration units will have negatives)
+ # else its either this absolute instant (no_time) or the past
+ if ( grep { $_ < 0 } @raw ) {
+ if ( exists $args{'future'} ) {
+ $say = $args{'future'}
+ }
+ }
+ else {
+ if ( exists $args{'past'} ) {
+ $say = $args{'past'}
+ }
+ }
+
+ ####
+ ## this is essencially the hashref that is returned from DateTime::Format::Human::Duration::en::get_human_span_hashref() : #
+ ####
+ my $setup = {
+ 'no_oxford_comma' => 0,
+ 'no_time' => 'no time', # The wait will be $formatted_duration
+ 'and' => 'and',
+ 'year' => 'year',
+ 'years' => 'years',
+ 'month' => 'month',
+ 'months' => 'months',
+ 'week' => 'week',
+ 'weeks' => 'weeks',
+ 'day' => 'day',
+ 'days' => 'days',
+ 'hour' => 'hour',
+ 'hours' => 'hours',
+ 'minute' => 'minute',
+ 'minutes' => 'minutes',
+ 'second' => 'second',
+ 'seconds' => 'seconds',
+ 'nanosecond' => 'nanosecond',
+ 'nanoseconds' => 'nanoseconds',
+ };
+
+ my $locale = DateTime::Format::Human::Duration::Locale::calc_locale($span, $args{'locale'});
+
+ if($locale) {
+ if ( ref $locale eq 'HASH' ) {
+ %{ $setup } = (
+ %{ $setup },
+ %{ $locale },
+ );
+ }
+ elsif ( ref $locale eq 'CODE') {
+ return $locale->( @n, \%args );
+ }
+ }
+
+ # this is what a locale's get_human_span_from_units_array() should do:
+ # my (@n, $args_hr) = @_;
+
+ # reorder @n use if appropriate for locale
+ # @n has been pass through abs() so that its never negative
+ my @parts = grep { $_ } (
+ $n[0] ? ( $n[0]. ' ' . ($n[0] == 1 ? $setup->{'year'} : $setup->{'years'})) : '',
+ $n[1] ? ( $n[1] . ' ' .($n[1] == 1 ? $setup->{'month'} : $setup->{'months'})) : '',
+ $n[2] ? ( $n[2] . ' ' .($n[2] == 1 ? $setup->{'week'} : $setup->{'weeks'})) : '',
+ $n[3] ? ( $n[3] . ' ' .($n[3] == 1 ? $setup->{'day'} : $setup->{'days'})) : '',
+ $n[4] ? ( $n[4] . ' ' .($n[4] == 1 ? $setup->{'hour'} : $setup->{'hours'})) : '',
+ $n[5] ? ( $n[5] . ' ' .($n[5] == 1 ? $setup->{'minute'} : $setup->{'minutes'})) : '',
+ $n[6] ? ( $n[6] . ' ' .($n[6] == 1 ? $setup->{'second'} : $setup->{'seconds'})) : '',
+ $n[7] ? ( $n[7] . ' ' .($n[7] == 1 ? $setup->{'nanosecond'} : $setup->{'nanoseconds'})) : '',
+ );
+
+ my $no_time = exists $args{'no_time'} ? $args{'no_time'} : $setup->{'no_time'};
+ return $no_time if !@parts;
+
+ my $last = @parts > 1 ? pop(@parts): '';
+
+ ## We want to use the so-called Oxford comma to avoid ambiguity.
+ ## For that reason we make locale's specifically tell us they do not want it.
+ my $string = $setup->{'no_oxford_comma'}
+ ? join(', ', @parts) . ($last ? " $setup->{'and'} $last" : '')
+ : join(', ', @parts) . (@parts > 1 ? ',' : '') . ($last ? " $setup->{'and'} $last" : '')
+ ;
+
+ if ( $say ) {
+ $string = $say =~ m{%s} ? sprintf($say, $string): "$say $string";
+ }
+
+ return $string;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+DateTime::Format::Human::Duration - Get a locale specific string describing the span of a given duration
+
+=head1 VERSION
+
+This document describes DateTime::Format::Human::Duration version 0.0.1
+
+=head1 SYNOPSIS
+
+ use DateTime;
+ use DateTime::Format::Human::Duration
+
+ my $span = DateTime::Format::Human::Duration->new();
+ my $dur = $dta - $dtb;
+ print $span->format_duration($dur); # 1 year, 2 months, 3 minutes, and 1 second
+
+ print $span->format_duration_between($dta, $dtb); # 1 year, 2 months, 3 minutes, and 1 second
+
+=head1 DESCRIPTION
+
+Get a localized string representing the duration.
+
+For example:
+
+ 1 second
+ 2 minutes and 3 seconds
+ 3 weeks, 1 day, and 5 seconds
+ 4 years, 1 month, 2 days, 6 minutes, 1 second, and 345000028 nanoseconds
+
+=head1 INTERFACE
+
+=head2 new()
+
+Create span object, no args
+
+=head2 format_duration()
+
+First argument is a DateTime::Duration object
+
+After that you can optionally pass some 'standard args' as a hash as described below
+
+=head2 format_duration_between()
+
+First two args are DateTime objects
+
+After that you can optionally pass some 'standard args' as a hash as described below
+
+=head2 standard args
+
+=over 4
+
+=item 1 'locale'
+
+locale of the $dt object will be used if you do not specify this
+
+Valid values are a string of the locale (E.g 'fr'), a DateTime object, or a DateTime object's 'locale' key.
+
+=item 2 since we're working with 2 datetime objects of known points we can have past and future tenses.
+
+=over 4
+
+=item * past
+
+String to use if duration is past tense. Can have a sprintf '%s' or else is prepended with a trailing space.
+
+=item * future
+
+String to use if duration is future tense. Can have a sprintf '%s' or else is prepended with a trailing space.
+
+=item * no_time
+
+Override the 'no_time' in the locale hash.
+
+=back
+
+If duration is baseless (IE ambiguouse) then 'past' and 'future' is used based on if $dur->in_units has negatives or not.
+
+Also by nature it's not split into type groups:
+
+An example is
+
+ DateTime::Duration->new('seconds'=> 62)
+
+Will result in '62 seconds' not '1 minute and 2 seconds'
+
+For more sane results always be specific by using 2 datetime object to get a duration object
+
+=back
+
+ print $dt->format_duration_between(
+ $dta,
+ $dtb,
+ 'past' => 'Your account expired %s ago.',
+ 'future' => 'Your account expires in %s.',
+ 'no_time'=> 'Your account just expired.',
+ );
+
+This facilitates, for example, this L<Locale::Maketext> vernacular which becomes:
+
+ 'Your account [duration,_1,_2,expired %s ago,expires in,just expired].' => '[Votre compte [duration,_1,_2,a expiré il ya,expire dans,vient d'expirer].'
+
+=head1 LOCALIZATION
+
+Localization is provided by the included DateTime::Format::Human::Duration::Locale modules.
+
+Included are DateTime::Format::Human::Duration::Locale::es, DateTime::Format::Human::Duration::Locale::fr, DateTime::Format::Human::Duration::Locale::pt
+
+More will be included as time permits/folks volunteer/CLDR becomes an option
+
+They are setup this way:
+
+DateTime::Format::Human::Duration::Locale::XYZ where 'XYZ' is the ISO code of DateTime::Locale
+
+It can have one of 2 functions used in this order:
+
+=over 4
+
+=item get_human_span_from_units_array()
+
+Try to use get_human_span_hashref() if the locale is disposed to it since its much easier... That said:
+
+Takes the arguments as described in the example below, should return the localized "span" string.
+
+ sub get_human_span_from_units_array {
+ my ($years, $months, $weeks, $days, $hours, $minutes, $seconds, $nanoseconds, $args_hr) = @_; # note: has no negative numbers
+ ...
+ return $string; # 1 year, 2days, 4 hours, and 17 minutes
+ }
+
+=item get_human_span_hashref()
+
+Takes no arguments, should return a hashref of this structure:
+
+ sub get_human_span_hashref {
+ return {
+ 'no_oxford_comma' => 1,
+ 'no_time' => 'pas le temps',
+ 'and' => 'et',
+ 'year' => 'an',
+ 'years' => 'ans',
+ 'month' => 'mois',
+ 'months' => 'mois',
+ 'week' => 'semaine',
+ 'weeks' => 'semaines',
+ 'day' => 'jour',
+ 'days' => 'jours',
+ 'hour' => 'heure',
+ 'hours' => 'heures',
+ 'minute' => 'minute',
+ 'minutes' => 'minutes',
+ 'second' => 'seconde',
+ 'seconds' => 'seconds',
+ 'nanosecond' => 'nanoseconde',
+ 'nanoseconds' => 'nanosecondes',
+ };
+ }
+
+=back
+
+=head1 LOCALIZATION of DateTime::Format modules
+
+L<DateTime> does an excellent job at implementing localization. Often L<DateTime::Format> based class's either don't support localization or they implement it haphazardly and inconsistently.
+
+With this module I hope to model a localization scheme that is inline with L<DateTime> and is consistent and reuseable between <DateTime::Format> based classes.
+
+The idea is to determine the locale to use based on a DateTime object.
+
+XYZ::Locale should handle looking up (and caching if appropriate) the locale and loading the necessary locale module XYZ::Locale::fr
+
+The specific locale module holds the data and possibly logic neccesary to do what XYZ does in the vernacular of the given locale.
+
+=head2 TODO
+
+Eventually the generic logic will be re-broken out into its own module for re-use by your class and I'll have more detailed POD about how to do it.
+
+In the meantime if you're interested please contact me and I'd be happy to help and/or expediate this TODO.
+
+Also, Dave Rolksy has mentioned to me that this sort of locale data might be appropriate for DateTime::Locale directly from CLDR. If that happens this module will be changed to use that if possible.
+
+=head1 FAQ
+
+=head2 Why would I want to use this?
+
+So you can localize your application's output of time periods without having to do a lot of logic each time you wanted to say it.
+
+L<Locale::Maketext::Utils> has/will have a duration() bracket notation method which prompted this module's existence
+
+duration() was prompted by its datetime() brother, all of which uses the most excellent DateTime project!
+
+=head2 Why did my duration say '62 seconds' instead of '1 minute and 2 seconds'
+
+Because you used an ambiguous duration (one without a base) so there is no way to
+apply date math and accurately represent the number of each given item in that
+duration since it may or may not span leap-[second, days, years, etc..]
+
+In other words do this (so that your duration can be specifically calculated):
+
+ $dtb = $dta->clone->add('seconds'=> 62);
+ my $duration = $dta - $dtb; # has a base, its not ambiguous
+ print $span->format_duration($duration); # 1 minutes and 2 seconds
+
+not this:
+
+ my $duration = DateTime::Duration->new('seconds'=> 62); # no base, it is ambiguous
+ print $span->format_duration($duration); # 62 seconds
+
+Note L</format_duration_between>(), does not suffer from this since we're using a specific DateTime object already.
+
+ print $span->format_duration_between( $dt, $dt->clone()->add('seconds'=> 62) ); # 1 minute and 2 seconds
+
+=head2 Why do you put a comma before the 'and' in a group of more than 2 items?
+
+We want to use the so-called Oxford comma to avoid ambiguity.
+
+=head2 My DateTime::Format::Human::Duration::Locale::XX still outputs in English!
+
+That is because it defined neither the get_human_span_hashref() or the get_human_span_from_units_array() functions
+
+It must define one of them or defaults are used.
+
+=head2 Why didn't you just use 'DateTime::Format::Duration'
+
+Essencially DateTime::Format::Duration is an object representing a single strftime() type string to apply to any given duration. This is not flexible enough for the intent of this module.
+
+DateTime::Format::Duration is not a bad module its just for a different purpose than DateTime::Format::Human::Duration
+
+=over 4
+
+=item * It was not localizable
+
+You either got '2 days' or '1 days' which a) forces it to be in English and b) doesn't even make sense in English.
+
+You could get around that by adding logic each time you wanted to call it but that is just messy.
+
+=item * Had to keep an item even if it was zero
+
+If 'days' was in there you got '0 days', we only want items with a value to show.
+
+That'd also require a lot of logic each time you wanted to call which is again messy.
+
+=item * This module has no need for reparsing output back into an object
+
+Since the datetime info for 2 points in time are generally in a form easily rendered into a DateTime object it'd be silly to even attempt to store and parse the output of this module back into an object.
+
+Plus since it all depends on the locale it is in it'd be difficult.
+
+=back
+
+The purpose of DateTime::Format::Human::Duration was to generate a localized human language description of a duration without the caller needing to supply any logic.
+
+=head1 DIAGNOSTICS
+
+Throws no warnings or errors of its own
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+DateTime::Format::Human::Duration requires no configuration files or environment variables.
+
+=head1 DEPENDENCIES
+
+None.
+
+=head1 INCOMPATIBILITIES
+
+None reported.
+
+=head1 BUGS AND LIMITATIONS
+
+No bugs have been reported.
+
+Please report any bugs or feature requests to
+C<bug-datetime-format-span@rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org>.
+
+=head1 AUTHOR
+
+Daniel Muey C<< <http://drmuey.com/cpan_contact.pl> >>
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2008, Daniel Muey C<< <http://drmuey.com/cpan_contact.pl> >>. All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
88 lib/DateTime/Format/Human/Duration/Locale.pm
@@ -0,0 +1,88 @@
+package DateTime::Format::Human::Duration::Locale;
+
+# require DateTime::Format::Locale;
+
+use strict;
+use warnings;
+
+sub calc_locale {
+ my ($span, $loc) = @_;
+
+ # DateTime::Format::Locale::
+ my $final = determine_locale_from({
+ 'base_object' => $span,
+ 'get_locale_from' => $loc,
+ 'locale_ns_path' => 'DateTime/Format/Human/Duration/Locale', # DateTime::Format::Human::Duration::Locale
+ });
+
+ if ($final) {
+ return $final if ref $final; # returned 'locale_cache' we created below
+
+ my $ns = "DateTime::Format::Human::Duration::Locale::$final";
+ if ( my $code_a = $ns->can('get_human_span_from_units_array') ) {
+ $span->{'locale_cache'}{ $final } = $code_a;
+ }
+ elsif ( my $code_b = $ns->can('get_human_span_hashref') ) {
+ $span->{'locale_cache'}{ $final } = $code_b->();
+ }
+
+ if ( exists $span->{'locale_cache'}{ $final } ) {
+ return $span->{'locale_cache'}{ $final };
+ }
+ }
+
+ return '';
+}
+
+# DateTime::Format::Locale::
+sub determine_locale_from {
+ my ($args_hr) = @_;
+
+ return '' if !$args_hr->{'get_locale_from'};
+
+ if (ref $args_hr->{'get_locale_from'}) {
+ my $ns = ref($args_hr->{'get_locale_from'});
+
+ if (exists $args_hr->{'get_locale_from'}{'locale'}) {
+ $ns = exists $args_hr->{'get_locale_from'}{'locale'}{'id'} ? $args_hr->{'get_locale_from'}{'locale'}{'id'} : ref($args_hr->{'get_locale_from'}{'locale'});
+ }
+ elsif ($ns =~ m{^DateTime::Locale::} && exists $args_hr->{'get_locale_from'}{'id'}) {
+ $ns = $args_hr->{'get_locale_from'}{'id'};
+ }
+ ($args_hr->{'get_locale_from'}) = reverse split /::/, $ns;
+ }
+
+ my ($short) = split(/[-_]+/,$args_hr->{'get_locale_from'});
+
+ my $final = '';
+ my @try = $args_hr->{'get_locale_from'} eq $short ? ($args_hr->{'get_locale_from'}) : ($args_hr->{'get_locale_from'}, $short);
+
+ NS:
+ for my $locale ( @try ) {
+ if ( exists $args_hr->{'base_object'}{'locale_cache'}{ $locale } ) {
+ if ( $args_hr->{'base_object'}{'locale_cache'}{ $locale } ) {
+ return $locale;
+ }
+ else {
+ next NS;
+ }
+ }
+
+ $args_hr->{'locale_ns_path'} =~ s{/$}{};
+ my $path = "$args_hr->{'locale_ns_path'}/$locale\.pm";
+
+ if( exists $INC{$path} || eval { $args_hr->{'loads'}{$locale}++; require $path } ) {
+ $final = $locale;
+ $args_hr->{'base_object'}{'locale_cache'}{ $locale } = 1;
+ last NS;
+ }
+ else {
+ push @{$args_hr->{'errors'}{$locale}}, $@;
+ $args_hr->{'base_object'}{'locale_cache'}{ $locale } = '';
+ }
+ }
+
+ return $final;
+}
+
+1;
43 lib/DateTime/Format/Human/Duration/Locale/es.pm
@@ -0,0 +1,43 @@
+package DateTime::Format::Human::Duration::Locale::es;
+
+use strict;
+use warnings;
+
+# Note to self: change 01.methods.t use of 'es' if this hashref changes or is removed
+
+# 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute, 1 second, and 1 nanosecond
+# 2 years, 2 months, 2 weeks, 2 days, 2 hours, 2 minutes, 2 seconds, and 2 nanoseconds
+
+sub get_human_span_hashref {
+ return {
+ 'no_oxford_comma' => 0,
+ 'no_time' => 'no hay tiempo',
+ 'and' => 'y',
+ 'year' => 'año',
+ 'years' => 'años',
+ 'month' => 'mes',
+ 'months' => 'meses',
+ 'week' => 'semana',
+ 'weeks' => 'semanas',
+ 'day' => 'día',
+ 'days' => 'días',
+ 'hour' => 'hora',
+ 'hours' => 'horas',
+ 'minute' => 'minuto',
+ 'minutes' => 'minutos',
+ 'second' => 'segundo',
+ 'seconds' => 'segundos',
+ 'nanosecond' => 'nanosegundo',
+ 'nanoseconds' => 'nanosegundos',
+ };
+}
+
+# get_human_span_from_units_array() is used instead of get_human_span_hashref() if get_human_span_from_units_array() exists
+#
+# sub get_human_span_from_units_array {
+# my ($years, $months, $weeks, $days, $hours, $minutes, $seconds, $nanoseconds, $args_hr) = @_; # note: has no negative numbers
+# ...
+# return $string; # 1 year, 2days, 4 hours, and 17 minutes
+# }
+
+1;
43 lib/DateTime/Format/Human/Duration/Locale/fr.pm
@@ -0,0 +1,43 @@
+package DateTime::Format::Human::Duration::Locale::fr;
+
+use strict;
+use warnings;
+
+# Note to self: change 01.methods.t use of 'fr' if this hashref changes or is removed
+
+# 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute, 1 second, and 1 nanosecond
+# 2 years, 2 months, 2 weeks, 2 days, 2 hours, 2 minutes, 2 seconds, and 2 nanoseconds
+
+sub get_human_span_hashref {
+ return {
+ 'no_oxford_comma' => 1,
+ 'no_time' => 'pas le temps',
+ 'and' => 'et',
+ 'year' => 'an',
+ 'years' => 'ans',
+ 'month' => 'mois',
+ 'months' => 'mois',
+ 'week' => 'semaine',
+ 'weeks' => 'semaines',
+ 'day' => 'jour',
+ 'days' => 'jours',
+ 'hour' => 'heure',
+ 'hours' => 'heures',
+ 'minute' => 'minute',
+ 'minutes' => 'minutes',
+ 'second' => 'seconde',
+ 'seconds' => 'seconds',
+ 'nanosecond' => 'nanoseconde',
+ 'nanoseconds' => 'nanosecondes',
+ };
+}
+
+# get_human_span_from_units_array() is used instead of get_human_span_hashref() if get_human_span_from_units_array() exists
+#
+# sub get_human_span_from_units_array {
+# my ($years, $months, $weeks, $days, $hours, $minutes, $seconds, $nanoseconds, $args_hr) = @_; # note: has no negative numbers
+# ...
+# return $string; # 1 year, 2days, 4 hours, and 17 minutes
+# }
+
+1;
38 lib/DateTime/Format/Human/Duration/Locale/pt.pm
@@ -0,0 +1,38 @@
+package DateTime::Format::Human::Duration::Locale::pt;
+
+use strict;
+use warnings;
+
+sub get_human_span_hashref {
+ return {
+ 'no_oxford_comma' => 0,
+ 'no_time' => 'nenhum momento',
+ 'and' => 'e',
+ 'year' => 'ano',
+ 'years' => 'anos',
+ 'month' => 'mês',
+ 'months' => 'meses',
+ 'week' => 'semana',
+ 'weeks' => 'semanas',
+ 'day' => 'dia',
+ 'days' => 'dias',
+ 'hour' => 'hora',
+ 'hours' => 'horas',
+ 'minute' => 'minuto',
+ 'minutes' => 'minutos',
+ 'second' => 'segundo',
+ 'seconds' => 'segundos',
+ 'nanosecond' => 'nanosegundo', # nanosecond ?
+ 'nanoseconds' => 'nanosegundos', # nanosegundos ?
+ };
+}
+
+# get_human_span_from_units_array() is used instead of get_human_span_hashref() if get_human_span_from_units_array() exists
+#
+# sub get_human_span_from_units_array {
+# my ($years, $months, $weeks, $days, $hours, $minutes, $seconds, $nanoseconds, $args_hr) = @_; # note: has no negative numbers
+# ...
+# return $string; # 1 year, 2days, 4 hours, and 17 minutes
+# }
+
+1;
7 t/00.load.t
@@ -0,0 +1,7 @@
+use Test::More tests => 1;
+
+BEGIN {
+use_ok( 'DateTime::Format::Human::Duration' );
+}
+
+diag( "Testing DateTime::Format::Human::Duration $DateTime::Format::Human::Duration::VERSION" );
65 t/01.methods.t
@@ -0,0 +1,65 @@
+use Test::More tests => 22;
+use lib '../lib';
+
+BEGIN {
+ use_ok( 'DateTime::Format::Human::Duration' );
+}
+
+diag( "Testing DateTime::Format::Human::Duration $DateTime::Format::Human::Duration::VERSION" );
+
+# plan skip_all => 'DateTime required for creating DateTime object and durations' if $@;
+# That fails under Test::More 0.70 like so:
+# You tried to plan twice at t/01.methods.t line 11.
+# Looks like you planned 22 tests but only ran 1.
+# Looks like your test died just after 1.
+
+SKIP: {
+ eval 'use DateTime';
+ skip 'DateTime required for creating DateTime object and durations', 22 if $@;
+
+ my $span = DateTime::Format::Human::Duration->new();
+ ok(ref $span eq 'DateTime::Format::Human::Duration', 'Obj creation');
+ my $time = time;
+ my $dua = DateTime->from_epoch( 'epoch' => $time );
+ my $dub = DateTime->from_epoch( 'epoch' => ($time + 2), 'locale' => 'fr' );
+ my $duc = DateTime->from_epoch( 'epoch' => ($time + 63) );
+ my $dud = DateTime->from_epoch( 'epoch' => ($time + 3625.4455) );
+ my $due = DateTime->from_epoch( 'epoch' => ($time + 23775453.345) );
+ my $duf = DateTime->from_epoch( 'epoch' => ($time + 61) );
+
+ my $dura = $dua - $dua;
+ # my $durb = $dua - $dub;
+ my $durc = $dua - $dub;
+ my $durd = $dub - $dua;
+ my $dure = $dua - $duc;
+ my $durf = $dua - $dud;
+ my $durg = $dua - $due;
+
+ ok( $span->format_duration($dura) eq 'no time', 'No difference w/ default no_time');
+ ok( $span->format_duration($dura, 'no_time' => 'absolutely no time' ) eq 'absolutely no time', 'No difference w/ no_time');
+ ok( $span->format_duration($dura, 'no_time' => '' ) eq '', 'No difference w/ empty no_time');
+ ok( $span->format_duration($durc) eq '2 seconds', '1 value');
+ ok( $span->format_duration_between($dub, $dua) eq '2 seconds', 'Reverse/Negative is still positive (not "no time")');
+ ok( $span->format_duration_between($dua, $duf) eq '1 minute and 1 second', '2 (singular values)');
+ ok( $span->format_duration($dure) eq '1 minute and 3 seconds', '2 values (mixed)' );
+ ok( $span->format_duration($durf) eq '1 hour, 25 seconds, and 445499897 nanoseconds', '> 2 values (3)');
+
+ ok( $span->format_duration($durg) eq '9 months, 1 day, 4 hours, 17 minutes, 33 seconds, and 345000028 nanoseconds', '> 2 values (5)');
+
+ ok( $span->format_duration($durc, 'future' => 'Hello, You have %s left') eq 'Hello, You have 2 seconds left', 'string with %s');
+ ok( $span->format_duration($durc, 'future' => 'You have') eq 'You have 2 seconds', 'string w/ out %s');
+ ok( $span->format_duration_between($dua, $dub) eq '2 seconds', 'DateTime object method format_duration_between()');
+
+ ok( $span->format_duration_between($dua, $duc, 'past'=>'Was done %s ago.','future' => 'Will be done in %s.') eq 'Will be done in 1 minute and 3 seconds.','$a->format_duration_between($b): $a < $b = future');
+ ok( $span->format_duration_between($duc, $dua, 'past'=>'Was done %s ago.','future' => 'Will be done in %s.') eq 'Was done 1 minute and 3 seconds ago.','$a->format_duration_between($b): $a > $b = past');
+
+ ok( $span->format_duration_between( $duc, $duc->clone()->add('seconds'=> 62) ) eq '1 minute and 2 seconds', 'clone exmple');
+ ok( $span->format_duration( DateTime::Duration->new('seconds'=> 62) ) eq '62 seconds', 'Ambiguous duration (baseless)');
+
+ # test 'locale' key
+ ok( $span->format_duration($dure, 'locale' => 'fr') eq '1 minute et 3 seconds', 'locale key as string format_duration()');
+ ok( $span->format_duration($dure, 'locale' => $dub) eq '1 minute et 3 seconds', 'locale key as $DateTime obj format_duration()');
+ ok( $span->format_duration($dure, 'locale' => $dub->{'locale'}) eq '1 minute et 3 seconds', 'locale key as $DateTime->{\'locale\'} format_duration()');
+ ok( $span->format_duration_between($dub, $duc) eq '1 minute et 1 seconde', 'Object\'s locale used in format_duration_between()');
+
+};
7 t/perlcritic.t
@@ -0,0 +1,7 @@
+#!perl -T
+
+use Test::More;
+eval 'use Test::Perl::Critic';
+plan skip_all => 'Test::Perl::Critic required for testing PBP compliance' if $@;
+plan skip_all => q($ENV{'do_perl_critic_tests'} must be true to run these 'development only' tests) if !$ENV{'do_perl_critic_tests'};
+Test::Perl::Critic::all_critic_ok();
10 t/pod-coverage.t
@@ -0,0 +1,10 @@
+#!perl -T
+
+use Test::More 'tests' => 1;
+eval 'use Test::Pod::Coverage 1.04';
+plan skip_all => 'Test::Pod::Coverage 1.04 required for testing POD coverage' if $@;
+
+Test::Pod::Coverage::pod_coverage_ok( "DateTime::Format::Human::Duration", { 'trustme' => [qr/^(new)$/,], } );
+
+# Locale.pm, es.pm, and fr.pm don;t have POD
+# all_pod_coverage_ok();
6 t/pod.t
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval 'use Test::Pod 1.14';
+plan skip_all => 'Test::Pod 1.14 required for testing POD' if $@;
+all_pod_files_ok();
Please sign in to comment.
Something went wrong with that request. Please try again.