Formatting a date/time in SQL Server 2005 - sql

I have a datetime field in a SQL Server 2005 table that has values like this:
2012-04-23 09:00:00.000
2012-04-23 14:00:00.000
The minutes, seconds, and microseconds are always zero.
I need to display a "time slot" (basically, the time plus one hour) like this:
2012/04/23 09:00 AM - 10:00 AM
2012/04/23 02:00 PM - 03:00 PM
I got what I needed using this:
SELECT SUBSTRING(CONVERT(VARCHAR, TimeSlot, 20), 1, 10) + ' ' +
RIGHT('00000' + LTRIM(SUBSTRING(CONVERT(VARCHAR(24), TimeSlot, 109), 13, 5)), 5) + ' ' +
SUBSTRING(CONVERT(VARCHAR(19), TimeSlot, 100),18,2) + ' - ' +
RIGHT('00000' + LTRIM(SUBSTRING(CONVERT(VARCHAR(24), DATEADD(hh, 1, TimeSlot), 109), 13, 5)), 5) + ' ' +
SUBSTRING(CONVERT(VARCHAR(19), DATEADD(hh, 1, TimeSlot), 100), 18, 2) AS TimeSlot
FROM MyTable
It works, but I feel so dirty.
I know I could do it easier in the code (VB .NET), but assume I have to do it in SQL.
Is there any cleaner way to do this?

