How do calculate and convert localtime to unix time stamp in Perl - perl

My script is :
use warnings;
use strict;
my $start_time = localtime();
$sql = "INSERT into Table1, Select (VAr1, Var2, Var3 ...........)";
my $end_time = localtime();
my $run_time = ($end_time - $start_time);
my #months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
my ($sec, $min, $hour, $day,$month,$year) = (localtime($run_time))[0,1,2,3,4,5];
print "Unix time ".$run_time." converts to ".$months[$month]." ".$day.", ".($year +=1900);
print " ".$hour.":".$min.":".$sec."\n";
When I run it, I don't get the desire output.
Unix time 648 converts to Jan , 1900 ::
I need to get hour:min:sec
the time the script took to calculate the insert into the table.
Thank you

$run_time isn't a timestamp (a number of seconds since epoch representing a date-time), so it makes no sense to pass it to localtime.
my $s = $run_time % 60; $run_time = ($run_time - $s) / 60;
my $m = $run_time % 60; $run_time = ($run_time - $m) / 60;
my $h = $run_time;
my $formatted_run_time = sprintf "%d:%02d:%02d", $h, $m, $s;
You can get fancier too:
my $formatted_run_time =
$h ? sprintf "%d:%02d:%02d", $h, $m, $s
$m ? sprintf "%d:%02d", $m, $s
$s;

localtime is used to convert unix timestamps to a human-readable time (or a list of parts of it), but you are giving it a duration in seconds. It will treat it like a timestamp, and give you a very low date and time.
print scalar localtime 648;
# gives Thu Jan 1 01:10:48 1970
Your code gives the following output:
Unix time 648 converts to Jan 1, 1970 1:10:48
The problem is that you are essentially mixing two concepts here.
You might want to use the Benchmark module instead, which is intended for this exact purpose.
use strict;
use warning;
use Benchmark;
my $t0 = Benchmark->new;
# do your database stuff here
my $t1 = Benchmark->new;
my $td = timediff($t1, $t0);
print "the code took:",timestr($td),"\n";
If you are on a Linux or Unix, you can also use the time program to meassure the overall runtime of your program.
$ time perl foo.pl
real 0m2.241s
user 0m2.236s
sys 0m0.000s
$

Related

Get previous hour of UTC/GMT time using Perl

I have a script which will print Start & End time of previous hour of UTC/GMT.
#!/usr/local/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
my ($tmp_date, $tmp_hour, $Start, $End);
my $date = strftime '%Y-%m-%d', gmtime();
print "Date:$date\n";
my $hour = strftime '%H', gmtime();
print "Hour:$hour\n";
if ($hour == "00"){
$tmp_date = $date-1;
$tmp_hour = "23";
} else {
$tmp_hour = $hour-1;
$tmp_date = $date;
}
$a = length($tmp_hour);
if ($a == 1 ){
$tmp_hour="0".$tmp_hour;
}
$Start = $tmp_date.".".$tmp_hour."00";
$End = $tmp_date.".".$hour."05";
if ($End =~ /0005/){
$tmp_date = `TZ=GMT-12 date +%Y%m%d`;
$End =$tmp_date.".".$hour."05";
}
print "Start:$Start, End:$End\n";
For example, lets say now UTC time is: Wed Jun 10 10:18:57 UTC 2020
This should print Start & End time as 2020-06-10.0900 2020-06-10.1005 respectively.
This script is working as expected. But when Daylight savings happens will there be any impact on fetching Start & End time?
I want experts suggestions how can I avoid unnecessary if statements and achieve it by the use of Perl module itself.
PS: Perl version: v5.10.1. Please suggest Perl modules which comes with standard Perl installation (Ex: POSIX, Time::Local etc.) for solution of above problem.
As you're using gmtime(), any DST changes will have no effect at all.
I'm not sure why your end time ends with '05', I would have thought that the end of the hour comes at '00'.
Here's how I'd write it with Time::Piece and Time::Seconds.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
use Time::Seconds;
my $end = gmtime->truncate(to => 'hour');
my $start = $end - ONE_HOUR;
my $format = '%Y-%m-%d %H:%M:%S';
say 'Start: ', $start->strftime($format);
say 'End: ', $end->strftime($format);
If you really want the end time to be five past the hour, then add this line after the ONE_HOUR line:
$end += (5 * ONE_MINUTE);
You can, of course, use any of the standard strftime() sequences to change the format of the output.

Getting garbage value while calculating difference between already defined and current time (perl script)

