384

I need a shell command or script that converts a Unix timestamp to a date. The input can come either from the first parameter or from stdin, allowing for the following usage patterns:

ts2date 1267619929

and

echo 1267619929 | ts2date

Both commands should output "Wed Mar 3 13:38:49 2010".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chiborg
  • 26,978
  • 14
  • 97
  • 115

18 Answers18

731

On systems with GNU Coreutils >= 5.3.0, e.g. Linux you can use:

date -d @1267619929
Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
a'r
  • 35,921
  • 7
  • 66
  • 67
  • 7
    One-liner: `date -d @$(date -u +%s)` – Mike Atlas Jan 05 '16 at 17:30
  • 35
    What is the `@` for? I don't see that in the GNU man page at all. – Mark E. Haase Feb 01 '16 at 03:06
  • 19
    @mehaase - the man page notes that the date string is too complex to be documented in the manpage, so it is described in info: **info '(coreutils) date invocation'** – MByD Mar 23 '16 at 08:48
  • 23
    `brew install coreutils; gdate -d @1267619929` – Mark Fox May 23 '17 at 22:21
  • 7
    `info 'Date input formats'` gets you straight to `date`'s formatting info node, with the pertinent Robert Grudin quote, and then a menu of format specifiers. – mike Aug 19 '19 at 01:02
  • 1
    In a pipe: `echo 1267619929 | date -d @$( – rkok Apr 18 '20 at 17:50
  • This didn't work with my timestamp. My timestamp is: "1673568280102". The output of this command was: "Sun Jan 2 12:00:00 AM PST 167356828" but if I use https://www.epochconverter.com/ it shows the correct timestamp: Thursday, January 12, 2023 4:04:40.102 PM It looks like your command doesn't account for time zones? – Raleigh L. Jan 21 '23 at 22:08
  • @RaleighL. usually UNIX/epoch time is measured in seconds, whereas your example is in milliseconds. So if you remove the final 3 digits, you should find it gives the date & time that you're expecting. – a'r Mar 14 '23 at 01:14
  • If your converted date looks like 50000 years from the future you should try removing the last 3 digits from the input. My best guess is that these were milliseconds. https://www.unixtimestamp.com/ ignored them and the results made sense. :-) – Benjamin Aug 28 '23 at 17:30
230
date -r <number>

works for me on Mac OS X.

Mundi
  • 79,884
  • 17
  • 117
  • 140
rafaelzlisboa
  • 2,638
  • 1
  • 13
  • 7
  • 7
    Yes, but it doesn't handle fractions of a second. – mgold Oct 31 '13 at 22:21
  • 1
    And it's confusing because it's not working in that way on most linux systems, because: freddy@hades ~ % date --help | grep -- -r -r, --reference=FILE display the last modification time of FILE – frank42 Sep 19 '17 at 16:02
  • 11
    while the comments on this answer are true, these are not rafa's fault, and they don't diminish his answer. – mike Aug 19 '19 at 01:07
32

This version is similar to chiborg's answer, but it eliminates the need for the external tty and cat. It uses date, but could just as easily use gawk. You can change the shebang and replace the double square brackets with single ones and this will also run in sh.

#!/bin/bash
LANG=C
if [[ -z "$1" ]]
then
    if [[ -p /dev/stdin ]]    # input from a pipe
    then
        read -r p
    else
        echo "No timestamp given." >&2
        exit
    fi
else
    p=$1
fi
date -d "@$p" +%c
Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • +1: very complete answer and I think date is faster than gawk. – Bruno Brant Mar 03 '10 at 14:19
  • @Bruno, how do you know date is faster than gawk.? – ghostdog74 Mar 03 '10 at 14:23
  • 3
    @Bruno, @ghostdog74: On my system, `gawk` is (very roughly) 15% faster than `date` in a timed `for` loop consisting only of `gawk 'BEGIN { print strftime("%c", 1256571985); }'` or `date -d '@1256571985' +%c` with output redirected to `/dev/null`. – Dennis Williamson Mar 03 '10 at 14:57
  • I chose this as the "best" answer because the script satisfies the "date input via parameter or stdin" better. Thanks for enhancing my shellscripting skills! – chiborg Mar 08 '10 at 21:31
  • 1
    date is marginally (5%) faster than gawk for me (mac osx 10.9.2, date 8.23, gawk 4.1.1), but the real advantage of (g)awk is to accept a pipe of a column of many timestamps (see my answer below), which makes the script e.g. 250x as fast for 1000 timestamps. – webb Sep 25 '14 at 23:10
  • 3
    Note that my answer meets the OP's requirements as stated in the question, but the now accepted answer doesn't. – Dennis Williamson Sep 26 '14 at 15:19
20

You can use GNU date, for example,

$ sec=1267619929
$ date -d "UTC 1970-01-01 $sec secs"

or

$ date -ud @1267619929
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • On macOS, you can run `brew install coreutils` and then use the `gdate` command, e.g. `gdate --date @1660859222` which yields `Thu Aug 18 14:47:02 PDT 2022`. – Kurt Peek Aug 18 '22 at 21:52
19

You can get formatted date from timestamp like this

date +'%Y-%m-%d %H:%M:%S' -d "@timestamp"
andranikasl
  • 1,242
  • 9
  • 10
14

I use this cross-platform one-liner:

date -d @1267619929 2>/dev/null || date -r 1267619929

It should work both in macOS and modern versions of popular Linux distributions.

Blago
  • 4,697
  • 2
  • 34
  • 29
11

Since Bash 4.2 you can use printf's %(datefmt)T format:

$ printf '%(%c)T\n' 1267619929
Wed 03 Mar 2010 01:38:49 PM CET

That's nice, because it's a shell builtin. The format for datefmt is a string accepted by strftime(3) (see man 3 strftime). Here %c is:

%c The preferred date and time representation for the current locale.


Now if you want a script that accepts an argument and, if none is provided, reads stdin, you can proceed as:

#!/bin/bash

if (($#)); then
    printf '%(%c)T\n' "$@"
else
    while read -r line; do
        printf '%(%c)T\n' "$line"
    done
fi
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
10

You can use this simple awk script:

#!/bin/gawk -f   
{ print strftime("%c", $0); }

Sample usage:

$ echo '1098181096' | ./a.awk 
Tue 19 Oct 2004 03:18:16 AM PDT
$
codaddict
  • 445,704
  • 82
  • 492
  • 529
6

I use this when converting log files or monitoring them:

tail -f <log file> | gawk \
'{ printf strftime("%c", $1); for (i=2; i<NF; i++) printf $i " "; print $NF }'
Emissary
  • 9,954
  • 8
  • 54
  • 65
user3544224
  • 61
  • 1
  • 2
  • exactly what I was looking for, can be shortened to `awk '{ printf strftime("%c: ", $1); $1 = ""; print $0; }'` – ChrisWue May 27 '19 at 07:36
6

In OSX, or BSD, there's an equivalent -r flag which apparently takes a unix timestamp. Here's an example that runs date four times: once for the first date, to show what it is; one for the conversion to unix timestamp with %s, and finally, one which, with -r, converts what %s provides back to a string.

$  date; date +%s; date -r `date +%s`
Tue Oct 24 16:27:42 CDT 2017
1508880462
Tue Oct 24 16:27:42 CDT 2017

At least, seems to work on my machine.

$ uname -a
Darwin XXX-XXXXXXXX 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64
erik258
  • 14,701
  • 2
  • 25
  • 31
  • If you already have a timestamp value and you just need to find the equivalent date, in OS X you can just type this at the command line: date -r 1509453417 – Noel Whitemore Oct 31 '17 at 12:21
  • 1
    That's true. If you look at my answer, I'm actually demonstrating that. – erik258 Nov 14 '17 at 21:41
4

I have written a script that does this myself:

#!/bin/bash
LANG=C
if [ -z "$1" ]; then
    if [  "$(tty)" = "not a tty" ]; then
            p=`cat`;
    else
            echo "No timestamp given."
            exit
    fi
else
    p=$1
fi
echo $p | gawk '{ print strftime("%c", $0); }'
chiborg
  • 26,978
  • 14
  • 97
  • 115
3

In this answer I copy Dennis Williamson's answer and modify it slightly to allow a vast speed increase when piping a column of many timestamps to the script. For example, piping 1000 timestamps to the original script with xargs -n1 on my machine took 6.929s as opposed to 0.027s with this modified version:

#!/bin/bash
LANG=C
if [[ -z "$1" ]]
then
    if [[ -p /dev/stdin ]]    # input from a pipe
    then
        cat - | gawk '{ print strftime("%c", $1); }'
    else
        echo "No timestamp given." >&2
        exit
    fi
else
    date -d @$1 +%c
fi
webb
  • 4,180
  • 1
  • 17
  • 26
2

some example:

$ date
Tue Mar 22 16:47:06 CST 2016

$ date -d "Tue Mar 22 16:47:06 CST 2016" "+%s"
1458636426

$ date +%s
1458636453

$ date -d @1458636426
Tue Mar 22 16:47:06 CST 2016

$ date --date='@1458636426'
Tue Mar 22 16:47:06 CST 2016


kai
  • 1,640
  • 18
  • 11
1

I like the previous solutions for special cases: printf '%(%c)T\n' $timestamp (if you have Bash>=4.2), date -d @$timestamp (if you have GNU Coreutils >= 5.3.0), date -r $timestamp (if you are on BSD/MacOS), but I needed something more portable and I opted for jq that I have everywhere to help with JSON:

jq 'todate' <<< $timestamp

Which could be used in a function to obtain the desired effect:

ts2date() { [[ -n "$1" ]] && { jq 'todate' <<< $1 ; true;} || jq 'todate' ; }
marco
  • 488
  • 4
  • 9
0

While not pure bash, the following script will convert timestamps of length 13 in a string to their equivalent date in your local timezone using perl

timestamp_to_date.sh

#!/usr/bin/env bash
IT=$(cat /dev/stdin)
re='(.*)([0-9]{13})(.*)'
while [[ $IT =~ $re ]]; do
  TIMESTAMP=${BASH_REMATCH[2]}
  AS_DATE=$(echo "$TIMESTAMP" | perl -pe 's/([\d]{10})([\d]{3})/localtime $1/eg;')
  IT="${IT/$TIMESTAMP/$AS_DATE}"    
done
echo "$IT"

input

{"timestamp":"1573121629939","level":"DEBUG","thread":"http-nio-15372-exec-3","logger":"org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor"}

output

$ cat input | timestamp_to_date.sh

{"timestamp":"Thu Nov  7 06:13:49 2019","level":"DEBUG","thread":"http-nio-15372-exec-3","logger":"org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor"}
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
0

I had to convert timestamps inline in my bash history to make sense to me.

Maybe the following coming from an answer to How do I replace a substring by the output of a shell command with sed, awk or such? could be interesting to other readers too. Kudos for the original sed inline code go to @Gabriel.

cat ~/.bash_history | sed "s/^#\([0-9]\+\)$/echo -n '#'; date -u --d @\1 '\+\%Y-\%m-\%d \%T'/e" | less
stefan123t
  • 183
  • 2
  • 5
0

If you're looking to format many timestamps all at once, I've written a C util called datefmt that formats timestamps in a text stream:

Let’s say we have some logs that contain unix timestamps:

$ cat logs.txt

EVENTS  1638499687   blahblah log1
EVENTS  1638499717   blahblah log2

We can pipe this log into datefmt to convert these timestamps into human-readable dates:

$ <logs.txt datefmt

EVENTS  2021-12-02 18:48   blahblah log1
EVENTS  2021-12-02 18:48   blahblah log2

Of course you can customize the format as well:

$ <logs.txt datefmt "DATE:'%m-%d %R'"

EVENTS  DATE:'12-02 18:48'   blahblah log1
EVENTS  DATE:'12-02 18:48'   blahblah log2

I've packaged this in NixOS, hopefully it will trickle out to other distros soon, but for now you will need to download the tarball and build it with make

William Casarin
  • 2,542
  • 2
  • 23
  • 24
-15

In PHP

$unix_time = 1256571985;

echo date("Y-m-d H:i:s",$unix_time)
Lucas
  • 3,059
  • 5
  • 33
  • 48