Perl POSIX returning wrong year - perl

I have the code:
use POSIX qw( strftime );
print POSIX::strftime("%a, %d %b %G %T-0000",localtime(1325427034));
which should output
Sun, 01 Jan 2012 09:10:34-0000
but instead it outputs
Sun, 01 Jan 2011 09:10:34-0000
In a few tests I did, it seems to happen whenever the time is in the first few days of 2010, 2011, or 2012. It works correctly in every other case including the first few days of 2013 and 2014. Is there something I should be doing differently?

%G is subject to ISO_8601 standard,
From perldoc
Consult your system's strftime() manpage for details about these and the other arguments.
man strftime
%G The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the
same format and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ)
print POSIX::strftime("%a, %d %b %Y %T-0000",localtime(1325427034));
^__ gives 2012

You want %Y, not %G. %G is intended to be used with %V to show ISO 8601 week numbers, which may start before the beginning of the year or end after it ends.
Example:
use POSIX 'strftime';
print strftime("%Y-%m-%d week %G-%V\n",0,0,0,$_,11,111) for 25..31;
print strftime("%Y-%m-%d week %G-%V\n",0,0,0,$_,0,112) for 1..9;
output:
2011-12-25 week 2011-51
2011-12-26 week 2011-52
2011-12-27 week 2011-52
2011-12-28 week 2011-52
2011-12-29 week 2011-52
2011-12-30 week 2011-52
2011-12-31 week 2011-52
2012-01-01 week 2011-52
2012-01-02 week 2012-01
2012-01-03 week 2012-01
2012-01-04 week 2012-01
2012-01-05 week 2012-01
2012-01-06 week 2012-01
2012-01-07 week 2012-01
2012-01-08 week 2012-01
2012-01-09 week 2012-02

Related

GNU date output specific time

I have a date command that outputs the last day of the previous month:
date -d"-1 day 1 $(date +%b)"
Outputs:
Sun Sep 30 00:00:00 BST 2018
How can I change the time part to be 23:59:59 on the same date?
Here is one solutions:
date -d "$(date +%b) -1.0 sec"
Notice the usage of a float and not an integer for the subtraction. This is to avoid timezone problems.
Thanks for the replies, sadly it gave me an error.
I got things working with:
EPOCH=$(date -d "-1 day 1 $(date +%b)" +%s)
END=`echo $EPOCH +86399 | bc -q`
Which gives me the EPOCH at 23:59:59 on the last day of the previous month.

Sort string + date by most current date in Perl

I have an array:
my #array = ( "\"Passing\" on Wed 12 Jan 2015 09:19:14 AM PST",
"\"Passing\" on Wed 12 Jan 2015 09:19:25 AM PST",
"\"Test Activation\" on Tues 14 Jan 2015 12:05:14 PM PST",
"\"Run Phase\" on Tues 14 Jan 2015 12:06:14 PM PST",
"\"Test Activation\" on Tues 13 Jan 2015 11:43:12 PM PST")
I want to remove the duplicate string line BUT keep the one that is most recent. So I want it to look like:
my #array = ("\"Passing\" on Wed 12 Jan 2015 09:19:25 AM PST",
"\"Test Activation\" on Tues 14 Jan 2015 12:05:14 PM PST",
"\"Run Phase\" on Tues 14 Jan 2015 12:06:14 PM PST")
I can't think of an easy way to do this... I was thinking about using some regex to compare the strings ( /\".*\"/ ) and have it remove duplicates it finds, but I'm not sure how to deal with the date/time.
Any suggestions are most welcome!
There's several options to parse and compare the dates. Simplest is to use the built in Time::Piece. Use strptime for parsing and compare with $time->epoch.
Unfortunately for you, abbreviated time zone names are ambiguous. PST can mean US Pacific Standard Time or Philippine Standard Time. This may cause strptime's %Z format to choke, YMMV. From my strptime man page...
The %Z format specifier only accepts time zone abbreviations of the local time zone, or the value "GMT". This limitation is because of ambiguity due to of the over loading of time zone abbreviations. One such example is EST which is both Eastern Standard Time and Eastern Australia Summer Time.
You may need to pre-process the date formats and convert them to time zone offsets. You can use Time::Zone for this and its distinctly North American slant.
use Time::Zone;
use Time::Piece;
my $offset = sprintf "%+d", (tz_offset("PST") / 60 / 60);
my $time = Time::Piece->strptime(
"Wed 12 Jan 2015 09:19:14 AM $offset",
"%a %d %b %Y %I:%M:%S %p %z"
);
print $time->datetime, "\n";
print $time->epoch, "\n";
But try %Z first and see if it works.
Extracting the dates is also left as an exercise.

