Working with Time zones in SQL - sql

I need to get a SQL query to output the date from a datetime field in the format Mmm dd yyyy hh:mm AM/PM. The best aproach I've been able to come up with so far is:
SELECT Left(
Convert(
nvarchar(30),
SWITCHOFFSET(
CAST(datetime1 as datetimeoffset),
'-05:00'
),
0
),
LEN(
Convert(
nvarchar(30),
SWITCHOFFSET(
CAST(datetime1 as datetimeoffset),
'-05:00'
)
)
)-11
)
However, a) it's ugly! I feel like this should be simpler than that; and b) I think going to have to change my query when Daylight savings time comes back.
The source data is a sharepoint calendar, so I can't simply change the datatype to datetimeoffset.
Any ideas?
Thanks,
Steve

As a general principal you should not be using SQL to format data into something presentable for the front end. You should be getting a DateTime type back, and using code on the format to change it. What if a future requirement comes in to support DD/MM/YYY? You'll need a separate query. It's better to let the front end format it for that.
With that in mind, store 2 pieces of data in the database. 1) DateTime as a UTC value 2) The current user's timezone (not offset)
The reason you store timezone and not offset is because of all the rule involved with DST. For example, the days that DST starts end aren't fixed in stone. They are set for a country each year, but that schedule can change, and that's a bad reason to need to update your code (unless you're writing a timezone library.)
Then once you have these two pieces of data, you retrieve the date, and the timezone, and construct a new object in the server that allows you to convert the time in the DB to the local time.

Am I missing something, or is it simply:
Convert(nvarchar(30), DATEADD(Hour, -5, datetime1), 100)
That should add -5 hours to datetime1, then convert it into the format you were specifying.
I do agree that you should try to deal with timezones instead of offsets whenever possible.

Related

Converting Date Format SQL to YYYY-MM-DD HH:MM:SS

I currently have the date format 2016-11-23 16:47:21.007 and I am trying to convert it with this SQL:
update user_table set login_date = CONVERT(datetime,login_date,120)
So that I only get 2016-11-23 16:47:21 but it's currently just staying as the same format.
Am I missing something?
Your current requirement is mainly a presentation need. Since your login_date is already stored as a datetime, there is no need to change anything internally in your database.
When you need to present the login_date without milliseconds, then go ahead and call CONVERT as you were:
SELECT CONVERT(varchar(19), login_date, 120) AS login_date_no_millis
FROM yourTable
But there is no need to do the UPDATE you showed us in your question.
Altering the column to datatype datetime2(0) will mean milliseconds aren't stored. If you're certain you don't need them.
Equally, casting it to datetime2(0) in your select will do the same in the presentation level whilst maintaining the milliseconds in the database if required. Also means that unlike cast/convert to varchar, it's still a datetime value rather than a string if that makes a difference to the app.

Need to calculate date difference of today date vs converted date field

I have a table with a column that was previously converted using
(CONVERT(VARCHAR(19), GETDATE(), 112)
With the getdate being the date when the field was inserted. Now I have to compare the current date against the date the field was inserted.
The issue I'm facing is that when the date field is from last month, say 20131004, I calculate date difference by (CONVERT(VARCHAR(19), GETDATE(), 112) - 20131004, the result is 200. Obviously this is wrong...
Could you please suggest me how I could calculate the true date difference?
You should be using the DATE or DATETIME data type for that column. Why on earth would you ever store a date as a string? Do you know how much you lose by doing so? Validation, for one - a VARCHAR(19) column will accept 20131004 12:34 PM but will also accept nonsense values like I am not a date!.
If the data is actually good, you can simply do this instead of lazy shorthand and without any explicit conversions:
SELECT DATEDIFF(DAY, column_name, GETDATE()) FROM dbo.table;
If you get an error message with this, then you have bad data. You can identify it like this:
SELECT column_name FROM dbo.table WHERE ISDATE(column_name) = 0;
Please read:
Bad habits to kick : choosing the wrong data type
Bad habits to kick : mis-handling date / range queries
Bad Habits to Kick : Using shorthand with date/time operations
I agree with #Aaron that you should store a date field in a date field, not a text field.
If for some reason you do want to store it in a text field then the easiest way to calculate the number of days is to convert it back to a date field and then compare it:
SELECT DATEDIFF(DAY, CAST(column_name as DATETIME), GETDATE()) FROM dbo.table
This will throw an error if the value in the column cannot be converted to a date. You'll also need to make sure that the date is formatted correctly for your database. Assuming you use the format 112 you should be ok, but if you have the value 04/12/2013 in the column is that the 4th December 2013 or the 12th April 2013? It depends on how your database is configured.
But anyway, if you always insert dates in that field then you're nuts not making it a date field.
If you need to display the date somewhere then convert it on the way out.

Change default dateformat stored in a database

I am seeeing my dates are stored in database in this format for a column (datetime datatype) 2011-01-14 10:15:41.787 i.e YYYY-MM-DD way . How could I make the default storage in YYYY-DD-MM format . Do I need to set that for all the DBS, or I can set it for single DB and how ?
I have the column in datetime datatype, right now it is saving as
2011-01-14 10:15:41.787 , my question is how can I set the db to store it as
2011-14-01 10:15:41.787
That is the crux of the confusion. Just because SQL Server Management Studio displays a datetime column in that format does not mean that it is stored AS TEXT YYYY-MM-DD hh:mm:ss.zzz. It is stored as binary, something like 0000101000001010..
Your dates are stored in SQL Server as a series of bytes (bits really) that make up some numeric value that is an offset from 1900-01-01. There is no inherent format the the dates. What you are referring to is that SSMS by default shows [display] datetime columns as YYYY-MM-DD hh:mm:ss.zzz. If you use a front-end programming tool, that too may impose a default [display] format unless you have asked for another one.
There is absolutely NO way to make SSMS show datetime data in another format through options or configuration. If you must, you would have to update the SQL query to convert the datetime column to a VARCHAR column containing the TEXTual equivalent in a particular format. That may be useful in SSMS, but would be bad when used as a data source to front-end GUI/web apps - since the values are not datetime and cannot be used for interval calculation, graphing, bound to date controls etc.
See this example of displaying time (getdate()) as YYYY-DD-MM, a very unusual format. Notice the date field/variable has to be used twice:
select stuff(convert(char(7), getdate(), 120), 5, 0, '-' + convert(char(2), getdate(), 3))
DATETIMEs are stored internally as two 4 byte integers, so firstly you are seeing a formatted representation for the UI - it's not actually stored in a particular date/time format as such.
e.g. if you insert just a date like "2010-01-01" then it will still hold the time element: 2010-01-01 00:00:00.000
If you're only interested in the DATE part, then you can format the DATETIME for output either in your front-end code or via your query:
e.g.
SELECT CONVERT(VARCHAR(8), GETDATE(), 121)
So even if the DATEs you insert contain a time, that will be ignored when returned. You could also ensure you only insert dates without the time specified - you need to handle that in whatever code is doing the INSERTs. e.g. from .NET, instead of passing in DateTime.Now you could pass in DateTime.Now.Date.
In SQL Server 2008, there is a DATE datatype which is there to only store a DATE (without time) which is really what you want in this kind of scenario.

Storing the date Only in SQL Server 2005

How do I avoid storing the time portion of a datetime in SQL Server, i.e. if I have a value of 2011-01-01 00:00:00.000 I want to store only 2011-01-01?
I want to ensure that only the date portion is stored.
The DateTime data type ALWAYS stores the date AND time. So you are left with using CONVERT/CAST to obtain a particular format, or use the YEAR(), MONTH() or DAY() methods to isolate date details depending on your need.
SQL Server Date Formats.
The easiest solution is just to not expose the time portion to the user. However, if you really need to make sure only the date part is stored, you could force the time portion to midnight/midday/any constant time before storing the value.
The built-in DATETIME data type stores both the date and time data. If you specify only the date portion then the time will be 12:00:00 or something like that.
Funny story: I saw a database once where there was a date and a time field, both stored the date and the time, but each was used only for half of the data. Some people do silly things :)
If you cast a DateTime to an Int and back you will get a DateTime with 00:00 as the time part.
So you could save all your dates as integers in the database.
Either add a computed column:
dateonly AS CONVERT(DATETIME, CONVERT(CHAR(8), date_with_time, 112), 112)
or truncate your date right on insert:
INSERT
INTO mytable (dateonly)
VALUES CONVERT(DATETIME, CONVERT(CHAR(8), GETDATE(), 112), 112)
, making a CHECK on your dateonly column to raise an error when someone tries to insert a non-truncated value:
CHECK (dateonly = CONVERT(DATETIME, CONVERT(CHAR(8), date_with_time, 112), 112))
Just represent the date as a yyyMMdd integer value.

How do I strip the date off of a datetime string in SQL SSIS?

I'm working on a data warehouse project and would like to know how to (preferably in a Derived Column component in a Data flow) strip the date piece off of a SQL datetime record.
Once I have the datetime converted to just a time I am going to do a lookup on the time to find the related time record in a time dimension table.
Can someone give me a simple function to accomplish this inside a derived column transform?
Example: Transform a datetime such as "12/02/2008 11:32:21 AM" into simply "11:32:21 AM".
I would just do a cast to DT_DBTIME type (using Derived Column transform, or Convert type transform). DT_DBTIME contains just (hours, minutes, seconds) part of the date/time, so you'll get rid of the date part.
If you need to do this in a variable expression Michael's solution won't work, but you can use the following expression:
(DT_DATE)(DT_DBDATE)GETDATE()
(DT_DBDATE) converts the current date and time to a date only. But the new datatype is not compatiple with SSIS's datetime. Therefore you'll have to use (DT_DATE) for converting to a compatible type.
Courtesy of this solution belongs to Russel Loski who has posted it in his blog:
http://www.bidn.com/blogs/RussLoski/ssas/1458/converting-datetime-to-date-in-ssis
Actually if you reverse the first 2 expressions like this: (DT_DBDATE)(DT_DATE)GETDATE()
instead of (DT_DATE)(DT_DBDATE)GETDATE(), then you will TRUNCATE the time off the date field.
If the DT_DATE expression is before the DT_DBDATE expression, you will still have the time portion in your output, but it will be reset to all zeroes.
Ran into this with writing a report for a scheduling app, needed the time that was stored as part of a datetime data type. I formated the datetime as 0 which gives you this mon dd yyyy hh:miAM (or PM), and just did a substring of that which returned the time only in an AM/PM format.
Example below.
DECLARE #S DATETIME = GETDATE()
SELECT SUBSTRING(CONVERT(NVARCHAR(30), #S , 0) , 13 , 10) AS ApptTime
, CONVERT(NVARCHAR(30), #S , 0) AS ApptDate
I personally use a series of functions for this. E.g.:
ALTER FUNCTION [dbo].[TIMEVALUE]
(
#Datetime datetime
)
RETURNS datetime
AS
BEGIN
RETURN (#Datetime - CAST(ROUND(CAST(#Datetime AS float), 0, 1) AS datetime))
END
I'd love to claim all the credit but it should really go to this guy.