📘 Computing leap years in Perl 6

Tell if the given year is leap or common.

The algorithm for detecting whether the year is leap includes a few divisibility tests. Take an extract in the pseudocode from Wikipedia:

if (year is not divisible by 4) then (it is a common year)
else if (year is not divisible by 100) then (it is a leap year)
else if (year is not divisible by 400) then (it is a common year)
else (it is a leap year)

It is possible to implement the above sequence of ifs and elses in Perl 6, but it is a better idea to join conditions using the logical operators.

my $year = 2018;
say ($year %% 400 or $year % 100 and $year %% 4) ??
    'Leap' !! 'Common';

Notice that both the modulo % and divisibility %% operators are used, which allow avoiding Boolean negations in the sub-conditions.

The following program prints the list of leap years in the range 1800–2400:

for 1800 .. 2400 -> $year {
    say $year if $year %% 400 or $year % 100 and $year %% 4;
}

There may be some considerations regarding the efficiency of the sequence of the checks because each year is first tested against 400, while it may be more optimal to check first if the year is divisible by 4. If this becomes an important argument, then the ifelse chain may be more efficient. To achieve an even higher speed, a pre-calculated array of leap years is better.

📘 Datetime arithmetic in Perl 6

Find the difference between the two dates. Add a given number of days to the date.

The DateTime class in Perl 6 defines the + and  operators, which can be used in combination with either another DateTime object or with the Duration object.

Let us first find the difference between the two given dates:

my $date1 = DateTime.new('2017-12-31T23:59:50');
my $date2 = DateTime.new('2018-01-01T00:00:10');

say $date2 - $date1; # 20

The type of the $date2 - $date1 expression is Duration. In the numeric context, it returns the number of seconds. Therefore, there are 20 seconds between our dates at hand.

Duration object can also be used instead of the second DateTime object.

For example, increase the given date by two minutes:

my $now = DateTime.now();
my $when = $now + Duration.new(120);
say "$now -> $when";

Or learn what were the date and time a week ago:

my $back = $now - Duration.new(3600 * 24 * 7);
say $back;

In the current design of the language, the constructor of the Duration class needs a number of seconds.

📘 Formatted date in Perl 6

Print the current date in an even better format.

In Perl 6, there is a built-in DateTime class. It is equipped with a few useful methods, so there’s no need to use external modules for many standard tasks. Save the current moment in the $now variable:

my $now = DateTime.now;

The easiest thing is to print the proper time using a dedicated method:

say $now.hh-mm-ss; # 22:37:16

To achieve a more granular control over both the date and time parts, use the formatter attribute of the constructor together with methods like day and month to get separate parts of the date and time (see Task 87, Current date and time): 

my $now = DateTime.now(
    formatter => {
        sprintf '%02d.%02d.%04d, %02d:%02d', 
        .day, .month, .year, .hour, .minute
    }
);

say $now; # 14.10.2017, 22:41

The formatting string '%02d.%02d.%04d, %02d:%02d' uses the standard POSIX printf format. The sprintf function forms a string that is returned when a DateTime object (the $now variable, in our case) is stringified. For instance, it is used when the variable is interpolated in a string:

say "Current date and time: $now.";

📘 Current date and time in Perl 6

Print current date and time as an epoch and in a human-readable format.

In Perl 6, the time function returns the current time as the Unix epoch:

say time;

The output is something like this: 1495785518.

For manipulating dates and times, use the built-in DateTime class:

say DateTime.now;

The date is now in a more human-readable format, although still is overloaded with many details: 2017-05-26T10:02:20.500209+02:00.

To access the separate elements of date and time, use the methods on the variable of the DateTime class:

my $dt = DateTime.now;

say $dt.day;   # 26
say $dt.month; # 5
say $dt.year;  # 2017
say $dt.hour;  # 10
say $dt.minute; # 9
say $dt.second; # 5.55802702903748

The meaning of all the elements is quite straightforward.

All the values except seconds are integer. For the seconds, you may want to take the integer part only:

say $dt.second.Int;