• subscribe
January 25, 2006 12:00 AM

Correctly Calculating Datetime Differences

Transcend DATEDIFF()’s limitations
SQL Server Pro
InstantDoc ID #48764
Downloads
48764.zip

The next query, at callout A, accepts D5 as its input and generates the final desired result, which Table 2 shows. The query uses simple integer division (/) and modulo (%) to calculate the hour (h), minute (mi), and second (s) units based on the original seconds difference value you received from derived table D5. The query also calculates the millisecond difference (ms) by subtracting the milliseconds unit of from_ts from that of 1000 plus the milliseconds unit of to_ts modulo 1000.The reason for adding 1000 to the ms unit of to_ts, then calculating the modulo by 1000, is to accommodate a case in which the ms unit of to_ts is smaller than the ms unit of from_ts.

Encapsulating Logic in a Function
You now have a way to correctly calculate differences between two datetime values. And you can encapsulate this logic in a function that accepts two datetime values as inputs. You just need to determine how to return the output. You can create a table-valued function and return the different datetime elements and the sign of the result in different columns of a result row. Or, you can create a scalar function that concatenates all output elements in a single character string. Listing 3 shows an example of a scalar function that calculates datetime differences.

The only change you need to make to the original solution is to add a layer that formats the output as a scalar value, as callout A in Listing 3 shows.

To test the function, run the following code:

SELECT dbo.fn_datediff 
('20030321
14:27:12.233',
'20060115
11:45:22.263');

You'll receive +0002-09-24 21:18:10.030 as the output, meaning that the difference between the two inputs is positive, 2 years, 9 months, 24 days, 21 hours, 18 minutes, 10 seconds, and 30ms. To verify that the result is correct,use DATEADD() to add all elements to the @from_ts input, and note that you get the following @to_ts input:

SELECT 
DATEADD(ms, 30,
DATEADD(second, 10,
DATEADD(minute, 18, DATEADD(hour, 21,
DATEADD(day, 24,
DATEADD(month, 9,
DATEADD(year, 2,
'20030321
14:27:12.233')))))));

As with many problems I cover in this column, solving the datetime-differences challenge involves a lot of logic. To stretch your logic muscles, remember to check out this month's Logical Puzzle, page XX. And don't miss the new section of this column, "Catch That Bug!"

Itzik Ben-Gan (itzik@solidqualitylearning.com), a mentor at Solid Quality Learning, teaches, lectures, and consults internationally. He manages the Israeli SQL Server Users Group, is a SQL Server MVP, and is a coauthor of Advanced Transact-SQL for SQL Server 2000 (Apress).



ARTICLE TOOLS

Comments
  • Karen
    3 years ago
    Jan 27, 2009

    Callouts are used to call attention to one or more lines of code in a listing. In the print version of this article, the callouts will appear as highlighted code with a letter next to it. In the online version, callouts are indicated by lines such as BEGIN CALLOUT A and END CALLOUT A.

    Unfortunately, the wrong code had been placed in the 48764.zip file. This code contained callout lines that were not commented, so the code would fail. I've replaced that code with the correct code. The Datetime_Difference.sql file contains all the code you see in Listings 1, 2, and 3 but without callout lines. Plus, the listing titles are treated as comments. You can obtain Datetime_Difference.sql by clicking the 48764.zip link under the "Download the Code" heading at the top of the page.

    If you have any questions about the new code, please let me know. And thanks for reading SQL Server Magazine!

    Karen Bemowski, senior editor
    SQL Server Magazine, Windows IT Pro

  • Bhaskar
    3 years ago
    Jan 27, 2009

    Possibly I am missing something obvious but what exactly is the Callout element?I am unable to compile the listing code because 'callout' is not recognised?

  • Tim
    6 years ago
    Jan 27, 2006

    Itzik, Enjoyed your solution as always. I do believe there is a small bug concerning your treatment of seconds as revealed by the example of the difference of 3ms to midnight vs. midnight. Your solution shows 1 sec + 3 ms where correctly it should just be 3 ms. To keep the same structure and correct the bug, I added an extra derived table to use a case statement to subtract 1 from the seconds total when adding the number of seconds gives a time greater than the to_ts time.

    (
    SELECT keycol, from_ts, to_ts, sgn, y, m, d,
    s - CASE WHEN DATEADD(second, s, from_ts) > to_ts
    THEN 1 ELSE 0 END AS s
    FROM

    Thanks again.

You must log on before posting a comment.

Are you a new visitor? Register Here