Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Aristotle (5147)

Aristotle
  pagaltzis@gmx.de
http://plasmasturm.org/

Blah blah blah blah blah [technorati.com]

Journal of Aristotle (5147)

Tuesday April 01, 2008
07:02 AM

RFC: DateTime::Weekdays

[ #36022 ]

I just went to set up a cron script to automatically mail the date of our next Perlmonger meeting to our mailing list, and was boggled to find that DateTime includes no way to do things like finding the next Tuesday past a given date, nor is there anything for that on CPAN. Instead you are apparently expected to copy-paste some slightly fiddly code from an FAQ and tweak it.

Sorry, that’s just not acceptable. So I whipped this up:

#!/usr/bin/perl
package DateTime::Weekdays;
use strict;
use DateTime ();
BEGIN { our @ISA = qw( DateTime ) }

sub set_day_of_week {
    my $self = shift;
    my %a = @_;

    my ( $dow, $wanted_dow ) = ( $self->day_of_week(), $a{ dow } );

    if( $a{past} ) { $_ = -$_ for $dow, $wanted_dow }

    my $delta = ( $wanted_dow - $dow + 7 ) % 7;

    return $self if $delta == 0 and not $a{always_differ};

    $delta += 7 if $a{always_differ};
    $delta = -$delta if $a{past};

    return $self->add( days => $delta );
};

sub next   { $_[0]->set_day_of_week( dow => $_[1] ) }
sub prev   { $_[0]->set_day_of_week( dow => $_[1], past => 1 ) }
sub coming { $_[0]->set_day_of_week( dow => $_[1], always_differ => 1 ) }
sub last   { $_[0]->set_day_of_week( dow => $_[1], always_differ => 1, past => 1 ) }

1;

__END__

=head1 NAME

DateTime::Weekdays - find nearby DateTimes by weekday

=head1 SYNOPSIS

use DateTime::Weekdays;
my $dt = DateTime::Weekdays->now;
my ( $Thu, $Fri ) = ( 4, 5 );

print 'First Friday of this month: ',
   $dt->clone->truncate( to => 'month' )
             ->next( $Fri );

print 'Last Friday of this month: ',
   $dt->clone->truncate( to => 'month' )
             ->add( months => 1 )
             ->subtract( days => 1 )
             ->prev( $Fri );

print 'The Friday before today: ',
   $dt->clone->last( $Fri );

print 'London.pm heretics meeting this month: ',
   $dt->clone->truncate( to => 'month' )
             ->coming( $Thu );

=head1 DESCRIPTION

A subclass of L<DateTime> that includes a few methods for weekday-based
calculations.

=head1 INTERFACE

The workhorse method in this class is C<set_day_of_week>. You are not expected
to call this directly; instead, use the convenience methods provided. They all
expect a single parameter: a weekday number.

=head2 next

The nearest future date with the given weekday. May be the current date
itself.

=head2 prev

The nearest past date with the given weekday. May be the current date itself.

=head2 coming

The next future date with the given weekday. This is always at least one day
in the future.

=head2 last

The most recent past date with the given weekday. This is always at least one
day in the past.

=head2 set_day_of_week

This method implements the calculation. It accepts the following named
parameters:

=over 4

=item C<dow>

The desired weekday number in DateTime convention (ie. a value in the range of
1..7 meaning Monday..Sunday). This parameter is requirement.

=item C<past>

A boolean specifying whether to look for a date with the desired weekday is in
the past, relative to the starting date.

=item C<always_differ>

A boolean specifying whether the date returned must always be in the future
(or past).

F.ex., if the date in question is a Friday and you ask for a Friday in the
future, then normally the date itself will be returned with no changes. If
you set this parameter, however, then the Friday in the following week will
be returned.

=back

=head1 AUTHOR

Aristotle Pagaltzis L<mailto:pagaltzis@gmx.de>

=head1 COPYRIGHT AND LICENCE

Copyright (c) 2008, Aristotle Pagaltzis. 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.

Look good? Anyone willing to write a test suite for me? :-P

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • I'd probably be willing to put set_day_of_week into DateTime.pm proper. I don't know about the sugar methods though.

    Regardless, DateTime::Weekdays probably isn't the right namespace. Please come discuss this on the datetime@perl.org list.
    • I know this is not the proper venue – I saw the pointer to the mailing list. I posted here instead of to the list because, well, I’d have to subscribe to the list, and then I’d be subscribed to yet another Perl list. I am on way too many of those already… I guess I should consolidate the mailboxes for the low-traffic lists and suck it up.

      Also, I know it’s not a good name. Don’t worry, I’m not putting this on CPAN quite yet. :-) That’s why I posted an RFC.

      I

      • I think set_day_of_week is a bad name and doesn't belong in the core of DateTime. The name doesn't really uniquely identify what the method does and implies several different inconsistent possibilities for its semantics.

        I think it'd be better if this functionality were wrapped in a DateTime::Set, which would give you the next and previous methods you are looking for. Maybe DateTime::Set::ByWeekday?

        --
        J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers
        • That sounds reasonable; I’m just not sure it makes sense. There is nothing of interest about the set as a whole: it’s no more than a set with weekly recurrence (and you can easily construct it that way if you do need such a set). The only property of interest is that 2 (or 3) specific members of the set are closest to a particular date.

          Also, I wanted to roll in a way to find the passed weekday that falls within the same week as the given date. To me there seems to be no reasonable way to conce

    • If this doesn't make it into DateTime proper, I'd rather it be a mixin that I could use just once and then all my datetime objects would behave thusly.
  • I hope I'm not starting a licensing flamewar here, but your licence section reads:

    This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L.

    Now, the same terms as Perl (?) itself, if we assume that Perl == perl 5, would mean a dual-GPLv2-and-above and Artistic 1.0 *only*. Now, the Artistic 1.0 licence is very vague and is considered neither GPL-compatible nor free by the Free Software Foundation [fsf.org]. And the GPL is well, the GPL [gnu.org] and has its own res

    • Do you realize this is the standard way of licensing Perl modules? What in the world made you single out this one module and this one author for this subject?

      --
      J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers
      • Do you realize this is the standard way of licensing Perl modules? What in the world made you single out this one module and this one author for this subject?

        I didn't single out Aristotle or his module in particular, nor accused him of doing anything wrong in particular. I just noted that in his general request-for-comments for the module because I noticed it there. (Better late than never, I guess).

        I just wanted to note that from now on, it would be a better idea from the legal standpoint to use a different wording of the licensing terms as I explained above to avoid the licensing problems that the "same as perl5" face.

        But thanks for noting that - I'

    • I don’t care. Show me someone who is having a problem.

      • With respect, by the time there is a problem, it's rather too late to change the license text.

        I've updated my modules to read that they are available under the same license as the Perl 5.8.x series.

        • That’s a different matter (and thanks for pointing it out; I’ma fix my module template right now).

          What Shlomi is asking for is a complete change of licences. Now, neither option in the Perl (5.8) licence is my personal best preference. (I like the LGPL best.) But that’s how 99.5% of the CPAN is licensed, and since these terms don’t cause big problems in practice, there’s more value in not forcing the user to evaluate yet another licence than there is in having slightly better

          • I misunderstood your concern; I agree with the desire to stay compatible with the rest of the CPAN.

  • IMO, doing math on dates with DateTime is asking for trouble because you are working with seconds. Date::Simple works with days and Date::Piece extends that to add some nice syntax.