• [h4]More DateTime info...[/h4]

    This time I teased out the internal representation of the DateTime data type. First I ran the following:

    Declare @d DateTime;

    Set @d='1753-01-01 00:00:00.000';

    Select Cast(@d as VarBinary),Cast(@d as BigInt);

    And received:

    0xFFFF2E4600000000 | -53690

    Immediately I noticed that 0xFFFF2E4600000000 is not equivalent to -53690! In fact -53690 is the decimal representation of the first 4 hexadecimal bytes! The Cast to VarBinary seemed to know that @d was 8 bytes long, why didn't the Cast to BigInt have the same length info? So then I tried this:

    Declare @d DateTime;

    Set @d='1753-01-01 00:00:00.000';

    Select Cast(@d as VarBinary),Cast(@d as BigInt),Cast(Cast(@d as VarBinary) as BigInt);

    And received:

    0xFFFF2E4600000000 | -53690 | -230596794122240

    Now that's more like it. 0xFFFF2E4600000000 is equivalent to -230596794122240! Next, based on a hunch from a previous post on this thread I ran:

    Declare @d DateTime;

    Set @d='1900-01-01 00:00:00.000';

    Select Cast(@d as VarBinary),Cast(@d as BigInt),Cast(Cast(@d as VarBinary) as BigInt);

    And received:

    0x0000000000000000| 0 | 0

    It looks like the internal representation is broken down in the following manor:

    Bytes 1 - 4 represent the date component (CCYY-MM-DD)

    Bytes 5-8 represent the time component (HH:MM:SS.mmm)

    But what are their units and intervals? Well, the following query will tell us that:

    Declare @d DateTime;

    Set @d='1753-01-01 00:00:00.000';

    Select Cast(@d as VarBinary),Cast(SubString(Cast(@d as VarBinary),1,4) as Int),Cast(SubString(Cast(@d as VarBinary),5,4) as Int);

    Set @d='1753-01-02 00:00:00.001';

    Select Cast(@d as VarBinary),Cast(SubString(Cast(@d as VarBinary),1,4) as Int),Cast(SubString(Cast(@d as VarBinary),5,4) as Int);

    Set @d='1753-01-02 00:00:00.003';

    Select Cast(@d as VarBinary),Cast(SubString(Cast(@d as VarBinary),1,4) as Int),Cast(SubString(Cast(@d as VarBinary),5,4) as Int);

    0xFFFF2E4600000000 | -53690 | 0

    0xFFFF2E4700000000 | -53689 | 0

    0xFFFF2E4700000001 | -53689 | 1

    Notice how I modified the date-time string before each query and notice the resultant output values. From an analysis of these values it can be seen that:

    Bytes 1 - 4 represent the date component in days, however, the range does not start with 0, it starts with -53690.

    Bytes 5-8 represent the time component in 3.33~ millisecond counts, i.e., this count increments by 1 for every 3.33~ milliseconds that pass; the range starts at 0.

    So what are the actual ranges for days and 3.33~ millisecond counts? Lets run another query:

    Declare @d DateTime;

    Set @d='1753-01-01 00:00:00.000';

    Select Cast(@d as VarBinary),Cast(SubString(Cast(@d as VarBinary),1,4) as Int),Cast(SubString(Cast(@d as VarBinary),5,4) as Int);

    Set @d='9999-12-31 23:59:59.997';

    Select Cast(@d as VarBinary),Cast(SubString(Cast(@d as VarBinary),1,4) as Int),Cast(SubString(Cast(@d as VarBinary),5,4) as Int);

    0xFFFF2E4600000000 | -53690 | 0

    0x002D247F018B81FF | 2958463 | 25919999

    From these results we can see that the day range is -53690 through 2958463, or 3012154 (don't forget to add 1 for the zero crossing) which is 8246 years, the full span from years 1753 to 9999.

    From these results we can also see that the 3.33~ millisecond count range is 0 through 25919999, or 25920000 counts which is 86400000 milliseconds (25920000*3.33~) or 1 days worth of milliseconds.

    So, there you have it. You could use this knowledge to craft an ElapsedTime function that will work across the full spectrum of time from 1753-01-01 00:00:00.000 through 9999-12-31 23:59:59.997 with some reasoned logic and judicious use of SubString, Cast, & Convert. It probably won't be the fastest performer but it will give you the full time span available without any "out-of-range datetime value" exceptions!

    Happy Casting!



    PeteK
    I have CDO. It's like OCD but all the letters are in alphabetical order... as they should be.