75

this code always returns 0 in PHP 5.2.5 for microseconds:

<?php
$dt = new DateTime();
echo $dt->format("Y-m-d\TH:i:s.u") . "\n";
?>

Output:

[root@www1 ~]$ php date_test.php
2008-10-03T20:31:26.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:27.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:27.000000
[root@www1 ~]$ php date_test.php
2008-10-03T20:31:28.000000

Any ideas?

The Archetypal Paul
  • 41,321
  • 20
  • 104
  • 134
eydelber
  • 1,217
  • 1
  • 11
  • 11

19 Answers19

27

This seems to work, although it seems illogical that http://us.php.net/date documents the microsecond specifier yet doesn't really support it:

function getTimestamp()
{
        return date("Y-m-d\TH:i:s") . substr((string)microtime(), 1, 8);
}
eydelber
  • 1,217
  • 1
  • 11
  • 11
  • 17
    By doing separate calls you have a small chance of two time stamps being out of order: eg call date at 1:29:22.999999 and mircotime at 1:29:23.000001. On my server consecutive calls are about 10 us apart. – Lucky Aug 06 '09 at 22:18
  • 29
    try: list($usec, $sec) = explode(" ", microtime()); return date (date("Y-m-d\TH:i:s", $sec) . $usec; – Lucky Aug 06 '09 at 22:27
  • @eydelber : Since the PHP date() function only accepts integer timestamps the u format character is only useful when using the date_format() function with user based timestamps created with date_create(). – AlexV Jul 12 '11 at 20:01
  • Good simple solution for the simple problem I was looking for. :) – YOMorales Jul 15 '13 at 22:05
  • 1
    This accepted answer induces indeed a race condition. @Lucky's solution is a much better workaround to cope with PHP weirdness. – Déjà vu Jul 16 '14 at 07:54
  • @Lucky your code is not formated correctly there and adds an additional zero to seconds, see my answer for corrections. :) http://stackoverflow.com/a/38334226/614616 – hozza Jul 12 '16 at 16:53
  • The digits format is correct but its accuracy is questionable. It's not a formatting of what time but a concatenation of two times. With an unknown gap between them. – Gherman Dec 02 '21 at 16:43
20

You can specify that your input contains microseconds when constructing a DateTime object, and use microtime(true) directly as the input.

Unfortunately, this will fail if you hit an exact second, because there will be no . in the microtime output; so use sprintf to force it to contain a .0 in that case:

date_create_from_format(
    'U.u', sprintf('%.f', microtime(true))
)->format('Y-m-d\TH:i:s.uO');

Or equivalently (more OO-style)

DateTime::createFromFormat(
    'U.u', sprintf('%.f', microtime(true))
)->format('Y-m-d\TH:i:s.uO');
IMSoP
  • 89,526
  • 13
  • 117
  • 169
tr0y
  • 201
  • 2
  • 2
17

This function pulled from http://us3.php.net/date

function udate($format, $utimestamp = null)
{
    if (is_null($utimestamp))
        $utimestamp = microtime(true);

    $timestamp = floor($utimestamp);
    $milliseconds = round(($utimestamp - $timestamp) * 1000000);

    return date(preg_replace('`(?<!\\\\)u`', $milliseconds, $format), $timestamp);
}

echo udate('H:i:s.u'); // 19:40:56.78128

Very screwy you have to implement this function to get "u" to work... :\

jmccartie
  • 4,956
  • 8
  • 50
  • 71
15

Try this and it shows micro seconds:

$t = microtime(true);
$micro = sprintf("%06d",($t - floor($t)) * 1000000);
$d = new DateTime( date('Y-m-d H:i:s.'.$micro,$t) );

print $d->format("Y-m-d H:i:s.u");
Paolo Moretti
  • 54,162
  • 23
  • 101
  • 92
dbwebtek
  • 245
  • 3
  • 5
  • Fairly elegant, I like this solution best. I've did some tests and didn't find any problems with this implementation. – Dynom Jun 26 '13 at 12:04
  • Great, got anything as elegant to convert back to a microtime? – malhal Nov 14 '14 at 17:21
  • Since you're concatenating the microseconds onto a formatted string anyway, you don't need the `DateTime` object: `date('Y-m-d H:i:s.'.$micro,$t)` is aready the desired string (or equivalently `date('Y-m-d H:i:s.',$t).$micro`) – IMSoP May 24 '16 at 11:19
