Convert YYDDD to YY/MM/DD - perl

I'm looking for a way to convert a date in format YYDDD to YY/MM/DD.
i.e. 12212 becomes 12/07/30.
An example in php can be found at http://www.longpelaexpertise.com.au/toolsJulian.php and you can find a DDD calendar at http://landweb.nascom.nasa.gov/browse/calendar.html
I'd appreciate any guidance both with and without perl modules.
Thanks!
edit: I'm not looking for a way to convert php2perl or anything like that. I'm simply looking for a way to convert YYDDD to YY/MM/DD using perl. I would prefer a way without using any additional perl modules however if that is the only way to do it, then I'll welcome examples using perl modules.

The nicest way would be to use Time::Piece to parse the date and reformat it, like this
Time::Piece->strptime('12212', '%y%j')->strftime('%y/%m/%d')
unfortunately however, the module doesn't accept %j (day of year) in its strptime format (although it is fine in strftime).
The second best option is strftime from the POSIX module. The string has to be split into year and day first, and the day of year is zero-based so one must be subtracted, but then the conversion is straightforward. It is probably best packaged as a subroutine, like this
use strict;
use warnings;
use POSIX 'strftime';
sub yj2ymd {
my ($year, $yday) = $_[0] =~ /(\d\d)(\d\d\d)/;
strftime('%y/%m/%d', 0, 0, 0, 0, 0, 2000+$year, 0, $yday-1);
}
print yj2ymd('12212');
output
12/07/30
You will have to do something fancier with the year if you expect any dates from last century.

I like Time::Piece simply because it comes with Perl since, I believe revision 5.10. You'll find dozens of date/time modules and everyone has their favorites, but with Time::Piece becoming part of the official distribution, it's time to switch to that.
To use Time::Piece, you need to know about strptime (STRing Parse TIME) and strftime (STRing Format TIME) both which uses %x letter formats to represent certain aspects of your time string. The reason is that Time::Piece uses the same %x format characters to both convert your time string to a time object and sometimes to format that time object into your string.
From strftime's manpage:
%j
The day of the year as a decimal number (range 001 to 366).
And:
%y
The year as a decimal number without a century (range 00 to 99).
Now to convert your date:
use Time::Piece;
my $old_time = "12212"; #YYddd
my $time = Time::Piece->strptime( $old_time, "%y%j" );
my $new_time = $time->ymd("/"); Now in YY/MM/DD format
Whoops
Hang on ā€” strptime does not grok %j. This will give the wrong answer.
I didn't test this because I have used Time::Piece so often that I can do it in my sleep. I never used %j before in Time::Piece. The perldoc mentions nothing about %j not working, and I don't get any sort of error. That's not nice.
New strategy. I can parse the YYddd string into year and days. Then, I can get the beginning of the year as 01/01/$year. After that, I can take the days, and add it to the year. However, to do this correctly, I need a constant from Time::Seconds:
use strict;
use warnings;
use feature qw(say);
use Time::Piece;
use Time::Seconds;
my $old_date = "12212";
$old_date =~ /(..)(.*)/;
my $year = $1;
my $days = $2;
my $time = Time::Piece->strptime("01/01/$year", "%m/%d/%y");
$time += ( ( $days - 1 ) * ONE_DAY); #01/01/$year is day 1 and not 0
say $time->strftime("%y/%m/%d");
That gives 12/07/30 as the answer.
Announcement
The owner of the Time::Piece module fixed the error in the module. Version 1.23 now works:
use warnings;
use strict;
use autodie;
use feature qw(say);
use Data::Dumper;
use Time::Piece;
my $old_time = "12212"; #YYddd
my $time = Time::Piece->strptime( $old_time, "%y%j" );
my $new_time = $time->ymd("/"); #Now in YY/MM/DD format
say "Version: $Time::Piece::VERSION";
say $new_time;
This prints out:
Version: 1.23
2012/07/30

Here's a short and sweet way to do what you want:
#!/usr/bin/perl
use strict;
use Date::Calc qw(Add_Delta_Days);
my $dt = '12212';
my $startYr = 2000 + substr($dt, 0, 2);
my $daysToAdd = substr($dt, 2) - 1;
my ($newYr, $newMo, $newDay) = Add_Delta_Days($startYr, 1, 1, $daysToAdd);
printf("%02d/%02d/%02d\n", $newYr % 100, $newMo, $newDay);

Related

Perl using Date::Parse module unable to print <STDIN> date in different format

