6

I am trying to import a excel file using PhpExcel lib for all other fields the getValue() function works but when it encounters a field with format date as set in ms-excel2013 the date field in exel file is in format d-m-Y like 16-11-2014 but when I try to import it's value the getValue() returns 11-16-14 which when passed to strtotime further returns false in turn causing the date('Y-m-d',strtotime($date)) to return 1970-01-01.

I searched whole of web and stackoverflow but none solution fixed my problem. In excel file i see the date as 16-11-2014 and want it to be imported as is.

Here's the code

protected function importExcel($filePath) {
    $excelData = array();
    if ($filePath) {
        $objPHPExcel = PHPExcel_IOFactory::load($filePath);
        $objPHPExcel->setReadDataOnly(true);
        foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) {
            $worksheetTitle = $worksheet->getTitle();
            $highestRow = $worksheet->getHighestRow(); // e.g. 10
            $highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
            $highestColumnIndex = PHPExcel_Cell::columnIndexFromString($highestColumn);
            $nrColumns = ord($highestColumn) - 64;
            $data = array();
            for ($row = 1; $row <= $highestRow; ++$row) {
                $values = array();
                for ($col = 0; $col < $highestColumnIndex; ++$col) {
                    $cell = $worksheet->getCellByColumnAndRow($col, $row);
                    if (PHPExcel_Shared_Date::isDateTime($cell))
                        throw new Exception("is date time"); // just a check
                    $val = $cell->getValue();
                    if (isset($val) && $val)
                        $data[$row][$col] = $val;
                }
            }
            $excelData[$worksheetTitle] = $data;
        }
        return $excelData;
    }
    return FALSE;
}
Joshua
  • 40,822
  • 8
  • 72
  • 132
Ashwani Shukla
  • 609
  • 2
  • 11
  • 30
  • @miken32 No, it's not the duplicate as I checked entire web before posting the question and answer too. Kindly read the question and answers mentioned over here, you'll come to know the difference. As Mark Baker(coordinator PhpExcel lib) himself tried helping me in it. – Ashwani Shukla Jul 15 '16 at 11:39

3 Answers3

9

A getValue() call on a field containing a date should return a value like 41959.00 if that field really does contain an MS Excel date value.... that is, an MS Excel serialized datetime stamp based on the number of days since 1st January 1900 (or 1st January 1904 if the file was created using the Mac version of MS Excel)

To get a formatted date string, you need to call getFormattedValue() instead; and PHPExcel then uses the number format mask for that cell to format the date according to that mask.

To identify if a cell contains an MS serialized datetime stamp, you can use a call to PHPExcel_Shared_Date::isDateTime() first.

foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) {
    echo 'Worksheet - ' , $worksheet->getTitle() , EOL;

    foreach ($worksheet->getRowIterator() as $row) {
        echo '    Row number - ' , $row->getRowIndex() , EOL;

        $cellIterator = $row->getCellIterator();
        $cellIterator->setIterateOnlyExistingCells(false); // Loop all cells, even if it is not set
        foreach ($cellIterator as $cell) {
            if (!is_null($cell)) {
                echo '        Cell - ' , $cell->getCoordinate() , ' - ';
                if (PHPExcel_Shared_Date::isDateTime($cell)) {
                    echo $cell->getFormattedValue() , EOL;
                } else {
                    echo $cell->getValue() , EOL;
                }
            }
        }
    }
}

Rather than returning a formatted data value, you can also ask PHPExcel to return the date as a Unix timestamp, or as a PHP DateTime object instead; and then you'll be able to format it however you want using PHP's built-in date functions or DateTime methods.

if (PHPExcel_Shared_Date::isDateTime($cell)) {
    $unixTimeStamp = PHPExcel_Shared_Date::ExcelToPHP($cell->getValue());
    echo date('d-M-Y H:i:s', $unixTimeStamp), PHP_EOL;
}

or

