Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Compute seconds since 1st Jan 1970.

94 views
Skip to first unread message

David Kirkby

unread,
Oct 10, 2009, 7:38:41 PM10/10/09
to
Does anyone have a *portable* shell script that will compute the
seconds since

I've seen some code which does

date -u "%s"

but the %s is a GNUism, and is not portable

I guess with a lot of work, and hopefull no errors, I could work this
out mysekf, but if anyone had done it, I'd apprecaite a copy.

pk

unread,
Oct 10, 2009, 8:18:35 PM10/10/09
to
David Kirkby wrote:

From http://shell.cfajohnson.com/cus-faq.html#Q6


h. Getting the number of seconds since the epoch

- GNU date has the %s format option which returns the epoch
time.

- More portably, use awk.

awk 'BEGIN {srand(); printf("%d\n", srand())}'

This works because srand() sets its seed value with the
current epoch time if not given an argument. It also returns
the previous seed value, so the second call gives the epoch
time.

Note that this doesn't work with older versions of awk. This
requires a version supporting the POSIX spec for srand(). For
example, on Solaris this will not work with /usr/bin/awk, but
will with nawk or /usr/xpg4/bin/awk.

Depending on scheduling, when the call is actually executed,
etc, this might be off by a second.

- Another way is to use perl if you have it.

perl -le 'print time'

- Also, zsh 4.1 and above has the zsh/datetime module that
provides the $EPOCHSECONDS and the strftime function.

Kaz Kylheku

unread,
Oct 10, 2009, 9:19:12 PM10/10/09
to

If you can rely on the "cc" command being installed, the most direct
solution would be a portable UNIX C program (in the classic K&R style, so it
even works with ancient compiler installations). Something like:

#include <stdio.h>
#include <time.h>

int main()
{
printf("%lu\n", (unsigned long) time(NULL));
return 0;
}

Kaz Kylheku

unread,
Oct 10, 2009, 9:39:57 PM10/10/09
to
On 2009-10-11, pk <p...@pk.invalid> wrote:
> Depending on scheduling, when the call is actually executed,
> etc, this might be off by a second.

That would be the case no matter how you access the time. There is
a time difference between when you sample the clock and when you use
the sample. That is a given.

> - Another way is to use perl if you have it.
>
> perl -le 'print time'

If you can get a perl package, you can probably get a C compiler package.

The above is a long-winded way to run some C code which calls time().

It's easier to get a C compiler onto system than perl, particularly
if cross-compiling is involved.

Keith Thompson

unread,
Oct 11, 2009, 4:59:55 AM10/11/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:
> On 2009-10-11, pk <p...@pk.invalid> wrote:
[...]

>> - Another way is to use perl if you have it.
>>
>> perl -le 'print time'
>
> If you can get a perl package, you can probably get a C compiler package.
>
> The above is a long-winded way to run some C code which calls time().
>
> It's easier to get a C compiler onto system than perl, particularly
> if cross-compiling is involved.

True, but if you already have Perl typing a one-line command is
going to be easier than writing, compiling, and executing a small
C program. And I have a system that has Perl, but not a C compiler.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Dave

unread,
Oct 11, 2009, 7:59:50 PM10/11/09
to

Thank you. At the point this script is run, we don't know if the system
has a C compiler of perl, so the awk solution perhaps looks the best of
the two. I don't know what version of POSIX insists on this behavior.

