Click here to Skip to main content
15,881,709 members
Articles / Database Development / SQL Server
Tip/Trick

Difference Between Two datetime Values in Hours, Minutes, etc.

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
18 Sep 2015CPOL3 min read 104.3K   198   6  
This tip shows how to calculate a difference between two datetime values and to express the difference in years, days, hours, minutes and so on.

Introduction

First of all, majority of the formatting should not be done in database queries. Instead, the database should return values and the values should then be formatted on the calling side taking different kinds of user settings, such as number formatting, language, etc. into account.

Having that said, when a difference between two dates is calculated, it sometimes makes sense to calculate the different parts of the difference on the database side and perhaps even format the values. 

Creating the Test Data

First of all, we need a place for the test data so let's create a temporary table for that:
SQL
-- Create a test table
CREATE TABLE #TextualDateDiffTest (
 StartDate datetime not null,
 EndDate   datetime not null
);

And then insert some data into it.

SQL
-- Insert test data
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            CONVERT(datetime, '2014-09-18 20:30:20', 120) ); 
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            CONVERT(datetime, '2014-09-19 20:30:20', 120) ); 
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            CONVERT(datetime, '2014-09-19 12:20:20', 120) ); 
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            CONVERT(datetime, '2014-10-19 20:55:44', 120) ); 
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            CONVERT(datetime, '2014-10-05 16:22:15', 120) ); 
INSERT INTO #TextualDateDiffTest (StartDate, EndDate) 
   VALUES ( CONVERT(datetime, '2014-09-18 19:30:00', 120), 
            GETDATE() ); 

DATEDIFF, Why Not?

There are a ton of examples how to calculate the difference for two datetime values using DATEDIFF function. So, why would a new tip be needed. DATEDIFF calculates the whole difference between two dates. In other words, if you choose the interval to be minutes a difference is expressed in minutes even if the difference is greater than a single hour. This introduces complexity in the calculation since in each date part, the previous date part value needs to be taken into account.

Let's have a look at this using an example:

SQL
-- Break into pieces and select the differences
SELECT a.StartDate, 
       a.EndDate,
       DATEDIFF(YEAR,   a.StartDate, a.EndDate) AS Years,
       DATEDIFF(MONTH,  a.StartDate, a.EndDate) AS  Months,
       DATEDIFF(DAY,    a.StartDate, a.EndDate) AS  Days,
       DATEDIFF(HOUR,   a.StartDate, a.EndDate) AS  Hours,
       DATEDIFF(MINUTE, a.StartDate, a.EndDate) AS  Minutes,
       DATEDIFF(SECOND, a.StartDate, a.EndDate) AS  Seconds 
FROM #TextualDateDiffTest a;

The query above returns the following data:

StartDate                 EndDate                   Years   Months   Days   Hours   Minutes   Seconds
-----------------------   -----------------------   -----   ------   ----   -----   -------   -------
2014-09-18 19:30:00.000   2014-09-18 20:30:20.000   0       0        0      1       60        3620
2014-09-18 19:30:00.000   2014-09-19 20:30:20.000   0       0        1      25      1500      90020
2014-09-18 19:30:00.000   2014-09-19 12:20:20.000   0       0        1      17      1010      60620
2014-09-18 19:30:00.000   2014-10-19 20:55:44.000   0       1        31     745     44725     2683544
2014-09-18 19:30:00.000   2014-10-05 16:22:15.000   0       1        17     405     24292     1457535
2014-09-18 19:30:00.000   2015-09-19 08:16:33.600   1       12       366    8773    526366    31581993

It's easy to see that on the first line, hours is correct but minutes contains the hour difference and seconds contain them both. So in order to get the desired result, more calculation would be needed...

Using Subtraction

There's a much more easier way to calculate the parts of the difference. Basically, all you need to do is to subtract the start date from the end date. Like this:

SQL
-- Calculate the difference between the dates
SELECT a.StartDate, 
       a.EndDate,
       a.EndDate - a.StartDate AS Difference
FROM #TextualDateDiffTest a;

The result is:

