Convert Ticks to Date Across Columns - sql

I'm running into this SQL error and I'm not exactly sure how to get around it. It involves translating .Net ticks to date. I've come across other posts on this issue but only covered instances where I'd be doing this for one entry.
My problem is that I have a column of ticks that I'd like to convert, similar to below.
ACCCOUNT STARTDATE ENDDATE
0001 635200704000000000 635200704000000000
0002 635203296000000000 635203296000000000
How would I translate this to be.
ACCCOUNT STARTDATE ENDDATE
0001 11/14/2013 11/14/2013
0002 11/17/2013 11/17/2013
I'm not exactly sure how I'd go about this. Can anyone please point me in the right direction. Here's a snippet of SQL that I've managed to piece together in attempting to solve this. Though I know this won't give me the format that I want.
SELECT READING.ACCOUNT,
datename(month,(READING.STARTDATE-599266080000000000)/864000000000) +
space(1) +
datename(d,(READING.STARTDATE-599266080000000000)/864000000000) +
', ' +
datename(year,(READING..STARTDATE-599266080000000000)/864000000000) as
READDATE
FROM READING

If you need exact precision down to the tick:
declare #ticks bigint
set #ticks = 3155378975999999999 -- (DateTime.MaxValue.Ticks)
select DATEADD(D, #ticks / 864000000000,
DATEADD(MS, (#ticks % 864000000000) / 10000,
DATEADD(NS, (#ticks % 10000) * 100,
convert(datetime2, '0001-01-01'))))
-- ouputs: 9999-12-31 23:59:59.9999999
Though in your case, I think you're only asking for precision to the whole day:
declare #ticks bigint
set ticks = 635200704000000000
select DATEADD(D, #ticks / 864000000000, convert(datetime2, '0001-01-01'))
-- 635200704000000000 outputs 2013-11-15 00:00:00.0000000
-- 635203296000000000 outputs 2013-11-18 00:00:00.0000000
Like I said in comments, the values are one day off from what you said you expected, so you probably have a time zone issue as well.
Given your column names, you would modify your query to this:
SELECT ACCOUNT,
DATEADD(D, STARTDATE / 864000000000, convert(datetime2, '0001-01-01')) as StartDate,
DATEADD(D, ENDDATE / 864000000000, convert(datetime2, '0001-01-01')) as EndDate
FROM READING

You can easily convert ticks to Datetime and vice-versa in .NET. Here is sample code for your reference.
long ticks = (DateTime.Now).Ticks;
//Convert these ticks to DateTime
DateTime dtresult = new DateTime(ticks);

Related

How to subtract one month from a date using SQL Server

I have a date in format dd/mm/yyyy. I want to subtract one month from it.
I am using this code but the output is "09/10/2020" I don't know why my code does the subtraction of the year -2 also.
This is my request
SELECT
FORMAT(CONVERT (DATE, DATEADD(MONTH, -1, CONVERT(char(9), GETDATE()))), 'dd/MM/yyyy')
you need to change it to:
select format(CONVERT (date,DATEADD(MONTH, -1,GETDATE())), 'dd/MM/yyyy' )
but as Larnu stated. it seems like you need to change the column.
Your current code doesn't work as expected because:
SELECT CONVERT(char(9), GETDATE());
Returns this (at least in my language):
Nov 9 20
Which is, unfortunately, and again in my language, a valid date (but in {20}20, not {20}22).
Even in the right style (103), char(9) would yield 10/11/202 tomorrow, since 9 digits is only enough if either the day or month is a single digit.
Don't know why you are converting GETDATE() to a string. Just perform date math on it and then format it if you need to (using a specific style number, e.g. 103 for d/m/y):
SELECT CONVERT(char(10), DATEADD(MONTH, -1, GETDATE()), 103);
I really wouldn't use FORMAT() for such simple output, as the CLR overhead really isn't worth it. Ideally you leave it as a date/time type until presentation time - surely your presentation layer can present your date as d/m/y if that's really a wise idea.
And if you are storing or passing dates as strings (and worse, in regional formats like d/m/y) you really should consider fixing that.
First of all,
You should be storing your Date as a string for easier manipulation. If you don't want to change the column, you can always convert from Date to Varchar and then (re)convert it.
Example:
First, convert Date to varchar using the style code '112' ISO for formatting as yyyyMMdd:
DECLARE #date DATE = GETDATE();
DECLARE #dateConverted as VARCHAR (8) = (SELECT CONVERT(VARCHAR, #date, 112));
Then you just subtract the month using DATEADD():
DECLARE #previousMonth AS VARCHAR (8) = (SELECT FORMAT(DATEADD(month, -1, #dateConverted), 'yyyyMMdd'));
Finally, convert varchar do Date again:
DECLARE #previousMonthConverted AS DATE = (SELECT CONVERT(CHAR(10), CONVERT(date, #previousMonth), 120));

Converting "42978.6736458333" varchar to datetime

I've a varchar column with a value of 42978.6736458333 that I want to convert back to a proper datetime. I've searched quite a bit and tried many of the suggestions but I cannot seem to find one that works with the data I have.
I got this value from excel when I did a CONCATENATE of all the fields in the sheet to do an insert to my DB; the output of the datetime column looks like "42978.6736458333" (the cell originally contained "2017-08-31 16:10:03"). I tried formatting and various things in excel to no avail.
Here are a few examples of what I've tried:
Select
convert(varchar(23), date, 112) DATE1,
convert(datetime, '20160805') DATE2,
convert(datetime, '2011-09-28 18:01:00', 120) DATE3,
dateadd(second, 42978.6736458333 * 24*24*60, '1899-12-31') DATE4
From
[dbo].[trainingLog]
Results:
DATE 1 = 42978.6736458333
DATE 2 = 2016-08-05 00:00:00.000
DATE 3 = 2011-09-28 18:01:00.000
DATE 4 = 1947-01-25 11:16:01.000
For every result. DATE 2/3/4 don't count up even though the original datetime varchar increments.
For example, here are more varchar values:
42981.5092361111
42982.7187615741
42983.8171527778
The above attempts return a value/date, but it's the same date even though my varchar value increments.
I expect any datetime format. I really only want the month/day/year in any format.
Try
SELECT CAST(42978.6736458333 AS DATETIME)
returns
2017-09-02 16:10:03.000
However SQL server uses 01/01/1900 as the epoch whereas your excel uses 30/12/1899 or 31/12/1899 as the epoch so it looks like you need to subtract 2 days off after you cast.
e.g.
SELECT dateadd(d, -2, CAST(42978.6736458333 AS DATETIME) )
returns
2017-08-31 16:10:03.000
From you comment I am not sure how you get your value as running
SELECT CAST(42981.5092361111 AS DATETIME)
returns for me
2017-09-05 12:13:18.000
This looks like an Excel time. So:
select dateadd(minute, 42978.6736458333 * 24*60, '1899-12-31')
If you try to use seconds, you will get an overflow error. If seconds are important, you can do:
select dateadd(day, floor(42978.6736458333), '1899-12-31') +
dateadd(second, (42978.6736458333 % 1)*24*60*60, 0)

SQL Full day difference

Does anyone know how to get the datdiff in full days between 2 days.
Im currentlly using
datediff(day,createddate,dateserved)
But need it to return how many full days
i.e
Created = 1/7/2010 2100
dateserved = 2/7/2010 2000
currently the datediff would show 1 day but i need it to show 0 until dateserved passes 2100
Any ideas
Sp
SELECT FLOOR(CAST(dateserved AS FLOAT) - CAST( createddate AS FLOAT))
Also the following seems to work and be more concise but may need some testing
SELECT FLOOR(CAST(dateserved-createddate AS FLOAT))
#Ian Jacobs got it in first, but here's how I'd do it in T-SQL. Assuming you're only concerned with hours:
DECLARE
#From datetime
,#Thru datetime
SET #From = 'Jan 1, 2010 21:00'
SET #Thru = 'Jan 3, 2010 20:00' -- 2/7/2010 2000
print datediff(dd, #From, #Thru)
print datediff(hh, #From, #Thru)
PRINT datediff(hh, #From, #Thru) / 24
...that is, calculate the hours difference between your datetimes, divide by 24, and truncate the decimal value. SQL appears to truncate, but if you're paranoid, use
print datediff(hh, #From, #Thru) / 24.0
PRINT floor(datediff(hh, #From, #Thru) / 24.0)
to ensure proper truncation. If you need precision down to the minute, second, or millisecond, add bit more arithmatic.
What you can do is go with the smallest possible resolution in the DATEDIFF() function you can feasibly get away with (minutes,seconds, whatever). Then to math to convert that to a day representations.
I'm basically proposing:
Floor(DATEDIFF(mi, createddate, dateserved)/60/24);
You could use:
DATEDIFF(dy, created_date, date_served) -
CASE
WHEN CAST(created_date AS TIME) > CAST(date_served AS TIME) THEN 1
ELSE 0
END
I originally proposed trying to use division, but when you get down to milliseconds you can quickly hit arithmetic overflows.

SQL Server: Get data for only the past year

I am writing a query in which I have to get the data for only the last year. What is the best way to do this?
SELECT ... FROM ... WHERE date > '8/27/2007 12:00:00 AM'
The following adds -1 years to the current date:
SELECT ... From ... WHERE date > DATEADD(year,-1,GETDATE())
I found this page while looking for a solution that would help me select results from a prior calendar year. Most of the results shown above seems return items from the past 365 days, which didn't work for me.
At the same time, it did give me enough direction to solve my needs in the following code - which I'm posting here for any others who have the same need as mine and who may come across this page in searching for a solution.
SELECT .... FROM .... WHERE year(*your date column*) = year(DATEADD(year,-1,getdate()))
Thanks to those above whose solutions helped me arrive at what I needed.
Well, I think something is missing here. User wants to get data from the last year and not from the last 365 days. There is a huge diference. In my opinion, data from the last year is every data from 2007 (if I am in 2008 now). So the right answer would be:
SELECT ... FROM ... WHERE YEAR(DATE) = YEAR(GETDATE()) - 1
Then if you want to restrict this query, you can add some other filter, but always searching in the last year.
SELECT ... FROM ... WHERE YEAR(DATE) = YEAR(GETDATE()) - 1 AND DATE > '05/05/2007'
The most readable, IMO:
SELECT * FROM TABLE WHERE Date >
DATEADD(yy, -1, CONVERT(datetime, CONVERT(varchar, GETDATE(), 101)))
Which:
Gets now's datetime GETDATE() = #8/27/2008 10:23am#
Converts to a string with format 101 CONVERT(varchar, #8/27/2008 10:23am#, 101) = '8/27/2007'
Converts to a datetime CONVERT(datetime, '8/27/2007') = #8/27/2008 12:00AM#
Subtracts 1 year DATEADD(yy, -1, #8/27/2008 12:00AM#) = #8/27/2007 12:00AM#
There's variants with DATEDIFF and DATEADD to get you midnight of today, but they tend to be rather obtuse (though slightly better on performance - not that you'd notice compared to the reads required to fetch the data).
Look up dateadd in BOL
dateadd(yy,-1,getdate())
GETDATE() returns current date and time.
If last year starts in midnight of current day last year (like in original example) you should use something like:
DECLARE #start datetime
SET #start = dbo.getdatewithouttime(DATEADD(year, -1, GETDATE())) -- cut time (hours, minutes, ect.) -- getdatewithouttime() function doesn't exist in MS SQL -- you have to write one
SELECT column1, column2, ..., columnN FROM table WHERE date >= #start
I, like #D.E. White, came here for similar but different reasons than the original question. The original question asks for the last 365 days. #samjudson's answer provides that. #D.E. White's answer returns results for the prior calendar year.
My query is a bit different in that it works for the prior year up to and including the current date:
SELECT .... FROM .... WHERE year(date) > year(DATEADD(year, -2, GETDATE()))
For example, on Feb 17, 2017 this query returns results from 1/1/2016 to 2/17/2017
For some reason none of the results above worked for me.
This selects the last 365 days.
SELECT ... From ... WHERE date BETWEEN CURDATE() - INTERVAL 1 YEAR AND CURDATE()
The other suggestions are good if you have "SQL only".
However I suggest, that - if possible - you calculate the date in your program and insert it as string in the SQL query.
At least for for big tables (i.e. several million rows, maybe combined with joins) that will give you a considerable speed improvement as the optimizer can work with that much better.
argument for DATEADD function :
DATEADD (*datepart* , *number* , *date* )
datepart can be: yy, qq, mm, dy, dd, wk, dw, hh, mi, ss, ms
number is an expression that can be resolved to an int that is added to a datepart of date
date is an expression that can be resolved to a time, date, smalldatetime, datetime, datetime2, or datetimeoffset value.
declare #iMonth int
declare #sYear varchar(4)
declare #sMonth varchar(2)
set #iMonth = 0
while #iMonth > -12
begin
set #sYear = year(DATEADD(month,#iMonth,GETDATE()))
set #sMonth = right('0'+cast(month(DATEADD(month,#iMonth,GETDATE())) as varchar(2)),2)
select #sYear + #sMonth
set #iMonth = #iMonth - 1
end
I had a similar problem but the previous coder only provided the date in mm-yyyy format. My solution is simple but might prove helpful to some (I also wanted to be sure beginning and ending spaces were eliminated):
SELECT ... FROM ....WHERE
CONVERT(datetime,REPLACE(LEFT(LTRIM([MoYr]),2),'-
','')+'/01/'+RIGHT(RTRIM([MoYr]),4)) >= DATEADD(year,-1,GETDATE())
Here's my version.
YEAR(NOW())- 1
Example:
YEAR(c.contractDate) = YEAR(NOW())- 1
For me this worked well
SELECT DATE_ADD(Now(),INTERVAL -2 YEAR);
If you are trying to calculate "rolling" days, you can simplify it by using:
Select ... FROM ... WHERE [DATE] > (GETDATE()-[# of Days])

What's a good way to check if two datetimes are on the same calendar day in TSQL?

Here is the issue I am having: I have a large query that needs to compare datetimes in the where clause to see if two dates are on the same day. My current solution, which sucks, is to send the datetimes into a UDF to convert them to midnight of the same day, and then check those dates for equality. When it comes to the query plan, this is a disaster, as are almost all UDFs in joins or where clauses. This is one of the only places in my application that I haven't been able to root out the functions and give the query optimizer something it can actually use to locate the best index.
In this case, merging the function code back into the query seems impractical.
I think I am missing something simple here.
Here's the function for reference.
if not exists (select * from dbo.sysobjects
where id = object_id(N'dbo.f_MakeDate') and
type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
exec('create function dbo.f_MakeDate() returns int as
begin declare #retval int return #retval end')
go
alter function dbo.f_MakeDate
(
#Day datetime,
#Hour int,
#Minute int
)
returns datetime
as
/*
Creates a datetime using the year-month-day portion of #Day, and the
#Hour and #Minute provided
*/
begin
declare #retval datetime
set #retval = cast(
cast(datepart(m, #Day) as varchar(2)) +
'/' +
cast(datepart(d, #Day) as varchar(2)) +
'/' +
cast(datepart(yyyy, #Day) as varchar(4)) +
' ' +
cast(#Hour as varchar(2)) +
':' +
cast(#Minute as varchar(2)) as datetime)
return #retval
end
go
To complicate matters, I am joining on time zone tables to check the date against the local time, which could be different for every row:
where
dbo.f_MakeDate(dateadd(hh, tz.Offset +
case when ds.LocalTimeZone is not null
then 1 else 0 end, t.TheDateINeedToCheck), 0, 0) = #activityDateMidnight
[Edit]
I'm incorporating #Todd's suggestion:
where datediff(day, dateadd(hh, tz.Offset +
case when ds.LocalTimeZone is not null
then 1 else 0 end, t.TheDateINeedToCheck), #ActivityDate) = 0
My misconception about how datediff works (the same day of year in consecutive years yields 366, not 0 as I expected) caused me to waste a lot of effort.
But the query plan didn't change. I think I need to go back to the drawing board with the whole thing.
This is much more concise:
where
datediff(day, date1, date2) = 0
You pretty much have to keep the left side of your where clause clean. So, normally, you'd do something like:
WHERE MyDateTime >= #activityDateMidnight
AND MyDateTime < (#activityDateMidnight + 1)
(Some folks prefer DATEADD(d, 1, #activityDateMidnight) instead - but it's the same thing).
The TimeZone table complicates matter a bit though. It's a little unclear from your snippet, but it looks like t.TheDateInTable is in GMT with a Time Zone identifier, and that you're then adding the offset to compare against #activityDateMidnight - which is in local time. I'm not sure what ds.LocalTimeZone is, though.
If that's the case, then you need to get #activityDateMidnight into GMT instead.
where
year(date1) = year(date2)
and month(date1) = month(date2)
and day(date1) = day(date2)
Make sure to read Only In A Database Can You Get 1000% + Improvement By Changing A Few Lines Of Code so that you are sure that the optimizer can utilize the index effectively when messing with dates
this will remove time component from a date for you:
select dateadd(d, datediff(d, 0, current_timestamp), 0)
Eric Z Beard:
I do store all dates in GMT. Here's the use case: something happened at 11:00 PM EST on the 1st, which is the 2nd GMT. I want to see activity for the 1st, and I am in EST so I will want to see the 11PM activity. If I just compared raw GMT datetimes, I would miss things. Each row in the report can represent an activity from a different time zone.
Right, but when you say you're interested in activity for Jan 1st 2008 EST:
SELECT #activityDateMidnight = '1/1/2008', #activityDateTZ = 'EST'
you just need to convert that to GMT (I'm ignoring the complication of querying for the day before EST goes to EDT, or vice versa):
Table: TimeZone
Fields: TimeZone, Offset
Values: EST, -4
--Multiply by -1, since we're converting EST to GMT.
--Offsets are to go from GMT to EST.
SELECT #activityGmtBegin = DATEADD(hh, Offset * -1, #activityDateMidnight)
FROM TimeZone
WHERE TimeZone = #activityDateTZ
which should give you '1/1/2008 4:00 AM'. Then, you can just search in GMT:
SELECT * FROM EventTable
WHERE
EventTime >= #activityGmtBegin --1/1/2008 4:00 AM
AND EventTime < (#activityGmtBegin + 1) --1/2/2008 4:00 AM
The event in question is stored with a GMT EventTime of 1/2/2008 3:00 AM. You don't even need the TimeZone in the EventTable (for this purpose, at least).
Since EventTime is not in a function, this is a straight index scan - which should be pretty efficient. Make EventTime your clustered index, and it'll fly. ;)
Personally, I'd have the app convert the search time into GMT before running the query.
You're spoilt for choice in terms of options here. If you are using Sybase or SQL Server 2008 you can create variables of type date and assign them your datetime values. The database engine gets rid of the time for you. Here's a quick and dirty test to illustrate (Code is in Sybase dialect):
declare #date1 date
declare #date2 date
set #date1='2008-1-1 10:00'
set #date2='2008-1-1 22:00'
if #date1=#date2
print 'Equal'
else
print 'Not equal'
For SQL 2005 and earlier what you can do is convert the date to a varchar in a format that does not have the time component. For instance the following returns 2008.08.22
select convert(varchar,'2008-08-22 18:11:14.133',102)
The 102 part specifies the formatting (Books online can list for you all the available formats)
So, what you can do is write a function that takes a datetime and extracts the date element and discards the time. Like so:
create function MakeDate (#InputDate datetime) returns datetime as
begin
return cast(convert(varchar,#InputDate,102) as datetime);
end
You can then use the function for companions
Select * from Orders where dbo.MakeDate(OrderDate) = dbo.MakeDate(DeliveryDate)
Eric Z Beard:
the activity date is meant to indicate the local time zone, but not a specific one
Okay - back to the drawing board. Try this:
where t.TheDateINeedToCheck BETWEEN (
dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, #ActivityDate)
AND
dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, (#ActivityDate + 1))
)
which will translate the #ActivityDate to local time, and compare against that. That's your best chance for using an index, though I'm not sure it'll work - you should try it and check the query plan.
The next option would be an indexed view, with an indexed, computed TimeINeedToCheck in local time. Then you just go back to:
where v.TheLocalDateINeedToCheck BETWEEN #ActivityDate AND (#ActivityDate + 1)
which would definitely use the index - though you have a slight overhead on INSERT and UPDATE then.
I would use the dayofyear function of datepart:
Select *
from mytable
where datepart(dy,date1) = datepart(dy,date2)
and
year(date1) = year(date2) --assuming you want the same year too
See the datepart reference here.
Regarding timezones, yet one more reason to store all dates in a single timezone (preferably UTC). Anyway, I think the answers using datediff, datepart and the different built-in date functions are your best bet.