Perl Weekly Challenge 347.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 347.
Task 1: Format Date
Submitted by: Mohammad Sajid Anwar
You are given a date in the form: 10th Nov 2025.
Write a script to format the given date in the form: 2025-11-10 using the set below.
@DAYS = ("1st", "2nd", "3rd", ....., "30th", "31st")
@MONTHS = ("Jan", "Feb", "Mar", ....., "Nov", "Dec")
@YEARS = (1900..2100)
Example 1
Input: $str = "1st Jan 2025"
Output: "2025-01-01"
Example 2
Input: $str = "22nd Feb 2025"
Output: "2025-02-22"
Example 3
Input: $str = "15th Apr 2025"
Output: "2025-04-15"
Example 4
Input: $str = "23rd Oct 2025"
Output: "2025-10-23"
Example 5
Input: $str = "31st Dec 2025"
Output: "2025-12-31"
Given the rigid input format, and if I don’t care about detecting errors, I can easily identify the number of the day, the name of the month and the year with a regular expression, and I may convert the month name to a number using a hash. This yields a two liner.
Examples:
perl -E '
$m{$_}=$c++ for qw(X Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
for(@ARGV){/(\d+)\w*\s+(\w+)\s+(\d+)/; say sprintf "%s -> %d-%02d-%02d", $_, $3, $m{$2}, $1}
' "1st Jan 2025" "22nd Feb 2025" "15th Apr 2025" "23rd Oct 2025" "31st Dec 2025"
Results:
1st Jan 2025 -> 2025-01-01
22nd Feb 2025 -> 2025-02-22
15th Apr 2025 -> 2025-04-15
23rd Oct 2025 -> 2025-10-23
31st Dec 2025 -> 2025-12-31
The full code is:
1 # Perl weekly challenge 347
2 # Task 1: Format Date
3 #
4 # See https://wlmb.github.io/2025/11/14/PWC347/#task-1-format-date
5 use v5.36;
6 use feature qw(try);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 D0 D1...
9 to translate the dates Dn into the format YYYY-MM-DD
10 The dates Dn are strings of the form "ordinal month year"
11 where ordinal is one of 1st, 2nd, 3rd, 4th, 5th...
12 month is one of the abbreviations Jan, Feb, Mar, Apr...
13 and year is a four digit year number
14 FIN
15 my @months=qw(jan feb mar apr may jun jul aug sep oct nov dec);
16 my @lengths=qw(0 31 28 31 30 31 30 31 31 30 31 30 31);
17 my %month_nums;
18 my $count=1;
19 $month_nums{$_}=$count++ for @months;
20 my %lengths;
21 for(@ARGV){
22 try {
23 die "Wrong format; $_" unless /^(\d+)(th|st|nd|rd)\s*([[:alpha:]]{3})\s*(\d{4})$/;
24 my ($day, $ending, $month_name, $year)=($1, $2, $3, $4);
25 die "Wrong ordinal: $day$ending" unless "$day$ending"=~/(1st|2nd$|3rd|\dth)$/;
26 my $month_num = $month_nums{lc $month_name};
27 die "Wrong month name: $month_name" unless defined $month_num;
28 my $leap=($year%4==0&&$year%100!=0)||($year%400==0);
29 die "Non-existing date: $_" unless
30 $day>0 && ($day <= $lengths[$month_num] || ($month_num==2 && $leap && $day==29));
31 say sprintf "%s -> %s-%02d-%02d", $_, $year, $month_num, $day;
32 }
33 catch($e){warn $e; }
34 }
Examples:
./ch-1.pl "1st Jan 2025" "22nd Feb 2025" "15th Apr 2025" "23rd Oct 2025" "31st Dec 2025"
Results:
1st Jan 2025 -> 2025-01-01
22nd Feb 2025 -> 2025-02-22
15th Apr 2025 -> 2025-04-15
23rd Oct 2025 -> 2025-10-23
31st Dec 2025 -> 2025-12-31
Examples with errors and leap years:
./ch-1.pl "2025 1st Jan" "22st Feb 2025" "15th Abr 2025" "29th Feb 2025" "29th Feb 2025" \
"29th Feb 2024" "29th Feb 2000" "29th Feb 2100" 2>&1
Results:
Wrong format; 2025 1st Jan at ./ch-1.pl line 24.
Wrong ordinal: 22st at ./ch-1.pl line 26.
Wrong month name: Abr at ./ch-1.pl line 28.
Non-existing date: 29th Feb 2025 at ./ch-1.pl line 30.
Non-existing date: 29th Feb 2025 at ./ch-1.pl line 30.
Non-existing date: 29th Feb 2100 at ./ch-1.pl line 30.
29th Feb 2024 -> 2024-02-29
29th Feb 2000 -> 2000-02-29
Task 2: Format Phone Number
Submitted by: Mohammad Sajid Anwar
You are given a phone number as a string containing digits, space and dash only.
Write a script to format the given phone number using the below rules:
1. Removing all spaces and dashes
2. Grouping digits into blocks of length 3 from left to right
3. Handling the final digits (4 or fewer) specially:
- 2 digits: one block of length 2
- 3 digits: one block of length 3
- 4 digits: two blocks of length 2
4. Joining all blocks with dashes
Example 1
Input: $phone = "1-23-45-6"
Output: "123-456"
Example 2
Input: $phone = "1234"
Output: "12-34"
Example 3
Input: $phone = "12 345-6789"
Output: "123-456-789"
Example 4
Input: $phone = "123 4567"
Output: "123-45-67"
Example 5
Input: $phone = "123 456-78"
Output: "123-456-78"
I just follow instructions, obtaining a one-liner.
Examples:
perl -E '
for(@ARGV){$i=$_;tr/- //d;1 while s/(\d{3})(\d{2})/$1-$2/;s/(\d{2})(\d{2})$/$1-$2/;say "$i -> $_"}
' 1-23-45-6 1234 "12 345-6789" "123 4567" "123 456-78"
Results:
1-23-45-6 -> 123-456
1234 -> 12-34
12 345-6789 -> 123-456-789
123 4567 -> 123-45-67
123 456-78 -> 123-456-78
The full code is:
1 # Perl weekly challenge 347
2 # Task 2: Format Phone Number
3 #
4 # See https://wlmb.github.io/2025/11/14/PWC347/#task-2-format-phone-number
5 use v5.36;
6 use feature qw(try);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 N0 N1...
9 to reformat the telephone numbers Ni,
10 each of which contains only digits, dashes and spaces.
11 FIN
12 for(@ARGV){
13 try {
14 my $input=$_;
15 die "Only digits, dashes and spaces allowed: $_" unless /^[-\s\d]+$/;
16 tr/- //d; # remove dashes and spaces
17 1 while s/(\d{3})(\d{2})/$1-$2/; # make groups of 3 separated by dashes
18 s/(\d{2})(\d{2})$/$1-$2/; # if 4 digits remain divide in two groups
19 # otherwise, less than four remain, leave alone
20 say "$input -> $_";
21 }
22 catch($e){warn $e}
23 }
Example:
./ch-2.pl 1-23-45-6 1234 "12 345-6789" "123 4567" "123 456-78"
Results:
1-23-45-6 -> 123-456
1234 -> 12-34
12 345-6789 -> 123-456-789
123 4567 -> 123-45-67
123 456-78 -> 123-456-78
/;
Written on November 14, 2025