if (PHPExcel_Shared_Date::isDateTime($cell)) {
    $dateTimeObject = PHPExcel_Shared_Date::ExcelToPHPObject($cell->getValue());
    echo $dateTimeObject->format('d-M-Y H:i:s'), PHP_EOL;
}
Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • 1
    PHPExcel_Shared_Date::isDateTime($cell) is returning false so the above suggestion didn't work neither the getFormattedValue(); the file was created using windows and MS-Excel 2013 – Ashwani Shukla Jul 24 '15 at 07:30
  • 1
    You didn't load the file using `setReadDataOnly(true)` did you? – Mark Baker Jul 24 '15 at 07:37
  • i did `$objPHPExcel = PHPExcel_IOFactory::load($filePath); $objPHPExcel->setReadDataOnly(true);` – Ashwani Shukla Jul 24 '15 at 07:39
  • I posted the code above, please see and suggest as the exception is not thrown @ `PHPExcel_Shared_Date::isDateTime($cell)` – Ashwani Shukla Jul 24 '15 at 07:44
  • 1
    Comment out the line `$objPHPExcel->setReadDataOnly(true);`.... because this line tells PHPExcel to only load raw data, and to ignore any style information such as date masks, so it's impossible to identify if a cell is a date or not if you use this – Mark Baker Jul 24 '15 at 08:09
  • the above suggestion also didn't worked out – Ashwani Shukla Jul 24 '15 at 10:53
  • Then I'd guess you don't actually have dates in your spreadsheet, but have strings; and the fact that `getValue()` doesn't return a value like `41959.00` but returns `11-16-14` instead seems to confirm that.... but as I can't see your spreadsheet, you'd have to check that yourself – Mark Baker Jul 24 '15 at 10:53
  • i have dates in my excel sheet. the cell format in the sheet is date : d-m-Y – Ashwani Shukla Jul 24 '15 at 10:56
  • If you have MS Excel dates in the spreadsheet, then changing a cell that contains one of those dates to format it as `number` will show a number like `41959.00`.... does it? – Mark Baker Jul 24 '15 at 10:58
  • yes it does---- But when the `PHPEXCEL` library calls the `Excel2007`, it explicitly typecasts the value to string. `PHPExcel_Reader_Excel2007` line no 847 - the `switch` calls the `default` case where the `if` condition does `$value = self::_castToString($c);` – Ashwani Shukla Jul 24 '15 at 11:01
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/84168/discussion-between-ashwani-shukla-and-mark-baker). – Ashwani Shukla Jul 24 '15 at 11:07
  • And then there is a check in lines 910-915 of that file: `// Check for numeric values if (is_numeric($value) && $cellDataType != 's') { if ($value == (int)$value) $value = (int)$value; elseif ($value == (float)$value) $value = (float)$value; elseif ($value == (double)$value) $value = (double)$value; }` which will cast numbers to integer or float as appropriate – Mark Baker Jul 24 '15 at 11:07
  • but since the $value is converted into string on line 897 so it won't satify the condition and will not be converted into number or float. As This sets the PHPExcel_Cell->_value to string, as a setValue is called in PHPExcel_Reader_Excel2007 line no. 926 – Ashwani Shukla Jul 24 '15 at 11:11
  • It will be converted to a float or int if it __is_numeric()__: `if (is_numeric($value) && $cellDataType != 's')`.... if it's not a numeric, then it shouldn't be cast to int or float – Mark Baker Jul 24 '15 at 11:14
  • thanks #mark baker , i value your suggestions but they aren't helping out. If you have a solution then please post. Or if you know that from where does the `PHPEXCEL` lib converts the date from `d-m-Y` || `timestamp` to `m-d-y` then pls tell me the file and line and I'll modify it accordingly to solve the purpose. – Ashwani Shukla Jul 24 '15 at 11:19
  • I don't have a solution.... I want to know what's so different about your spreadsheet that means the existing code doesn't work.... if I can't identify that difference, and there is a bug in PHPExcel, then I'm not going to be able to fix it.... the existing code works perfectly correctly for dates in every test that I have, so I can't understand why it doesn't work for your dates – Mark Baker Jul 24 '15 at 11:21
  • Even if PHPExcel was reading your dates as strings, it should not be changing those strings in any way.... if `PHPExcel_Shared_Date::isDateTime($cell))` returns a false, then there isn't a number mask associated with that cell that can identify it as a date; that's also something that I can't understand – Mark Baker Jul 24 '15 at 11:23
  • Is there any way that you can email me the file to run some tests, or is it confidential information? – Mark Baker Jul 24 '15 at 11:25
  • I have attached a link to the file in the question above. it will be valid for 2 days only. Kindly lemme know if you find a solution. Mail me at ashwanishukla@live.com – Ashwani Shukla Jul 24 '15 at 11:29