StartDate                 EndDate                   Difference
-----------------------   -----------------------   -----------------------
2014-09-18 19:30:00.000   2014-09-18 20:30:20.000   1900-01-01 01:00:20.000
2014-09-18 19:30:00.000   2014-09-19 20:30:20.000   1900-01-02 01:00:20.000
2014-09-18 19:30:00.000   2014-09-19 12:20:20.000   1900-01-01 16:50:20.000
2014-09-18 19:30:00.000   2014-10-19 20:55:44.000   1900-02-01 01:25:44.000
2014-09-18 19:30:00.000   2014-10-05 16:22:15.000   1900-01-17 20:52:15.000
2014-09-18 19:30:00.000   2015-09-18 21:06:54.833   1901-01-01 01:36:54.833

Now if we put the subtraction into the original statement, splitting the parts of the date, the statement could look like this:

SQL
-- Calculate the difference and extract parts
SELECT a.StartDate, 
       a.EndDate,
       DATEPART(YEAR,   a.EndDate - a.StartDate) AS Years,
       DATEPART(MONTH,  a.EndDate - a.StartDate) AS Months,
       DATEPART(DAY,    a.EndDate - a.StartDate) AS Days,
       DATEPART(HOUR,   a.EndDate - a.StartDate) AS Hours,
       DATEPART(MINUTE, a.EndDate - a.StartDate) AS Minutes,
       DATEPART(SECOND, a.EndDate - a.StartDate) AS Seconds
FROM #TextualDateDiffTest a;

And the result would be:

StartDate                 EndDate                   Years   Months   Days   Hours   Minutes   Seconds
-----------------------   -----------------------   -----   ------   ----   -----   -------   -------
2014-09-18 19:30:00.000   2014-09-18 20:30:20.000   1900    1        1      1       0         20
2014-09-18 19:30:00.000   2014-09-19 20:30:20.000   1900    1        2      1       0         20
2014-09-18 19:30:00.000   2014-09-19 12:20:20.000   1900    1        1      16      50        20
2014-09-18 19:30:00.000   2014-10-19 20:55:44.000   1900    2        1      1       25        44
2014-09-18 19:30:00.000   2014-10-05 16:22:15.000   1900    1        17     20      52        15
2014-09-18 19:30:00.000   2015-09-19 08:16:33.600   1901    1        1      12      46        33

Not quite what we wanted yet... Now the difference is correct but the date portion contains the date where SQL Server starts to calculate date values. The initial date is 1900-01-01 so in order to get the correct results, we need to subtract corresponding years, months, and minutes. Like this:

SQL
-- Calculate the difference and extract parts, remove initial date elements
SELECT a.StartDate, 
       a.EndDate,
       DATEPART(YEAR,   a.EndDate - a.StartDate) - 1900 AS Years,
       DATEPART(MONTH,  a.EndDate - a.StartDate) - 1    AS Months,
       DATEPART(DAY,    a.EndDate - a.StartDate) - 1    AS Days,
       DATEPART(HOUR,   a.EndDate - a.StartDate)        AS Hours,
       DATEPART(MINUTE, a.EndDate - a.StartDate)        AS Minutes,
       DATEPART(SECOND, a.EndDate - a.StartDate)        AS Seconds
FROM #TextualDateDiffTest a;

Now the result looks much better:

StartDate                 EndDate                    Years   Months   Days   Hours   Minutes   Seconds
-----------------------   -----------------------    -----   ------   ----   -----   -------   -------
2014-09-18 19:30:00.000   2014-09-18 20:30:20.000    0       0        0      1       0         20
2014-09-18 19:30:00.000   2014-09-19 20:30:20.000    0       0        1      1       0         20
2014-09-18 19:30:00.000   2014-09-19 12:20:20.000    0       0        0      16      50        20
2014-09-18 19:30:00.000   2014-10-19 20:55:44.000    0       1        0      1       25        44
2014-09-18 19:30:00.000   2014-10-05 16:22:15.000    0       0        16     20      52        15
2014-09-18 19:30:00.000   2015-09-19 08:16:33.600    1       0        0      12      46        33

The good thing is that the SQL statement contains no extra calculation and is very understandable. This would be easy to use as a result set for further formatting regardless of the programming language on the client side.

