Reputation: 447
I'm trying to transform strings of the form 201302_1
to 2013-02-09
using a hash. Unfortunately, my knowledge of Perl is rather limited and I haven't gotten it to work yet.
BEGIN {
use strict;
use warnings;
use 5.010;
my %cycle = qw (
1 '09' 2 '12' 3 '12' 4 '18' 5 '21'
6 '24' 7 '27' 8 '01' 9 '03' 10 '06'
);
}
s/(\d{4})(\d{2})_(\d+)$/$1-$2-$cycle{$3}/g
It would be great if this script also solved the issue of adding 1 month (increasing $2
and eventually $1
) if $3
is 8, 9 or 10.
I'm running this from the terminal as perl -p script.pl sample.txt
Edit: I ended up writing the following based on the answers which seems to work:
my %cycle = qw (
1 09 2 12 3 12 4 18 5 21
6 24 7 27 8 01 9 03 10 06
);
s{(\d{4})(\d{2})_(\d+)1\.csv$}{
my @r = (undef, $1, $2, $3);
if ($3 > 7) {
$r[2] = sprintf("%02d", $r[2]+1);
}
if ($r[2] > 12) {
$r[2] = "01";
$r[1] = $r[1] + 1;
}
"$r[1]-$r[2]-$cycle{$r[3]}";
}ge;
Upvotes: 0
Views: 78
Reputation: 6642
use strict;
use warnings;
use 5.010;
use DateTime;
my $input = $ARGV[0];
my %cycle2day = ( 1 => 9, 2 => 12, 3 => 12, 4 =>18, 5 => 21,
6 => 24, 7 => 27, 8 => 1, 9 => 3, 10 => 6,
);
my ($year, $month, $cycle) = $input =~ /(\d{4})(\d{2})_(\d+)/;
unless (grep { $cycle == $_ } keys %cycle2day) {
die "Unknown cycle $cycle!";
}
my $date = DateTime->new( year => $year,
month => $month,
day => $cycle2day{$cycle},
);
if ( $cycle == 8 or $cycle == 9 or $cycle == 10 ) {
$date->add(months => 1);
}
say $date->ymd;
Outputs:
C:\>perl test_cycle.pl 201302_1
2013-02-09
C:\>perl test_cycle.pl 201302_9
2013-03-03
Upvotes: 0
Reputation: 67900
Solving date math with regexes is usually not a good idea, as it is fairly complicated and filled with edge cases. Use Time::Piece
instead:
use strict;
use warnings;
use Time::Piece;
my %cycle = qw (
1 09 2 12 3 12 4 18 5 21
6 24 7 27 8 01 9 03 10 06
);
while (<DATA>) {
chomp;
s/_(\d+)$/$cycle{$1}/;
my $t = Time::Piece->strptime($_, "%Y%m%d");
$t = $t->add_months(1);
print $t->strftime("%Y-%m-%d");
}
__DATA__
201302_1
Here I have guessed (since you didn't say) that the values in %cycle
are days, and therefore can be treated like days. So we just replace the digit with the "day" from the hash, then use strptime
to parse the date, add a month, and then print the date, using strftime
.
Note that this code assumes the whole string is a date, so you may need to tweak it for use on other kinds of input.
Note also that you can change <DATA>
to <>
to use the script as you did before, but without the -p
switch, i.e.:
perl script.pl sample.txt
Upvotes: 2
Reputation: 98388
Your strict and warnings are only scoped to the BEGIN block so you aren't seeing the warning that results because %cycle is also only scoped to the begin block, and a different (empty) %cycle hash is actually used in the substitution.
Try
use strict;
use warnings;
use 5.010;
my %cycle;
BEGIN {
%cycle = ...
Since you plan on adjusting the month and year, I'd avoid the substitution and instead do:
my ($year, $month, $cycle) = /\A(\d{4})(\d{2})_(\d+)\z/ or die "bad input: $_\n";
my $day = $cycle{$cycle};
if ( $cycle >= 8 ) { # or have a second hash that indicates "following month"
Upvotes: 4