I have time (only in hours & minutes) defined in one text file and getting current time from machine. Below is my code and am getting garbage value as 3. Code in perl script. Data in text file is as:
##ID::name::time
228::one::18 30 30
97::two::20 30 00
Code is as : Please suggest.
my $timefromfile;
open(my $fh, "<", $paramfile) or die("Can't open $paramfile\n") ;
{
print "before while\n";
while (my $line = <$fh>) {
print "$line\n";
if($line !~ m/^\#/){
chomp($line);
#if line isn't blank, process it
if($line ne ""){
push(#all_linesSettings, $line);
my #lineseparator = split(/\::/, $line);
$timefromfile = $lineseparator[-1];
my $namefromfile = $lineseparator[-2];
my $IDfromfile = $lineseparator[-3];
print "File Time for $IDfromfile $namefromfile is
$timefromfile \n";
print "**********\n";
(my $sec,my $min,my $hour) = localtime();
printf("Current Time is in - HH:MM:SS format ");
#get time into a variable
my #t;
#t = ($hour, $min, $sec);
print "\n#t\n";
print "\n**********\n";
my $format = '%H %M %S';
my $diff = #t - Time::Piece->strptime($timefromfile, $format );
print "\n now slatime is $timefromfile\n";
print "difference in seconds $diff\n";
}
}
}
}
close $fh;
}
Step 1 of troubleshooting perl: Turn on use strict; and use warnings:
You try to parse $SLAtime but you don't ever define or declare it.
You declare $timefromfile but with no ; so that won't compile.
Your indentation is broken. (You've mismatched brackets).
You subtract a value from #t. That value is undefined because of the above and because you don't have strict or warnings just silently evaluates as zero. #t contains 3 elements, and that's how it's evaluated in a scalar context. That's probably where your 3 is coming from.
You have a printf mixed in there, with no format string or variable. Which is a bit weird.
It's unclear if you even load Time::Piece in the first place. Which makes it considerably harder to use.
Time::Piece->strptime uses the epoch as your baseline. You're comparing with 1st Jan 1970, so your delta will be much bigger than you think.
Something like:
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
while (<DATA>) {
print;
unless (m/^\s*\#/) {
chomp;
my ( $ID, $name, $timestr ) = split /::/;
my $now = localtime;
#note - this assumes time stamps are from today, which may not be what you want.
my $converted_time = Time::Piece->strptime(
join( " ", $timestr, $now->year, $now->mon, $now->mday ),
'%H %M %S %Y %m %d' );
my $diff = $converted_time - $now;
print "Diff is: $diff s\n";
}
}
__DATA__
##ID::name::time
228::one::18 30 30
97::two::20 30 00
Gets you a bit closer.
my $diff = #t - Time::Piece->strptime($SLAtime, $format );
I'm guessing that you expect this to do a pairwise subtraction? (i.e., Subtract the first item in each list, then the second item, then the third item.)
It doesn't.
It evaluates each of the operands in scalar context, then subtracts those scalars. If one of the scalars is a string, it will first be coerced into a number.
When an array is evaluated in scalar context, the result is the number of items in the array.
So:
You start with #t - Time::Piece->strptime($SLAtime, $format )
#t is an array with three items in it and the strptime is a string looking something like "Sunday 3rd Nov, 1943" (the example from the Time::Piece docs; I don't know what your $format looks like), so the subtraction becomes 3 - "Sunday 3rd Nov, 1943"
"Sunday 3rd Nov, 1943" doesn't start with a digit, so its value as a number is 0, making your subtraction 3 - 0
Therefore, you always get 3 as the result.
If you want to subtract dates and/or times correctly, take a look at the DateTime module, which includes methods for performing date arithmetic.
You are trying to subtract a Time::Piece object from an array, which won't work.
You need to get the current time into a Time::Piece object too, and then subtracting them will return a Time::Seconds object, like this:
use Time::Piece;
use Time::Seconds;
my $current_time = Time::Piece->localtime();
my $timefromfile="2018-01-17 18 30 30";
my $format = "%Y-%m-%d %H %M %S";
my $diff = $current_time - Time::Piece->strptime($timefromfile, $format);
print "Difference in seconds is ", $diff->seconds(), "\n";
You'll also need to give the date to the strptime command. If you know the date is always today (and watch out for this around midnight) then you can simply do this:
my $timefromfile = $current_time->ymd() . " 18 30 30";
NOTE! I'm putting in the time from file as a hard-coded constant so the code above is runnable. You'll need to adapt accordingly.

Perl subtract two dates

I am fairly new in Perl.
I am trying to subtract two dates in this format
15.07.16 23:13:34
15.07.16 20:04:24
I know that I have to convert this string in a date object. My problem is I am restricted to the basic perl without installing extra packages. Is there a way to do it?
My Version is v5.8.4 and the output should be 03:09:10.
You say that you're using Perl 5.8.4. You really need to get that updated and get the ability to install CPAN modules.
But, here's a way to do what you want using only core Perl functionality that was available in 5.8.4.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
my $date1 = '15.07.16 23:13:34';
my $date2 = '15.07.16 20:04:24';
my $diff = date2sec($date1) - date2sec($date2);
print secs2duration($diff);
sub date2sec {
my ($date) = #_;
my ($day, $mon, $yr, $hr, $min, $sec) = split(/[. :]/, $date);
# I've assumed that your timestamps are in your local timezone,
# so I've used timelocal() here. If your timestamps are actually
# UTC, you should use timegm() instead.
return timelocal($sec, $min, $hr, $day, $mon-1, 2000 + $yr);
}
sub secs2duration {
my ($secs) = #_;
my $hours = int($secs / (60*60));
$secs %= (60*60);
my $mins = int($secs / 60);
$secs %= 60;
return sprintf '%02d:%02d:%02d', $hours, $mins, $secs;
}

Perl convert YYYYMMDD to 16digit epoch time

Is there a perl function that converts YYYYMMDD to a 16digit epoch time?
I'm currently calling the date command:
date -d 20160219 +%s%6N
for the conversion but my script takes a long time to go through the millions of dates in my data set.
By "16digit epoch time", I mean a 16-digit decimal integer representing microseconds since the epoch. For example, 2016-02-19 00:00:00 UTC is 1455840000 seconds after the epoch, so the result I want is "1455840000000000".
You can also use Time::Moment. In the interest of full disclosure, I am the author of Time::Moment.
use v5.10;
use Time::Moment;
say Time::Moment->from_string('20160219' . 'T00Z')
->strftime('%s%6N');
Output:
1455840000000000
Time::Piece can do all the things you want. After that just right pad 0's to epoch, so that it becomes 16 digits.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $t = Time::Piece -> strptime("20160219",
"%Y%m%d");
my $epoch = $t -> epoch;
# make $epoch 16 digits
$epoch .= "0" x (16 - length($epoch));
print "$epoch\n";
You can use the Time::Local core module:
use Time::Local 'timelocal';
$date = "20160218";
($year, $month, $day) = unpack "A4A2A2", $date;
$time = timelocal(0, 0, 0, $day, $month-1, $year);
$time .= '000000';
Wow, I just actually wrote a script to take the timestamp where it takes a timestamp where it's in seconds since epoch and convert it to human readable format. Not what you asked, but perhaps you can reverse what I wrote.
my $ts = $ARGV[0]; #2012-09-04-20:24:19.870 1030825460
my $newTs = toTimeString($ts);
print "\n".$newTs."\n";
## Convert from time since epoch in seconds to string YYYY-MM-DD-HH:mm:ss.sss
sub toTimeString{
my $ts=shift;
$ts += 432000; # Add 5 days to make computation easy.
my $sec = $ts-int($ts/60)*60; # Seconds
$ts = int($ts/60); # Convert to minutes
my $min = $ts%60; # minutes
$ts = int($ts/60); # Convert to hours.
my $hr = $ts%24; # hours;
$ts = int($ts/24); # Convert to days.
my $yr;
my $mo;
my $day;
my $days = [ [31,28,31,30,31,30,31,31,30,31,30,31],
[31,29,31,30,31,30,31,31,30,31,30,31]
];
our $cumDays = [ [0,31,59,90,120,151,181,212,243,273,304,334],
[0,31,60,91,121,152,182,213,244,274,305,335]
];
if($ts<366){$yr=1980;}
else{
$yr = 1981 + int(($ts-366)/1461)*4;
$ts = $ts - 366 - int(($ts-366)/1461)*1461;
if($ts < 1095){$yr += int($ts/365); $ts = $ts - int($ts/365)*365;}
else{$yr = $yr+3; $ts=$ts-1095;}
}
my $leap=0;
my $i;
if($yr%4 == 0){$leap=1;}
for($i=0;$i<12;$i++){
if($ts > $cumDays->[$leap]->[$i]){
$mo=$i+1;
}
}
$day=$ts-$cumDays->[$leap]->[$mo-1]+1;
my $ret;
$ret = sprintf("%04d",$yr)."-".sprintf("%02d",$mo)."-".sprintf("%02d",$day)."-";
$ret = $ret.sprintf("%02d",$hr).":".sprintf("%02d",$min).":".sprintf("%02d",int($sec));
$ret = $ret."\.".sprintf("%03d",int($sec*1000)%1000);
return ($ret)
}

