perl to open csv file with dynamic date in it? - perl

How can I open a CSV file with a date that changes each day, where the date format is yyyy for year, dd for day and mmm for a 3 letter month.
This is as far as I've got
#!/usr/bin/perl
use strict;
use warnings;
#Set-up Input Files
#Inputfile
$INFILE = "C:\\DBR_%yyyy\\%b\\Failures_input%d%h\\.csv";
#Open input file for reading
open (INPUT,"$INFILE") or die " cannot open $INFILE ";

It is very unclear what you are asking for, and you don't mention %d and %h in your pattern.
If you want to open the latest CSV file then you need to do a nested search of the path, finding the latest date and time.
Here is something that may help. This code generates the path to the file that would be created for the current date and time. It uses the Time::Piece module, which is part of core Perl and shouldn't need installing. By default it overloads the localtime operator so that it returns a Time::Piece object in scalar context. That allows the module's utility methods to be applied directly.
use strict;
use warnings;
use Time::Piece;
my $failures_file = localtime->strftime('C:\DBR_%Y\%b\Failures_input%d%b.csv');
my $invoices_file = localtime->strftime('C:\%Y\%b\invoices%d%b.csv');
print $failures_file, "\n";
print $invoices_file, "\n";
output
C:\DBR_2014\Mar\Failures_input17Mar.csv
C:\2014\Mar\invoices17Mar.csv
However I think it is more likely that you want the name of the latest file with a path of that form, which is a little more complex (and a dreadful system design). Please verify your requirement and we will be able to help you further.

I wanted to write an answer about the localtime Perl function, but its documentation is so complete that it even includes stuff about the three-letter abbreviations.
A few remarks on your string literal for the filename:
You can use single forward slashes, even on Windows
You don't have to escape the dot with a backslash
You can "interpolate" variables into strings: "DBR_$yyyy" if $yyyy is a variable.

I second SzG's general recommendations.
The following shows how to add the date formatting to your filename using two core modules.
Using Time::Piece (in Perl core since version 5.8):
use Time::Piece;
use strict;
use warnings;
my $infile = localtime->strftime('C:\DBR_%Y\%b\Failures_input%d.csv');
Or using POSIX and strftime if you're working on an ancient box. To find the available specifiers, just google strftime:
use POSIX qw(strftime);
use strict;
use warnings;
my $infile = strftime 'C:\DBR_%Y\%b\Failures_input%d.csv', localtime;
Outputs:
C:\DBR_2014\Mar\Failures_input16.csv

Related

Convert ISO-8601 timestamp from UTC-7:00 to "Europe/Amsterdam" (CEST) time, which is UTC+2:00

In Perl I use a bash command to convert an ISO-8601 timestamp to local format YYYY-mm-dd HH:MM:SS.
These timestamps, for example, are used in Google Cloud JSON APIs. For example
2016-08-09T17:05:05.414-07:00. Check the source below for a working example.
This works fine, but is there a way to accomplish the same in Perl without using a bash command? Of course I'm sure there is, but I couldn't find anything simple.
#!/usr/bin/perl -w
use strict;
use warnings;
my $f = `date -d 2016-08-09T17:05:05.414-07:00 +'%Y-%m-%d %H:%M:%S'`;
chop $f;
print "$f\n"; # 2016-08-10 02:05:05
The DateTime modules are the obvious choice for this. Your original string is formatted according to the ISO-8601 standard so the DateTime::Format::ISO8601 will parse it directly
Then it is simply a matter of setting the time zone to zero and formatting the result as required
Here's the code
use strict;
use warnings 'all';
use feature 'say';
use DateTime::Format::ISO8601;
say localise_time('2016-08-09T17:05:05.414-07:00');
sub localise_time {
my ($dt) = #_;
$dt = DateTime::Format::ISO8601->parse_datetime($dt);
$dt->set_time_zone('Europe/Amsterdam');
$dt->strftime('%Y-%m-%d %H:%M:%S');
}
output
2016-08-10 02:05:05