8
\DateTime::createFromFormat('U.u', microtime(true));

Will give you (at least on most systems):

object(DateTime)(
  'date' => '2015-03-09 17:27:39.456200',
  'timezone_type' => 3,
  'timezone' => 'Australia/Darwin'
)

But there is a loss of precision because of PHP float rounding. It's not truly microseconds.

Update

This is probably the best compromise of the createFromFormat() options, and provides full precision.

\DateTime::createFromFormat('0.u00 U', microtime());

gettimeofday()

More explicit, and maybe more robust. Solves the bug found by Xavi.

$time = gettimeofday(); 
\DateTime::createFromFormat('U.u', sprintf('%d.%06d', $time['sec'], $time['usec']));
Ryan
  • 4,594
  • 1
  • 32
  • 35
  • I am curious about "on most system" statement. Is it a problem of host time resolution or DateTime implementation on certain systems? – cernio Sep 20 '16 at 08:06
  • I don't understand: which is this "float precision issue" you refer to? Can I use safely simply `\DateTime::createFromFormat('U.u', microtime(true));`? – Aerendir Dec 29 '16 at 18:27
  • I've just answered that in another question. Basically, microtime(true) will lose precision because it's stored as a float. It won't actually give you microseconds, but 10ths of milliseconds. http://stackoverflow.com/questions/41390315/what-is-the-highest-date-precision-in-php – Ryan Dec 30 '16 at 05:12
  • Very good point at the float rounding and getting `00` at the end! The `gettimeofday()` solution worked like a charm! – Xavi Montero Mar 22 '17 at 19:15
  • OOPS ERROR!! WARNING!! - I Found a bug. Case: When the number of microseconds is in the lowest 1/10th. For example: If `{seconds:1498953412,microseconds:13}` then the UTC represented is `2017-07-01T23:56:52.130000Z` and it should be `2017-07-01T23:56:52.000013Z` just because it is concatenating `1498953412` + `.` + `13` yielding in: `1498953412.13` while it should result in `1498953412.000013` - This can be problematic in logging and sequenced-events if they happen too close one from each other. – Xavi Montero Jul 02 '17 at 08:40
  • I think I found a neater solution to the zero-padding problem. But I also found a tidier solution using microtime()... – Ryan Jan 08 '18 at 23:56
6

Right, I'd like to clear this up once and for all.

An explanation of how to display the ISO 8601 format date & time in PHP with milliseconds and microseconds...

milliseconds or 'ms' have 4 digits after the decimal point e.g. 0.1234. microseconds or 'µs' have 7 digits after decimal. Seconds fractions/names explanation here

PHP's date() function does not behave entirely as expected with milliseconds or microseconds as it will only except an integer, as explained in the php date docs under format character 'u'.

Based on Lucky's comment idea (here), but with corrected PHP syntax and properly handling seconds formatting (Lucky's code added an incorrect extra '0' after the seconds)

These also eliminate race conditions and correctly formats the seconds.

PHP Date with milliseconds

Working Equivalent of date('Y-m-d H:i:s').".$milliseconds";

list($sec, $usec) = explode('.', microtime(true));
echo date('Y-m-d H:i:s.', $sec) . $usec;

Output = 2016-07-12 16:27:08.5675

PHP Date with microseconds

Working Equivalent of date('Y-m-d H:i:s').".$microseconds"; or date('Y-m-d H:i:s.u') if the date function behaved as expected with microseconds/microtime()/'u'

list($usec, $sec) = explode(' ', microtime());
echo date('Y-m-d H:i:s', $sec) . substr($usec, 1);

Output = 2016-07-12 16:27:08.56752900

Community
  • 1
  • 1
hozza
  • 619
  • 2
  • 12
  • 27
  • 2
    good answer, but actually a millisecond is to 3 decimal places and a microsecond is to 6, and neither of which are technically valid in a true ISO 8601 format. – Jeff Puckett May 31 '17 at 16:50
  • Also worth noting that `DateTime::createFromFormat("U.u", $t)` **would even fail** (rather than just losing precision) if `$t` had 7 (or any more than 6) digits after the decimal point, in PHP... – Sz. Jun 02 '20 at 15:35
