How can I parse a strftime formatted string in Perl? - perl

I am new to Perl and I wan to know whether there is an inverse function to the strftime().
Look,
use POSIX qw(strftime);
print strftime("%YT%mT%d TTTT%H:%M:%S", localtime)
I get: 2009T08T14 TTTT00:37:02. How can I do the oposite operation? From "2009T08T14 TTTT00:37:02" string to get 2009-08-14 00:37:02, knowing the formatting string "%YT%mT%d TTTT%H:%M:%S"?

One option is to parse the numbers using a regular expression and then use Time::Local. However, now that I understand your question is how to go from a strftime formatted string to a time in general, that approach is bound to be cumbersome.
You mention in your answer POSIX::strptime which is great if your platform supports it. Alternatively, you can use DateTime::Format::Strptime:
#!/usr/bin/perl
use strict;
use warnings;
use DateTime::Format::Strptime;
use POSIX qw(strftime);
my $f = "%YT%mT%d TTTT%H:%M:%S";
my $s = strftime($f, localtime);
print "$s\n";
my $Strp = DateTime::Format::Strptime->new(
pattern => $f,
locale => 'en_US',
time_zone => 'US/Eastern',
);
my $dt = $Strp->parse_datetime($s);
print $dt->epoch, "\n";
print scalar localtime $dt->epoch, "\n";
$dt is a DateTime object so you can do pretty much whatever you want with it.

I think I have found the solution: strptime($strptime_pattern, $string)

So easyy
use Time::ParseDate;
my $t = '2009T08T14 TTTT00:37:02';
$t =~ s/TTTT//;
$t =~ s/T/-/g;
$seconds_since_jan1_1970 = parsedate($t)

Related

Date::Manip::Delta - the number of seconds [duplicate]

The code below only expresses the difference in months and days like so:
0:2:0:5:0:0:0
So it works, but I want to know the total number of days given that $ADDate can vary quite a bit. Hopefully this is simple, and I just completely missed how to do it.
#!/usr/bin/perl
use Date::Manip 6.42;
my $ADDate = "20131211000820.0Z";
my $var;
my #val;
my $diff;
calc_period($ADDate = "20131211000820.0Z");
sub calc_period
{
$ADDate =~ s/^([\d][\d][\d][\d])([\d][\d])([\d][\d])/$1-$2-$3/gs;
$ADDate =~ s/.........$//gs;
$today = ParseDate("today");
$beginning = ParseDate($ADDate);
$end = ParseDate($today);
$delta = DateCalc($beginning,$end,\$err,1);
#$delta =~ s/([\d+][:][\d+]):.*$/$1/gs;
print "$delta\n";
print "$ADDate\n";
}
I'm not familiar with Date::Manip, but I think another way to do this is to use Time::Piece to parse your string and do whatever you like with that since taking the difference of two Time::Piece object returns a Time::Seconds object.
The following example will show the difference of the current time and the hardcoded time and show it in days.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $d = "20131211000820.0Z";
my $t = Time::Piece->strptime($d, "%Y%m%d%H%M%S.0Z");
my $now = Time::Piece->localtime();
my $diff = Time::Seconds->new($now - $t);
print $diff->days, "\n";
NigoroJr has already given you an answer. However, just as an FYI, the following is how I would clean up the code you originally provided:
#!/usr/bin/perl
use Date::Manip 6.42;
use strict;
use warnings;
calc_period("20131211000820.0Z");
sub calc_period {
my $date = shift;
$date =~ s/^(\d{4})(\d{2})(\d{2}).*/$1-$2-$3/;
my $beginning = ParseDate($date);
my $end = ParseDate("today");
my $delta = DateCalc($beginning, $end, \my $err, 1);
#$delta =~ s/([\d+][:][\d+]):.*$/$1/gs;
print "$delta\n";
print "$date\n";
}
Biggest differences being the proper use of a function and scoped variables, and a simplification of your regex.
I was unable to find a clean way to get Date::Manip to output a strict delta in days though, so the other module is the way to go.

