This is my seventh week participating into the weekly challenge.





Easy Challenge

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



In perl 5, I used Datetime and DateTime::Event::Recurrence to do the heavy lifting for this task, looping through each month and have DateTime::Event::Recurrence calculate the weekdays.



https://metacpan.org/pod/DateTime



https://metacpan.org/pod/DateTime::Event::Recurrence



In Raku I just looped through all the days in a year and stored the value in a hash using the Date objects day-of-week method to find if the day is a weekday.

Perl 5 solution

#!/usr/bin/perl # Test: ./ch1.pl 2019 use strict; use warnings; use feature qw /say/; use DateTime; use DateTime::Event::Recurrence; show_weekdays_per_year($ARGV[0]); sub show_weekdays_per_year { my $year = shift || 2019; for my $month (1..12) { show_weekdays_per_month($month, $year); } } sub show_weekdays_per_month { my ($month, $year) = @_; my $working_days = DateTime::Event::Recurrence->weekly( days => [1 .. 5] ); # Start of the month my $start = DateTime->new( year => $year, month => $month, day => 1 ); # End of the month my $end = $start->clone; $end->add( months => 1 ) ->subtract( days => 1 ); my $num_days = $working_days->as_list( start => $start, end => $end ); say $start->month_abbr() . ": $num_days days"; }

Output

Jan: 23 days Feb: 20 days Mar: 21 days Apr: 22 days May: 23 days Jun: 20 days Jul: 23 days Aug: 22 days Sep: 21 days Oct: 23 days Nov: 21 days Dec: 22 days

Raku solution

# Test: perl6 ch1.p6 2019 use v6.d; sub MAIN(Int $year = 2019) { show-weekdays-per-year($year); } # Weekdays per year sub show-weekdays-per-year(Int $year) { my $current = Date.new($year, 1, 1); my %months{Int}; my @mon = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ); while ($current.year == $year) { %months{$current.month}++ if ($current.day-of-week == (1 .. 5).any); $current++; } for %months.keys.sort -> $key { say @mon[$key - 1] ~ ': ' ~ %months{$key} ~ ' days'; } }

Output

Jan: 23 days Feb: 20 days Mar: 21 days Apr: 22 days May: 23 days Jun: 20 days Jul: 23 days Aug: 22 days Sep: 21 days Oct: 23 days Nov: 21 days Dec: 22 days

Hard Challenge

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.

For the perl5 solution I used DateTime to model the two months and Web::Scraper to scrape the daylight data for each of these month. Then I converted the daylight data from hh::mm::ss into seconds and calculated the difference between the two months. Then I used Time::Seconds to output the data in a readable format.

The reason I decided to scrape the data was that I didn’t want to bother with the api, not figuring out how to cut and paste the data out of the table.

The Raku solution is basically the same, except I don’t Scrape or use Time::Piece.





EDIT: I fixed a bug where the convert_time_to seconds wasn’t calculated correct. It was adding 60 rather than the number of seconds in the hh:mm:ss string.

Perl 5 solution

#!/usr/bin/perl # Test: ./ch2.pl use strict; use warnings; use URI; use Web::Scraper; use feature qw /say/; use String::Util qw /trim/; use DateTime; use Time::Seconds; my $nov = DateTime->new( year => 2019, month => 11, day => 1 ); my $dec = DateTime->new( year => 2019, month => 12, day => 1 ); compare_lengths($nov, $dec); # Compare the daylight lengths of 2 months sub compare_lengths { my ($date1, $date2) = @_; # Get the data from the web my $date1_data = get_data($date1->month, $date1->year); my $date2_data = get_data($date2->month, $date2->year); # Calculate totals my $date1_total = calculate_daylight_total($date1_data); my $date2_total = calculate_daylight_total($date2_data); my $difference = $date1_total - $date2_total; # Print the output say $date1->month_abbr . ' ' . $date1->year . ' has ' . Time::Seconds->new($date1_total)->pretty . ' of daylight.'; say $date2->month_abbr . ' ' . $date2->year . ' has ' . Time::Seconds->new($date2_total)->pretty . ' of daylight.'; say "The difference is: " . Time::Seconds->new($difference)->pretty . '.'; } # Calculates the total daylight hours from data sub calculate_daylight_total { my ($data) = @_; my $total = 0; for my $time_string (@{$data}) { $total += convert_time_to_seconds($time_string); } return $total; } # Convert hh::mm::ss to seconds sub convert_time_to_seconds { my ($hh, $mm, $ss) = split(':', shift); return $hh * 3600 + $mm * 60 + $ss; } # Get the data from the web sub get_data { my ($month, $year) = @_; my @data; # Scrape the date my $url = 'https://www.timeanddate.com/sun/uk/london?' . "month=$month&year=$year"; my $times = scraper { process 'table[id="as-monthsun"] td', "times[]" => { td_text => 'TEXT', } }; my $res = $times->scrape( URI->new( $url ) ); # Parse the times for my $time (@{$res->{times}}) { my $e_time = trim($time->{td_text}); push @data, $e_time if ($e_time =~ /^(\d)+\:(\d)+\:(\d)+$/); } # Output the data return \@data; }

