6

I'd like to re-arrange a timestamp with a Perl regex with the least code possible. The original format of the time stamp is as follows:

2011/12/29 20:19:45

All I need to do is convert it so that the year at the front instead comes after the month/date as follows:

12/29/2011 20:19:45

I was able to achieve this with the 3 lines of code below. I'm just wondering if there is a way to do this with less code. In particular I tried to do away with the middle line saving $1 into an intermediate variable, and specifying $1 from the first substitution directly in the regex for the second substitution, but this resulted in the error: "Use of uninitialized value $1 in concatenation (.) or string."

If the second line cannot be gotten rid of, then it would seem like this can't be gotten down to one line either?

#my $ts = '2011/12/29 20:19:45'; #input to a subroutine

$ts =~ s/^(\d{4})\///;
my $year = $1;
$ts =~ s/ /\/$year /;
1
  • +1 on George's comment. Please don't down-mod without an explanation. That's not helpful to anyone.
    – AWT
    Commented Jan 5, 2012 at 18:03

3 Answers 3

5

Here are you go:

$ts =~ s|^(\d{4})/(\d{2})/(\d{2})(.+)$|$2/$3/$1$4|;

Please note that the above expression expects timestamps having exactly 2 digits for months and days and 4 digits for years. But you can make it even shorter yet more reliable:

$ts =~ s|^(\d+)/(\d+)/(\d+)(.+)$|$2/$3/$1$4|;

This one will handle timestamps like 1/12/98 12:34:56 properly.

6
  • Thanks for your expanded answer including explanation and improvement over my "leaning toothpicks"
    – Dexygen
    Commented Jan 5, 2012 at 17:24
  • Any time. BTW, you can make the regex even more short by combining day and month together: s|^(\d+)/(\d+/\d+)(.+)$|$2/$1$3| :) Commented Jan 5, 2012 at 17:33
  • Good answer. Both the question and answer were helpful to me, duly up-modded.
    – AWT
    Commented Jan 5, 2012 at 18:04
  • I tend not to combine day/month together, if only because there are lots of different date formats. Keeping fields grouped makes is more sensible if you ever intend to use the code in something similar again.
    – Rohaq
    Commented Jan 5, 2012 at 18:06
  • 3
    There is no need to capture the rest of the string. If you didn't specify it to change, it won't. Shorter yet is s|(\d{4})/(\d+)/(\d+)|$2/$3/$1|;
    – Axeman
    Commented Jan 5, 2012 at 18:39
1

Using Back References you can get the desired formatting -

[jaypal:~/Temp] echo "2011/12/29 20:19:45" | 
perl -pe 's@([0-9]{4})/([0-9]{2})/([0-9]{2})@$3/$1/$2@'
29/2011/12 20:19:45
2
  • 2
    \3 etc better written as $3.
    – TLP
    Commented Jan 5, 2012 at 18:36
  • 1
    True ... I don't really know perl just used it's sed capabilities. Will update the answer as it looks better and thanks for the feedback. :) Commented Jan 5, 2012 at 18:39
0

For more generic timestamp manipulation you possibly want to roundtrip via strptime/strftime. In this case it's just field reordering, but for more complex cases such as turning month numbers into names, strftime will be required.

my @t = strptime "2011/12/29 20:19:45", "%Y/%m/%d %H:%M:%S";

say strftime "%d/%m/%Y %H:%M:%S", @t;    #  29/12/2011 20:19:45
say strftime "%d %b %Y, %H:%M:%S", @t;   #  29 December 2011, 20:19:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.