4

This has worked for me and is a simple three-liner:

function udate($format='Y-m-d H:i:s.', $microtime=NULL) {
    if(NULL === $microtime) $microtime = microtime();
    list($microseconds,$unix_time) = explode(' ', $microtime);
    return date($format,$unix_time) . array_pop(explode('.',$microseconds));
}

This, by default (no params supplied) will return a string in this format for the current microsecond it was called:

YYYY-MM-DD HH:MM:SS.UUUUUUUU

An even simpler/faster one (albeit, with only half the precision) would be as follows:

function udate($format='Y-m-d H:i:s.', $microtime=NULL) {
    if(NULL === $microtime) $microtime = microtime(true);
    list($unix_time,$microseconds) = explode('.', $microtime);
    return date($format,$unix_time) . $microseconds;
}

This one would print out in the following format:

YYYY-MM-DD HH:MM:SS.UUUU

KyleFarris
  • 17,274
  • 5
  • 40
  • 40
1

date_create

time: String in a format accepted by strtotime(), defaults to "now".

strtotime

time: The string to parse, according to the GNU » Date Input Formats syntax. Before PHP 5.0.0, microseconds weren't allowed in the time, since PHP 5.0.0 they are allowed but ignored.

scronide
  • 12,012
  • 3
  • 28
  • 33
1

How about this?

$micro_date = microtime();
$date_array = explode(" ",$micro_date);
$date = date("Y-m-d H:i:s",$date_array[1]);
echo "Date: $date:" . $date_array[0]."<br>";

Sample Output

2013-07-17 08:23:37:0.88862400

Nadeem
  • 11
  • 1
1

This should be the most flexible and precise:

function udate($format, $timestamp=null) {
    if (!isset($timestamp)) $timestamp = microtime();
    // microtime(true)
    if (count($t = explode(" ", $timestamp)) == 1) {
        list($timestamp, $usec) = explode(".", $timestamp);
        $usec = "." . $usec;
    }
    // microtime (much more precise)
    else {
        $usec = $t[0];
        $timestamp = $t[1];
    }
    // 7 decimal places for "u" is maximum
    $date = new DateTime(date('Y-m-d H:i:s' . substr(sprintf('%.7f', $usec), 1), $timestamp));
    return $date->format($format);
}
echo udate("Y-m-d\TH:i:s.u") . "\n";
echo udate("Y-m-d\TH:i:s.u", microtime(true)) . "\n";
echo udate("Y-m-d\TH:i:s.u", microtime()) . "\n";
/* returns:
2015-02-14T14:10:30.472647
2015-02-14T14:10:30.472700
2015-02-14T14:10:30.472749
*/
mgutt
  • 5,867
  • 2
  • 50
  • 77
1

Working from Lucky's comment and this feature request in the PHP bug database, I use something like this:

class ExtendedDateTime extends DateTime {
    /**
     * Returns new DateTime object.  Adds microtime for "now" dates
     * @param string $sTime
     * @param DateTimeZone $oTimeZone 
     */
    public function __construct($sTime = 'now', DateTimeZone $oTimeZone = NULL) {
        // check that constructor is called as current date/time
        if (strtotime($sTime) == time()) {
            $aMicrotime = explode(' ', microtime());
            $sTime = date('Y-m-d H:i:s.' . $aMicrotime[0] * 1000000, $aMicrotime[1]);
        }

        // DateTime throws an Exception with a null TimeZone
        if ($oTimeZone instanceof DateTimeZone) {
            parent::__construct($sTime, $oTimeZone);
        } else {
            parent::__construct($sTime);
        }
    }
}

$oDate = new ExtendedDateTime();
echo $oDate->format('Y-m-d G:i:s.u');

Output:

2010-12-01 18:12:10.146625
Community
  • 1
  • 1
enobrev
  • 22,314
  • 7
  • 42
  • 53
0

Inside of an application I am writing I have the need to set/display microtime on DateTime objects. It seems the only way to get the DateTime object to recognize microseconds is to initialize it with the time in format of "YYYY-MM-DD HH:MM:SS.uuuuuu". The space in between the date and time portions can also be a "T" as is usual in ISO8601 format.

The following function returns a DateTime object initialized to the local timezone (code can be modified as needed of course to suit individual needs):