2

I've checked the file that you provided, looking at your date issue.

The format that you're using for those dates in your spreadsheet is a locale-aware date format, flagged by MS Excel with an asterisk (*) if you look at the cell format

This means (quoting from Excel's notes on the format mask display):

Date formats that begin with an asterisk (*) respond to changes in regional date and time settings that are specified for the Operating System.

Because PHPExcel is not locale-aware, but does recognise the format mask as a date value, it uses a generic formatting.


Running the following code

var_dump($objPHPExcel->getActiveSheet()->getCell('I5')->getValue());
var_dump(PHPExcel_Shared_Date::isDateTime($objPHPExcel->getActiveSheet()->getCell('I5')));
var_dump($objPHPExcel->getActiveSheet()->getCell('I5')->getStyle()->getNumberFormat()->getFormatCode());
var_dump($objPHPExcel->getActiveSheet()->getCell('I5')->getFormattedValue());

gives

float(42062)
bool(true)
string(8) "mm-dd-yy"
string(8) "02-27-15"

So (as long as you've removed that setReadDataOnly(true) call from the Reader, call from the Reader, you can still identify date cells as dates, and format them manually, overriding the default locale-formatting

if (PHPExcel_Shared_Date::isDateTime($objPHPExcel->getActiveSheet()->getCell('I5'))) {
    $dateTimeObject = PHPExcel_Shared_Date::ExcelToPHPObject($objPHPExcel->getActiveSheet()->getCell('I5')->getValue());
    echo $dateTimeObject->format('d-m-Y'), PHP_EOL;
}
Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • 1
    though dates in the spreadsheet is a locale-aware date format, flagged by MS Excel with an asterisk (*) if we look at the cell format but the format is that of `d-m-Y` which is understood by PHP that's why is transforming the date to `m-d-y` but since it uses the same separator as in date in excel file therefore it creates a problem, as `m-d-y` as provided by `PHPEXCEL` is understood by php `strtotime` function as `d-m-y` and since `02-27-15` is not a valid `d-m-y` format (as month cannot be greater than 12) therefore the `strtotime` function `return false` so the `date` return `1970-01-01`. – Ashwani Shukla Jul 27 '15 at 05:02
  • 1
    thanks #mark baker , I value your suggestions but they aren't helping out. I found an alternate and converted the column to `m-d-Y` and now the things are fine but it's an alternate and not a solution. So, If you have a solution then please post. Or if you know that from where does the `PHPEXCEL` lib converts the date from `d-m-Y` || `timestamp` to `m-d-y` then pls tell me the file and line and I'll modify it accordingly to solve the purpose. As it's a bug in `PHPEXCEL` lib which I cannnot entertain. – Ashwani Shukla Jul 27 '15 at 05:04
  • This is not a __bug__ in PHPExcel, simply the fact that PHPExcel is not aware of your locale, and therefore cannot convert a date to a format that it doesn't know..... the code that I've provided should work correctly if you've implemented it as I showed above.... where you specify exactly what format you wish to apply when converting the MS Serialized timestamp values to a formatted string – Mark Baker Jul 27 '15 at 07:00
  • Nor is it anything to do with `/` or `-`. The MS Excel serialized date contains neither of those characters because it's a float, a unix timestamp contains neither of those characters because it's an integer, nor does a PHP DateTime object.... only the formatted end result as a string contains either of those characters – Mark Baker Jul 27 '15 at 07:15
  • #mark baker : I have changed the cell format of date to Indian date format. Now You will not see the `* ` in cell format. But still I am facing the same problem. The issue is actually what I told you in the above comment. I have added the modified file in the link above. Please download the file and check and then tell me what actually the problem is. – Ashwani Shukla Aug 01 '15 at 06:22
  • 1
    I am unable to replicate this "bug" with any non-locale date format..... I've even completely rewritten the date formatting code (albeit for other reasons) and pushed that to the 1.8 branch on github last night..... but f I can't replicate any problem, it's extremely difficult to know if it's fixed or not – Mark Baker Aug 01 '15 at 09:49
  • #mark try using the English(UK) => d-m-Y format in the sheet. Still the same error. – Ashwani Shukla Aug 01 '15 at 11:22
  • what i saw: Errors in file 1 16/11/2014==1970-01-01 (Row: 1) 2 16/11/2014==1970-01-01 (Row: 2) 3 23/12/2014==1970-01-01 (Row: 3) 4 27/02/2015==1970-01-01 (Row: 4) 5 27/02/2015==1970-01-01 (Row: 5) 6 18/11/2014==1970-01-01 (Row: 6) 7 31/01/2015==1970-01-01 (Row: 7) 8 30/11/2014==1970-01-01 (Row: 8) 9 29/12/2014==1970-01-01 (Row: 9) 10 20/02/2015==1970-01-01 (Row: 10) 11 23/02/2015==1970-01-01 (Row: 11) 12 20/11/2014==1970-01-01 (Row: 12) 13 10/01/2015==2015-10-01 (Row: 13) 14 12/02/2015==2015-12-02 (Row: 14) – Ashwani Shukla Aug 01 '15 at 11:33
  • still there's the prob. after implementing your latest code. – Ashwani Shukla Aug 01 '15 at 12:05
1

I have found out the solution:

Method _formatAsDate in the file PHPExcel/Style/NumberFormat.php

The method _formatAsDate in NumberFormat.php

if the date is like 16/11/2014, when passed to strtotime will result in false as the date is supposed to be in format m/d/Y by strtotime. So if you change the format to m/d/Y if it's d/m/Y then the solution will always be correct.

Earlier:

  1. 16/11/2014==1970-01-01 (Row: 1)
  2. 16/11/2014==1970-01-01 (Row: 2)
  3. 23/12/2014==1970-01-01 (Row: 3).

Now:

  1. 11/16/2014==2014-11-16 (Row: 1)
  2. 11/16/2014==2014-11-16 (Row: 2)
  3. 12/23/2014==2014-12-23 (Row: 3)

Code is still the same and simple to import the file:

protected function importExcel($filePath) {
    $excelData = array();
    if ($filePath) {
        $objPHPExcel = PHPExcel_IOFactory::load($filePath);
        foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) {
            $worksheetTitle = $worksheet->getTitle();
            $highestRow = $worksheet->getHighestRow(); // e.g. 10
            $highestColumn = $worksheet->getHighestColumn(); // e.g 'F'
            $highestColumnIndex = PHPExcel_Cell::columnIndexFromString($highestColumn);
            $nrColumns = ord($highestColumn) - 64;
            $data = array();
            for ($row = 1; $row <= $highestRow; ++$row) {
                $values = array();
                for ($col = 0; $col < $highestColumnIndex; ++$col) {
                    $cell = $worksheet->getCellByColumnAndRow($col, $row);
                    $val = $cell->getValue();
                    if (isset($val) && $val)
                        $data[$row][$col] = $val;
                }
            }
            $excelData[$worksheetTitle] = $data;
        }
        return $excelData;
    }
    return FALSE;
}
Pandawan
  • 2,027
  • 1
  • 18
  • 24
Ashwani Shukla
  • 609
  • 2
  • 11
  • 30