Converting SQL date formats not ending up the same - sql

I am trying to remove the timestamp from my date values so I end up with something like mm/dd/yy or mm/dd/yyyy. I referenced many technet, stackoverflow, and w3schools articles but still can't get the date to appear correctly. All of my columns in the table are defined as a datetime and they come from the same table.
I am using convert statements like this: CONVERT(VARCHAR(10), E.PD_DT, 101) AS 'Paid Date'
In place of 101 I have used 10 and 11, still have the same issue with the data (below). What's happening is when the date value (not including the time) is 8 characters, I am getting an additional character from the time, as seen in the Claim Adjustment Date-10 and Claim Adjustment Date-11 columns. Here is my data:
Claim Paid Date-101 Claim Paid Date Claim Adjustment Date-10 Claim Adjustment Date-11 Claim Adjustment Date
10/23/2012 10/23/12 12:00 AM 9/4/2012 1 9/4/2012 1 9/4/12 12:00 AM
10/23/2012 10/23/12 12:00 AM 9/4/2012 1 9/4/2012 1 9/4/12 12:00 AM
10/23/2012 10/23/12 12:00 AM 9/4/2012 1 9/4/2012 1 9/4/12 12:00 AM
09/06/2011 09/06/11 12:00 AM 9/4/2012 1 9/4/2012 1 9/4/12 12:00 AM
10/23/2012 10/23/12 12:00 AM 8/21/2012 8/21/2012 8/21/12 12:00 AM
09/06/2011 09/06/11 12:00 AM 8/21/2012 8/21/2012 8/21/12 12:00 AM
The strange thing is that all of the dates in the "Claim Paid Date" column have a zero for padding if the month or day is < 10. This makes the convert come out just fine but where the month or day is < 10 and doens't have a zero is where I get my problem.