Taking the built-in conversions, I would use something like this:
SELECT
convert(varchar, [TimeSlot], 111) + ' ' +
RIGHT(convert(varchar, [TimeSlot], 0), 7) + ' - ' +
RIGHT(convert(varchar, dateadd(hour, 1, [TimeSlot]), 0), 7)
FROM
[MyTable];
this gives you the line you want as
2012/04/23 9:00AM - 10:00AM
in detail:
convert(varchar, #dt, 111) converts the datetime to the Japanese format yy/mm/dd
convert(varchar, #dt, 0) converts the datetime to Apr 23 2012 9:00AM so we can use the time part
dateadd(hour, 1, #dt) adds one hour to the current datetime value
you can test it without tables with:
DECLARE #dt DATETIME
SET #dt = '2012-04-23 09:00:00'
PRINT convert(varchar, #dt, 111) + ' ' +
RIGHT(convert(varchar, #dt, 0), 7) + ' - ' +
RIGHT(convert(varchar, dateadd(hour, 1, #dt), 0), 7);

The built in date/time formats in SQL Server will only format to an actual date format, so you're up for custom formatting anyway. If you're feeding SSRS with this then you might be able to use an expression to format it within SSRS. Otherwise you're up for formatting it with the query using something like the technique you're describing.
If you're actually displaying it with a VB app then you could do that client-side.
If your date/time values are always on the hour or some consistent interval you could also make a reference table keyed on the time with the formatted interval stored as a varchar column and use that. That would save you in-lining the formatting expression into every query that needed to produce the interval description.

FollowingConcernedOfTunbridgeW's suggestion, if you maintain a two-column table with a primary key int column [hr] that goes from 0 to 23 and a char(20) column [rng] containing the space-prefixed range string for each hour
' 12:00 AM - 01:00 AM' (in the row where hr = 0)
...
' 11:00 PM - 12:00 AM' (in the row where hr = 23)
this simply query should produce what you want:
select
convert(char(10),TimeSlot, 111) + rng as TimeRange
from T join RangesByHr
on datepart(hour,TimeSlot) = hr;

Related

Converting an irregular varchar to datetime in sql server

I'm working on revamping a database right now, and all the datetime's are stored in varchar's formatted like so: DDMonYY hhmm (ex. 13Mar99 2032).
I don't have a lot of experience with SQL, as I only just finished my first year of college so I'm having a really hard time trying to get this to work so any help would be appreciated.
All you should need to do for your particular string is to get a colon in between the hour and minute values. Something like:
SELECT CAST(STUFF('13Mar99 2032', LEN('13Mar99 2032') - 1, 0, ':') AS datetime)
should work.
You can pick a datetime format and then shift parts of the string to match it. For example, format 7 is:
select convert(varchar(25), getdate(), 7)
-->
'Jun 23, 14'
So you can use substring to transform your string:
declare #orig varchar(30) = '13Mar99 2032'
declare #conv varchar(30)
select #conv = substring(#orig, 3, 3) + ' ' + substring(#orig, 1, 2) + ', ' +
substring(#orig, 6, 2) + ' ' + substring(#orig, 9, 2) + ':' + substring(#orig, 11, 2)
select #conv
, convert(datetime, #conv, 7)
-->
'Mar 13, 99 20:32' 1999-03-13 20:32:00.000

Convert SQL datetime to specific format

I have one table with one datetime colomn as below:
tbTest
ID int
SetDate datetime
and one entry like:
ID SetDate
1 04/10/2014 2:38:16 PM
I want to convert SetDate to dd-mm-yyyy hh:mm:ss PM or dd-mm-yyyy hh:mm PM
Desired Output:
ID SetDate
1 10-04-2014 2:38:16 PM
or
1 10-04-2014 2:38 PM
You can use sql convert method for getting Desired output :-23-04-2014 10:16 AM
SELECT CONVERT(VARCHAR(10), getdate(), 105) + ' ' + REPLACE(REPLACE(RIGHT('0'+LTRIM(RIGHT(CONVERT(varchar,getDate(),100),7)),7),'AM',' AM'),'PM',' PM')
You can use convert
select convert(varchar(10), getdate(), 105) + ' ' +
SUBSTRING(CONVERT(varchar, getdate(), 100), 14, 4)+ ' ' +
RIGHT(convert(varchar, getdate(), 100), 2)
SELECT replace(convert(NVARCHAR(100), getdate(), 106), ' ', '-')
+' ' + CONVERT(VARCHAR(30), CAST(GETDATE()AS Time), 100)
Use CONVERT with the appropriate styles:
SELECT CONVERT(CHAR(10), #SetDate, 110)
+ ' ' + SUBSTRING(CONVERT(CHAR(19), #SetDate, 100), 14, 4)
+ ' ' + SUBSTRING(CONVERT(CHAR(19), #SetDate, 100), 18, 2)
The first is for the date part, the second for the 12h time part and the last for the am/pm designator.
Demo
The shortest and best looking query you can use is:
SELECT CONVERT(VARCHAR(10), SetDate, 105) + ' ' +
CONVERT(VARCHAR(8), CAST(SetDate AS TIME), 100)
Output:
31-07-2001 12:00AM
No string manipulation required.
Another example:
DECLARE #Date AS DATETIME
SET #Date = '2014-31-12 18:00:00'
SELECT CONVERT(VARCHAR(10), #Date, 105) + ' ' +
CONVERT(VARCHAR(8), CAST(#Date AS TIME), 100) AS FormatDate
Outputs:
31-12-2014 6:00PM

DATEDIFF in HH:MM:SS format

I need to calculate the total length in terms of Hours, Minutes, Seconds, and the average length, given some data with start time and end time.
For example the result must be something like 45:15:10 which means 45 hours 15 min 10 sec, or 30:07 for 30 min 07 sec.
We're using SQL Server 2008 R2 and the conversion failed when time is more than 24:59:59. Any idea of how I could do this?
For information, the columns in the table are Id, StartDateTime, EndDateTime, etc. I need to make a monthly report which contains the recordings count of the month, the total length of these records, and the average length. I'd like to know if there is an easy way to perform all of this.
You shouldn't be converting to time - it is meant to store a point in time on a single 24h clock, not a duration or interval (even one that is constrained on its own to < 24 hours, which clearly your data is not). Instead you can take the datediff in the smallest interval required (in your case, seconds), and then perform some math and string manipulation to present it in the output format you need (it might also be preferable to return the seconds to the application or report tool and have it do this work).
DECLARE #d TABLE
(
id INT IDENTITY(1,1),
StartDateTime DATETIME,
EndDateTime DATETIME
);
INSERT #d(StartDateTime, EndDateTime) VALUES
(DATEADD(DAY, -2, GETDATE()), DATEADD(MINUTE, 15, GETDATE())),
(GETDATE() , DATEADD(MINUTE, 22, GETDATE())),
(DATEADD(DAY, -1, GETDATE()), DATEADD(MINUTE, 5, GETDATE())),
(DATEADD(DAY, -4, GETDATE()), DATEADD(SECOND, 14, GETDATE()));
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CONVERT(VARCHAR(5), d/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CONVERT(VARCHAR(5), a/60/60)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
Results:
id StartDateTime EndDateTime delta_HH:MM:SS avg_HH:MM:SS
-- ------------------- ------------------- -------------- ------------
1 2013-01-19 14:24:46 2013-01-21 14:39:46 48:15:00 42:10:33
2 2013-01-21 14:24:46 2013-01-21 14:46:46 0:22:00 42:10:33
3 2013-01-20 14:24:46 2013-01-21 14:29:46 24:05:00 42:10:33
4 2013-01-17 14:24:46 2013-01-21 14:25:00 96:00:14 42:10:33
This isn't precisely what you asked for, as it won't show just MM:SS for deltas < 1 hour. You can adjust that with a simple CASE expression:
;WITH x AS (SELECT id, StartDateTime, EndDateTime,
d = DATEDIFF(SECOND, StartDateTime, EndDateTime),
a = AVG(DATEDIFF(SECOND, StartDateTime, EndDateTime)) OVER()
FROM #d
)
SELECT id, StartDateTime, EndDateTime,
[delta_HH:MM:SS] = CASE WHEN d >= 3600 THEN
CONVERT(VARCHAR(5), d/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), d/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), d % 60), 2),
[avg_HH:MM:SS] = CASE WHEN a >= 3600 THEN
CONVERT(VARCHAR(5), a/60/60) + ':' ELSE '' END
+ RIGHT('0' + CONVERT(VARCHAR(2), a/60%60), 2)
+ ':' + RIGHT('0' + CONVERT(VARCHAR(2), a % 60), 2)
FROM x;
This query changes the delta column in the 2nd row in the above result from 0:22:00 to 22:00.
I slightly modified Avinash's answer as it may end with error if difference is too big. If you need only HH:mm:ss it is sufficient to distinguish at seconds level ony like this:
SELECT CONVERT(time,
DATEADD(s,
DATEDIFF(s,
'2018-01-07 09:53:00',
'2018-01-07 11:53:01'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
SELECT CONVERT(time,
DATEADD(mcs,
DATEDIFF(mcs,
'2007-05-07 09:53:00.0273335',
'2007-05-07 09:53:01.0376635'),
CAST('1900-01-01 00:00:00.0000000' as datetime2)
)
)
If you want to do averages, then the best approach is to convert to seconds or fractions of a day. Day fractions are convenient in SQL Server, because you can do things like:
select avg(cast(endtime - starttime) as float)
from t
You can convert it back to a datetime using the reverse cast:
select cast(avg(cast(endtime - starttime as float) as datetime)
from t
The arithmetic to get the times in the format you want . . . that is a pain. You might consider including days in the final format, and using:
select right(convert(varchar(255), <val>, 120), 10)
To get the hours exceeding 24, here is another approach:
select cast(floor(cast(<val> as float)*24) as varchar(255))+right(convert(varchar(255), <val>, 120), 6)
It uses convert for minutes and seconds, which should be padded with 0s on the left. It then appends the hours as a separate value.
Starting in SQL SERVER 2012, you don't need to use DATEDIFF function. You can use FORMAT function to achieve what you want:
SELECT
FORMAT(CONVERT(TIME, [appoitment].[Start] - [appointment].[End]), N'hh\:mm') AS 'Duration'
FROM
[tblAppointment] (NOLOCK)
A way that avoids overflows and can include days and go all the way to milliseconds in the output:
DECLARE #startDate AS DATETIME = '2018-06-01 14:20:02.100'
DECLARE #endDate AS DATETIME = '2018-06-02 15:23:09.000'
SELECT CAST(DATEDIFF(day,'1900-01-01', #endDate - #startDate) AS VARCHAR) + 'd ' + CONVERT(varchar(22), #endDate - #startDate, 114)
The above will return
1d 01:03:06:900
And, off course, you can use the formatting of your choice
SQL Supports datetime substraction which outputs a new datetime relative to the MIN date (for instance 1900-01-01, you can probably get this value from some system variable) This works better than DATEDIFF, because DATEDIFF will count ONE for each "datepart boundaries crossed", even if the elapsed time is less than a whole datapart. Another nice thing about this method is that it allows you to use the date formatting conversions.
If days is the (positive) number of days, like 0.5 for 12 hours, use this expression to format it as a proper duration:
CONVERT(varchar(9), FLOOR(days * 24)) + RIGHT(CONVERT(char(19), CAST(days AS datetime), 120), 6)
Excel will understands values up to 9999:59:59 when pasted. There apply a custom format: [h]:mm:ss in the English version ([u]:mm:ss for Dutch).

Format SQL DATETIME result like --> 8/25/2011 4:35 PM <-- NOT --> 2011-08-25 16:35:51.000 <--

I have a SQL Query running on SQL Server 2008 R2 that returns a DATETIME column. The current formatting returns this:
2011-08-25 16:35:51.000
and what I would rather see is this:
8/25/2011 4:35 PM
Ideas of how I can modify my SELECT statement? Here a simple statement to work with...
SELECT DtCheckIn from Ticket
Thank you, Andrew
You could do the following:
SELECT convert(varchar, getdate(), 101) + RIGHT(convert(varchar, getdate(), 100), 7)
Resources on date time formatting:
http://anubhavg.wordpress.com/2009/06/11/how-to-format-datetime-date-in-sql-server-2005/
http://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/
EDIT - it's a little hokey but it should give you what you are looking for.
Basically the first part SELECT convert(varchar, getdate(), 101) gives you your date 08/25/2011.
The second part SELECT convert(varchar, getdate(), 100) gives you your date and time in the format Aug 25 2011 5:35PM. You only want the time portion which is in the format hh:mmAM or hh:mmPM the total number of characters you want is 7. So if you change this to SELECT RIGHT(convert(varchar, getdate(), 100), 8) you will get your time 5:37PM. I used 8 in my RIGHT() to get an extra space. But now you only want your time not AM/PM so add SELECT LEFT(RIGHT(convert(varchar, getdate(), 100), 8), 6) and you will get your hh:mm.
Finally you want your AM/PM SELECT RIGHT(convert(varchar, getdate(), 100), 2) this will get you the last 2 characters.
Put it all together and you get:
SELECT convert(varchar, getdate(), 101)
+ LEFT(RIGHT(convert(varchar, getdate(), 100), 8), 6)
+ ' ' + RIGHT(convert(varchar, getdate(), 100), 2)
Which gives your final product:
08/25/2011 5:39 PM

SQL Reporting Services Daylight saving time query

I have been tasked with writting a report from MS Dynamic CRM. The report contains appointment and account information. I am using Visual Studio 2005 and SQL 2005.
The problem I have found is that if an appointment has a scheduled start date that falls after Britsh Summer Time but has a creation date before (BST) then the ScheduledStart field of the dbo.Appointment DB udates with the offset time + or - on hour. This means when I look for an appointment using reporting services and key in the time the appointment is due to start it returns no results because in the DB the time is either one hour earlier or later. I can manually amend the time for my search but this then returns the incorrect time on the report.
This will of course ony happen twice a year as appointments are only arranged a couple of weeks in advance but it is still a pain!
Is there a way using Transact SQL (or any method availbale to SSRS) I can allow for daylight saving times so even though the DB shows a Sceduled Start (dbo.Appointment.ScheduledStart) of say 11:00:00 the appointment is actually due to start at 10:00:00?
Alternately this case statement will work inside your existing query.
SELECT CASE
WHEN ([Created_Date] BETWEEN
Dateadd(yy, Datediff(yy, 0, [Scheduled_Date]), 0)
AND
Convert(DATETIME, Convert(VARCHAR(4), Year([Created_Date])) + '-03-' + Convert(VARCHAR(2), (31 - (5 * Year([Created_Date])/4 + 4) % 7)) + ' 01:00:00', 20))
AND
([ScheduledDate] BETWEEN
Convert(DATETIME, Convert(VARCHAR(4), Year([Created_Date])) + '-03-' + Convert(VARCHAR(2), (31 - (5 * Year([Created_Date])/4 + 4) % 7)) + ' 01:00:00', 20)
AND
Convert(DATETIME, Convert(VARCHAR(4), Year([Created_Date])) + '-10-' + Convert(VARCHAR(2), (31 - (5 * Year([Created_Date])/4 + 1) % 7)) + ' 00:00:00', 20))
THEN Dateadd(hh, 1, [Scheduled_Date])
ELSE [Scheduled_Date]
END AS [Scheduled_Date]
CREATE FUNCTION OffsetBST (#CreatedDateTime DATETIME,
#ScheduledDateTime DATETIME)
RETURNS DATETIME
AS
BEGIN
DECLARE #InDateYear VARCHAR(4)
DECLARE #StartDay VARCHAR(2)
DECLARE #EndDay VARCHAR(2)
DECLARE #BSTStart DATETIME
DECLARE #BSTEnd DATETIME
DECLARE #OffsetDateTime DATETIME
SET #StartDay = Convert(VARCHAR(2), (31 - (5 * Year(#CreatedDateTime)/4 + 4) % 7))
SET #EndDay = Convert(VARCHAR(2), (31 - (5 * Year(#CreatedDateTime)/4 + 1) % 7))
SET #InDateYear = Convert(VARCHAR(4), Year(#CreatedDateTime))
SET #BSTStart = Convert(DATETIME, #InDateYear + '-03-' + #StartDay + ' 01:00:00', 20)
SET #BSTEnd = Convert(DATETIME, #InDateYear + '-10-' + #EndDay + ' 00:00:00', 20)
IF (#CreatedDateTime BETWEEN DATEADD(yy, DATEDIFF(yy,0,#ScheduledDateTime), 0) AND #BSTStart)
AND (#ScheduledDateTime BETWEEN #BSTStart AND #BSTEnd)
SET #OffsetDateTime = Dateadd(hh, 1, #ScheduledDateTime)
ELSE
SET #OffsetDateTime = Dateadd(hh, 0, #ScheduledDateTime)
RETURN #OffsetDateTime
END
You can use this in your data source select statement. You will provide it with the created date and scheduled date and it will determine if the scheduled date requires a BST offset.
Like so:
SELECT A.Appointment_ID, A.Appointment_Name, dbo.OffsetBST(A.Created_Date,
A.Scheduled_Date) as Scheduled_Date
FROM AppointmentsTable as A