Formatting the Result

Now since the data is correct, it's easy to add formatting to the statement, if needed. As an example, let's extract the parts and put an explaining text in the end to indicate the meaning of the number, like this:

SQL
-- Now the difference in text
SELECT   CAST( DATEPART(YEAR,   a.EndDate - a.StartDate) - 1900 AS nvarchar(100)) + ' Years '
       + CAST( DATEPART(MONTH,  a.EndDate - a.StartDate) - 1    AS nvarchar(100)) + ' Months '
       + CAST( DATEPART(DAY,    a.EndDate - a.StartDate) - 1    AS nvarchar(100)) + ' Days '
       + CAST( DATEPART(HOUR,   a.EndDate - a.StartDate)        AS nvarchar(100)) + ' Hours '
       + CAST( DATEPART(MINUTE, a.EndDate - a.StartDate)        AS nvarchar(100)) + ' Minutes '
       + CAST( DATEPART(SECOND, a.EndDate - a.StartDate)        AS nvarchar(100)) + ' Seconds' AS Difference
FROM #TextualDateDiffTest a;

The result would be: 

Difference
-------------------------------------------------------------------
0 Years 0 Months 0 Days 1 Hours 0 Minutes 20 Seconds
0 Years 0 Months 1 Days 1 Hours 0 Minutes 20 Seconds
0 Years 0 Months 0 Days 16 Hours 50 Minutes 20 Seconds
0 Years 1 Months 0 Days 1 Hours 25 Minutes 44 Seconds
0 Years 0 Months 16 Days 20 Hours 52 Minutes 15 Seconds
1 Years 0 Months 0 Days 12 Hours 46 Minutes 33 Seconds

If you want to go a bit further and remove the parts of the difference that are zero valued, consider the following example:

SQL
-- Remove 0 elements from the output
SELECT   CASE 
            WHEN DATEPART(YEAR,  a.EndDate - a.StartDate) > 1900 
            THEN CAST( DATEPART(YEAR,  a.EndDate - a.StartDate) - 1900 AS nvarchar(100)) + ' Year ' 
            ELSE ''
         END
       + CASE 
            WHEN DATEPART(MONTH,  a.EndDate - a.StartDate) > 1 
            THEN CAST( DATEPART(MONTH,  a.EndDate - a.StartDate) - 1 AS nvarchar(100)) + ' Months '
            ELSE ''
         END
       + CASE 
            WHEN DATEPART(DAY,  a.EndDate - a.StartDate) > 1 
            THEN CAST( DATEPART(DAY, a.EndDate - a.StartDate) - 1 AS nvarchar(100)) + ' Days '
            ELSE ''
         END
       + CASE 
            WHEN DATEPART(HOUR,  a.EndDate - a.StartDate) > 0 
            THEN CAST( DATEPART(HOUR, a.EndDate - a.StartDate) AS nvarchar(100)) + ' Hours '
            ELSE ''
         END
       + CASE 
            WHEN DATEPART(MINUTE,  a.EndDate - a.StartDate) > 0 
            THEN CAST( DATEPART(MINUTE, a.EndDate - a.StartDate) AS nvarchar(100)) + ' Minutes '
            ELSE ''
         END
       + CASE 
            WHEN DATEPART(SECOND,  a.EndDate - a.StartDate) > 0 
            THEN CAST( DATEPART(SECOND, a.EndDate - a.StartDate) AS nvarchar(100)) + ' Seconds'
            ELSE ''
         END AS Difference
FROM #TextualDateDiffTest a;

And the result set would now be: 

Difference
-------------------------------------
1 Hours 20 Seconds
1 Days 1 Hours 20 Seconds
16 Hours 50 Minutes 20 Seconds
1 Months 1 Hours 25 Minutes 44 Seconds
16 Days 20 Hours 52 Minutes 15 Seconds
1 Year 1 Hours 36 Minutes 54 Seconds

This looks like it could be in understandable format for the user.

References

Some references you may find useful:

History

  • 18th September, 2015: Created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Europe Europe
Biography provided

Comments and Discussions

 
-- There are no messages in this forum --