26

I am calling an API from where I am getting date /Date(1365004652303-0500)/, I don't understand what format this is. How is this date format called? I was not sure what to google for such type of format.

Can anyone help me out in getting this date in Y-m-d H:i:s format?

The API I am calling is on .NET server. And when I call it using PHP's file_get_contents and json_decode it gives me the following Date format for created date: /Date(1365004652303-0500)/

clami219
  • 2,958
  • 1
  • 31
  • 45
Kalpesh
  • 5,635
  • 2
  • 23
  • 39
  • 1
    It's UNIX time stamp. Before outputting your JSON convert it from UNIX to normal datetime string. – Stan May 25 '13 at 12:57
  • 2
    A UNIX time stamp with timezone offset – hek2mgl May 25 '13 at 12:59
  • The interesting part would be to see an example with a positive timezone offset. How does it look like then? – hakre May 25 '13 at 13:11
  • @hakre: `/Date(1365004652303+0500)/` ? – zerkms May 25 '13 at 13:11
  • 1
    Well, that's one option I see, as well as `/Date(13650046523030500)/` and then that's where the fun starts :) – hakre May 25 '13 at 13:12
  • @hakre: http://stackoverflow.com/q/668488/251311 – zerkms May 25 '13 at 13:13
  • 3
    Please add more information which API this is. – hakre May 25 '13 at 13:15
  • 1
    The odd thing about this timestamp is that it's in milliseconds. If you divide by 1,000, you see a more reasonable number. That's what had me stumped at first. – Scott C Wilson May 25 '13 at 13:31
  • Totally think you picked the wrong answer. – Jimbo May 25 '13 at 13:49
  • @Kalpesh Mehta: As long as you don't say what the GMT offset of your `Y-m-d H:i:s` format is, it is technically not possible to answer your question. – hakre May 25 '13 at 15:08
  • @hakre it got solved, using Baba's and Steven's answers. I wanted GMT-5 hours – Kalpesh May 26 '13 at 09:52
  • @KalpeshMehta: Again: Which API? Reference please! "Got solved" is no-saying, it already got solved earlier on this website, you just missed to understand what the problem is. If you would be so kind to provide more reference with your question, there is more for the future than just yesterdays fun. – hakre May 26 '13 at 10:05
  • @hakre Ok just updated the question. Actually it's on .NET server and our company's internal project, so I can't give the URL. And as I was only concerned about converting the date, I gave sample of date I was getting. – Kalpesh May 26 '13 at 14:11
  • It's not so much about sharing the URL but more the specifics. E.g. which JSON serialization library is used in .NET by that API? – hakre May 26 '13 at 14:17
  • possible duplicate of [Can a JSON object returned by PHP contain a date object](http://stackoverflow.com/questions/1428598/can-a-json-object-returned-by-php-contain-a-date-object) – Gajus Nov 30 '13 at 13:51
  • @KalpeshMehta - Can you please move the checkmark to Baba's answer so I can delete mine? Thanks. – Steven Moseley Jul 17 '14 at 11:29
  • @StevenMoseley Done. Thanks for being honest! – Kalpesh Jul 17 '14 at 23:26

11 Answers11

25

First you need to understand the format you have

/Date(1365004652303-0500)/

Then you have

  • time stamp (U) = 1365004652
  • Milliseconds (u) = 303
  • Difference to Greenwich time (GMT) (O) = -0500

Build a Format

$date = '/Date(1365004652303-0500)/';
preg_match('/(\d{10})(\d{3})([\+\-]\d{4})/', $date, $matches);
$dt = DateTime::createFromFormat("U.u.O",vsprintf('%2$s.%3$s.%4$s', $matches));
echo $dt->format('r');

Output

Wed, 03 Apr 2013 15:57:32 -0500
                            ^
                            |= Can you see the GMT ? 

interface DateFormatParser
{
    /**
     * @param $string
     *
     * @return DateTime
     */
    public function parse($string);

}

abstract class PregDateParser implements DateFormatParser
{
    protected $pattern, $format, $mask;

    public function parse($string) {
        $string = (string)$string;

        $pattern = $this->pattern;
        $format  = $this->format;
        $mask    = $this->mask;

        $r = preg_match($pattern, $string, $matches);
        if (!$r) {
            throw new UnexpectedValueException('Preg Regex Pattern failed.');
        }
        $buffer = vsprintf($mask, $matches);
        $result = DateTime::createFromFormat($format, $buffer);
        if (!$result) {
            throw new UnexpectedValueException(sprintf('Failed To Create from Format "%s" for "%s".', $format, $buffer));
        }
        return $result;
    }
}

class JsonTimestampWithOffsetParser extends PregDateParser
{
    protected $pattern = '/^\/Date\((\d{10})(\d{3})([+-]\d{4})\)\/$/';
    protected $format  = 'U.u.O';
    protected $mask    = '%2$s.%3$s.%4$s';
}

$date   = '/Date(1365004652303-0500)/';
$parser = new JsonTimestampWithOffsetParser;
$dt     = $parser->parse($date);

echo $dt->format('r');
Lionel C.
  • 3
  • 4
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 2
    @StevenMoseley my English is bad but the PHP doc says [u](http://php.net/manual/en/function.date.php) is [Microseconds (added in PHP 5.2.2). Note that date() will always generate 000000 since it takes an integer parameter, whereas DateTime::format() does support microseconds](http://php.net/manual/en/function.date.php) .... – Baba May 25 '13 at 13:44
  • 3
    @KalpeshMehta please note that `0500` is not timezone don't let anyone confuse you – Baba May 25 '13 at 13:47
  • I added `$dt->sub(new DateInterval('PT5H'));` just after `$dt=Datetime::createFromFormat..` to subtract 5 hours from the date and it works. – Kalpesh May 25 '13 at 14:10
  • Seem to not support negative dates (before 1970) – Vivien Nov 27 '18 at 13:43
  • It is important to note that this is close, but not close enough. It will not match negative timestamps, that is times before the 1970 epoch. It will also not work for timestamps with fewer than 10 digits. Also `DateTime::createFromFormat()` will now ignore any timezone passed to it when decoding unix timezones, so the timezone must be added as a separate step. – Jason May 30 '19 at 15:02
7

Try this out:

var_dump(date('Y-m-d H:i:s', '1365004652303'/1000));
$str = '/Date(1365004652303-0500)/';

$match = preg_match('/\/Date\((\d+)([-+])(\d+)\)\//', $str, $date);

$timestamp = $date[1]/1000;
$operator = $date[2];
$hours = $date[3]*36; // Get the seconds

$datetime = new DateTime();

$datetime->setTimestamp($timestamp);
$datetime->modify($operator . $hours . ' seconds');
var_dump($datetime->format('Y-m-d H:i:s'));

Returns:

string(19) "2013-04-03 17:57:32"
string(19) "2013-04-03 12:57:32"
Farkie
  • 3,307
  • 2
  • 22
  • 33
  • 1
    This is the correct answer. My answer gets the timezone from the offset, but doesn't correctly account for daylight savings. – Steven Moseley May 25 '13 at 14:08
  • Be careful: this will lead to incorrect conversions when hour contains minutes as well, like 0930. Must split hours and minutes and do calculation properly. – Ghigo Jul 24 '17 at 09:08
4

Let's break /Date(1365004652303-0500)/ down to:

  • Date
  • 1365004652303
  • -0500

First string makes itself pretty clear.

The next large number is the epoch value

The -0500 represents the timezone in which the dates were originally stored. It is relative to UTC and thus, it is referring to Eastern Standard Time.


EDIT

The epoch is with a milisecond precision. Try this code:

<?php
    $str = "/Date(1365004652303-0500)/";
    preg_match( "#/Date\((\d{10})\d{3}(.*?)\)/#", $str, $match );
    echo date( "r", $match[1] );
?>

You can also use the timezone for setting the date relative to your own. http://codepad.viper-7.com/RrSkMy

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
2

This timestamp is in milliseconds, which is why it's so large.

You can use the PHP date() call to format this timestamp as you wish. Just divide by 1,000 first. In standard US format, it would be

$mydate = date('m d Y', $timestamp);

(where $timestamp is 1365004652303)

To format it in the format you requested (Y-m-d H:i:s) you would use 'Y-m-d H:i:s' as the format string (first parameter). Chop off text starting with "-".

$stamps = preg_split("/-/", $time);
$stamps[0] = $stamps[0]/1000; 
$mydate = date('Y-m-d H:i:s', $stamps[0]); 

This yields 2013-04-03 11:57:32

Others have suggested the 0500 is an offset; if so, you'd want to adjust $stamps[0] accordingly.

Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83
  • Never use `preg_split()` to do what `explode()` can do. I never use `explode()` to do what `strstr()` with a `true` 3rd param can do. – mickmackusa Feb 21 '20 at 01:31
2

This is what I'm using to parse timestamps from a Xero API. It accounts for:

  • Negative timestamps, dates before the epoch.
  • Timestamps with less than 10 digits (but on the epoch - time zero - not sure what that format would even look like)
  • Fractions of a second.
  • Optional timezone offset, both positive and negative.

Code:

if (preg_match('#^(/Date\()([-]?[0-9]+)([0-9]{3})([+-][0-9]{4})?(\)/)$#', $data, $matches)) {
    // Handle Xero API DateTime formats. Examples:
    // "/Date(1436961673000)/" - unix timestamp with milliseconds
    // "/Date(1436961673000+0100)/" - with an additional timezone correction
    // "/Date(-1436961673000-0530)/" - before the epoch, 1924 here
    //
    // RE matches for "/Date(1436961673090+0100)/":
    // [1] = (/Date\()          "/Date("
    // [2] = ([-]?[0-9]+)       "1436961673"    epoch seconds
    // [3] = ([0-9]{3})         "090"           milliseconds
    // [4] = ([+-][0-9]{4})?    "+0100" or ""   optional timezone
    // [5] = (\)/)              ")"

    $result = \DateTime::createFromFormat('U u', $matches[2] . ' ' . $matches[3] . '000')
        ->setTimezone(new \DateTimeZone($matches[4] ?: '+0000'));
}
Jason
  • 4,411
  • 7
  • 40
  • 53
  • Coming back to this, it can probably be simplified a little more. By removing the parenthesis grouping from the start and the end, there would be just the middle three RE matches to use. [2] to [4] would become [1] to [3]. – Jason Aug 06 '19 at 09:34
1

If your date is like /Date(-62135578800000)/, a positive or negative integer without timezone:

$date = substr('/Date(-62135578800000)/', 6, -5);
$date = date('m/d/Y H:i:s', $date + date('Z', $date) * -1);
// 01/01/0001 05:00:00
1

I had some slightly different experience which led me to make a couple of slight changes to Baba's excellent answer.

Using Newtonsoft's JSON library to encode messages in .NET, which I then send to our website (the PHP part), I get a space before the timezone offset, and no +/- character.

I have also seen negative numbers from pre-epoch dates which means I needed to cater for a - sign before the millisecond value.

I altered the regex to this and it works perfectly for me:

^/Date(([-]?\d{10})(\d{3})\s?([+-]?\d{4}))/$

The two differences are

[-]? before the 10-digit millisecond value, and \s? before the timezone offset.

I would put this as a comment on Baba's answer, but my lack of reputation doesn't permit me. I hope this is appropriate for me to post here as I thought it might be useful.

Dan Hobbs
  • 63
  • 1
  • 8
0

The following example uses the preg_match() and the DateTime class:

$date = '/Date(1365004652303-0500)/';

// get the timestamp
$pattern = '~/Date\(([0-9]*)~';
preg_match($pattern, $date, $matches);
$timestamp = round(((int) $matches[1]) / 1000);

$dt = new DateTime();
$dt->setTimestamp($timestamp);

echo $dt->format('Y-m-d H:i:s');
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
0

Baba's answer is very helpful, yet it does not cover 3 cases I need:

  • The timestamp may be negative
  • The timestamp may be less than 10 digits
  • GMT offset may be missing.

This is what I use for getting the timestamp:

$date_string = '/Date(-594262800300+0100)/';
preg_match('/([+-]?\d+)([+-]\d{4})?/', $date_string, $matches);
$timestamp = $matches[1] / 1000;
$hours = $matches[2] * 36; // Get the seconds
return $timestamp + $hours;

Again, as Ghigo noted this is not correct if the offset contains minutes, but it works for my case.

  • If you have a new question, please ask it by clicking the [Ask Question](https://stackoverflow.com/questions/ask) button. Include a link to this question if it helps provide context. - [From Review](/review/low-quality-posts/20956088) – Luceos Sep 25 '18 at 13:07
  • Thanks for the review @Luceos. I do not have a new question, I'm adding more info which was not present in any of the previous answers nor comments, and I believe it might be useful to other users. – Rumyana Ruseva Sep 27 '18 at 12:27
  • Your answer is a new evolution of the question. Answers should only answer the question given. You can ask a new question with your situation and answer it yourself. This is allowed if self answered questions pose value to the community. – Luceos Sep 27 '18 at 17:33
0

You can use this package to parse the JSON dates:

https://github.com/webapix/dot-net-json-date-formatter

use \Webapix\DotNetJsonDate\Date;

$dateTime = Date::toDateTime('/Date(1365004652303-0500)/'); 
// return with \DateTime object, you can format it: $dateTime->format('Y-m-d H:i:s')
zsocakave
  • 66
  • 2
0

Adjusting slightly the excellent answer given by Jason for the Xero API:

if (preg_match('#^\\\/Date\(([-]?[0-9]+)([0-9]{3})([+-][0-9]{4})?\)\\\/$#', $data, $matches)) {
    // Handle Xero API DateTime formats. Examples:
    // "\/Date(1436961673000)\/" - unix timestamp with milliseconds
    // "\/Date(1436961673000+0100)\/" - with an additional timezone correction
    // "\/Date(-1436961673000-0530)\/" - before the epoch, 1924 here
    //
    // RE matches for "\/Date(1436961673090+0100)\/":
    // [1] = ([-]?[0-9]+)       "1436961673"    epoch seconds
    // [2] = ([0-9]{3})         "090"           milliseconds
    // [3] = ([+-][0-9]{4})?    "+0100" or ""   optional timezone
    $result = \DateTime::createFromFormat('U.u', $matches[1] . '.' . $matches[2] . '000')
        ->setTimezone(new \DateTimeZone($matches[3] ?? '+0000'));
}

This uses the current Xero format with additional backslashes, and the simplification to reduce regex groupngs suggested by Jason in a comment.

Jonathan
  • 449
  • 4
  • 11