Date formatting in Perl

I have a variable which contain value "20140720". I need to change it to the format "20/07".
My code is shown below.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $date = '20140720';
my $date_format = Time::Piece->strptime($date, '%d/%m');
my $new_date = $date_format->strftime('%d/%m');
print $new_date;
I get following error during execution.
Error parsing time at /usr/lib/perl5/5.10.0/x86_64-linux-thread-multi/Time/Piece.pm line 470.
In this line — Time::Piece->strptime($date, '%d/%m'); — you specified the format that $date is currently in incorrectly. The second argument describes how the string should be parsed, not the format you want it to be in (which is what the following line is for).
Use '%Y%m%d' instead.
With a fixed string, you should use the pack/unpack function:
use strict;
use warnings;
my $date = '20140720';
my (undef, $m, $d) = unpack 'A4A2A2', $date;
print "$d/$m";
If you don't need further date processing, using a simple regular expression may be simpler:
use strict;
use warnings;
my $date = '20140720';
my $new_date = $date;
$new_date =~ s!\d{4}(\d{2})(\d{2})$!$2/$1!;
print $new_date, "\n";

How to display 4/25/10 to 2010-25-04?

my$str= '4/25/10';
my$sr = join(' ',split (/\//,$str));
#my$s = sprintf '%3$d %2$d %1$d',$srt;
print$sr,"\n";
output:
4 25 10
But i want output like 2010-25-04.Can any one suggest me how display the my desire output.Give me your suggestion
you answers will be appreciable.
Well, a braindead solution might be:
my #date = split( /\//,$str)
printf("%04d-%02d-%02d", $date[2] + 2000, $date[1], $date[0]);
You could write something a little more self-documenting by highlighting what you expect to be year, month and day like so:
my ($day, $month, $year) = split /\//, $str;
printf("%04d-%02d-%02d", $year + 2000, $month, $day);
You're not that far off.
Instead of splitting and joining in a single operation, you can keep individual variables to handle the data better:
my ($d,$m,$y) = split /\//, $str;
Then you can format it in most any way you please, for example:
printf "20%02d-%02d-%02d\n", $y, $d, $m;
A few notes, though:
I won't comment about the source format, but the format you're converting to doesn't make a lot of sense. You'd probably be better using ISO-8601: 2010-04-25.
Obviously, this way of doing only works up to year 2099.
For anything more serious, you'd be better off delegating this kind of work to date handling modules. See for example this question for parsing and this question for formatting
use DateTime::Format::Strptime;
my $strp = DateTime::Format::Strptime->new(
pattern => '%m/%d/%y',
locale => 'en_US'
);
my $dt = $strp->parse_datetime('4/25/10');
print $dt->strftime('%Y-%d-%m'), "\n";
Gives:
2010-25-04
No need to do to DateTime for this. Time::Piece has been included with the Perl core distribution for years.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $date = '4/25/10';
my $date_tp = Time::Piece->strptime($date, '%m/%d/%y');
say $date_tp->strftime('%Y-%m-%d');

Today's Date in Perl in MM/DD/YYYY format

I'm working on a Perl program at work and stuck on (what I think is) a trivial problem. I simply need to build a string in the format '06/13/2012' (always 10 characters, so 0's for numbers less than 10).
Here's what I have so far:
use Time::localtime;
$tm=localtime;
my ($day,$month,$year)=($tm->mday,$tm->month,$tm->year);
You can do it fast, only using one POSIX function. If you have bunch of tasks with dates, see the module DateTime.
use POSIX qw(strftime);
my $date = strftime "%m/%d/%Y", localtime;
print $date;
You can use Time::Piece, which shouldn't need installing as it is a core module and has been distributed with Perl 5 since version 10.
use Time::Piece;
my $date = localtime->strftime('%m/%d/%Y');
print $date;
output
06/13/2012
Update
You may prefer to use the dmy method, which takes a single parameter which is the separator to be used between the fields of the result, and avoids having to specify a full date/time format
my $date = localtime->dmy('/');
This produces an identical result to that of my original solution
use DateTime qw();
DateTime->now->strftime('%m/%d/%Y')
expression returns 06/13/2012
If you like doing things the hard way:
my (undef,undef,undef,$mday,$mon,$year) = localtime;
$year = $year+1900;
$mon += 1;
if (length($mon) == 1) {$mon = "0$mon";}
if (length($mday) == 1) {$mday = "0$mday";}
my $today = "$mon/$mday/$year";
use Time::Piece;
...
my $t = localtime;
print $t->mdy("/");# 02/29/2000
Perl Code for Unix systems:
# Capture date from shell
my $current_date = `date +"%m/%d/%Y"`;
# Remove newline character
$current_date = substr($current_date,0,-1);
print $current_date, "\n";
Formating numbers with leading zero is done easily with "sprintf", a built-in function in perl (documentation with: perldoc perlfunc)
use strict;
use warnings;
use Date::Calc qw();
my ($y, $m, $d) = Date::Calc::Today();
my $ddmmyyyy = sprintf '%02d.%02d.%d', $d, $m, $y;
print $ddmmyyyy . "\n";
This gives you:
14.05.2014

How to change ctime to normal string representation?

Using File::stat we can get the ctime of a given file. My question is how to change the ctime, which means the inode change time in seconds since the epoch, to a normal time representation like "2009-08-26 17:28:28". Is there any build-in or module can solve this task?
The most standard way is to use POSIX module and it's strftime function.
use POSIX qw( strftime );
use File::stat;
my $stat_epoch = stat( 'some_file.name' )->ctime;
print strftime('%Y-%m-%d %H:%M:%S', localtime( $stat_epoch ) );
All these markers like %Y, %m and so on, are defined in standard, and work the same in C, system "date" command (at least on Unix) and so on.
If all you want is a human-readable representation, then
print scalar localtime stat($filename)->ctime;
will do the job. This prints something like "Wed Jun 10 19:25:16 2009".
You can't influence the format, though.
If you want the time in GMT, use "scalar gmtime" instead.
This is a special behaviour of localtime and gmtime in scalar context.
use DateTime;
$dt = DateTime->from_epoch( epoch => $epoch );
The datetime object then contains the representations you require, e.g. $year = $dt->year; etc. In scalar context you get a nice human-readable representation, e.g.
$epoch = 123456789;
$dt = DateTime->from_epoch( epoch => $epoch );
print $dt;
1973-11-29T21:33:09
use File::stat;
use Time::CTime;
$file = "BLAH BLAH BLAH";
$st = stat($file) or die "No $file: $!";
print strftime('%b %o', localtime($st->ctime));
#Feb 11th, for example.
As you can see from the number of answers here, you have lots of options. I like the Date::Format module:
#!/usr/bin/perl
use strict;
use warnings;
use Date::Format;
use File::Stat;
my $fs = File::Stat->new( '.vimrc' );
my $mtime = $fs->ctime();
print time2str( "changed in %Y on %B, %o at %T\n", $mtime );
First, are you really using File::Stat rather than File::stat? If so, and if you have 5.8 or greater, switch to File::stat.
perldoc localtime
localtime EXPR
localtime
Converts a time as returned by the time function to a 9-element list with the time analyzed for the local time zone.
...
In scalar context, localtime() returns the ctime(3) value:
Once you realize thatlocaltime can take an argument, then your mind opens up to all the other possibilities mentioned in this thread.
#!/usr/bin/perl
use strict;
use warnings;
use File::stat;
my $stat = stat 't.pl';
print "$_\n" for $stat->ctime, scalar localtime($stat->ctime);
There's an example in Time::localtime perldoc for using it's ctime() to do this sort of thing.
use File::stat;
use Time::localtime;
my $date_string = ctime(stat($file)->ctime);