Perl - How to get date of Previous wednesday from the given date without using DateTime

All,
I want to find out the date of previous wednesday from the given date.
Eg. I have date as "20150804" and i would need "20150729".
DateTime is not available and i cannot install it as well.
I looked at few examples but they were using DateTime.
Can you please redirect me where i can get some help.? Thanks.
I am planning to code something like below.
Code:
#!/opt/perl-5.8.0/bin/perl
use warnings;
use strict;
my $dt="20150804";
my $prevWednesday=getPrevWednesday($dt);
sub getPrevWednesday()
{
my $givenDt=shift;
...
}
Another brute force approach, this time using another core module Time::Local.
#!/usr/bin/perl
use warnings;
use strict;
use Time::Local;
sub prev_wednesday {
my $date = shift;
my ($year, $month, $day) = $date =~ /(....)(..)(..)/;
my $time = timelocal(0, 0, 12, $day, $month - 1, $year);
do { $time -= 60 * 60 * 24 } until (localtime $time)[6] == 3; # <- Wednesday
my ($y, $m, $d) = (localtime $time)[5, 4, 3];
return sprintf "%4d%02d%02d\n", 1900 + $y, $m + 1, $d;
}
print $_, ' ', prev_wednesday($_), for qw( 20150804 20150805 20150806
20150101 20000301 20010301 );
Using Time::Piece :
use feature qw(say);
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $str = '20150804';
my $fmt = '%Y%m%d';
my $t = Time::Piece->strptime($str, $fmt);
do {
$t = $t - ONE_DAY;
} until ( $t->day eq 'Wed');
say $t->strftime($fmt);
There's always the brute force approach.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use POSIX 'strftime';
my $ONE_DAY = 24 * 60 * 60;
# Get now
my $time = time;
# Subtract days until you get to a Wednesday
do {
$time -= $ONE_DAY;
} until (localtime($time))[6] == 3;
# Format
say strftime '%Y%m%d', localtime $time;
But if you're working in a Perl environment where you can't install modules from CPAN, then it is always worth working to get that restriction removed. Modern Perl programming is often a case of plumbing together the right series of CPAN modules. If you don't have access to CPAN then you're just making your life much harder than it needs to be.
If you really can't get the restriction lifted, then look for another job. It's not worth dealing with people who impose such pointless restrictions.
Update: Just noticed that you're also using a prehistoric version of Perl. You'll need to remove the use 5.010 and replace the say with print. And brush up your CV :-/
Update 2: choroba's solution is better. It deals with any date in the correct format. Mine just deals with the current date. The advice about fixing your working environment still holds though.
Here is a more elegant solution that does not do bruteforce.
use strict;
use warnings;
use Time::Local 'timelocal';
use POSIX 'strftime';
my $dt = "20150804";
say getPrevWednesday($dt);
# note you do not want () here,
# see http://perldoc.perl.org/perlsub.html#Prototypes
sub getPrevWednesday {
my $givenDt = shift;
# parse the string into a unix timestamp
my ( $year, $month, $day ) = $givenDt =~ /(....)(..)(..)/;
my $timestamp = timelocal( 0, 0, 12, $day, $month - 1, $year );
# get the day of week, ignore the rest
my ( undef, undef, undef, undef, undef, undef, $wday ) =
localtime $timestamp;
# because we start the week with Sunday on day 0
# and to get to the previous Wednesday from Sunday it's
# 4 days (Wednesday is 3) we can add 4 to the
# number of this day, divide by 7, take the leftover (modulo)
# and then subtract that many days
# (86_400 is one day in seconds)
# v- -6 ------
# 6 % 7 = 6
# +4 -----v
# v
# 0 1 2 3 4 5 6 0 1 2 3 4 5 6
# S M T W T F S S M T W T F S
my $prev_wed = $timestamp - ( ( $wday + 4 ) % 7 * 86_400 );
# go one week back if we got the same day
$prev_wed -= ( 7 * 86_400 ) if $prev_wed == $timestamp;
# debug output
warn "in: " . localtime($timestamp) . "\n";
warn "out: " . localtime($prev_wed) . "\n\n";
# put it back into your format
return strftime('%Y%m%d', localtime $timestamp);
}
Output:
# STDOUT
20150804
# STDERR
in: Tue Aug 4 12:00:00 2015
out: Wed Jul 29 12:00:00 2015