43

I know about

date -d @<timestamp in seconds>

and

awk '{print strftime("%c", <timestamp in seconds>)}'

but what if I have milliseconds. Is there trivial way to do this without dropping the final three characters of the millisecond-timestamp (not that dropping characters is difficult, but I would think there'd be a one-step way for such a straightforward task)?

jonderry
  • 23,013
  • 32
  • 104
  • 171

8 Answers8

42

Instead of dropping characters, you could divide by 1000:

awk '{print strftime("%c", ( <timestamp in milliseconds> + 500 ) / 1000 )}'

Or:

date -d @$(  echo "(MilliSecondTimeStamp + 500) / 1000" | bc)

Or (MacOS):

gdate -d @$(  echo "(MilliSecondTimeStamp + 500) / 1000" | bc)

Edit: Adjusted for the quotients instead of division. Edit2: Thx zeekvfu, fixed.

slm
  • 15,396
  • 12
  • 109
  • 124
joepd
  • 4,681
  • 2
  • 26
  • 27
  • Didn't realize the awk solution would work after seeing the output of division in scientific notation. – jonderry Sep 11 '12 at 18:44
  • Gawk (and I presume that this will go for the other flavours as well) has [elementary arithmetic options](http://www.gnu.org/software/gawk/manual/gawk.html#Arithmetic-Ops). You can build more complicated ones from here, of course :) – joepd Sep 11 '12 at 20:01
  • 1
    The `date` command doesn't work at all. The correct answer should be `date -d @$(echo "(millisecond_timestamp+500)/1000" | bc)`. – zeekvfu Jul 19 '14 at 04:16
  • 1
    can you guys tell me what's bad with going like this: `echo $(( ${MilliSecondTimeStamp} / 1000 ))` ? – Alex Feb 01 '17 at 16:23
  • 2
    Dividing by 1000 will round wrong. In shell, the floor integer will be the answer. `3800 / 1000` will be `3`, and not `4`. – joepd Feb 01 '17 at 17:04
  • 1
    Why do you add `+500`? – Bowi Sep 05 '19 at 08:30
  • @Bowi: `3800 / 1000` equals 3 in bash. If you add 500, rounding will do the right thing. – joepd Sep 17 '19 at 03:44
  • 2
    So it is just a very basic `ceil()` function? – Bowi Sep 17 '19 at 09:08
  • @Bowi No it doesn't. After testing it by assuming that the timestamp is postive only, I can conclude that the algorithm above use `functional round to nearest: round half up (towards +∞)` rounding method according to [Wikipedia: Rounding, Comparison of approaches for rounding to an integer](https://en.wikipedia.org/wiki/Rounding#Comparison_of_approaches_for_rounding_to_an_integer). Ceil function use `functional directed rounding: Round up (towards +∞)` method and it's different. – Unknown123 Jul 01 '20 at 14:55
  • @zeekvfu : there's no such thing as a correct answer for `date's` syntax, since `POSIX` spent all their time mandating all kinds of junk, except the ONE thing that's being used quite often by many - the `date` utility. `gnu-date` and `bsd-date` has ridiculously different syntax. – RARE Kpop Manifesto May 16 '23 at 19:23
19

What I have in my bash profile on Mac:

day() {
  date -r $(($1 / 1000))
}

day 1486743904359 returns Fri Feb 10 08:25:04 PST 2017

Benoit Duffez
  • 11,839
  • 12
  • 77
  • 125
Zaheer
  • 2,794
  • 5
  • 28
  • 33
  • 3
    In my copy of date (`GNU coreutils date, 8.26`), the `-r` option is `-r, --reference=FILE ... display the last modification time of FILE`. I used your bash arithmetic with [Kugelman's date invocation](https://stackoverflow.com/a/3249936/1981415) and got the result I needed. – daveloyall Aug 31 '17 at 21:14
15
perl -e 'print scalar localtime(<timestamp> / 1000)'
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Very useful for viewing csv data that has a timestamp: `cat mydata.csv | perl -pe 's{(\d{13})}{print scalar localtime($1/1000)}ge'` – Sridhar Sarnobat Dec 22 '16 at 19:43
  • 2
    Actually I found that the "print" word was screwing up the order of the fields. Simply remove the print keyword, i.e. `cat mydata.csv | perl -pe 's{(\d{13})}{scalar localtime($1/1000)}ge'` – Sridhar Sarnobat Dec 27 '16 at 05:39
3

In BASH

ms_date=1603884641215; date -d @$(((ms_date + 500)/1000))

Explained:

  1. date -d @number-of-seconds receives seconds (since-epoch) and returns formatted date
  2. $(()) represents mathematical interpolation in bash e.g. echo $((1 + 2))
  3. divided milliseconds by 1000 to get required seconds measurement
  4. 500 is added for purposes of milliseconds rounding up to nearest second
yosefrow
  • 2,128
  • 20
  • 29
1

This works great for me in bash and it's a bit simpler than the accepted answer above:

date -r $(( (<timestamp in milliseconds> + 500) / 1000 ))
Eric Green
  • 1,151
  • 9
  • 17
  • 1
    Why do you add 500? Does it have to do with how bash math interpolation works? – yosefrow Jan 12 '21 at 09:59
  • I just copied that algorithm from above and converted it to bash syntax, but presumably so that the divide by 1000 will round up instead of round down in any given case. – Eric Green Jan 13 '21 at 01:38
1

no need to lose any of the precision:

ts=1684254777119
echo $(date -d @${ts:0:10} --utc --iso=s | cut -d '+' -f 1).${ts:10}+00:00

output:

2023-05-16T16:32:57.119+00:00

also works with higher precision:

ts=1684254777119581
echo $(date -d @${ts:0:10} --utc --iso=s | cut -d '+' -f 1).${ts:10}+00:00

output:

2023-05-16T16:32:57.119581+00:00
grenade
  • 31,451
  • 23
  • 97
  • 126
0

Expanding on Greg's perl approach, I use this Perl script https://github.com/moose1089/epoch2iso to automatically scan for epoch timestamps and convert to ISO8061.

This script handles both seconds and milliseconds.

As it as it converts any number it finds, you don't have to extract the specific field with the timestamps, but conversely it might also convert numbers that were not intended to be times.

moose1089
  • 121
  • 5
0

you don't need to use date to keep that precision :

 echo 1684254777119 | 
mawk '{ print strftime("%c ( %s."  substr( $1, 
                length($1) - 2) " )", int( $1 / 1000 ) ) }' 

Tue May 16 12:32:57 2023 ( 1684254777.119 )

you can even go to nanoseconds if your input has that precision level :

echo 5994354333719137839 | 
gawk '{ print strftime("%c ( %s."  substr( $1, 
                length($1) - 8) " )", int( $1 / 10^9)) }' 

Fri Dec 14 21:25:33 2159 ( 5994354333.719137839 )
RARE Kpop Manifesto
  • 2,453
  • 3
  • 11