Bash convert string to timestamp

12,002

Solution 1

With GNU date, you can convert YYYY-MM-DDTHH:MM:SS to epoch time (seconds since 1-1-1970) easily, like so:

date -d '2014-12-25T09:30:00' +%s

To do this starting without any delimiters:

in=20141225093000
rfc_form="${in:0:4}-${in:4:2}-${in:6:2}T${in:8:2}:${in:10:2}:${in:12:2}"
epoch_time=$(date -d "$rfc_form" +%s)

Solution 2

This Bash function does the conversion with builtins:

# Convert UTC datetime string (YYYY-MM-DD hh:mm:ss) to Unix epoch seconds
function ymdhms_to_epoch
{
    local -r ymdhms=${1//[!0-9]}    # Remove non-digits

    if (( ${#ymdhms} != 14 )) ; then
        echo "error - '$ymdhms' is not a valid datetime" >&2
        return 1
    fi

    # Extract datetime components, possibly with leading zeros
    local -r year=${ymdhms:0:4}
    local -r month_z=${ymdhms:4:2}
    local -r day_z=${ymdhms:6:2}
    local -r hour_z=${ymdhms:8:2}
    local -r minute_z=${ymdhms:10:2}
    local -r second_z=${ymdhms:12:2}

    # Remove leading zeros from datetime components to prevent them
    # being treated as octal values
    local -r month=${month_z#0}
    local -r day=${day_z#0}
    local -r hour=${hour_z#0}
    local -r minute=${minute_z#0}
    local -r second=${second_z#0}

    # Calculate Julian Day Number (jdn)
    # (See <http://en.wikipedia.org/wiki/Julian_day>, Calculation)
    local -r -i a='(14-month)/12'
    local -r -i y=year+4800-a
    local -r -i m=month+12*a-3
    local -r -i jdn='day+(153*m+2)/5+365*y+(y/4)-(y/100)+(y/400)-32045'

    # Calculate days since the Unix epoch (1 Jan. 1970)
    local -r -i epoch_days=jdn-2440588

    local -r -i epoch_seconds='((epoch_days*24+hour)*60+minute)*60+second'

    echo $epoch_seconds

    return 0
}

Example usage:

$ ymdhms_to_epoch '1970-01-01 00:00:00'
0
$ ymdhms_to_epoch '2014-10-18 00:10:06'
1413591006
$ ymdhms_to_epoch '2014-12-25 09:30:00'
1419499800

Solution 3

You need to transform the string before calling date:

#!/bin/bash

s="20141225093000"
s=$(perl -pe 's/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/$1-$2-$3 $4:$5:$6/g' <<< "$s")
date -d "$s" +%s

Yet another way:

perl -MDate::Parse -MPOSIX -le '$s="20141225093000"; $s =~ s/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/$1-$2-$3 $4:$5:$6/g ; print str2time($s);'
1419499800

Solution 4

GNU awk:

gawk -v t=20141225093000 'BEGIN {gsub(/../, "& ", t); sub(/ /,"",t); print mktime(t)}'

If GNU date is not available, then it's likely GNU awk may not be. Perl probably has the highest chance of being available. This snippet uses strptime so you don't have to parse the time string at all:

perl -MTime::Piece -E 'say Time::Piece->strptime(shift, "%Y%m%d%H%M%S")->epoch' 20141225093000
Share:
12,002
user3979986
Author by

user3979986

Updated on June 14, 2022

Comments

  • user3979986
    user3979986 almost 2 years

    I have a string in format 20141225093000 which represents Dec 25, 2014 09:30:00 and I want to convert the original format to a unix timestamp format so i can do time operations on it.How would I do this in bash?

    I can easily parse out the values with expr but I was hoping to be able to identify a format like YYYYmmddHHMMSS and then convert it based on that.

  • Charles Duffy
    Charles Duffy over 9 years
    If you're going to use perl, why not do the whole thing there? That way you don't need to depend on GNU date (and will work on non-GNU platforms such as MacOS)... and Perl's date-time libraries will take a format string, so you can do the actual conversion in a shorter/more readable way.
  • Charles Duffy
    Charles Duffy over 9 years
    +1: Tooling with a reasonable chance of being installed in places where GNU date is not.
  • Tiago Lopo
    Tiago Lopo over 9 years
    @CharlesDuffy it could be done in perl but when I see sh bash tags I limit my perl commands to one-liner and avoid using non-core modules which may not be installed.
  • Charles Duffy
    Charles Duffy over 9 years
    fair 'nuff, but if you're trying to honor the bash tag, none of the functionality you used in perl is anything bash doesn't already have built-in (there's already support for regular expressions with capture and group extraction via the =~ operator and BASH_REMATCH).
  • Tiago Lopo
    Tiago Lopo over 9 years
    @CharlesDuffy Date::Parse and POSIX are core modules so I can use :)
  • Charles Duffy
    Charles Duffy over 9 years
    +1; adds something new and useful to the other answers (by being something that'll work on systems with neither GNU date nor GNU awk). :)
  • Charles Duffy
    Charles Duffy over 9 years
    Interesting. Have you compared results against a canonical implementation? I'm curious as to whether there are other factors such as leap seconds involved.
  • pjh
    pjh over 9 years
    I compared the results of this implementation against GNU 'date' for a large number of random dates and times between 1970 and 2038 on a modern Linux system. They all agreed. The GNU 'date' documentation (gnu.org/software/coreutils/manual/html_node/…) suggests that most systems do not use leap seconds. Even on a system that did use leap seconds, this implementation could be useful for the purposes suggested in the question.