Perl: Converting a date held in a string to a different format

I have a date that I have extracted from a log file in YYYY-MM-DD HH:MM:SS format.
I have it in a variable called $field0 along with another nine variables $field1 - $field9.
Using Perl I want to change the format of the string to DD-MM-YYYY HH:MM:SS and put it back in $field0
Surely there must be a really easy way to do this?
Can anyone assist please or point me somewhere that has am example?
There are two obvious approaches. The first is to use the core Time::Piece module to parse the date-time string and reformat it, and the second is to use a regex substitution to match the three date fields and reverse them
Here are both techniques used in a single program
use strict;
use warnings 'all';
use feature 'say';
use Time::Piece;
my $time0 = my $time1 = '2016-05-28 12:53:19';
$time0 = Time::Piece->strptime($time0, '%Y-%m-%d %H:%M:%S')->strftime('%d-%m-%Y %H:%M:%S');
say $time0;
$time1 =~ s/^(\d\d\d\d)-(\d\d)-(\d\d)/$3-$2-$1/;
say $time1;
output
28-05-2016 12:53:19
28-05-2016 12:53:19

Perl - store output of localtime into hash

In perl I am able to get current seconds using this sequence of commands:
my #time = ($sec,$min,$hour,$day,$mon,$year_1900,$wday,$yday,$isdst)=localtime;
print $time[0]
Is there any equivalent of this but using hashes? So one can type something like this:
print $time{"sec"}
I have tried:
my %time= ("sec","min","hour","day","mon","year_1900","wday","yday","isdst")=localtime;
print $time{"sec"}
But it ended with following error:
Can't modify constant item in list assignment at -e line 1, near "localtime;"
Execution of -e aborted due to compilation errors.
Thanks
Instead of storing everything in a hash, you can store it in an object using Time::Piece:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $t = localtime;
print $t->sec;
Time::Piece can do date math, comparisons, parsing, and output, making it more flexible than a simple hash.
You can use hash slice
my %time;
#time{"sec","min","hour","day","mon","year_1900","wday","yday","isdst"} = localtime;
# or shorter
# #time{qw(sec min hour day mon year_1900 wday yday isdst)} = localtime;
print $time{"sec"};
There are a few standard Perl modules that can help you.
The first is Time::localtime. Thie replaces the internal localtime command with a by name methods for accessing the various parts of the time:
use strict; # Always
use warnings; # Always
use feature qw(say); # An improved version of the print command
use Time::localtime
my $time = localtime; # A Time::Tm object, not your standard localtime command
say $time->sec; # Prints the seconds
This isn't quite the hash you've requested, but you can see that it greatly improves the access to the various pieces provided by localtime in a way that's almost like a hash.
You can also see the use of say which is like print except that you don't need that pesky \n on the end (like you forgot in your example).
Taking things to the next step...
Another nice standard module is called Time::Piece. It provides even easier ways to parse time and to display it.
use strict; # Always
use warnings; # Always
use feature qw(say); # An improved version of the print command
use Time::Piece
my $time = localtime;
say $time->sec; # Prints the seconds. Looks pretty much the same
#
# But now look!
#
say $time->ymd; # Prints the time as YYYY-MM-DD
say $time->ymd("/"); # Prints the time as YYYY/MM/DD
say $time->mdy("/"); # Prints the time as MM/DD/YYYY
say $time->month; # Prints the name of the month
say $time->cdate; # Prints date and time as a string
I prefer Time::Piece because of its flexibility and the ease of initializing a date that's not the current time. If you have a string for the date/time, and you can describe it, you can easily create a new Time::Piece date/time object that can be manipulated.
One of the many things Perl programmers miss is the cornucopia of standard Perl modules that are packed into almost every Perl distribution. These modules do not require downloading or installation. They're there and available on almost any computer that has that particular Perl release.
You can use File::Copy for that missing file copy command. You can use File::Basename for that missing basename and dirname command. You can use the hundreds of modules that come with Perl to make your life easier. Take a tour of the standard Perl documentation and play around with it.