Perl workweek conversion is incorrect

I'm faced a weird problem.
I have date in form of Tue Feb 25 00:20:13 2014.
my task is to calculate the week number and the week day.
I tried the following
use Time::Piece;
my $date="Tue Feb 25 00:20:13 2014";
my $db_date=Time::Piece->strptime($date, "%a %b %d %H:%M:%S %Y");
my $ww=$db_date->strftime("%W.%w-%Y);
print $ww;
When I run the script I get the output as
08.2-2014
which is wrong, the expected output is
09.2-2014
I want to know where did i go wrong?
pls help...
You're using the "%W" strftime() conversion. Time::Piece doesn't specify the meaning of "%W", but the documentation for the equivalent C function says that "%W" starts counting with the first week that contains a Monday. It sounds like you want the ISO 8601 week number, which starts counting with the first week that contains at least four days, in which case the "%V" conversion should do what you want.

Month ago from a date given a date in bash or perl

I will be reading random dates from a files, I want to substract 4 months from that date. I am programming in Perl but I can use a bash command.
If i have the 10/14/2013 i will get 06/14/2013.
Thanks a lot!
Karem
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%m/%d/%Y',
on_error => 'croak',
);
my $dt = $format->parse_datetime('10/14/2013');
$dt->subtract( months => 4 );
say $format->format_datetime($dt);
$ date '+%m/%d/%Y' --date='02/14/2013 4 month ago'
10/14/2012
The CPAN module Date::Manip is perfect for this sort of thing. Date::Manip is quite powerful in that it understands business days and holidays... and can parse arbitrary date strings like "next Tuesday" or "3rd Thursday in August".
You should give it a shot; I bet you'll find it quite useful.
Here's a short example the solves your current problem:
#!/usr/bin/perl
use Date::Manip;
my $d = new Date::Manip::Date("10/18/2013");
print $d->printf("%c"), "\n";
my $delta = new Date::Manip::Delta("4 months ago");
my $od = $d->calc($delta);
print $od->printf("%c"), "\n";
...and this generates the following output:
Fri Oct 18 00:00:00 2013
Tue Jun 18 00:00:00 2013
Beware of months that have more days in them than others!
Here's an example where subtracting one month gives you an answer that you are probably not expecting
endOfMarch=$(date -d "2017-03-31")
today=$endOfMarch
echo $(date -d "$today -1 month" "+%Y-%m-%d")
2017-03-02
Because February only has 28 days, subtracting one month has effectively created the date 2017-02-31 which obviously doesn't exist and so has spilled over the extra days into March given you 2017-03-02
Depending on what you are trying to solve you can reliably calculate n months ago, by creating a date variable set to the first day of the current month, then subtracting the months.
endOfMarch=$(date -d "2017-03-31")
today=$endOfMarch
firstDayOfMonth=$(date -d "$today" "+%Y-%m-01")
echo $(date -d "$firstDayOfMonth -1 month" "+%Y-%m-%d")
2017-02-01
So now you have the correct Month (i.e. February when subtracting 1 month) and you then have to figure out what Day to set the new date to based on your use case.
So adding/subtracting Months is not as straight forward as one might think

Convert seconds to date from Jan 01 1901 in unix/linux

im trying to convert a time stamp in seconds from Jan 01 1901 to the current date.
for example,
time stamp 3465468225 translate to a date in 2010. does anyone know of a way to do this in unix/linux? thanks.
In R, it is as simple as this:
> as.POSIXct(3465468225, origin="1901-01-01")
[1] "2010-10-25 15:03:45 CDT"
>
This uses appropriate wrappers around C-level calls gmtime() / localtime() plus time formatting via strftime().
On GNU and POSIX systems you can obtain the date string using seconds since Epoch (1970-01-01 00:00:00 UTC) as:
$ date --date=#1289495920
Thu Nov 11 12:18:40 EST 2010
You should handle the offset since Jan 01 1901 yourself.