Requirement - I have file name called "Rajesh.1202242219". Numbers are nothing but a date "date '+%y''%m''%d''%H''%M'" format.
Now I am trying to write a perl script to extract the numbers from file name and compare with current system date and time and based on output of this comparison, print some value using perl.
Approach:
Extract the Digit from File name:
if ($file =~ /Rajesh.(\d+).*/) {
print $1;
}
Convert this time into readable time in perl
my $sec = 0; # Not Feeded
my $min = 19;
my $hour = 22;
my $day = 24;
my $mon = 02 - 1;
my $year = 2012 - 1900;
my $wday = 0; # Not Feeded
my $yday = 0; # Not Feeded
my $unixtime = mktime ($sec, $min, $hour, $day, $mon, $year, $wday, $yday);
print "$unixtime\n";
my $readable_time = localtime($unixtime);
print "$readable_time\n";
find Current time and compare...
my $CurrentTime = time();
my $Todaydate = localtime($startTime);
But the problem here is, I am not getting solution of how to extract 2 digit from $1 and assign to $sec, $min, etc. Any help?
Also, if you have good approach for this problem statement, Please share with me
I like to use time objects to simplify the logic. I use Time::Piece here because it is simple and light weight (and part of the core). DateTime can be another choice.
use Time::Piece;
my ( $datetime ) = $file =~ /(\d+)/;
my $t1 = Time::Piece->strptime( $datetime, '%y%m%d%H%M' );
my $t2 = localtime(); # equivalent to Time::Piece->new
# you can do date comparisons on the object
if ($t1 < $t2) {
# do something
print "[$t1] < [$t2]\n";
}
Might as well teach DateTime::Format::Strptime to make the comparison much simpler:
use DateTime qw();
use DateTime::Format::Strptime qw();
if (
DateTime::Format::Strptime
->new(pattern => '%y%m%d%H%M')
->parse_datetime('Rajesh.1202242219')
< DateTime->now
) {
say 'filename timestamp is earlier than now';
} else {
say 'filename timestamp is later than now';
};
my ($year, $month, $day, $hour, $min) = $file =~ /(\d{2})/g;
if ($min) {
$year += 100; # Assuming 2012 and not 1912
$month--;
# Do stuff
}
I think unpack might be a better fit.
if ( my ( $num ) = $file =~ /Rajesh.(\d+).*/ ) {
my ( $year, $mon, $day, $hour, $min ) = unpack( 'A2 A2 A2 A2 A2', $num );
my $ts = POSIX::mktime( 0, $min, $hour, $day, $mon - 1, $year + 100 );
...
}
Using a module that parses dates might be nice. This code will parse the date and return a DateTime object. Refer to the documentation to see the many ways to manipulate this object.
use DateTime::Format::Strptime;
my $date = "1202242219";
my $dt = get_obj($date);
sub get_obj {
my $date = shift;
my $strp = DateTime::Format::Strptime->new(
pattern => '%y%m%d%H%M'
);
return $strp->parse_datetime($date);
}
Related
I need to make a loop (foreach) for all the months specified in a range like:
01-2013 to 09-2015 (month-year) format.
The tricky part is that in every loop i need the month - year data as well to run an sql query, so i cannot use a simple +1 counter.
I looked as Date::Calc and Date::Simple but it did not offer me a solution.
Does anybody have a code snippet i could use or come up with an idea on how to tackle this challenge?
The DateTime module has a nice function add which allows you to add whatever amount of time you want to an object:
use strict;
use warnings;
use DateTime;
use feature 'say';
my $start = DateTime->new(year => 2013, month => 1);
my $end = DateTime->new(year => 2015, month => 9);
while ($start <= $end) {
$start->add(months => 1);
say $start->strftime("%m-%Y");
}
If you only need to loop through the dates, why not just use this:
for my $year (2013..2015) {
for my $month (1..12) {
my $date = sprintf "%02d-%d", $month, $year;
# do your $date processing here
...
last if ($date eq "09-2015");
}
}
Date::Calc is awesome. Check it again
use Date::Calc();
my ($month, $year, $end_month, $end_year) = (1, 2013, 9, 2015);
while (($year < $end_year) || ($year == $end_year && $month <= $end_month)) {
print "year: $year, month: $month\n";
($year, $month) = Date::Calc::Add_Delta_YMD($year,$month,1,0,1,0);
}
my $start_date = '01-2013';
my $end_date = '09-2015';
my ($sm, $sy) = split '-', $start_date;
my ($em, $ey) = split '-', $end_date;
for my $y ($sy..$ey) {
for my $m (1..12) {
next if ($y==$sy && $m<$sm);
last if ($y==$ey && $m>$em);
# use $m and $y for further processing sql query
# print "Month: $m\t Year: $y\n";
# ...
}
}
This is a perl script for sql data pulling each day for 100 days starting from Oct 1 and
SQL is quite picky in date formats(yyyy-mm-dd), so I've written the script as follows.
However, at a specific day, on 2011-11-06, the time to date conversion is incorrect, and start and end date become the same.
$srt_date='2011-11-06'
$end_date='2011-11-06'
I don't know if this is perl error or something else.
use DBI;
use DBD::Oracle qw(:ora_types);
use Compress::Zlib;
use FileHandle;
use Date::Parse;
use Date::Format;
$st_day=str2time('2011-10-1');
#days=(0..100);
foreach $daynum (#days){
$dt1 = $st_day+3600*(24*$daynum);
$dt2 = $st_day+3600*(24*($daynum+1));
$srt_date = time2str("%d-%h-%Y", $dt1);
$end_date = time2str("%d-%h-%Y", $dt2);
print $srt_date, ',' ,$end_date, '\n';
my $sqlGetEid = "select x,y from z where DATETIME>='$srt_date' and DATETIME<'$end_date'";
}
Here's how DateTime handles the DST transitions correctly:
use strict; #ALWAYS!
use warnings; #ALWAYS!
use DateTime;
my $st_day = '2011-10-1';
my ($year, $month, $day) = split /-/, $st_day;
my $dt = DateTime->new(
year => $year,
month => $month,
day => $day,
time_zone => 'local',
);
my #days = 0..100;
foreach my $daynum (#days) {
my $dt1 = $dt->ymd;
my $dt2 = $dt->add(days => 1)->ymd;
printf "%s,%s\n", $dt1, $dt2;
}
I'm not sure what you want to achieve exactly, but why bother executing 100 SQL statements when you can get away with something like:
SELECT trunc(datetime, 'DD') truncdate, x,y
FROM z WHERE datetime between '2011-10-01'
AND to_date('20111001', 'YYYYMMDD') + 99
Populate a hash with truncdate as key, and if your dates are ISO 8601, you'll get the same ordering by looping over the hash with a regular (cmp) sort.
EDIT: I'll clarify how you could do this:
my $sth = $mdbh->prepare("SELECT trunc(datetime, 'DD') truncdate, x,y
FROM z WHERE datetime between '2011-10-01'
AND to_date('20111001', 'YYYYMMDD') + 99
ORDER BY truncdate");
$sth->execute();
my $lastdate = "";
my $fh;
while (my $row = $sth->fetchrow_hashref()) {
# If new date, create new file
if ($row->{truncdate} ne $lastdate) {
close($fh) if $fh;
open($fh, ">", "$row->{truncdate}.csv") or die "Unable to create file '$row->{truncdate}.csv': $!\n";
}
print $fh "$row->{x},$row->{y}\n";
$lastdate = $row->{truncdate};
}
close($fh) if $fh;
How to convert date format YYYY-MM-DDTHH:MM:SSZ to YYYY-MM-DD HH:MM + 8 hours?
For example:
Input: 2011-07-07T18:05:45Z
Output: 2011-07-08 02:05
Let's start with Rahul's snippet, and add in the date math and output formatting...
use DateTime;
use DateTime::Format::ISO8601;
use DateTime::Format::Strptime;
my $string = '2011-07-07T18:05:45Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string );
die "Impossible time" unless $dt;
my $formatter = new DateTime::Format::Strptime(pattern => '%Y-%m-%d %T');
$dt->add( hours => 8 )->set_formatter($formatter);
print "$dt\n";
I've added the use of DateTime::Format::Strptime, in order to specify the desired output format.
Then I've added three more lines:
First I create a formatter, and feed it the output pattern I desire.
Next I add eight hours to the original date, and I assign the output
formatter by chaining the set_formatter() call to the add() call.
Then I print it.
Are you using the DateTime modules?
Specifically, here's a link to DateTime::Format::ISO8601 that reads/writes ISO 8601 format you mentioned as your input.
If you don't have DateTime, you surely have Time::Piece:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds qw(ONE_HOUR);
my $str = '2011-07-07T18:05:45Z';
my $t = Time::Piece->strptime($str, "%Y-%m-%dT%TZ");
$t += 8 * ONE_HOUR;
print $t->strftime("%Y-%m-%d %H:%M"),"\n";
Taken From
How can I validate a "yyyy-MM-dd'T'HH:mm:ssZ" date/timestamp in UTC with Perl?
use DateTime;
use DateTime::Format::ISO8601;
my $string = '2010-02-28T15:21:33Z';
my $dt = DateTime::Format::ISO8601->parse_datetime( $string ); die "Impossible time" unless $dt;
It doesn't work, result is 2010-02-28T15:21:33
Then, do it the hard way...
use Time::Local
use warnings;
use strict;
$time = '2010-02-28T15:21:33Z';
my ($year, month, day) = split (/-/, $time)
$year -= 1900; #Year is an offset of 1900
$month -= 1; #Months are 0 - 11
#Now split the time off of the day (DDTHH:MM:SS)
$day = substr($day, 0, 2);
time = substr($day, 3)
#Now split the time
(my $hour, $minute, $second) = split(/:/, $time);
$second =~ s/Z$//; #Remove Z
my $time_converted = timelocal($second, $minute, $hour, $day, $month, $year);
#Now you have the time, Add eight hours
my $hours_in_seconds = 8 * 60 * 60;
$time_converted += $hours_in_seconds;
# Almost done: Convert time back into the correct array:
($second, $minute, $hour, $day, $month, $year) = localtime($time_converted);
$year += 1900;
$month += 1;
# Now, reformat:
my $formatted_time = sprint (%04d-%02d-%02d %02d:%02d),
$year, $month, $day, $hour, $minute;
For example: from date: 10/02/2010
How do I convert an equal timestamp for 10/02/2010 00:00:00 in Perl?
I can't use local time or time .. is there another way to achieve this?
You can use the Time::Local core module:
use Time::Local 'timelocal';
my ($d, $m, $y) = split '/', '10/02/2010';
my $time = timelocal(0, 0, 0, $d, $m-1, $y);
Note that the month argument for timelocal() is in the range 0..11.
Without localtime():
use Time::Local;
$time = timelocal($sec, $min, $hour, $mday, $mon, $year);
(See perldoc.)
A standard way would be something like:
use POSIX;
use strict;
use warnings;
my $sec = 0;
my $min = 0;
my $hour = 0;
my $day = 10;
my $mon = 2 - 1;
my $year = 2010 - 1900;
my $wday = 0;
my $yday = 0;
my $unixtime = mktime ($sec, $min, $hour, $day, $mon, $year, $wday, $yday);
print "$unixtime\n";
my $readable_time = localtime($unixtime);
print "$readable_time\n"
(From Converting Unix time and readable time with Perl)
You could use Date::Parse:
use Date::Parse;
print str2time('10/02/2010 00:00:00');
On my machine this prints 1285970400, which corresponds to October 2nd, 2010 (I live in +1 GMT with +1 Wintertime.)
I think you want the built-in module Time::Local.
The DateTime module should be helpful here. In particular, I believe the DateTime::Format::Natural module can parse a user-supplied date string. From there, you have a DateTime object and can print it out or transform it as you like.
Depending on where your initial date is coming from you might be able to parse it using
Date::Manip
and calling
ParseDate("10/02/2010")
You can then take that output and convert it into whatever format you wish.
I have a file in below format.
DATE Time, v1,v2,v3
05:33:25,n1,n2,n3
05:34:25,n4,n5,n5
05:35:24,n6,n7,n8
and so on upto 05:42:25.
I want calculate the values v1, v2 and v3 for every 5 min interval. I have written the below sample code.
while (<STDIN>) {
my ($dateTime, $v1, $v2, $v3) = split /,/, $_;
my ($date, $time) = split / /, $dateTime;
}
I can read all the values but need help to sum all the values for every 5 min interval. Can anyone please suggest me the code to add the time and values for every 5 min.
Required output
05:33 v1(sum 05:33 to 05:37) v2(sum 05:33 to 05:33) v3(sum 05:33 to 05:33)
05:38 v1(sum 05:38 to 05:42) v2(sum 05:38 to 05:42) v3(sum 05:38 to 05:42)
and so on..
The code is a variation the previous answer by Sinan Ünür below, except:
(1) Function timelocal will allow you to read in Day,Month,Year -- so you can sum any five minute gap.
(2) Should deal with case where final time gap is < 5 minutes.
#!/usr/bin/perl -w
use strict;
use warnings;
use Time::Local;
use POSIX qw(strftime);
my ( $start_time, $end_time, $current_time );
my ( $totV1, $totV2, $totV3 ); #totals in time bands
while (<DATA>) {
my ( $hour, $min, $sec, $v1, $v2, $v3 ) =
( $_ =~ /(\d+)\:(\d+)\:(\d+)\,(\d+),(\d+),(\d+)/ );
#convert time to epoch seconds
$current_time =
timelocal( $sec, $min, $hour, (localtime)[ 3, 4, 5 ] ); #sec,min,hr
if ( !$end_time ) {
$start_time = $current_time;
$end_time = $start_time + 5 * 60; #plus 5 min
}
if ( $current_time <= $end_time ) {
$totV1 += $v1;
$totV2 += $v2;
$totV3 += $v3;
}
else {
print strftime( "%H:%M:%S", localtime($start_time) ),
" $totV1,$totV2,$totV3\n";
$start_time = $current_time;
$end_time = $start_time + 5 * 60; #plus 5 min
( $totV1, $totV2, $totV3 ) = ( $v1, $v2, $v3 );
}
}
#Print results of final loop (if required)
if ( $current_time <= $end_time ) {
print strftime( "%H:%M:%S", localtime($start_time) ),
" $totV1,$totV2,$totV3\n";
}
__DATA__
05:33:25,29,74,96
05:34:25,41,69,95
05:35:25,24,38,55
05:36:25,96,63,70
05:37:25,84,65,74
05:38:25,78,58,93
05:39:25,51,38,19
05:40:25,86,40,64
05:41:25,80,68,65
05:42:25,4,93,81
Output:
05:33:25 352,367,483
05:39:25 221,239,229
Obviously, not tested much, for lack of sample data. For parsing the CSV, use either Text::CSV_XS or Text::xSV rather than the naive split below.
Note:
This code does not make sure the output has all consecutive five minute blocks if the input data has gaps.
You will have problems if there are time stamps from multiple days. In fact, if the time stamps are not in 24-hour format, you will have problems even if the data are from a single day.
With those caveats, it should still give you a starting point.
#!/usr/bin/perl
use strict;
use warnings;
my $split_re = qr/ ?, ?/;
my #header = split $split_re, scalar <DATA>;
my #data;
my $time_block = 0;
while ( my $data = <DATA> ) {
last unless $data =~ /\S/;
chomp $data;
my ($ts, #vals) = split $split_re, $data;
my ($hr, $min, $sec) = split /:/, $ts;
my $secs = 3600*$hr + 60*$min + $sec;
if ( $secs > $time_block + 300 ) {
$time_block = $secs;
push #data, [ $time_block ];
}
for my $i (1 .. #vals) {
$data[-1]->[$i] += $vals[$i - 1];
}
}
print join(', ', #header);
for my $row ( #data ) {
my $ts = shift #$row;
print join(', ',
sprintf('%02d:%02d', (localtime($ts))[2,1])
, #$row
), "\n";
}
__DATA__
DATE Time, v1,v2,v3
05:33:25,1,3,5
05:34:25,2,4,6
05:35:24,7,8,9
05:55:24,7,8,9
05:57:24,7,8,9
Output:
DATE Time, v1, v2, v3
05:33, 10, 15, 20
05:55, 14, 16, 18
This is a good problem for Perl to solve. The hardest part is taking the value from the datetime field and identifying which 5 minute bucket it belongs to. The rest is just hashes.
my (%v1,%v2,%v3);
while (<STDIN>) {
my ($datetime,$v1,$v2,$v3) = split /,/, $_;
my ($date,$time) = split / /, $datetime;
my $bucket = &get_bucket_for($time);
$v1{$bucket} += $v1;
$v2{$bucket} += $v2;
$v3{$bucket} += $v3;
}
foreach my $bucket (sort keys %v1) {
print "$bucket $v1{$bucket} $v2{$bucket} $v3{$bucket}\n";
}
Here's one way you could implement &get_bucket_for:
my $first_hhmm;
sub get_bucket_for {
my ($time) = #_;
my ($hh,$mm) = split /:/, $time; # looks like seconds are not important
# buckets are five minutes apart, but not necessarily at multiples of 5 min
# (i.e., buckets could go 05:33,05:38,... instead of 05:30,05:35,...)
# Use the value from the first time this function is called to decide
# what the starting point of the buckets is.
if (!defined $first_hhmm) {
$first_hhmm = $hh * 60 + $mm;
}
my $bucket_index = int(($hh * 60 + $mm - $first_hhmm) / 5);
my $bucket_start = $first_hhmm + 5 * $bucket_index;
return sprintf "%02d:%02d", $bucket_start / 60, $bucket_start % 60;
}
I'm not sure why you would use the times starting from the first time, instead of round 5 minute intervals (00 - 05, 05 - 10, etc), but this is a quick and dirty way to do it your way:
my %output;
my $last_min = -10; # -10 + 5 is less than any positive int.
while (<STDIN>) {
my ($dt, $v1, $v2, $v3) = split(/,/, $_);
my ($h, $m, $s) = split(/:/, $dt);
my $ts = $m + ($h * 60);
if (($last_min + 5) < $ts) {
$last_min = $ts;
}
$output{$last_min}{1} += $v1;
$output{$last_min}{2} += $v2;
$output{$last_min}{3} += $v3;
}
foreach my $ts (sort {$a <=> $b} keys %output) {
my $hour = int($ts / 60);
my $minute = $ts % 60;
printf("%01d:%02d v1(%i) v2(%i) v3(%i)\n", (
$hour,
$minute,
$output{$ts}{1},
$output{$ts}{2},
$output{$ts}{3},
));
}
Not sure why you would do it this way, but there you go in procedural Perl, as example. If you need more on the printf formatting, go here.