Perl date format transformations

I'm taking 02/29/2012 from an html form, but will need to work with the ISO 2012-02-29 from then on.
I am certain that it'd be easier for me to do it with Perl without touching on the JS datepicker of which I have zero understanding.
$date = '02/29/2012';
$date =~ s#(\d+)/(\d+)/(\d+)#$3-$1-$2#;
To do your transformation, just use
s{([0-9]{2})/([0-9]{2})/([0-9]{4})}{$3-$2-$1}
But AFAIK, ISO is not YYYY-DD-MM, but YYYY-MM-DD.
You could use DateTime or Date::Manip. There are a plenty of subroutines that perform various manipulations with date. For example, using Date::Manip:
$string = '02/29/2012';
$date = ParseDate($string);
$out = UnixDate($date, '%Y-%m-%d');
Edit: as I see, a similar answer was provided while I was typing
You can use the Date::Manip::Date module.
This is a little costlier but does validation of the dates.
Using standard Perl:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my $date = '02/09/2012';
say join '-', (split m|/|, $date)[2,1,0];
Using DateTime:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use DateTime::Format::Strptime;
my $date = '02/09/2012';
my $parser = DateTime::Format::Strptime->new(
pattern => '%d/%m/%Y',
);
my $dt = $parser->parse_datetime($date);
say $dt->ymd;
If you want to deal with dates and times in Perl, then DateTime and its friends are the tools that you want.
It depends on how much work you're going to be doing on the dates, and whether you're going to be culturally aware of how different people enter dates, and whether you're going to accept month names or abbreviations as well as pure numerics, and ...
On CPAN, there's a major section on Data and Data Types which has sub-sections for Date and Time with many modules in each. Some are extremely elaborate: for example, the DateTime module is extremely thorough, but rather heavy-weight (and is listed in neither the Date nor the Time section). Others are just clever: Date::Calc and Date::Manip. The main problem at CPAN is the embarrassment of riches - what you need is probably there.
If the simple regexes in other answers will work, use them. Otherwise, consider one of Date::Calc or Date::Manip, unless you find something else that will work better for you.
The strptime function can also be leveraged from the core module, Time::Piece :
use Time::Piece;
my $date = q(02/29/2012);
my $t = Time::Piece->strptime( $date, "%m/%d/%Y" );
print $t->ymd, "\n";
As the input is not trusted (web form...) I suggest to basically use choroba's answer (with bounds checking added), but in a test that checks if the input matches:
# input and result are in $_
unless (s{^([0-9]{2})/([0-9]{2})/([0-9]{4})$}{$3-$2-$1}) {
die "invalid input";
}
# From here input is both validated and transformed

Undefined subroutine &main::timelocal error

I have the following error when I execute my perl module :
Undefined subroutine &main::timelocal
I have defined time and I want in the format of DDMMYYYY without any seperators.
Can anyone help me on this?
To use timelocal like that, you need to import it:
use Time::Local 'timelocal';
(and make sure you are calling it correctly; see Time::Local)
But perhaps you meant localtime? Or you might want POSIX::strftime.
You was not very specific where to get the time. This works for current date, using core Time::Piece module:
use Time::Piece;
print localtime->dmy(''); # 05042011
If you have time in variable, you can do
use Time::Piece;
print localtime($time)->dmy('');
The empty string in dmy call is separator.
The core POSIX module contains a 'strftime' function that handles all of the standard Unix date/time formatting sequences.
$ perl -MPOSIX=strftime -le'print strftime "%d%m%Y", localtime'
Or, in a program,
use POSIX 'strftime';
print strftime '%d%m%Y', localtime, "\n";
It's an old one but I had this exact error and solved it from looking at the above examples with a different solution.
Reason was that I had not included the ';' at the end of the use statement!
use Time::Local ;