views:

1794

answers:

3

How do I convert from excel serial date to a .NET date time?

For example 39938 is 05/05/2009.

+4  A: 

Where 39938 is the number of days since 1/1/1900?

In that case, use this function (C#):

public static DateTime FromExcelSerialDate(int SerialDate)
{
    if (SerialDate > 59) SerialDate -= 1; //Excel/Lotus 2/29/1900 bug   
    return new DateTime(1899, 12, 31).AddDays(SerialDate);
}

VB:

Public Shared Function FromExcelSerialDate(ByVal SerialDate As Integer) As DateTime
    If SerialDate > 59 Then SerialDate -= 1 ''// Excel/Lotus 2/29/1900 bug
    Return New DateTime(1899, 12, 31).AddDays(SerialDate)
End Function


[Update]:
Hmm... A quick test of that shows it's actually two days off. Not sure where the difference is.

Okay: problem fixed now. See the comments for details.

Joel Coehoorn
I know 1 day is for Lotus having a bug about 02/29/1900 it had it as a day that doesn't exist. I don't know what the other day is.
David Basarab
VBA dates (Excel) start at 12/30/1899
DJ
It works if you subtract 2, but it fails for serial dates of at least below 60 (02/29/1900) and the second sneak day in there.
David Basarab
I think you get 2 days too much with this conversion, i.e. the epoch date would have to be moved back to DateTime(1899, 12, 30). This is due to Excel's leap year bug I assume.
0xA3
Cool: I'll update my answer.
Joel Coehoorn
Oops, everyone else was faster ;-) And yes, the bug originally comes from Lotus as Joel explained: http://www.joelonsoftware.com/items/2006/06/16.html
0xA3
You also need to do a check for any number under 60, if it is under 60 do not subtract 1 because you are not in the bug yet. If it is over you must subtract one from the date because of the 02/29/1900 bug.
David Basarab
Wow, even a fix for the first to months of the beginning of time (in Excel universe) is included! Impressed :-)
0xA3
You add to 12/31/1899, so that 1/1/1900 is day #1 rather than day #0. After that it's the Lotus leap year thing.
Joel Coehoorn
A: 
void ExcelSerialDateToDMY(int nSerialDate, int &nDay, 
                          int &nMonth, int &nYear)
{
    // Excel/Lotus 123 have a bug with 29-02-1900. 1900 is not a
    // leap year, but Excel/Lotus 123 think it is...
    if (nSerialDate == 60)
    {
        nDay    = 29;
        nMonth    = 2;
        nYear    = 1900;

        return;
    }
    else if (nSerialDate < 60)
    {
        // Because of the 29-02-1900 bug, any serial date 
        // under 60 is one off... Compensate.
        nSerialDate++;
    }

    // Modified Julian to DMY calculation with an addition of 2415019
    int l = nSerialDate + 68569 + 2415019;
    int n = int(( 4 * l ) / 146097);
            l = l - int(( 146097 * n + 3 ) / 4);
    int i = int(( 4000 * ( l + 1 ) ) / 1461001);
        l = l - int(( 1461 * i ) / 4) + 31;
    int j = int(( 80 * l ) / 2447);
     nDay = l - int(( 2447 * j ) / 80);
        l = int(j / 11);
        nMonth = j + 2 - ( 12 * l );
    nYear = 100 * ( n - 49 ) + i + l;
}

Cut and Paste of someone elses talents...

Ian Brown

Zymotik
+4  A: 

I find it simpler using FromOADate method, for example:

                    DateTime dt = DateTime.FromOADate(39938);

Using this code dt is "05/05/2009".

Narcís Calvet
+1 This seems much more elegant than the other answers...
Daniel Fortunov
This also handles dates with time values (floating point in the underlying data format), which the accepted solution does not.
nullptr