However, using the Solaris 'nawk' (new awk, or the awk in
/usr/xpg4/bin/awk this works on Solaris. I've also tested it on Linux,
OS X, HP-UX 11i, Solaris (both SPARC and x86) and AIX 6.1. The awk
version worked on all of them, so that seems pretty good to me. I've not
tried on Tru64 or IRIX, but those are not high priority systems.

Dave

--
I respectfully request that this message is not archived by companies as
unscrupulous as 'Experts Exchange' . In case you are unaware,
'Experts Exchange' take questions posted on the web and try to find
idiots stupid enough to pay for the answers, which were posted freely
by others. They are leeches.

Stephane CHAZELAS

unread,
Oct 12, 2009, 5:17:06 AM10/12/09
to
2009-10-12, 00:59(+01), Dave:
[...]

>> - More portably, use awk.
>>
>> awk 'BEGIN {srand(); printf("%d\n", srand())}'
[...]

> Thank you. At the point this script is run, we don't know if the system
> has a C compiler of perl, so the awk solution perhaps looks the best of
> the two. I don't know what version of POSIX insists on this behavior.
>
> However, using the Solaris 'nawk' (new awk, or the awk in
> /usr/xpg4/bin/awk this works on Solaris. I've also tested it on Linux,
> OS X, HP-UX 11i, Solaris (both SPARC and x86) and AIX 6.1. The awk
> version worked on all of them, so that seems pretty good to me. I've not
> tried on Tru64 or IRIX, but those are not high priority systems.
[...]

It's in every version of the POSIX spec, but POSIX only says
that srand() should use the time of day, not necessarily
expressed as the number of seconds since 1970. In practice,
though, the POSIX spec is largely based on the /new/ awk (nawk)
that implements it that way, and all the other POSIX awk
implementations implement it that way.

But a script that would use that to expect the number of seconds
since 1970 wouldn't be POSIX. POSIX doesn't guarantee that it
will work like that, and future versions of awk may implement it
otherwise (for instance could use the number of microseconds
since midnight, which would be better BTW), the POSIX spec
leaves that open.

The only POSIX tool that I know that may expose Unix time is
pax, but it would be really awkward to use it that way.

In pratice though the srand()-based one works nowadays.

command -p awk

(in a POSIX shell), should make sure you use the POSIX awk.

--
Stᅵphane

pgas

unread,
Oct 12, 2009, 5:29:35 AM10/12/09
to
Stephane CHAZELAS <stephane...@yahoo.fr> wrote:
> 2009-10-12, 00:59(+01), Dave:

>>>
>>> awk 'BEGIN {srand(); printf("%d\n", srand())}'
>
> In pratice though the srand()-based one works nowadays.

It doesn't work on openbsd and return 0. If I understand the source
correctly it uses arc4random() to return a random number when
srand() has been called without an argument.


--
pgas @ SDF Public Access UNIX System - http://sdf.lonestar.org

Stephane CHAZELAS

unread,
Oct 12, 2009, 5:40:26 AM10/12/09
to
2009-10-12, 09:29(+00), pgas:

> Stephane CHAZELAS <stephane...@yahoo.fr> wrote:
>> 2009-10-12, 00:59(+01), Dave:
>>>>
>>>> awk 'BEGIN {srand(); printf("%d\n", srand())}'
>>
>> In pratice though the srand()-based one works nowadays.
>
> It doesn't work on openbsd and return 0. If I understand the source
> correctly it uses arc4random() to return a random number when
> srand() has been called without an argument.
[...]

If arc4random() is not expressed wrt the time of day, then
that's a conformance bug. And if it returns 0, then, that's a
bug as well.

Are you sure you tried it in a POSIX environment?

--
Stᅵphane

Joachim Schmitz

unread,
Oct 12, 2009, 5:43:35 AM10/12/09
to
pgas wrote:
> Stephane CHAZELAS <stephane...@yahoo.fr> wrote:
>> 2009-10-12, 00:59(+01), Dave:
>>>>
>>>> awk 'BEGIN {srand(); printf("%d\n", srand())}'
>>
>> In pratice though the srand()-based one works nowadays.
>
> It doesn't work on openbsd and return 0. If I understand the source
> correctly it uses arc4random() to return a random number when
> srand() has been called without an argument.

Doesn't work here either, awk does call srand(time(NULL)) (i.e. seconds
since the epoch), but as srand() returns nothing (prototype is void
srand(unsigned int seed);), it prints 0 ?!?

bye, Jojo

pgas

unread,
Oct 12, 2009, 6:38:25 AM10/12/09
to

I've tried with the default awk installed with openbsd, afaict
there is nothing more I can do to make it "more posix".
The man page claims that awk is compliant with posix
( http://tinyurl.com/yzgm8x7 )

arc4random() is not expressed wrt the time of day again afaict
( http://tinyurl.com/yfqeotr )

It would not surprise me from the openbsd team to choose to
break posix to provide better random numbers, in fact the cvs log says:

"Use arc4random() unless the user specifies a specific seed, in which
case, call srandom() and use random(). Avoid using rand() because
it does not produce good random numbers. Based on a diff from deraadt@"

( http://tinyurl.com/yfoe6by )

Keith Thompson

unread,
Oct 12, 2009, 4:10:39 PM10/12/09
to
Stephane CHAZELAS <stephane...@yahoo.fr> writes:
[...]
>>> - More portably, use awk.
>>>
>>> awk 'BEGIN {srand(); printf("%d\n", srand())}'
[...]
> But a script that would use that to expect the number of seconds
> since 1970 wouldn't be POSIX. POSIX doesn't guarantee that it
> will work like that, and future versions of awk may implement it
> otherwise (for instance could use the number of microseconds
> since midnight, which would be better BTW), the POSIX spec
> leaves that open.
[...]

I can imagine that using microseconds since midnight might cause
problems. Consider a script executed once a day from a cron job;
you're not likely to get the same seed every time it runs, but it
could be more predictable than you'd like.

Then again, if you care that much about unpredictability (say, for
a cryptographic application), you probably shouldn't be depending
on the behavior of srand() and rand(), either in awk or in C.

Charles Seeger

unread,
Oct 12, 2009, 11:37:13 PM10/12/09
to
In article <9282108e-04b1-41a0...@g23g2000yqh.googlegroups.com>,

ISTR that there used to be a FAQ for this question somewhere.
In lieu of that...

For ksh/bash/posix using date:

typeset -i DAYS UNIXTIME
set -- $(env LC_ALL=C LC_TIME=C LANG=C date -u '+%Y %j %H %M %S')
# %j might have two leading zeros
set -- "${1#0}" "${2#0}" "${3#0}" "${4#0}" "${5#0}"
set -- "${1#0}" "${2#0}" "${3#0}" "${4#0}" "${5#0}"
set -- ${1:-0} ${2:-0} ${3:-0} ${4:-0} ${5:-0}
DAYS=$(( 365*($1 - 1970) + ($1 - 1969)/4 + $2 - 1 ))
# This will begin to fail in 2100 A.D., unless the following line
# that adjusts for 100 and 400 year rules is uncommented.
#(( $1 >= 2100 )) && DAYS=$(( $DAYS - ($1-2000)/100 + ($1-2000)/400 ))
UNIXTIME=$(( $5 + 60*($4 + 60*($3 + 24*$DAYS)) ))

For old Bourne sh using date and expr (without any post-2099 corrections):

set -- `env LC_ALL=C LC_TIME=C LANG=C date -u '+%Y %j %H %M %S'`
DAYS=`expr 365 \* \( $1 - 1970 \) + \( $1 - 1969 \) / 4 + $2 - 1`
UNIXTIME=`expr $5 + 60 \* \( $4 + 60 \* \( $3 + 24 \* $DAYS \) \)`

The DAYS intermediate variable is something of an artifact, in that
this shell code originated to calculate the last change time for the
passwd shadow file.

If you may need to run this on a system without an env command,
you could guard the date command line with something like:

if type env >/dev/null 2>&1 ; then
set -- `env LC_ALL=C LC_TIME=C LANG=C date -u '+%Y %j %H %M %S'`
else
set -- `date -u '+%Y %j %H %M %S'`
fi

HTH,
Chuck

Wayne

unread,
Oct 13, 2009, 1:41:08 AM10/13/09
to
Kaz Kylheku wrote:
> On 2009-10-10, David Kirkby <drki...@gmail.com> wrote:
>> Does anyone have a *portable* shell script that will compute the
>> seconds since
>>
>> I've seen some code which does
>>
>> date -u "%s"
>>
>> but the %s is a GNUism, and is not portable

I tried to get the Austin group to add that, but after
initially adding it, they removed it again. There is
no elegant way on a POSIX system to do this.

>
> If you can rely on the "cc" command being installed, the most direct
> solution would be a portable UNIX C program (in the classic K&R style, so it
> even works with ancient compiler installations). Something like:
>
> #include <stdio.h>
> #include <time.h>
>
> int main()
> {
> printf("%lu\n", (unsigned long) time(NULL));
> return 0;
> }

All POSIX Unix systems should have "c99" installed, so that
is probably more portable than "cc".

Here is a shell script that should work on POSIX systems:

<script "timestamp.sh">
SEC=$(date -u +%S)
MIN=$(date -u +%M)
HR=$(date -u +%H)
DAY=$(( $(date -u +%j) - 1 )) # Day of year in range [0..365]
YR=$(( $(date -u +%Y) - 1900 ))

TIME=$(printf '%s\n' "$SEC + $MIN*60 + $HR*3600 + $DAY*86400 + \
($YR-70)*31536000 + ( ($YR-69)/4)*86400 - \
( ($YR-1)/100)*86400 + ( ($YR+299)/400)*86400" |bc)

printf '%s\n' "$TIME"
</script>

(Frankly I prefer the simple C program.)
--
Wayne

Wayne

unread,
Oct 13, 2009, 1:44:25 AM10/13/09
to
Stephane CHAZELAS wrote:
> ...

> The only POSIX tool that I know that may expose Unix time is
> pax, but it would be really awkward to use it that way.

I've looked at the pax documentation but don't see how you
would use it for this; could you post some sample code?


--
Wayne

Geoff Clare

unread,
Oct 13, 2009, 8:44:38 AM10/13/09
to
Wayne wrote:

touch foo
printf '%d\n' 0"$(pax -wxcpio foo |
dd bs=1 skip=48 count=11 2>/dev/null)"

--
Geoff Clare <net...@gclare.org.uk>


Geoff Clare

unread,
Oct 13, 2009, 9:04:03 AM10/13/09
to
Charles Seeger wrote:

> For ksh/bash/posix using date:
>
> typeset -i DAYS UNIXTIME

typeset isn't required by POSIX (and isn't needed here anyway)

> set -- $(env LC_ALL=C LC_TIME=C LANG=C date -u '+%Y %j %H %M %S')

There's no need to use env here, and LC_ALL overrides the other
locale variables, so just:

set -- $(LC_ALL=C date -u '+%Y %j %H %M %S')

(I'm not sure LC_ALL is really needed either, since the conversions
are all simple numbers, but it doesn't hurt.)

> # %j might have two leading zeros
> set -- "${1#0}" "${2#0}" "${3#0}" "${4#0}" "${5#0}"
> set -- "${1#0}" "${2#0}" "${3#0}" "${4#0}" "${5#0}"
> set -- ${1:-0} ${2:-0} ${3:-0} ${4:-0} ${5:-0}
> DAYS=$(( 365*($1 - 1970) + ($1 - 1969)/4 + $2 - 1 ))
> # This will begin to fail in 2100 A.D., unless the following line
> # that adjusts for 100 and 400 year rules is uncommented.
> #(( $1 >= 2100 )) && DAYS=$(( $DAYS - ($1-2000)/100 + ($1-2000)/400 ))

It might also fail in 2038, since POSIX allows shells to use 32 bit
arithmetic. (Hopefully before then it will require 64 bit.)
To ensure it works with shells that do 32 bit arithmetic, the last
UNIXTIME=... calculation could be done in bc instead of shell.

> UNIXTIME=$(( $5 + 60*($4 + 60*($3 + 24*$DAYS)) ))
>

--
Geoff Clare <net...@gclare.org.uk>

Gary Johnson

unread,
Oct 14, 2009, 9:13:30 PM10/14/09
to
Wayne <nos...@all.4me.invalid> wrote:

> Here is a shell script that should work on POSIX systems:
>
> <script "timestamp.sh">
> SEC=$(date -u +%S)
> MIN=$(date -u +%M)
> HR=$(date -u +%H)
> DAY=$(( $(date -u +%j) - 1 )) # Day of year in range [0..365]
> YR=$(( $(date -u +%Y) - 1900 ))
>
> TIME=$(printf '%s\n' "$SEC + $MIN*60 + $HR*3600 + $DAY*86400 + \
> ($YR-70)*31536000 + ( ($YR-69)/4)*86400 - \
> ( ($YR-1)/100)*86400 + ( ($YR+299)/400)*86400" |bc)
>
> printf '%s\n' "$TIME"
> </script>

Since each of those components of the time is the result of a separate
invocation of date, there is no guarantee that they are components of
the same time. It would be better to run date with a format containing
all of those components and parse the result.

--
Gary Johnson

Wayne

unread,
Oct 15, 2009, 3:52:59 AM10/15/09
to

I thought of that but decided not to bother. But others
have posted already on how to do that:
set -- $(date ... )
SEC=$1
MIN=$2
and so on.

I never used to care until this thread pointed out that
the awk definition of srand is ambiguous, so until/if
that is fixed there is no simple way to compute this.
I'm going back to using the C program!

[Geoff: I hate to annoy the Austin group by beating
a dead horse; I give up on ``date +%s'', but would it
be worth discussing fixing awk's srand function on the
mailing list? Apparently only OpenBSD would need to
be "fixed" if srand's definition changed from the
vague "time of day" to the precise "seconds since the
Epoch".

And, thanks for posting the pax solution; but it would
be difficult to use well. You can't "touch foo" in a
production-quality script and POSIX doesn't define
"mktemp", so using that method would be involved.]

--
Wayne

Geoff Clare

unread,
Oct 15, 2009, 8:59:23 AM10/15/09
to
Wayne wrote:

> [Geoff: I hate to annoy the Austin group by beating
> a dead horse; I give up on ``date +%s'',

My recollection is that somebody mentioned on the mailing list
the possibility of adding %s, but there was no further
discussion, and no formal request to add it.

If you submit a formal request (in Mantis) I'm sure it won't
annoy the group, and it probably stands a good chance of being
accepted.

> but would it
> be worth discussing fixing awk's srand function on the
> mailing list?

I already tried that in 2004. The request was rejected.

--
Geoff Clare <net...@gclare.org.uk>


Dave

unread,
Oct 15, 2009, 6:37:57 PM10/15/09
to


Thank you very much. The other developers of Sage were not happy about
the use of srand(). I doubt they will like this, complaining it is too
long - some of them seem to think the world starts and ends at linux,
and there is no need to test on other platforms.

I get ridiculed for trying to make mode portable!

Kaz Kylheku

unread,
Oct 16, 2009, 1:52:22 PM10/16/09
to
On 2009-10-15, Dave <f...@coo.com> wrote:
> I get ridiculed for trying to make mode portable!

Did you make a convincing business case for portability, backed with some kind
of credible numbers relevant to your project, and still get ridiculed?

0 new messages