Perl Weekly Challenge 037: Weekdays and Daylight Gain/Loss

Weekdays

Write a script to calculate the total number of weekdays (Mon-Fri) in each month of the year 2019.

I used the core module Time::Piece and its companion from the same distribution, Time::Seconds. Let’s start on the first day of the month, and keep adding one day while we stay in the same month. Along the way, count the days that aren’t Saturdays and Sundays.

#!/usr/bin/perl
use warnings;
use strict;

use Time::Piece;
use Time::Seconds qw{ ONE_DAY };

sub days_in_month {
    my ($month) = @_;
    my $date = 'Time::Piece'->strptime("2019 $month 1 12:00",
                                       '%Y %b %d %H:%M');
    my $count = 0;
    while ($date->month eq $month) {
        ++$count unless grep $date->day eq $_, qw( Sat Sun );
        $date += ONE_DAY;
    }
    return $count
}

And here’s a test that the numbers are correct:

use Test::More tests => 12;

is days_in_month('Jan'), 23, 'Jan';
is days_in_month('Feb'), 20, 'Feb';
is days_in_month('Mar'), 21, 'Mar';
is days_in_month('Apr'), 22, 'Apr';
is days_in_month('May'), 23, 'May';
is days_in_month('Jun'), 20, 'Jun';
is days_in_month('Jul'), 23, 'Jul';
is days_in_month('Aug'), 22, 'Aug';
is days_in_month('Sep'), 21, 'Sep';
is days_in_month('Oct'), 23, 'Oct';
is days_in_month('Nov'), 21, 'Nov';
is days_in_month('Dec'), 22, 'Dec';

Daylight Gain/Loss

Write a script to find out the DayLight gain/loss in the month of December 2019 as compared to November 2019 in the city of London. You can find out sunrise and sunset data for November 2019 and December 2019 for London.

We need to fetch a HTML page. I usually reach for WWW::Mechanize. For simple tasks, LWP::UserAgent is usually enough, but simple tasks tend to grow to complex tasks where the advanced features of the former module might become handy.

To extract tables from HTML, I’ve used HTML::TableExtract for years. Just specify what table you’re interested in and the module gives it to you as a list of rows.

So, we’ll download the page, extract the table, and sum the daylight times for each month separately. The difference of the two sums gives us the daylight loss (yes, days are getting shorter) in December compared to November.

You can see a common indecency in the solution: it implements its own templating system. One should rather use an existing one from CPAN, but here I really needed to just insert the month name.

Also, I calculated the hours and minutes from the seconds myself. It was faster than searching CPAN for a module that implements it (there should be at least two!)

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use Syntax::Construct qw{ /r };  # Fix syntax highlighting on blogs.perl.org: /

use WWW::Mechanize;
use HTML::TableExtract;

my $url_template
    = 'https://www.timeanddate.com/sun/uk/london?month=%m%&year=2019';
my $mech = 'WWW::Mechanize'->new;

my %seconds;
for my $month (11, 12) {
    my $url = $url_template =~ s/%m%/$month/r;
    $mech->get($url);
    my $te = 'HTML::TableExtract'->new;
    $te->parse($mech->content);
    my $table = $te->first_table_found;
    for ($table->rows) {
        my $time = $_->[3] or next;

        my ($h, $m, $s) = $time =~ /^([0-9]+):([0-9]+):([0-9]+)$/
            or next;
        $s += $m * 60 + $h * 60 * 60;
        $seconds{$month} += $s;
    }
}

my $s       = $seconds{11} - $seconds{12};
my $seconds = $s % 60;
my $m       = ($s - $seconds) / 60;
my $minutes = $m % 60;
my $hours   = ($m - $minutes) / 60;

say sprintf "%d seconds or %d:%02d:%02d", $s, $hours, $minutes, $seconds;

Unfortunately, the table doesn’t contain just the needed times, there’s the header and some other “noisy” lines. That’s why we need the or next lines.

The output is

71208 seconds or 19:46:48

Leave a comment

About E. Choroba

user-pic I blog about Perl.