Output

Nov 2019 has 11 days, 3 hours, 0 minutes, 40 seconds of daylight.

Dec 2019 has 10 days, 5 hours, 45 minutes, 1 second of daylight.

The difference is: 21 hours, 15 minutes, 39 seconds.

Raku solution

# Test: perl6 ch2.p6 use v6.d; # Box configurations sub MAIN () { my $date1 = Date.new(2019,11,1); my $date2 = Date.new(2019,12,1); compare_lengths($date1, $date2); } sub compare_lengths(Date $date1, Date $date2) { # Months my @mon = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ); # Get the data from the web my @date1_data = get-data($date1); my @date2_data = get-data($date2); # Calculate totals my $date1_total = calculate-daylight-total(@date1_data); my $date2_total = calculate-daylight-total(@date2_data); my $difference = $date1_total - $date2_total; # Print the output say @mon[$date1.month - 1] ~ ' ' ~ $date1.year ~ ' has ' ~ convert-seconds-to-string($date1_total) ~ ' of daylight.'; # Print the output say @mon[$date2.month - 1] ~ ' ' ~ $date2.year ~ ' has ' ~ convert-seconds-to-string($date2_total) ~ ' of daylight.'; say "The difference is: " ~ convert-seconds-to-string($difference) ~ '.'; } # Calculates the total daylight hours from data sub calculate-daylight-total(@data) { my $total = 0; for (@data) -> $daylight { $total += convert-time-to-seconds($daylight).Int; } return $total } # Convert seconds to readable string sub convert-seconds-to-string (Int $seconds) { return ( $seconds.polymod(60, 60, 24) Z ('seconds', 'minutes', 'hours', 'days') ).reverse.join(", "); } # Convert hh::mm::ss to seconds sub convert-time-to-seconds(Str $time_string) { my ($hh, $mm, $ss) = $time_string.split(':'); return $hh * 3600 + $mm * 60 + $ss; } # A bit of a cheat, I built the Scraper # in perl5 so don't reinvent the wheel # Gets the daytime data sub get-data(Date $date) { my %data = ( '2019-11-01' => ( '9:40:44', '9:37:10', '9:33:37', '9:30:07', '9:26:38', '9:23:11', '9:19:45', '9:16:22', '9:13:01', '9:09:42', '9:06:25', '9:03:11', '8:59:59', '8:56:50', '8:53:44', '8:50:40', '8:47:39', '8:44:42', '8:41:48', '8:38:57', '8:36:09', '8:33:25', '8:30:45', '8:28:09', '8:25:36', '8:23:08', '8:20:44', '8:18:24', '8:16:09', '8:13:59' ), '2019-12-01' => ( '8:11:53', '8:09:53', '8:07:57', '8:06:07', '8:04:22', '8:02:42', '8:01:08', '7:59:40', '7:58:17', '7:57:00', '7:55:50', '7:54:45', '7:53:46', '7:52:54', '7:52:07', '7:51:27', '7:50:54', '7:50:27', '7:50:06', '7:49:52', '7:49:44', '7:49:43', '7:49:48', '7:50:00', '7:50:19', '7:50:44', '7:51:15', '7:51:53', '7:52:37', '7:53:27', '7:54:24' ), ); return @(%data.{$date}); }

Output

Nov 2019 has 11 days, 3 hours, 0 minutes, 40 seconds of daylight.

Dec 2019 has 10 days, 5 hours, 45 minutes, 1 seconds of daylight.

The difference is: 0 days, 21 hours, 15 minutes, 39 seconds.