1

I have an XML file I am parsing and then inserting into my database using PHP. The date nodes however are in a weird XML format that I believed to be milliseconds. My attempts to convert this so far have been unsuccessful and I was hoping someone could point out my error.

XML

<est__start>1335744000000</est__start>
<est__end>1342742400000</est__end>

Which was this much more readable date before being converted

<est__start> 04-30-2012 </est__start>
<est__end> 07-20-2012 </est__end>

PHP

$start = $data->est__start;
if (empty($start)) {
    $start = '';
} else {
    $start = intval($start);            
    $seconds = $start / 1000;
    $start = date("d-m-Y", $seconds);
}
$end = $data->est__start;
if (empty($end)) {
    $end = '';
} else {
    $end = intval($end);            
    $seconds = $end / 1000;
    $end = date("d-m-Y", $end);
}

The end result however is always something similar to this

25-01-1970

and I need it to look like it did before the conversion. Like this

04-30-2012

Since I'm not handling the original date conversion when it goes into the XML file I'm not sure where I'm going wrong. Any help is appreciated.

hakre
  • 193,403
  • 52
  • 435
  • 836
i_me_mine
  • 1,435
  • 3
  • 20
  • 42
  • I *guess* quickly that `intval` is standing in your way. `echo PHP_INT_MAX;` to learn about what the system you're using does offer you. It will cap the seconds to 32 bit signed max. – hakre Aug 21 '13 at 20:49
  • @hakre Although the question is related to converting Unix timestamps, the OP's problem is related to how he wrote the code and the finite size of integers. This is a good question. – Marc Audet Aug 21 '13 at 20:59
  • Yes I was quick with the dupe, you need to use floats instead of casting to in. I also put this in my answer http://stackoverflow.com/a/18367563/367456 and it's the standard way to deal with it on 32 bit system in PHP. The precision should be OK for these cases otherwise this needs bcmath or gmp. – hakre Aug 21 '13 at 21:02

4 Answers4

1

You're using intval but the numbers are too large for int. Just use:

date("m-d-Y", 1335744000000 / 1000); // 04-30-2012
lafor
  • 12,472
  • 4
  • 32
  • 35
  • thanks, im checking it out now, although this makes perfect sense – i_me_mine Aug 21 '13 at 20:53
  • You might also check the PHP constant `PHP_INT_MAX` to be sure. Note that the results will vary depending on the OS of the server running PHP (32 bit versus 64 bit). – Marc Audet Aug 21 '13 at 20:56
  • Depends on your PHP_INT_SIZE ini. Try `echo PHP_INT_SIZE;` Mine is 8 and those numbers are processed just fine. – Geo Aug 21 '13 at 21:09
1

PHP uses the string value of the SimpleXMLElement if you use it in intval or as well directly in numeric calculations (which also cast to int).

As I already commented, my first quick guess was right, it truncates the number than to the maximum integer available on a 32 bit system signed (231 − 1) (I guess you're using one) which results in:

2 147 483 647
25-01-1970

This number btw. is sort of somewhat famous and has it's own Wikipedia page: 2147483647.

Instead use the floatval function, it gives you what you're looking for:

$seconds = floatval($start) / 1000;
echo date("d-m-Y", $seconds); # prints "30-04-2012"

Bonus: Here is some code you might find handy to have the conversion code in a central place and easily make use of:

/**
 * Class MySimpleXMLElement
 *
 * Extended SimpleXMLElement
 */
class MySimpleXMLElement extends SimpleXMLElement
{
    /**
     * @param string $format
     *
     * @return bool|string
     */
    public function getDateFromMilliseconds($format = "d-m-Y") {
        return date("d-m-Y", floatval($this) / 1000);
    }

    /**
     * @param DateTimeZone $timezone
     *
     * @return DateTime
     */
    public function createDateTimeFromMilliseconds(DateTimeZone $timezone = NULL) {
        return new DateTime('@' . (floatval($this) / 1000), $timezone);
    }
}

$data  = new MySimpleXMLElement('<r><est__start>1335744000000</est__start><est__end>1342742400000</est__end></r>');
/* @var $start MySimpleXMLElement */
$start = $data->est__start;

echo $start->getDateFromMilliseconds(), "\n"; # prints "30-04-2012"

echo $start->createDateTimeFromMilliseconds()->format('Y-m-d'); # prints "2012-04-30"

It works by extending from SimpleXMLElement and adding the date-creation functions, here one for a formatted string and for more advanced datetime operations one that gives back a DateTime object.

Last time I extended SimpleXMLElement on Stackoverflow was in an answer to the "How to replace XML node with SimpleXMLElement PHP" question.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • this makes a lot of sense, but when I run the code with `floatval` rather than `date` it throws an error: `floatval() expects exactly 1 parameter, 2 given` – i_me_mine Aug 21 '13 at 21:06
  • well not rather than date, rather than intval :) Look the example, I have not replaced date(). – hakre Aug 21 '13 at 21:07
  • indeed sir, however the problem remains, I find it kind of odd. I am only passing 1 param. Why would it think it's two? – i_me_mine Aug 21 '13 at 21:08
  • ha, I spotted my goof. Silly little typos, get you everytime. It works, thanks. I will be accepting this answer as it is the most complete – i_me_mine Aug 21 '13 at 21:12
  • @i_me_mine: Well, mistakes happen. I also added you some bonus. – hakre Aug 21 '13 at 21:14
  • awesome, thanks. Hopefully this helps people in the future as well. – i_me_mine Aug 21 '13 at 21:15
1

you are not passing seconds in the second run:

enter image description here

Geo
  • 12,666
  • 4
  • 40
  • 55
  • very nice catch, not the root of this problem, but definitely would of been once I had this figured out. Thank you! – i_me_mine Aug 21 '13 at 21:07
0

Possible UTC, and switch day and month:

echo gmdate('m-d-Y',1342742400000 / 1000);

Result:

07-20-2012
vidario
  • 479
  • 2
  • 5