You have a string source, not a DATETIME source.
What happens with DATETIME:
SELECT GETDATE() -- DATETIME
,CAST(GETDATE() AS DATE) --DATE
,CONVERT(VARCHAR(10),GETDATE(),101) --101 format
Result:
DateTime Date 101 Format
----------------------- ---------- ----------
2013-09-24 13:58:48.880 2013-09-24 09/24/2013
What happens with strings parading as DATETIME:
DECLARE #fake_date VARCHAR(25) = '10/23/12 12:00 AM'
SELECT CAST(#fake_date AS DATETIME) --DATETIME
,CAST(#fake_date AS DATE) --DATE
,CONVERT(VARCHAR(10),#fake_date,101) --101 format
Result:
DateTime Date 101 Format
----------------------- ---------- ----------
2012-10-23 00:00:00.000 2012-10-23 10/23/12 1
So perhaps you want:
CONVERT(VARCHAR(10),CAST(#fake_date AS DATE),101)

The correct way to do this is to store the data as DATETIME in the first place. Currently you are storing the data as strings, and you shouldn't be doing this, for a variety of reasons:
you lose the ability to sort (9/1/2012 will sort after 12/12/2012) without converting first. Converting is expensive.
you lose the ability to perform meaningful range queries without converting first (again, 9/1/2012 > 12/12/2012)
you lose all means of built-in validation - anyone can enter 13/33/2999 or 02/32/2099 or simply foo as a "date"
you lose the ability to perform date-related functions such as DATEADD, DATEPART or DATEDIFF against these columns without first converting
you need to convert to another type first before you can perform a conversion that will allow you to do things like trim time or present the date in a specific format
If you can't fix the table design and want to continue storing dates using the wrong data type, then the next best thing is to simply format the string in the client. If the client knows it's a date/time, then using .Format() or .ToString() should allow you to present the date without the time in whatever format you want.
You should be presenting dates to users in unambiguous formats, since you never know when some of your readers will see 6/12/2012 and not be sure if that's June 12 or December 6 (Canadians and Americans will see those differently, for example). So you can use, as an example:
DateVariable.Format('yyyy-MM-dd')
This will present an unambiguous date like 2012-06-12, which can only be misinterpreted in some very isolated regions and age groups in France (where y-d-m seems to remain in vogue).
However, if you absolutely want to present the ambiguous format, you can just use:
DateVariable.Format('MM/dd/yyyy')
...with no reason to change your SQL query at all. If, for some reason, you absolutely want or need to do this at the query level, then you can use:
SELECT CONVERT(CHAR(10), CONVERT(DATE, col), 101) -- mm/dd/yyyy
If you want to use less ambiguous forms, like yyyymmdd or yyyy-mm-dd, you can use:
SELECT CONVERT(CHAR(8), CONVERT(DATE, col), 112) -- yyyymmdd
SELECT CONVERT(CHAR(10), CONVERT(DATE, col), 120) -- yyyy-mm-dd
But again, conversion at the database layer is expensive, whereas string formatting at the client layer (where you're already handling these things as strings at that final step) is relatively cheap. You should be keeping these as date values as long as possible, both on the way in and on the way out of the database. There is just no advantage to treating them as strings anywhere SQL Server is concerned.
So, summary:
fix the table
perform the conversion on the client (preferably to an unambiguous format)
if you can't perform the conversion on the client, use a double-nested CONVERT (again, preferably to an unambiguous format)

Related

Change timestamp without effect date

I am using SSMS and I have a table with date/time stored in a column. What i need to do is change the time but leave the date.
Example: 2020-07-28 00:00:00. I need to change the timestamp 00:00:00 to 23:59:00 without changing the date for the whole column.
CPRDate -- Column Header
2020-07-28 00:00:00
2020-01-01 00:00:00
2017-01-01 00:00:00
You can just add them together -- as datetimes:
select cprdate + convert(datetime, convert(time, '23:59:00'))
This construct is clearly documented and supported by SQL Server:
The plus (+) and minus (-) operators can also be used to run arithmetic operations on datetime and smalldatetime values.
You can use the same construct in an update if you want to change the stored value.
If cprdate is a date, you need to convert it to a datetime for this to work.
You could add subtract one minute and add a day to achieve this, i.e.
update MYTABLE SET CPRDate = DATEADD(day, 1, DATEADD(minute, -1, CPRDATE));
Here's a SQL Fiddle to show it working.

SQL to query greater than today's date and time, with time defaulting to 12:00:00 AM

Format of the time/date in the table from which I am querying is the following: 11/12/2015 12:00:00 AM.
I would like to be able to query if the date is greater than or equal today, but look for results as the above format. Using getdate() returns the current specific time. Also, the specific format includes two spaces between the date and time.
Thanks for you help.
You can remove the time from consideration as follows:
WHERE CAST(YourDateTime AS DATE) >= CAST(getdate() AS DATE)

T-SQL : convert(datetime) to include/exclude certain hours

Using date range to select values, but also need to use an hour range to determine if a record should be selected. The date ranges and time ranges are not necessarily associated.
game_time (between 6 am and 6 pm)
have tried straight between statement and datepart, but cannot get anything to capture what we need.
create table gametime(name varchar, start_time datetime, end_time datetime)
insert assorted name, start_times and end_times
Desired results
name start_time end_time
name1 8:00 AM 10:00 AM
name2 8:00 AM 11:30 AM
name3 4:00 PM 5:30 PM
name4 6:00 PM 9:00 PM
datetime is used is storage, but not needed in presentation.. only times are needed in presentation.
Selected games should only start between the hours of 6:00 AM and 6:00 PM.
Any and all suggestions and insight appreciated......
Using
LTRIM(RIGHT(CONVERT(VARCHAR(20), start_time, 100), 7))
to get the correct format for presentation,
but when I try to use
LTRIM(RIGHT(CONVERT(VARCHAR(20), start_time, 100), 7)) > 6
I get conversion errors.
I would use DATEPART rather than relying on converting to/comparing strings:
WHERE DATEPART(hour,start_time) BETWEEN 6 AND 18
Try CONVERT(VARCHAR(5),start_time,108) BETWEEN '06:00' AND '18:00'. Right now you're trying to compare a string to an integer.
Here's another way, provided you're on SQL Server 2008 or higher and have the TIME type available:
SELECT *
FROM gametime
WHERE CAST(start_time AS TIME) BETWEEN '06:00:00' and '18:00:00'
This can be a bit more flexible when your time range is not anchored to exact hours. It also is sarg-able -- i.e. it will use an index, where calling DATEPART will prevent that.

SQL Server DateTimeOffset matching same "day"

My application needs to collect "Tuesday's" purchases for all locations world wide, where "Tueday" is the location's Tuesday (regardless of time zone). And if the user needs to re-run the report next week, I need to still get "Last Tuesday's" data. All of our data is stored using DateTimeOffset.
So
9/4/12 00:00:00 -7 through 9/4/12 23:59:59 -7
must MATCH
9/4/12 00:00:00 +11 through 9/4/12 23:59:59 +11
when I am executing my WHERE clause.
I can't convert to UTC in the WHERE clause because that will pick up the data for "Tuesday" in London (depending on DST), not the location's Tuesday.
I tried converting from DateTimeOffset to DateTime, but that seems to convert to UTC. (In my tests, passing 9/1/12 through 9/30/12 picked up 8/31/12 data.)
Is there a trick to doing something like this with TSQL?
Thanks!
IMHO
DateTimeOffset = DateTime+Offset(from UTC)
So your data is already representing Client's Local date and time. Just cast it to DateTime and you will get the client's local Date and time.
But in-case if you want to add the Offset to the datetime and want the resultant Datetime then
DECLARE #PurchaseDate DATETIMEOFFSET(7) = CAST('2007-05-08 12:30:29.1234567 +5:00' AS datetimeoffset(7))
SELECT CAST(SWITCHOFFSET (#PurchaseDate , '+00:00') AS DATETIME)
have a look at this blog for further info.
http://blogs.msdn.com/b/bartd/archive/2009/03/31/the-death-of-datetime.aspx
When casting a DATETIMEOFFSET as DATETIME it takes the date and time as offset in the value and simply drops the time zone. The same is true when casting as DATE or TIME. So, I think you can simply cast the column as DATE and compare that to the date-only value you wish to match:
DECLARE #targetDate DATETIME2 = '2012-09-04' --Or we could use DATE here
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = #targetDate
I'm not sure how efficient this will be (hopefully not bad if the provider is truly clever--which SQL Server likely would be), but you might improve it by bounding the original column value as well:
DECLARE #targetDate DATETIME2 = '2012-09-04' --DATETIME2 so we can adjust by hours
SELECT [PurchaseId], [PurchaseTime], CAST([PurchaseTime] AS DATE) AS "PurchaseDate"
FROM [Purchases]
WHERE CAST([PurchaseTime] AS DATE) = #targetDate --Keep only the local-date matches
AND [PurchaseTime] >= DATEADD(hh, -14, #targetDate) --Up to 14-hour time zone offset
AND [PurchaseTime] <= DATEADD(hh, 38, #targetDate) --24 hours later plus 14
This should efficiently index down to the set of possibilities and then filter properly on the conversion to the local date. Note that time zone offsets can be up to 14 hours (New Zealand is furthest I know at +13:00, but they can go to +/- 14:00 according to MSDN) and the #targetDate will be at the start of the day, so it compares back to 14 hours earlier and 24+14=38 hours later. DATETIME2 has the same range and precision as DATETIMEOFFSET, so it's better for this purpose than the original DATETIME (but it may also work okay with DATETIME instead).

Sql query to get range of values

I have a table called TimeList
SlotID SlotStartTime SlotEndTime
(int identity) (varchar(10)) (varchar(10))
1 8:00AM 8:15AM
2 8:15AM 8:30AM
3 8:30AM 8:45AM
4 8:45AM 9:00AM
5 9:00AM 9:15AM
6 9:15AM 9:30AM
7 9:30AM 9:45AM
8 9:45AM 10:00AM
If I am passing SlotStartTime and SlotEndTime I want to get times in between.
I used the following query to get timeslots in b/w slotStarttime 8:00AM amd slotEndTime 9:00AM
select * from TimeList1 where StartTime >='8:00AM' and EndTime <= '9:00AM'
Here the result is coming as:
SlotID SlotStartTime SlotEndTime
1 8:00AM 8:15AM
2 8:15AM 8:30AM
3 8:30AM 8:45AM
8 9:45AM 10:00AM
I want to get slotstarttime starting from 8:00AM and slotendtime ending 9:00AM means
expected result is:
SlotID SlotStartTime SlotEndTime
1 8:00AM 8:15AM
2 8:15AM 8:30AM
3 8:30AM 8:45AM
4 8:45AM 9:00AM
What change do I have to make in my query to get the result as above?
When you're using varchars (or strings in most languages), you'll find that "10:00" < "9:00" just because of character sequencing rules (since "1" < "9").
You should be storing date and time values in date and time columns. If you must use varchars, you'll need to convert them to fixed-size 24-hour format to do it properly (e.g., "01:30", "12:15", "18:25").
But my advice is to store them as proper DB date and time values. Your queries will be easier and faster.
The following solution may get you what you want if I understand your data storage (one entry per quarter hour) but my professional opinion is to fix the column types, since that will allow for far more expressive conditions in your select statements:
select * from TimeList1
where left(StartTime,1) = '8'
and right(StartTime,2) = 'AM'
Part of the problem, as has been noted, is that you are using strings to represent times. The other part of your problem is that the AM/PM notation is completely ghastly for computational purposes. Remember, the sequence is:
12:00AM
12:15AM
12:30AM
12:45AM
1:00AM
1:15AM
...
9:45AM
10:00AM
10:15AM
...
11:30AM
11:45AM
12:00PM
12:15PM
12:30PM
12:45PM
1:00PM
1:15PM
...
The 24-hour clock has many merits. If you used:
00:00
00:15
00:30
00:45
01:00
01:15
...
09:45
10:00
10:15
...
11:30
11:45
12:00
12:15
12:30
12:45
13:00
13:15
...
(for the same set of numbers) then your VARCHAR strings could be made into CHAR(5) and would automatically sort correctly in time sequence. You would still have to phrase your queries correctly, including the leading zero(es) on times less than 10:00, but the values would sort correctly and compare correctly.
Some DBMS provide support for sub-sets of time. In IBM Informix Dynamic Server, you could use DATETIME HOUR TO MINUTE as the type for the column. That would give you back the flexibility of dropping leading zeroes.
See also Convert 12-hour date/time to 24-hour date/time.
Your query is correct. But it's your data types that are wrong.
10:00AM indeed comes before 9:00AM considering they are string, not datetime values.
So if you can't change the data types now, your best luck is to try this query :
Select * from TimeList1
where StartTime >= Cast('8:00AM' as datetime)
and EndTime <= Cast('9:00AM' as datetime)
//make sure StartTime and EndTime is type of datetime
Try this:
SELECT *
FROM TimeList1
WHERE (CONVERT(datetime, CONVERT(varchar, GETDATE(), 103) + ' ' + StartTime) >=
CONVERT(datetime, CONVERT(varchar, GETDATE(), 103) + ' ' + '8:00AM'))
AND
(CONVERT(datetime, CONVERT(varchar, GETDATE(), 103) + ' ' + EndTime) <=
CONVERT(datetime, CONVERT(varchar, GETDATE(), 103) + ' ' + '9:00AM'))
select *
from TimeList1
where StartTime >= '8:00AM'
and EndTime <= '9:00AM'
Less than or equal, not greater than or equal.
Though really, that doesn't work like you expect when your columns are of type varchar. Use a time or datetime type.
The first two paragraphs don't explain a thing as far as I can see. The last paragraph contains a flimsy explanation of the problem. – Jonathan Leffler
In the original post, before you edited it, the OP had as his select statement,
select *
from TimeList1
where StartTime >= '8:00AM'
and EndTime >= '9:00AM'
I was responding to that.
My response may be "flimsy", but I wanted to address the obvious problem and get the guy moving forward.