// Return DateTime object including microtime for "now"
function dto_now()
{
    list($usec, $sec) = explode(' ', microtime());
    $usec = substr($usec, 2, 6);
    $datetime_now = date('Y-m-d H:i:s\.', $sec).$usec;
    return new DateTime($datetime_now, new DateTimeZone(date_default_timezone_get()));
}
crashmaxed
  • 510
  • 3
  • 9
0

PHP documentation clearly says "Note that date() will always generate 000000 since it takes an integer parameter...". If you want a quick replacement for date() function use below function:

function date_with_micro($format, $timestamp = null) {
    if (is_null($timestamp) || $timestamp === false) {
        $timestamp = microtime(true);
    }
    $timestamp_int = (int) floor($timestamp);
    $microseconds = (int) round(($timestamp - floor($timestamp)) * 1000000.0, 0);
    $format_with_micro = str_replace("u", $microseconds, $format);
    return date($format_with_micro, $timestamp_int);
}

(available as gist here: date_with_micro.php)

Manu Manjunath
  • 6,201
  • 3
  • 32
  • 31
0

Building on Lucky’s comment, I wrote a simple way to store messages on the server. In the past I’ve used hashes and increments to get unique file names, but the date with micro-seconds works well for this application.

// Create a unique message ID using the time and microseconds
    list($usec, $sec) = explode(" ", microtime());
    $messageID = date("Y-m-d H:i:s ", $sec) . substr($usec, 2, 8);
    $fname = "./Messages/$messageID";

    $fp = fopen($fname, 'w');

This is the name of the output file:

2015-05-07 12:03:17 65468400
JScarry
  • 1,507
  • 1
  • 13
  • 25
0

Some answers make use of several timestamps, which is conceptually wrong, and overlapping issues may occur: seconds from 21:15:05.999 combined by microseconds from 21:15:06.000 give 21:15:05.000.

Apparently the simplest is to use DateTime::createFromFormat() with U.u, but as stated in a comment, it fails if there are no microseconds.

So, I'm suggesting this code:

function udate($format, $time = null) {

    if (!$time) {
        $time = microtime(true);
    }

    // Avoid missing dot on full seconds: (string)42 and (string)42.000000 give '42'
    $time = number_format($time, 6, '.', '');

    return DateTime::createFromFormat('U.u', $time)->format($format);
}
Community
  • 1
  • 1
Gras Double
  • 15,901
  • 8
  • 56
  • 54
0

String in a format accepted by strtotime() It work!

0

Here is my one-liner solution:

\DateTime::createFromFormat('U.u', microtime(true))->format('Y-m-d H:i:s.uP');

Output: 2023-08-11 10:15:50.004500+00:00

Theodore R. Smith
  • 21,848
  • 12
  • 65
  • 91
-1

This method is safer than the accepted answer:

date('Y-m-d H:i:s.') . str_pad(substr((float)microtime(), 2), 6, '0', STR_PAD_LEFT)

Output:

2012-06-01 12:00:13.036613

Update: Not recommended (see comments)

f.ardelian
  • 6,716
  • 8
  • 36
  • 53
  • Bad idea. Get the microtime first (use microtime(true) instead of casting it as a float). Use the fraction portion (the microtime) and pass the integer portion as the 2nd param to date(). Otherwise you risk mismatch. – Shane H Jul 31 '12 at 22:47
  • @Encoderer: What do you mean by mismatch risk? Could you give an example? – f.ardelian Aug 12 '12 at 17:05
  • 4
    Suppose the actual time is 2012-08-12 13:53:30.9999. Your call to date() returns '2012-08-12 13:53:30'. Your subsequent call to microtime(), one ten-thousandth of a second later, returns 00 (the second ticked-over between the two calls). While that is an edge case, it's still a circumstance where your code will be broken. If I'm using this code to create an audit-log of events traveling through my system, you've just changed history. The right answer here is to do a single call to microtime(true) and using that result. – Shane H Aug 12 '12 at 19:57
  • oneliner: date("Y-m-d H:i:s.", $m = microtime(1)) . str_pad(floor(fmod($m, 1)*1e+6), 6, "0", STR_PAD_LEFT); – Alex Yaroshevich Oct 08 '13 at 17:39
-2

date('u') is supported only from PHP 5.2. Your PHP may be older!