Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

RFC: List of first day of each month

by TieUpYourCamel (Scribe)
on Jan 31, 2020 at 15:17 UTC ( [id://11112183]=perlmeditation: print w/replies, xml ) Need Help??

My task here is to make a list of all of the "first day of the month" days between a start date and an end date. I'm interested to hear thoughts about my solution:
use strict; use warnings; use Time::Piece; use feature 'say'; my $date = Time::Piece->strptime( localtime->year() - 4 . " 01 01", "%Y %m %d" ); my $endDate = localtime(); while ( $date <= $endDate ) { say $date; # Get the next first of the month by going to the end of the # month and add one day my $year = $date->year; my $mon = $date->mon; my $day = $date->month_last_day; $date = Time::Piece->strptime( "$year $mon $day", "%Y %m %d" ) +; $date += Time::Seconds::ONE_DAY; }

Replies are listed 'Best First'.
Re: RFC: List of first day of each month
by haukex (Archbishop) on Jan 31, 2020 at 16:47 UTC

    TIMTOWTDI, I'm a fan of DateTime:

    use warnings; use strict; use DateTime; my $now = DateTime->now; my $start = DateTime->new( year=>$now->year-4 ); my $end = $now; my $dt = $start->clone; $dt->truncate(to=>'month')->add(months=>1) if $dt->day!=1; for ( ; $dt<$end; $dt->add(months=>1) ) { print $dt->ymd, "\n"; }
Re: RFC: List of first day of each month
by choroba (Cardinal) on Jan 31, 2020 at 15:28 UTC
    Looks good. I'd probably do it the same way. Alternatively, you can implement part of the logic yourself:
    while ($date <= $endDate) { say $date; $date = 'Time::Piece'->strptime( join(' ', $date->year + ($date->mon == 12), $date->mon % 12 + 1 +, 1), '%Y %m %d'); }
    Update: You can shorten your original solution. There's no need to parse the date again, just add the days.
    while ($date <= $endDate) { say $date; $date += Time::Seconds::ONE_DAY * $date->month_last_day; }

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: RFC: List of first day of each month
by 1nickt (Canon) on Jan 31, 2020 at 15:53 UTC

    Hi,

    I eschew Time::Piece for date math. It has some weird internal issues with constants and overloading, which I don't completely understand, but people I trust including some wiser and more experienced Monks than I, recommend against it. (See e.g. comments in this discussion.)

    Alternate solution using Time::Moment which IIUC is as accurate as DateTime but considerably lighter-weight:

    use v5.10; use Time::Moment; my $tm1 = Time::Moment->new(year => '2019', month => '6', day => '1'); my $tm2 = Time::Moment->now; $tm1 = (say $tm1->strftime('%c') and $tm1->plus_months(1)) while $tm1 +<= $tm2;
    Output:
    Mon Jul 1 00:00:00 2019 Thu Aug 1 00:00:00 2019 Sun Sep 1 00:00:00 2019 Tue Oct 1 00:00:00 2019 Fri Nov 1 00:00:00 2019 Sun Dec 1 00:00:00 2019 Wed Jan 1 00:00:00 2020

    Hope this helps!


    The way forward always starts with a minimal test.

      ++ Thank you very much for bringing this up. I had somehow missed this package and it’s exactly what I need for something right now. I love DateTime for its pedantic correctness and flexibility but it’s untenable for situations when you want to be creating thousands or millions of objects a second. I had backed up to using raw Date::Calc but it’s harder to mentally track and debug and pass around in a consistent way.

Re: RFC: List of first day of each month
by hippo (Bishop) on Jan 31, 2020 at 16:27 UTC

    TIMTOWTDI, of course. A slightly different approach, still with Time::Piece but adding a month at a go. It's shorter and (to me) a little clearer.

    #!/usr/bin/env perl use strict; use warnings; use Time::Piece '1.32'; my $date = localtime->add_years (-4)->truncate (to => 'year'); my $end_date = localtime; while ($date <= $end_date) { print "$date\n"; $date = $date->add_months (1); }

    There's nothing wrong with your method AFAICS.

Re: RFC: List of first day of each month -- oneliner
by Discipulus (Canon) on Jan 31, 2020 at 20:09 UTC
    Hello TieUpYourCamel,

    it is a nice question and you posted nice code and had back interesting and wise answers.. but when I see so simple tasks I cannot resist to try a oneliner solution ;)

    perl -e "while($ARGV[0]<=$ARGV[1]){$t=scalar localtime($ARGV[0]);print + $t.$/ if $t=~/\s1\s/;$ARGV[0]+=86400}" 1580500427 1591220427 Sat Feb 1 20:53:47 2020 Sun Mar 1 20:53:47 2020 Wed Apr 1 21:53:47 2020 Fri May 1 21:53:47 2020 Mon Jun 1 21:53:47 2020

    it is not bugged for year 1 ;)

    L*

    PS shorter

    perl -e "$t=shift;while($t<=$ARGV[0]){print gmtime($t)=~/(.*\w 1)/?$1 +.$/:'';$t+=86400}" # or perl -e "$t=shift;while($t<=$ARGV[0]){print gmtime($t)=~/(.* 1)/?$1.$ +/:'';$t+=86400}" # or perl -le "$t=shift;while($t<=$ARGV[0]){gmtime($t)=~/(.* 1)/?print$1:0 +;$t+=86400}" 1580500427 1591220427 Sat Feb 1 Sun Mar 1 Wed Apr 1 Fri May 1 Mon Jun 1

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: RFC: List of first day of each month
by Tux (Canon) on Jan 31, 2020 at 16:03 UTC

    I might be old-fashioned, but I think Time::Piece is overkill for this task:

    sub usage { my $err = shift and select STDERR; say "usage: $0 YYYYMMDD [YYYYMMDD]"; exit $err; } # usage use Time::Local "timelocal_posix"; my $from = shift or usage (1); my $to = shift || do { my @d = localtime; (($d[5] + 1900) * 100 + $d[4] + 1) * 100 + $d[3 +] }; my ($y, $m, $d, $Y, $M, $D) = "$from:$to" =~ m{ ([1-9][0-9]{3}) -? (0[1-9]|1[0-2]) -? (0[1-9]|[12][0-9]|3[01]) : ([1-9][0-9]{3}) -? (0[1-9]|1[0-2]) -? (0[1-9]|[12][0-9]|3[01]) }x or usage (1); $from gt $to and usage (1); my @w = qw( Sun Mon Tue Wed Thu Fri Sat ); $to = timelocal_posix (0, 0, 13, $D, $M - 1, $Y - 1900); if ($d > 1) { $d = 1; $m++; } if ($m > 12) { $m = 1; $y++; } while (($from = timelocal_posix (0, 0, 12, $d, $m - 1, $y - 1900)) < $ +to) { my @d = localtime ($from); printf "%s %4d-%02d-%02d\n", $w[$d[6]], $d[5] + 1900, $d[4] + 1, $ +d[3]; if (++$m > 12) { $m = 1; $y++; } }

    Enjoy, Have FUN! H.Merijn
Re: RFC: List of first day of each month
by cavac (Parson) on Feb 17, 2020 at 16:20 UTC

    As a side note, you can also look into Custom date calculations using Date::Manip for the weird and wonderful possibilities of using Date::Manip for doing custom date maths.

    perl -e 'use Crypt::Digest::SHA256 qw[sha256_hex]; print substr(sha256_hex("the Answer To Life, The Universe And Everything"), 6, 2), "\n";'

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://11112183]
Approved by choroba
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2024-03-28 10:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found