Friday, October 26, 2007 2:25 PM usoup

Next Datetime, please

Have you ever be given a task to use SQL Server T-SQL to find out the date value of next day, at 00 hour? Say, now is '2007-10-26 13:30:50', and you need to get a '2007-10-27 00:00:00'?

Naturally, I think most of us will do something like this:

declare @today datetime

declare @tomorrow datetime

set @today = convert(datetime, convert(nvarchar(4), datepart(yyyy, getdate())) + convert(nvarchar(2), datepart(mm, getdate())) + convert(nvarchar(2), datepart(dd, getdate())))

set @tomorrow = dateadd(dd, 1, @today)

A lot of datepart and convert involved just to get the day/month/year value. There is a much simpler way actually, considering that datetime internal value is in fact numeric.

declare @today datetime

declare @tomorrow datetime

set @today = convert(bigint, dateadd(hh, -12, getdate()))

set @tomorrow = dateadd(dd, 1, @today)

Note the part that convert datetime to bigint. That's where the rounding took place. Let's step back a little bit and see how that works.

select getdate() as datetimeType, convert(bigint, getdate()) as bigintType, convert(decimal(18,12), getdate()) as decimalType

 

datetimeType            bigintType decimalType

------------            ---------- -----------

2007-10-26 13:50:27.560 39380      39379.576707870372

 

(1 row(s) affected)

From the SQL query above, we can see that a datetime can be converted to decimal type. The numbers on the left hand side of the "." denotes the number of days since "1900-01-01 00:00:00". The ones on the right hand side denotes the fraction of milliseconds in a day. That means, "1.0" means 1 day, means 24 hour, means 1440 minutes, means 86400 seconds, means 86400000 milliseconds. That means, this query below:

select convert(datetime, (convert(decimal(18,12), @tomorrow) + (2.0000000000000/24))) as tom_2am

is equivalent to "2007-10-27 02:00:00.000". Cool huh? But of course, the query below:

select dateadd(hh, 2, @tomorrow) as tom_2am

is a lot easier to understand. :D

Oh? Why "dateadd(hh, -12, getdate())", you ask? That's because of the rounding during conversion to bigint. So, let's see a few time during the day:

select convert(decimal(18,12),@dt)

decimal(18,12) bigint
2007-10-26 00:00:00 39379.000000000000 39379
2007-10-26 03:00:00 39379.125000000000 39379
2007-10-26 12:00:00 39379.500000000000 39380
2007-10-26 15:00:00 39379.625000000000 39380
2007-10-27 00:00:00 39380.000000000000 39380

Any time before 12 noon on the day (< 0.5 in numeric) will be rounded down. Any other time will be rounded up. So, if we want to get today at mid night time value, we will need a source range from yesterday noon to today before noon, that can be rounded to today midnight. Hence, the "-12 hours" above.

 

PS: I have always wanted to write about this, but since I don't think it'll make any much difference to programming, I've always canceled writing this. Until I asked myself, "how worse can writing this be?", and I don't have the answer. :D If you have a better ways of doing this, please let us know.

Filed under:

# re: Next Datetime, please

Wednesday, December 12, 2007 12:35 PM by sidyk

Good insightful write up, much appreciated.

When it comes to datetime, it is Unfortunate practically noone is willing to adhere to any guidelines. As long as there is no universal consensus, programmers will FOREVER be pulling their hair follicles over the simple datetime conversions.....

heres my 2cents tip, although you must have already known this, in your t-sql:

SET DATEFORMAT dmy;

to force the datetime variables to conform to what you want...

and in sql2008:

chk out the new data types at our disposal date, time and datetime2 ....

well with increasing complexity, i am expecting more hairs coming off our scalp.....sigh

# re: Next Datetime, please

Wednesday, December 12, 2007 12:55 PM by usoup

There is universal consensus, the ISO 8601. But, who follows?

And btw, your keyword on the "SET DATEFORMAT" is:

"to force the datetime variables to conform to what you want"

Do we have a choice here?