I want to accept a user date on the command line in format
dd/mm/yyyy
then print the date out to the user in
yyyy/mm/dd
I am trying to use the Date::Parse module to parse into a date to be reprinted.
The Date:Parse docs show that I should be able to get $day, $month and $year from user input.
use Date::Parse;
$time = str2time($date);
($ss,$mm,$hh,$day,$month,$year,$zone) = strptime($date);
This is my current code:
use strict;
use Date::Parse;
print "Enter a date in dd/mm/yyy format: ";
my $user_date = <STDIN>;
my #date = strptime($user_date);
# ( $day, $month, $year ) = strptime($user_date);
# my $user_day = ( ($day) = strptime($user_date) );
print "%Y/%m/%d", #date;
However the print fails and it appears from output that entered 10 of 10 is 9 in output.
Output
Enter a date in dd/mm/yyy format: 16/10/1952
%Y/%m/%d1952916s
What should I do?
The documentation for Date::Parse isn't clear, but it looks like you get the values back in the format that localtime() would expect. The year, for example, seems to be the year minus 1900. This means that the month number will be 0 to 11 rather than 1 to 12.
Date::Parse hasn't been updated for over five years. I'd suggest that it should best be avoided these days. There are much better options to choose from. These include Time::Piece that has been included as a standard part of the Perl distribution since version 5.10.0. You can use its strptime() (string parse time) method to parse your string and its strftime() (string format time) method to format the date object as you like.
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
print "Enter a date in dd/mm/yyy format: ";
chomp(my $user_date = <STDIN>);
my $tp = Time::Piece->strptime($user_date, '%d/%m/%Y');
say $tp->strftime('%Y/%m/%d');
Update:
Also, it's really not clear what this line is supposed to do:
print "%Y/%m/%d", #date;
I think you were thinking of using the strftime() method from POSIX.pm.
print strftime "%Y/%m/%d", #date;
But with use warnings this generates warnings because of all the undefined values in #data (that's a rather bizarre design decision in that module and, in my opinion, another reason to avoid it). You can fix that by replacing:
my #date = strptime($user_date);
With:
my #date = map { $_ // 0 } strptime($user_date);

how to convert from a calendar date to an ordinal date with out any modules

I need to search for a time range in a log file. The time stamps are in JDE Julian date format and I cannot use modules.
The date format is YYYYJJJHHMMSS where JJJ is the days in Julian.
I need to convert the user input in JDE with out using a module.
The fact that these number are date/time values makes no difference. They are integers (albeit rather large ones) and they can be compared in exactly the same way as any other integer.
while (<$your_input_filehandle>) {
# I have no idea of the format of your input data, so I can't
# begin to implement extract_timestamp()
my $this_records_timestamp = extract_timestamp($_);
if ($this_records_timestamp >= $min_timestamp and
$this_records_timestamp <= $max_timestamp) {
# timestamp is within the given range
}
}
Update: To convert YYYYMMDD to YYYYJJ
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $in_format = '%Y%m%d';
my $out_format = '%Y%j';
my $in_date = shift
|| die "Please pass date in format YYYYMMDD\n";
my $date = Time::Piece->strptime($in_date, $in_format);
say $date->strftime($out_format);
You should have access at least to core modules as POSIX:
perl -MPOSIX=mktime -pe'#F=/(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;$j=sprintf"%03d",1+(localtime(mktime(#F[5,4,3,2],$F[1]-1,$F[0]-1900)))[7];s/.{4}\K.{4}/$j/;'
or with Time::Piece
perl -MTime::Piece -ne'print Time::Piece->strptime($_,"%Y%m%d%H%M%S\n")->strftime("%Y%j%H%M%S\n")'

How to convert unknown date/time format into normal using Perl? [duplicate]

This question already has answers here:
How can I parse dates and convert time zones in Perl?
(5 answers)
Closed 7 years ago.
I have a date/time like this: 2015-07-31T13:30:00.000+01:00
And I want to convert it to normal date and time using Perl and Time::Piece->strptime
Here is my code:
sub changeDateFormat {
my ($date, $fromFormat, $toFormat) = (#_);
return Time::Piece->strptime($date, $fromFormat)->strftime($toFormat);
}
The call:
print changeDateFormat($that_date, '%Y-%m-%dT%H:%M:%S.%N+%z', '%Y:%m:%d');
I think that .000 are nano seconds and +01.00 stands for time zone.
But the given code gives this:
Error parsing time at /usr/lib64/perl5/Time/Piece.pm line 470
Any help is appreciated.
There's a couple of problems I think.
%N isn't in my strftime manpage. So that might well not work.
And %z - I'm pretty sure +01:00 isn't valid.
%z The +hhmm or -hhmm numeric timezone (that is, the hour and
minute offset from UTC). (SU)
This works though:
my $date = '2015-07-31T13:30:00+0100';
my $fromFormat = '%Y-%m-%dT%H:%M:%S%z';
print Time::Piece->strptime($date, $fromFormat);
So I'd suggest - unless your milliseconds are important - you could just strip those via a regex, and likewise the timezone. (And it they are important, I don't think Time::Piece does ms resolution anyway)
You can probably use a regular expression to 'correct' your input date if you were so inclined. I'm unsure if fits your use case but:
$date =~ s/\+(\d{2}):(\d{2})$/+$1$2/;
$date =~ s/\.\d{3}+/+/;
You can use strptime in Time::Piece and adding the time zone manually as shown in this answer, or you could try using DateTime::Format::Strptime instead:
use feature qw(say);
use strict;
use warnings;
use DateTime::Format::Strptime;
my $timestamp = '2015-07-31T13:30:00.000+0100';
my $strp = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S.%N%z'
);
my $dt = $strp->parse_datetime( $timestamp );
say $dt->strftime('%Y:%m:%d');
Output:
2015:07:31
use DateTime;
use DateTime::Format::ISO8601;
use DateTime::Format::Strptime;
my $string = '2015-07-31T13:30:00.000+01:00';
my $date = DateTime::Format::ISO8601->parse_datetime( $string );
die "Error" unless $date;
my $formatter = new DateTime::Format::Strptime(pattern => '%Y-%m-%d %T');
$date->set_formatter($formatter);
print "$date\n";

convert perl timestamp to human readable

I am reading a log file which contains time stamps which I want to convert to human readable.
In this command, $1 contains a time stamp (like this 1403457192.663): $temp = localtime->mon($1) but instead of storing the month, $temp contains the same timestamp that was input. What am I doing wrong?
You're close. The time should be passed to the localtime function, not the mon method.:
$temp = localtime($1)->mon; # 6
You can use strftime with this to turn it into any arbitrary format
localtime($1)->strftime("%b %d %a"); # Jun 22 Sun
Or if you're not picky about the format you can just stringify it:
$temp = localtime($1);
print "$temp\n"; # Sun Jun 22 13:13:12 2014
This assumes that Time::Piece is loaded.
I'd simply go with
$ perl -E'
use POSIX qw( strftime );
say strftime("%Y/%m/%d %H:%M:%S", localtime(1403457192.663));
'
2014/06/22 13:13:12
But you're using Time::localtime. That module overrides the localtime builtin, so you need a slight modification if you use that.
Either avoid using Time::localtime's localtime
$ perl -E'
use POSIX qw( strftime );
use Time::localtime qw( localtime );
say strftime("%Y/%m/%d %H:%M:%S", CORE::localtime(1403457192.663));
'
2014/06/22 13:13:12
or flatten an existing Time::localtime object.
$ perl -E'
use POSIX qw( strftime );
use Time::localtime qw( localtime );
my $tm = localtime(1403457192.663);
say strftime("%Y/%m/%d %H:%M:%S", #$tm);
'
2014/06/22 13:13:12
All of these solutions lose the millisecond precision. If it's relevant, you'll have to extract it from the original input and reinsert it in the output.
For formatting dates most system strftime manual pages will list a few "shortcuts" to get you certain "standard" formats.
e.g. %F is equivalent to ā€œ%Y-%m-%dā€.
~/% perl -MPOSIX -E'say strftime"%D",localtime'
06/25/14
~/% perl -MPOSIX -E'say strftime"%F",localtime'
2014-06-25
These can make using "ye olde" strftime easier ;-)
Perl since 5.10 now contains Time::Piece. This makes it the official way to handle time in Perl. Or, about as official as something gets in Perl. Since it's always available, you might as well learn to use that:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds; # More time fun!
my $time = Time::Piece->new; # Gets the current timestamp
my $month = $time->mon(); # Month from 1 to 12
my $month = $time->month(); # Abbreviation of the name of month
my $month = $time->fullmonth(); # Full name of the month
my $time = $time + (ONE_DAY * 30) # Add thirty days to the time
my $date = $time->mdy # The date 30 days from now.

Converting epoch time into mm-dd-yyyy local date

so I am currently using
my $date = DateTime->now->mdy;
which gives me the format in "09-10-2013"
then i used
my $modTime = localtime((stat($some_file))[9]);
which gives me the date in format "Tue Sep 10 15:29:29 2013"
is there a way built in perl to format the $modTime to the format like $date?
or do i have to do it manually?
thanks!
Well, depending on what you mean by "manually", you can do it in a couple of short steps:
# demo.
# Put the whole local time/stat/etc in parentheses where I put localtime.
#
($m,$d,$y) = (localtime)[4,3,5]; # extract date fields using a list slice
$modtime = sprintf('%02d-%02d-%4d', $m+1, $d, $y+1900); # format the string
There's probably a way to do it in a single line, incorporating both the extraction and a more complex format string, but that would lead to unnecessary obfuscation.
-Tom Williams
Create a DateTime object using the from_epoch constructor.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use DateTime;
# Using $0 (the current file) in these examples.
my $epoch = (stat $0)[9];
my $dt = DateTime->from_epoch(epoch => $epoch);
say $dt->dmy;
Of course, that can be rewritten as a single line:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use DateTime;
say DateTime->from_epoch(epoch => (stat $0)[9])->dmy;
But for something this easy, I'd probably use Time::Piece (which is part of the Perl standard distribution) rather than DateTime.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
say localtime((stat $0)[9])->dmy;
Late to answer, already seen the good answer to use from_epoch (DateTime->from_epoch(epoch => $epoch);). You could also use POSIX's function strftime like: print strftime "%m-%d-%Y", localtime( ( stat $some_file )[9] );