How to get the last day of month (SQL Server) - sql

I'm using SQL Server 2008 R2 and need to return all data with the last day of month through selected start- and enddate:
When the user select two dates something like:
Startdate: 2017-01-01
Enddate: 2017-22-02
The result have to be:
2017-31-01 AND 2017-22-02
I tried the following code, but I got the wrong result => 2017-28-02
SELECT
DATEADD(d, -1, DATEADD(mm, DATEDIFF(m, 0,'2017-22-02') + 1, 0)) AS DiffDate,
MEMBER_ID
FROM
dbo.tblOne
WHERE
DATUM >= '2017-01-01'
AND
DATUM <= '2017-22-02'
Could anyone has an idea what I'm doing wrong?!
Edit:
I expect the following result:
DiffDate | MemberID | ...
---------------------------------
2017-01-31 | CBK01
2017-01-31 | KKM05
2017-01-31 | ABC99
2017-02-22 | CBK01
2017-02-22 | KKM05
2017-02-22 | ABC99

I can suggest this method to get last date of a month of a date:
declare #date datetime = '2017-04-22';
select
dateadd(d,
-datepart(d,#date) -- count of days of #date to get back
,dateadd(m,1,#date) -- get date of next month of #date
) lastDate

I am using UDF fn_DateSerial to simplify such date operations:
--sample value
Declare #Startdate datetime
set #Startdate = GetDate()
--last day of month
select DateAdd(month, 1, dbo.fn_DateSerial(Year(#Startdate),Month(#Startdate),1,0,0,0,0)) - 1
So basically I am striping day part (making it 1st day), then 1 month is added, and finally one day is subtracted.
This UDF was inspired by vb6 function of same name.
/*
Function composes datetime from its parts - year, month, day, minute, etc.
*/
CREATE FUNCTION dbo.fn_DateSerial(#year int, #month int, #day int, #hour int, #minute int, #second int, #millisecond int) RETURNS datetime
BEGIN
DECLARE #dt datetime, #dtStr varchar(255)
--mm/dd/yyyy hh:mi:ss.mmm(24h)
SET #dtStr = ''
SET #dtStr = #dtStr + right('00'+convert(varchar(2), #month),2) + '/'
SET #dtStr = #dtStr + right('00'+convert(varchar(2), #day),2) + '/'
SET #dtStr = #dtStr + right('0000'+convert(varchar(4),#year),4)
SET #dtStr = #dtStr + ' ' + right('00'+convert(varchar(2), #hour),2) + ':'
SET #dtStr = #dtStr + right('00'+convert(varchar(2), #minute),2) + ':'
SET #dtStr = #dtStr + right('00'+convert(varchar(2), #second),2) + '.'
SET #dtStr = #dtStr + right('000'+convert(varchar(3), #millisecond),3)
SET #dt = CONVERT(datetime, #dtStr, 101)
return #dt
END

Leaving the date format out of the solution here but that is something you really need to resolve. As I understand it you simply need to find the last day of the previous month for a given date. This will do that using getdate() as the base. You could easily change getdate() to be a column in your data.
select LastDayOfPreviousMonth = dateadd(day, -1, dateadd(month, datediff(month, 0, GETDATE()), 0))

Related

Get 'Week Of' Date from Date

I have a field named 'AirDate' which is in mm/dd/yyyy format. I'd like to create a 'WeekOf' field which uses the AirDate field to give me the WeekOf date using Tuesday as the start of the week.
AirDate = 11/11/2019, WeekOf = 11/5/2019
AirDate = 11/12/2019, WeekOf = 11/12/2019
AirDate = 11/13/2019, WeekOf = 11/12/2019
etc.
What's the proper way to write the query to return the 'WeekOf' date in this format?
sql server you can use datefirst to set tuesday. See documentation
set datefirst 2
select wkTuesday = dateadd(dd, (-1)* (datepart(dw,'11/11/2019')-1), '11/11/2019')
set datefirst 7
Try this
and replace getdate() with the date that you want
SELECT dateadd(dd, (-1 * DATEPART(WEEKDAY, getdate())) + 1, getdate())
EDIT
declare #YourDate date = '19 NOv 2019'
declare #DayNo int
SET #DayNo = CASE WHEN DATEPART(WEEKDAY, #YourDate) < 3 THEN DATEPART(WEEKDAY, #YourDate) + 5 ELSE DATEPART(WEEKDAY, #YourDate) - 2 END
SELECT 'AirDate = ' + cast(#YourDate as varchar(100)) + ' , WeekOf = ' + cast( dateadd(dd, (-1 * #DayNo) + 1, #YourDate) as varchar(100))

SQL Query for getting nth Weekday date between two dates

My scenario is as below:
#StartDate = 13th of current month
#EndDate = 12th of next month.
I want to get all the date with the day-name for Mondays, Tuesdays, Wednesdays, Thursdays, Fridays, Saturdays and Sundays lying between the start and end date.
Try this:
declare #startDate datetime = '2016-01-13'
declare #endDate datetime = '2016-02-12'
;with dateRange as
(
select [Date] = dateadd(dd, 1, #startDate)
where dateadd(dd, 1, #startDate) < #endDate
union all
select dateadd(dd, 1, [Date])
from dateRange
where dateadd(dd, 1, [Date]) < #endDate
)
select [Date], datename(dw,[Date])
from dateRange
To count the number of each day as per your comment (this should be part of the question really), change the last part of James' answer to this:
select datename(dw,[Date]) as day_name, count([Date]) as number_days
from dateRange group by datename(dw,[Date]), datepart(DW,[Date])
order by datepart(DW,[Date]);
You can try something like this :
DECLARE #StartDate DATETIME
DECLARE #StartDateFixed DATETIME
DECLARE #EndDate DATETIME
DECLARE #NumberOfDays int
SET #StartDate = '2016/01/01'
SET #EndDate = '2016/01/02'
SET #NumberOfDays = DATEDIFF(DAY,#StartDate,#EndDate) + 1
SET #StartDateFixed = DATEADD(DD,-1,#StartDate)
SELECT WeekDay , COUNT(WeekDay)
FROM (
SELECT TOP (#NumberOfDays) WeekDay = DATENAME(DW , DATEADD(DAY,ROW_NUMBER() OVER(ORDER BY spt.name), #StartDateFixed))
FROM [master].[dbo].[spt_values] spt
) A
GROUP BY WeekDay
The output will be
WeekDay
------------------------------ -----------
Friday 1
Saturday 1
(2 row(s) affected)
In case if you need to get current and next date from date number specified such as 13 and 12
Current Month
DECLARE #cur_mont INT = (SELECT MONTH(GETDATE()))
Current Year
DECLARE #cur_year INT = (SELECT YEAR(GETDATE()))
Next Month
DECLARE #nxt_mont INT = (SELECT MONTH(DATEADD(month, 1, GETDATE())))
Next Month year (In case of December year change)
DECLARE #nxt_year INT = (SELECT YEAR(DATEADD(month, 1, GETDATE())))
Create start date
DECLARE #startDate DATETIME = (SELECT CAST(CAST(#cur_year AS varchar) + '-' + CAST(#cur_mont AS varchar) + '-' + CAST(13 AS varchar) AS DATETIME))
Create end date
DECLARE #endDate DATETIME = (SELECT CAST(CAST(#nxt_year AS varchar) + '-' + CAST(#nxt_mont AS varchar) + '-' + CAST(12 AS varchar) AS DATETIME))
Dates between start and end date
SELECT TOP (DATEDIFF(DAY, #startDate, #endDate) + 1)
DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate) AS Date,
DATENAME(DW, DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate)) AS Day
FROM sys.all_objects a CROSS JOIN sys.all_objects b;
DECLARE #dayStart int = 13, --The day of current month
#dayEnd int = 12, --The day of another month
#howManyMonth int = 1, --How many month to take
#dateStart date,
#dateEnd date
--Here we determine range of the dates
SELECT #dateStart = CONVERT (date,
CAST(DATEPART(Year,GETDATE()) as nvarchar(5))+ '-' +
CASE WHEN LEN(CAST(DATEPART(Month,GETDATE()) as nvarchar(5))) = 1
THEN '0'+CAST(DATEPART(Month,GETDATE()) as nvarchar(5))
ELSE CAST(DATEPART(Month,GETDATE()) as nvarchar(5)) END + '-' +
CAST (#dayStart as nvarchar(5))),
#dateEnd = CONVERT (date,
CAST(DATEPART(Year,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))+ '-' +
CASE WHEN LEN(CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))) = 1
THEN '0'+CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5))
ELSE CAST(DATEPART(Month,DATEADD(Month,#howManyMonth,GETDATE())) as nvarchar(5)) END + '-' +
CAST (#dayEnd as nvarchar(5)))
;WITH cte AS (
SELECT #dateStart as date_
UNION ALL
SELECT DATEADD(day,1,date_)
FROM cte
WHERE DATEADD(day,1,date_) <= #dateEnd
)
--Get results
SELECT DATENAME(WEEKDAY,date_) as [DayOfWeek],
COUNT(*) as [DaysCount]
FROM cte
GROUP BY DATEPART(WEEKDAY,date_),
DATENAME(WEEKDAY,date_)
ORDER BY DATEPART(WEEKDAY,date_)
OPTION (MAXRECURSION 0)
Output:
DayOfWeek DaysCount
----------- -----------
Sunday 4
Monday 4
Tuesday 4
Wednesday 5
Thursday 5
Friday 4
Saturday 4
(7 row(s) affected

Date calculation in variable

I'm doing my best to set a date variable so I can compare it later. I would like something that basically says:
If the current day-of-month is less than 11, then date is 10th of LAST month
If the current day-of-month is greater-than-or-equal-to 11, then date is 10th of THIS month
Date is 11/6/2012 expected output:
#PODate = 10/10/2012
Date is 11/16/2012 expected output:
#PODate = 11/10/2012
Currently all I have is this:
DECLARE #PODate as DATETIME
Set #PODate = Convert(varchar(8),GetDate(),1)
Any tips or help would be greatly appreciated. Thank you!!
Trying to keep it as straightforward as possible:
declare #PODate datetime
select #PODate = DATEADD(month,
DATEDIFF(month,'20010110',CURRENT_TIMESTAMP) +
CASE WHEN DATEPART(day,CURRENT_TIMESTAMP) <= 10 THEN -1 ELSE 0 END,
'20010110')
The surrounding DATEADD/DATEDIFF pair are being used to normalize the date to the 10th of the current month. We then use a small CASE expression to subtract a month if the day is less than or equal to the 10th.
Whatever solution you pick, please try to avoid ones that do it as string manipulation. The usual cause of datetime related bugs in SQL is when people treat dates as strings. Keeping the data types appropriately is usually the best way to prevent these issues.
There are, admittedly, 2 strings in my solution, but these are fixed constant strings (all that matters is that they're both for the same year and month, and the second one is for the 10th of the month) and are in an unambiguous format.
Try this: SQL Fiddle
DECLARE
#PODate as DATETIME,
#LastMonth as DateTime,
#strDate as Varchar(50)
set #PODate = '11/16/2012'
set #LastMonth = DATEADD(MONTH, -1, #PODate)
if(DAY(#PODate) < 11)
SET #strDate = CAST(MONTH(#LastMonth) AS VARCHAR)+'/10/'+CAST(YEAR(#LastMonth) AS VARCHAR)
else
SET #strDate = CAST(MONTH(#PODate) AS VARCHAR)+'/10/'+CAST(YEAR(#PODate) AS VARCHAR)
Select CAST(#strDate AS DateTime)
DECLARE #PODate date = '20121116'
SELECT CASE WHEN DATEPART(day, #PODate) < 11 THEN DATEADD(mm, DATEPART(mm, GETDATE()) - DATEPART(mm, #PODate) - 1, DATEADD(day, 10 - DATEPART(day, #PODate), #PODate))
ELSE DATEADD(mm, DATEPART(mm, GETDATE()) - DATEPART(mm, #PODate), DATEADD(day, 10 - DATEPART(day, #PODate), #PODate)) END
Demo on SQLFiddle
DECLARE #currDate DATE = dbo.GetDate()
DECLARE #day INT = day(#currDate)
DECLARE #month INT
DECLARE #year INT
DECLARE #PODate DATE
IF( #day >= 11)
BEGIN
SET #month = month(#currDate)
SET #year = year(#currDate)
END
ELSE BEGIN
SET #month = month(dateadd(m,-1,#currDate))
SET #year = year(dateadd(m,-1,#currDate))
END
SET #PODate = convert(DATE,'10-' + convert(VARCHAR,#month) + '-' + convert(VARCHAR,#year))
PRINT #PODate
if #currDate = '11-jan-2013' , #PODate will be '10-jan-2013', and if #currDate = '07-jan-2013' , #PODate will be '10-Dec-2012'

How to calculate the local datetime from a utc datetime in tsql (sql 2005)?

i want to loop over a period of time in tsql, and print the utc datetimes and our local variant.
We live in UTC +1, so i could easily add 1 hour, but in the summertime we live in UTC +2.
In C# i can create a datetime and use a method to ask for the UTC variant and vice versa.
Till now i have this:
declare #counter int
declare #localdate datetime
declare #utcdate datetime
set #counter = 0
while #counter < 100
begin
set #counter = #counter + 1
print 'The counter is ' + cast(#counter as char)
set #utcdate = DATEADD(day,#counter,GETUTCDATE())
--set #localdate = ????
print #localdate
print #utcdate
end
I've been waiting for 5 years for a more elegant solution but since one has not emerged, I'll post what I've been using thus far...
CREATE FUNCTION [dbo].[UDTToLocalTime](#UDT AS DATETIME)
RETURNS DATETIME
AS
BEGIN
--====================================================
--Set the Timezone Offset (NOT During DST [Daylight Saving Time])
--====================================================
DECLARE #Offset AS SMALLINT
SET #Offset = -5
--====================================================
--Figure out the Offset Datetime
--====================================================
DECLARE #LocalDate AS DATETIME
SET #LocalDate = DATEADD(hh, #Offset, #UDT)
--====================================================
--Figure out the DST Offset for the UDT Datetime
--====================================================
DECLARE #DaylightSavingOffset AS SMALLINT
DECLARE #Year as SMALLINT
DECLARE #DSTStartDate AS DATETIME
DECLARE #DSTEndDate AS DATETIME
--Get Year
SET #Year = YEAR(#LocalDate)
--Get First Possible DST StartDay
IF (#Year > 2006) SET #DSTStartDate = CAST(#Year AS CHAR(4)) + '-03-08 02:00:00'
ELSE SET #DSTStartDate = CAST(#Year AS CHAR(4)) + '-04-01 02:00:00'
--Get DST StartDate
WHILE (DATENAME(dw, #DSTStartDate) <> 'sunday') SET #DSTStartDate = DATEADD(day, 1,#DSTStartDate)
--Get First Possible DST EndDate
IF (#Year > 2006) SET #DSTEndDate = CAST(#Year AS CHAR(4)) + '-11-01 02:00:00'
ELSE SET #DSTEndDate = CAST(#Year AS CHAR(4)) + '-10-25 02:00:00'
--Get DST EndDate
WHILE (DATENAME(dw, #DSTEndDate) <> 'sunday') SET #DSTEndDate = DATEADD(day,1,#DSTEndDate)
--Get DaylightSavingOffset
SET #DaylightSavingOffset = CASE WHEN #LocalDate BETWEEN #DSTStartDate AND #DSTEndDate THEN 1 ELSE 0 END
--====================================================
--Finally add the DST Offset
--====================================================
RETURN DATEADD(hh, #DaylightSavingOffset, #LocalDate)
END
GO
Notes:
This is for North American servers that observer Daylight Saving Time. Please change the variable #Offest to the Timezone offset of the server running the SQL function (While NOT Observing the Daylight Savings time)...
--====================================================
--Set the Timezone Offset (NOT During DST [Daylight Saving Time])
--====================================================
DECLARE #Offset AS SMALLINT
SET #Offset = -5
As the DST rules change update them here...
--Get First Possible DST StartDay
IF (#Year > 2006) SET #DSTStartDate = CAST(#Year AS CHAR(4)) + '-03-08 02:00:00'
ELSE SET #DSTStartDate = CAST(#Year AS CHAR(4)) + '-04-01 02:00:00'
--Get DST StartDate
WHILE (DATENAME(dw, #DSTStartDate) <> 'sunday') SET #DSTStartDate = DATEADD(day, 1,#DSTStartDate)
--Get First Possible DST EndDate
IF (#Year > 2006) SET #DSTEndDate = CAST(#Year AS CHAR(4)) + '-11-01 02:00:00'
ELSE SET #DSTEndDate = CAST(#Year AS CHAR(4)) + '-10-25 02:00:00'
--Get DST EndDate
WHILE (DATENAME(dw, #DSTEndDate) <> 'sunday') SET #DSTEndDate = DATEADD(day,1,#DSTEndDate)
Cheers,
Assuming you are using SQL 2005 upwards, you can develop a SQL CLR function to take a UTC date and convert to the local date.
This link is an MSDN How-To explaining how you can create a scalar UDF in C#.
Create a SQL function along the lines of
[SqlFunction()]
public static SqlDateTime ConvertUtcToLocal(SqlDateTime utcDate)
{
// over to you to convert SqlDateTime to DateTime, specify Kind
// as UTC, convert to local time, and convert back to SqlDateTime
}
Your sample above would then become
set #localdate = dbo.ConvertUtcToLocal(#utcdate)
SQL CLR has its overheads in terms of deployment, but I feel cases like this are where it fits in best.
This solution seems too obvious.
If you can get UTC Date with GETUTCDATE() and you can get your local date with GETDATE() you have an offset that you can apply for any datetime
SELECT DATEADD(hh, DATEPART(hh, GETDATE() - GETUTCDATE()) - 24, GETUTCDATE())
this should return the local time you executed the query,
SELECT DATEADD(hh, DATEPART(hh, GETDATE() - GETUTCDATE()) - 24, N'1/14/2011 7:00:00' )
this will return 2011-01-14 02:00:00.000 because i'm in UTC +5
Unless I'm missing something?
You can use my SQL Server Time Zone Support project to convert between IANA standard time zones, as listed here.
Example:
SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')
While the question's title mentions SQL Server 2005, the question is tagged with SQL Server in general.
For SQL Server 2016 and later, you can use:
SELECT yourUtcDateTime AT TIME ZONE 'Mountain Standard Time'
A list of time zones is available with SELECT * FROM sys.time_zone_info
Here is a function (again US ONLY) but it is a bit more flexible. It will convert a UTC date to the server local time.
It starts by adjusting the appointment date based on the current offset and then adjusts based on the difference of the current offset and the offset of the date of the appointment.
CREATE FUNCTION [dbo].[fnGetServerTimeFromUTC]
(
#AppointmentDate AS DATETIME,
#DateTimeOffset DATETIMEOFFSET
)
RETURNS DATETIME
AS
BEGIN
--DECLARE #AppointmentDate DATETIME;
--SET #AppointmentDate = '2016-12-01 12:00:00'; SELECT #AppointmentDate;
--Get DateTimeOffset from Server
--DECLARE #DateTimeOffset; SET #DateTimeOffset = SYSDATETIMEOFFSET();
DECLARE #DateTimeOffsetStr NVARCHAR(34) = #DateTimeOffset;
--Set a standard DatePart value for Sunday (server configuration agnostic)
DECLARE #dp_Sunday INT = 7 - ##DATEFIRST + 1;
--2006 DST Start First Sunday in April (earliest is 04-01) Ends Last Sunday in October (earliest is 10-25)
--2007 DST Start Second Sunday March (earliest is 03-08) Ends First Sunday Nov (earliest is 11-01)
DECLARE #Start2006 NVARCHAR(6) = '04-01-';
DECLARE #End2006 NVARCHAR(6) = '10-25-';
DECLARE #Start2007 NVARCHAR(6) = '03-08-';
DECLARE #End2007 NVARCHAR(6) = '11-01-';
DECLARE #ServerDST SMALLINT = 0;
DECLARE #ApptDST SMALLINT = 0;
DECLARE #Start DATETIME;
DECLARE #End DATETIME;
DECLARE #CurrentMinuteOffset INT;
DECLARE #str_Year NVARCHAR(4) = LEFT(#DateTimeOffsetStr,4);
DECLARE #Year INT = CONVERT(INT, #str_Year);
SET #CurrentMinuteOffset = CONVERT(INT, SUBSTRING(#DateTimeOffsetStr,29,3)) * 60 + CONVERT(INT, SUBSTRING(#DateTimeOffsetStr,33,2)); --Hours + Minutes
--Determine DST Range for Server Offset
SET #Start = CASE
WHEN #Year <= 2006 THEN CONVERT(DATETIME, #Start2006 + #str_Year + ' 02:00:00')
ELSE CONVERT(DATETIME, #Start2007 + #str_Year + ' 02:00:00')
END;
WHILE #dp_Sunday <> DATEPART(WEEKDAY, #Start) BEGIN
SET #Start = DATEADD(DAY, 1, #Start)
END;
SET #End = CASE
WHEN #Year <= 2006 THEN CONVERT(DATETIME, #End2006 + #str_Year + ' 02:00:00')
ELSE CONVERT(DATETIME, #End2007 + #str_Year + ' 02:00:00')
END;
WHILE #dp_Sunday <> DATEPART(WEEKDAY, #End) BEGIN
SET #End = DATEADD(DAY, 1, #End)
END;
--Determine Current Offset based on Year
IF #DateTimeOffset >= #Start AND #DateTimeOffset < #End SET #ServerDST = 1;
--Determine DST status of Appointment Date
SET #Year = YEAR(#AppointmentDate);
SET #Start = CASE
WHEN #Year <= 2006 THEN CONVERT(DATETIME, #Start2006 + #str_Year + ' 02:00:00')
ELSE CONVERT(DATETIME, #Start2007 + #str_Year + ' 02:00:00')
END;
WHILE #dp_Sunday <> DATEPART(WEEKDAY, #Start) BEGIN
SET #Start = DATEADD(DAY, 1, #Start)
END;
SET #End = CASE
WHEN #Year <= 2006 THEN CONVERT(DATETIME, #End2006 + #str_Year + ' 02:00:00')
ELSE CONVERT(DATETIME, #End2007 + #str_Year + ' 02:00:00')
END;
WHILE #dp_Sunday <> DATEPART(WEEKDAY, #End) BEGIN
SET #End = DATEADD(DAY, 1, #End)
END;
--Determine Appointment Offset based on Year
IF #AppointmentDate >= #Start AND #AppointmentDate < #End SET #ApptDST = 1;
SET #AppointmentDate = DATEADD(MINUTE, #CurrentMinuteOffset + 60 * (#ApptDST - #ServerDST), #AppointmentDate)
RETURN #AppointmentDate
END
GO
For those stuck in SQL Server 2005 and don't want or can't use a udf - and particularly does outside of the USA - I've taken #Bobman's approach and generalized it. The following will work in the USA, Europe, New Zealand and Australia, with the caveat that not all Australian states observe DST, even states that are in the same "base" timezone. It's also easy to add DST-rules that aren't yet supported, just add a line to the #calculation values.
-- =============================================
-- Author: Herman Scheele
-- Create date: 20-08-2016
-- Description: Convert UTC datetime to local datetime
-- based on server time-distance from utc.
-- =============================================
create function dbo.UTCToLocalDatetime(#UTCDatetime datetime)
returns datetime as begin
declare #LocalDatetime datetime, #DSTstart datetime, #DSTend datetime
declare #calculation table (
frm smallint,
til smallint,
since smallint,
firstPossibleStart datetime,-- Put both of these with local non-DST time!
firstPossibleEnd datetime -- (In Europe we turn back the clock from 3 AM to 2 AM, which means it happens 2 AM non-DST time)
)
insert into #calculation
values
(-9, -2, 1967, '1900-04-24 02:00', '1900-10-25 01:00'), -- USA first DST implementation
(-9, -2, 1987, '1900-04-01 02:00', '1900-10-25 01:00'), -- USA first DST extension
(-9, -2, 2007, '1900-03-08 02:00', '1900-11-01 01:00'), -- USA second DST extension
(-1, 3, 1900, '1900-03-25 02:00', '1900-10-25 02:00'), -- Europe
(9.5,11, 1971, '1900-10-01 02:00', '1900-04-01 02:00'), -- Australia (not all Aus states in this time-zone have DST though)
(12, 13, 1974, '1900-09-24 02:00', '1900-04-01 02:00') -- New Zealand
select top 1 -- Determine if it is DST /right here, right now/ (regardless of input datetime)
#DSTstart = dateadd(year, datepart(year, getdate())-1900, firstPossibleStart), -- Grab first possible Start and End of DST period
#DSTend = dateadd(year, datepart(year, getdate())-1900, firstPossibleEnd),
#DSTstart = dateadd(day, 6 - (datepart(dw, #DSTstart) + ##datefirst - 2) % 7, #DSTstart),-- Shift Start and End of DST to first sunday
#DSTend = dateadd(day, 6 - (datepart(dw, #DSTend) + ##datefirst - 2) % 7, #DSTend),
#LocalDatetime = dateadd(hour, datediff(hour, getutcdate(), getdate()), #UTCDatetime), -- Add hours to current input datetime (including possible DST hour)
#LocalDatetime = case
when frm < til and getdate() >= #DSTstart and getdate() < #DSTend -- If it is currently DST then we just erroneously added an hour above,
or frm > til and (getdate() >= #DSTstart or getdate() < #DSTend) -- substract 1 hour to get input datetime in current non-DST timezone,
then dateadd(hour, -1, #LocalDatetime) -- regardless of whether it is DST on the date of the input datetime
else #LocalDatetime
end
from #calculation
where datediff(minute, getutcdate(), getdate()) between frm * 60 and til * 60
and datepart(year, getdate()) >= since
order by since desc
select top 1 -- Determine if it was/will be DST on the date of the input datetime in a similar fashion
#DSTstart = dateadd(year, datepart(year, #LocalDatetime)-1900, firstPossibleStart),
#DSTend = dateadd(year, datepart(year, #LocalDatetime)-1900, firstPossibleEnd),
#DSTstart = dateadd(day, 6 - (datepart(dw, #DSTstart) + ##datefirst - 2) % 7, #DSTstart),
#DSTend = dateadd(day, 6 - (datepart(dw, #DSTend) + ##datefirst - 2) % 7, #DSTend),
#LocalDatetime = case
when frm < til and #LocalDatetime >= #DSTstart and #LocalDatetime < #DSTend -- If it would be DST on the date of the input datetime,
or frm > til and (#LocalDatetime >= #DSTstart or #LocalDatetime < #DSTend) -- add this hour to the input datetime.
then dateadd(hour, 1, #LocalDatetime)
else #LocalDatetime
end
from #calculation
where datediff(minute, getutcdate(), getdate()) between frm * 60 and til * 60
and datepart(year, #LocalDatetime) >= since
order by since desc
return #LocalDatetime
end
This function looks at the difference between local and utc time at the moment it runs to determine which DST-rules to apply. It then knows whether doing datediff(hour, getutcdate(), getdate()) includes a DST hour or not and subtracts it if it does. Then it determines whether it was or will be DST at the date of the input UTC datetime and if so adds the DST hour back.
This comes with one quirk, which is that during the last hour of DST and the first hour of non-DST, the function has no way of determining which it is and assumes the latter. So regardless of input-datetime, if this codes runs during the last hour of DST it will give the wrong outcome. Which means this works 99.9886% of the time.
GETUTCDATE() just gives you the current time in UTC, any DATEADD() you do to this value will not include any daylight savings time shifts.
Your best bet is build your own UTC conversion table or just use something like this:
http://www.codeproject.com/KB/database/ConvertUTCToLocal.aspx
Bobman's answer is close, but has a couple bugs:
1) You must compare local DAYLIGHT time (instead of local STANDARD time) to the Daylight Saving End DateTime.
2) SQL BETWEEN is Inclusive so you should be comparing using ">= and <" instead of BETWEEN.
Here is a working modified version along with some test cases:
(Again, this only works for United States)
-- Test cases:
-- select dbo.fn_utc_to_est_date('2016-03-13 06:59:00.000') -- -> 2016-03-13 01:59:00.000 (Eastern Standard Time)
-- select dbo.fn_utc_to_est_date('2016-03-13 07:00:00.000') -- -> 2016-03-13 03:00:00.000 (Eastern Daylight Time)
-- select dbo.fn_utc_to_est_date('2016-11-06 05:59:00.000') -- -> 2016-11-06 01:59:00.000 (Eastern Daylight Time)
-- select dbo.fn_utc_to_est_date('2016-11-06 06:00:00.000') -- -> 2016-11-06 01:00:00.000 (Eastern Standard Time)
CREATE FUNCTION [dbo].[fn_utc_to_est_date]
(
#utc datetime
)
RETURNS datetime
as
begin
-- set offset in standard time (WITHOUT daylight saving time)
declare #offset smallint
set #offset = -5 --EST
declare #localStandardTime datetime
SET #localStandardTime = dateadd(hh, #offset, #utc)
-- DST in USA starts on the second sunday of march and ends on the first sunday of november.
-- DST was extended beginning in 2007:
-- https://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States#Second_extension_.282005.29
-- If laws/rules change, obviously the below code needs to be updated.
declare #dstStartDate datetime,
#dstEndDate datetime,
#year int
set #year = datepart(year, #localStandardTime)
-- get the first possible DST start day
if (#year > 2006) set #dstStartDate = cast(#year as char(4)) + '-03-08 02:00:00'
else set #dstStartDate = cast(#year as char(4)) + '-04-01 02:00:00'
while ((datepart(weekday,#dstStartDate) != 1)) begin --while not sunday
set #dstStartDate = dateadd(day, 1, #dstStartDate)
end
-- get the first possible DST end day
if (#year > 2006) set #dstEndDate = cast(#year as char(4)) + '-11-01 02:00:00'
else set #dstEndDate = cast(#year as char(4)) + '-10-25 02:00:00'
while ((datepart(weekday,#dstEndDate) != 1)) begin --while not sunday
set #dstEndDate = dateadd(day, 1, #dstEndDate)
end
declare #localTimeFinal datetime,
#localTimeCompare datetime
-- if local date is same day as #dstEndDate day,
-- we must compare the local DAYLIGHT time to the #dstEndDate (otherwise we compare using local STANDARD time).
-- See: http://www.timeanddate.com/time/change/usa?year=2016
if (datepart(month,#localStandardTime) = datepart(month,#dstEndDate)
and datepart(day,#localStandardTime) = datepart(day,#dstEndDate)) begin
set #localTimeCompare = dateadd(hour, 1, #localStandardTime)
end
else begin
set #localTimeCompare = #localStandardTime
end
set #localTimeFinal = #localStandardTime
-- check for DST
if (#localTimeCompare >= #dstStartDate and #localTimeCompare < #dstEndDate) begin
set #localTimeFinal = dateadd(hour, 1, #localTimeFinal)
end
return #localTimeFinal
end
I recently had to do the same thing. The trick is figuring out the offset from UTC, but it's not a hard trick. You simply use DateDiff to get the difference in hours between local and UTC. I wrote a function to take care of this.
Create Function ConvertUtcDateTimeToLocal(#utcDateTime DateTime)
Returns DateTime
Begin
Declare #utcNow DateTime
Declare #localNow DateTime
Declare #timeOffSet Int
-- Figure out the time difference between UTC and Local time
Set #utcNow = GetUtcDate()
Set #localNow = GetDate()
Set #timeOffSet = DateDiff(hh, #utcNow, #localNow)
DECLARE #localTime datetime
Set #localTime = DateAdd(hh, #timeOffset, #utcDateTime)
-- Check Results
return #localTime
End
GO
This does have on crucial short coming: If a time zone uses a fractional offset, such as Nepal which is GMT+5:45, this will fail because this only deals with whole hours. However, it should fit your needs just fine.

How to determine the number of days in a month in SQL Server?

I need to determine the number of days in a month for a given date in SQL Server.
Is there a built-in function? If not, what should I use as the user-defined function?
In SQL Server 2012 you can use EOMONTH (Transact-SQL) to get the last day of the month and then you can use DAY (Transact-SQL) to get the number of days in the month.
DECLARE #ADate DATETIME
SET #ADate = GETDATE()
SELECT DAY(EOMONTH(#ADate)) AS DaysInMonth
You can use the following with the first day of the specified month:
datediff(day, #date, dateadd(month, 1, #date))
To make it work for every date:
datediff(day, dateadd(day, 1-day(#date), #date),
dateadd(month, 1, dateadd(day, 1-day(#date), #date)))
Most elegant solution: works for any #DATE
DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,#DATE),0)))
Throw it in a function or just use it inline. This answers the original question without all the extra junk in the other answers.
examples for dates from other answers:
SELECT DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,'1/31/2009'),0))) Returns 31
SELECT DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,'2404-feb-15'),0))) Returns 29
SELECT DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,'2011-12-22'),0))) Returns 31
--Last Day of Previous Month
SELECT DATEPART(day, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)))
--Last Day of Current Month
SELECT DATEPART(day, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0)))
--Last Day of Next Month
SELECT DATEPART(day, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+2,0)))
Personally though, I would make a UDF for it if there is not a built in function...
I would suggest:
SELECT DAY(EOMONTH(GETDATE()))
This code gets you the number of days in current month:
SELECT datediff(dd,getdate(),dateadd(mm,1,getdate())) as datas
Change getdate() to the date you need to count days for.
--- sql server below 2012---
select day( dateadd(day,-1,dateadd(month, 1, convert(date,'2019-03-01'))))
-- this for sql server 2012--
select day(EOMONTH(getdate()))
Solution 1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]
Solution 2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
You do need to add a function, but it's a simple one. I use this:
CREATE FUNCTION [dbo].[ufn_GetDaysInMonth] ( #pDate DATETIME )
RETURNS INT
AS
BEGIN
SET #pDate = CONVERT(VARCHAR(10), #pDate, 101)
SET #pDate = #pDate - DAY(#pDate) + 1
RETURN DATEDIFF(DD, #pDate, DATEADD(MM, 1, #pDate))
END
GO
SELECT Datediff(day,
(Convert(DateTime,Convert(varchar(2),Month(getdate()))+'/01/'+Convert(varchar(4),Year(getdate())))),
(Convert(DateTime,Convert(varchar(2),Month(getdate())+1)+'/01/'+Convert(varchar(4),Year(getdate()))))) as [No.of Days in a Month]
select datediff(day,
dateadd(day, 0, dateadd(month, ((2013 - 1900) * 12) + 3 - 1, 0)),
dateadd(day, 0, dateadd(month, ((2013 - 1900) * 12) + 3, 0))
)
Nice Simple and does not require creating any functions Work Fine
You need to create a function, but it is for your own convenience. It works perfect and I never encountered any faulty computations using this function.
CREATE FUNCTION [dbo].[get_days](#date datetime)
RETURNS int
AS
BEGIN
SET #date = DATEADD(MONTH, 1, #date)
DECLARE #result int = (select DAY(DATEADD(DAY, -DAY(#date), #date)))
RETURN #result
END
How it works: subtracting the date's day number from the date itself gives you the last day of previous month. So, you need to add one month to the given date, subtract the day number and get the day component of the result.
select add_months(trunc(sysdate,'MM'),1) - trunc(sysdate,'MM') from dual;
I upvoted Mehrdad, but this works as well. :)
CREATE function dbo.IsLeapYear
(
#TestYear int
)
RETURNS bit
AS
BEGIN
declare #Result bit
set #Result =
cast(
case when ((#TestYear % 4 = 0) and (#testYear % 100 != 0)) or (#TestYear % 400 = 0)
then 1
else 0
end
as bit )
return #Result
END
GO
CREATE FUNCTION dbo.GetDaysInMonth
(
#TestDT datetime
)
RETURNS INT
AS
BEGIN
DECLARE #Result int
DECLARE #MonthNo int
Set #MonthNo = datepart(m,#TestDT)
Set #Result =
case #MonthNo
when 1 then 31
when 2 then
case
when dbo.IsLeapYear(datepart(yyyy,#TestDT)) = 0
then 28
else 29
end
when 3 then 31
when 4 then 30
when 5 then 31
when 6 then 30
when 7 then 31
when 8 then 31
when 9 then 30
when 10 then 31
when 11 then 30
when 12 then 31
end
RETURN #Result
END
GO
To Test
declare #testDT datetime;
set #testDT = '2404-feb-15';
select dbo.GetDaysInMonth(#testDT)
here's another one...
Select Day(DateAdd(day, -Day(DateAdd(month, 1, getdate())),
DateAdd(month, 1, getdate())))
I know this question is old but I thought I would share what I'm using.
DECLARE #date date = '2011-12-22'
/* FindFirstDayOfMonth - Find the first date of any month */
-- Replace the day part with -01
DECLARE #firstDayOfMonth date = CAST( CAST(YEAR(#date) AS varchar(4)) + '-' +
CAST(MONTH(#date) AS varchar(2)) + '-01' AS date)
SELECT #firstDayOfMonth
and
DECLARE #date date = '2011-12-22'
/* FindLastDayOfMonth - Find what is the last day of a month - Leap year is handled by DATEADD */
-- Get the first day of next month and remove a day from it using DATEADD
DECLARE #lastDayOfMonth date = CAST( DATEADD(dd, -1, DATEADD(mm, 1, FindFirstDayOfMonth(#date))) AS date)
SELECT #lastDayOfMonth
Those could be combine to create a single function to retrieve the number of days in a month if needed.
SELECT DAY(SUBDATE(ADDDATE(CONCAT(YEAR(NOW()), '-', MONTH(NOW()), '-1'), INTERVAL 1 MONTH), INTERVAL 1 DAY))
Nice 'n' Simple and does not require creating any functions
Mehrdad Afshari reply is most accurate one, apart from usual this answer is based on formal mathematical approach given by Curtis McEnroe in his blog https://cmcenroe.me/2014/12/05/days-in-month-formula.html
DECLARE #date DATE= '2015-02-01'
DECLARE #monthNumber TINYINT
DECLARE #dayCount TINYINT
SET #monthNumber = DATEPART(MONTH,#date )
SET #dayCount = 28 + (#monthNumber + floor(#monthNumber/8)) % 2 + 2 % #monthNumber + 2 * floor(1/#monthNumber)
SELECT #dayCount + CASE WHEN #dayCount = 28 AND DATEPART(YEAR,#date)%4 =0 THEN 1 ELSE 0 END -- leap year adjustment
To get the no. of days in a month we can directly use Day() available in SQL.
Follow the link posted at the end of my answer for SQL Server 2005 / 2008.
The following example and the result are from SQL 2012
alter function dbo.[daysinm]
(
#dates nvarchar(12)
)
returns int
as
begin
Declare #dates2 nvarchar(12)
Declare #days int
begin
select #dates2 = (select DAY(EOMONTH(convert(datetime,#dates,103))))
set #days = convert(int,#dates2)
end
return #days
end
--select dbo.daysinm('08/12/2016')
Result in SQL Server SSMS
(no column name)
1 31
Process:
When EOMONTH is used, whichever the date format we use it is converted into DateTime format of SQL-server. Then the date output of EOMONTH() will be 2016-12-31 having 2016 as Year, 12 as Month and 31 as Days.
This output when passed into Day() it gives you the total days count in the month.
If we want to get the instant result for checking we can directly run the below code,
select DAY(EOMONTH(convert(datetime,'08/12/2016',103)))
or
select DAY(EOMONTH(convert(datetime,getdate(),103)))
for reference to work in SQL Server 2005/2008/2012, please follow the following external link ...
Find No. of Days in a Month in SQL
DECLARE #date DATETIME = GETDATE(); --or '12/1/2018' (month/day/year)
SELECT DAY(EOMONTH ( #date )) AS 'This Month';
SELECT DAY(EOMONTH ( #date, 1 )) AS 'Next Month';
result:
This Month
31
Next Month
30
DECLARE #m int
SET #m = 2
SELECT
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, +#m -1, 0)),
DATEADD(DAY, 0, DATEADD(m,+ #m, 0))
) AS [Days in Month]
RETURN day(dateadd(month, 12 * #year + #month - 22800, -1))
select day(dateadd(month, 12 * year(date) + month(date) - 22800, -1))
A cleaner way of implementing this is using the datefromparts function to construct the first day of the month, and calculate the days from there.
CREATE FUNCTION [dbo].[fn_DaysInMonth]
(
#year INT,
#month INT
)
RETURNS INT
AS
BEGIN
IF #month < 1 OR #month > 12 RETURN NULL;
IF #year < 1753 OR #year > 9998 RETURN NULL;
DECLARE #firstDay DATE = datefromparts(#year, #month, 1);
DECLARE #lastDay DATE = dateadd(month, 1, #firstDay);
RETURN datediff(day, #firstDay, #lastDay);
END
GO
Similarily, you can calculate the days in a year:
CREATE FUNCTION [dbo].[fn_DaysInYear]
(
#year INT
)
RETURNS INT
AS
BEGIN
IF #year < 1753 OR #year > 9998 RETURN NULL;
DECLARE #firstDay DATE = datefromparts(#year, 1, 1);
DECLARE #lastDay DATE = dateadd(year, 1, #firstDay);
RETURN datediff(day, #firstDay, #lastDay);
END
GO
use SQL Server EOMONTH Function nested with day to get last day of month
select Day(EOMONTH('2020-02-1')) -- Leap Year returns 29
select Day(EOMONTH('2021-02-1')) -- returns 28
select Day(EOMONTH('2021-03-1')) -- returns 31
For any date
select DateDiff(Day,#date,DateAdd(month,1,#date))
select first_day=dateadd(dd,-1*datepart(dd,getdate())+1,getdate()),
last_day=dateadd(dd,-1*datepart(dd,dateadd(mm,1,getdate())),dateadd(mm,1,getdate())),
no_of_days = 1+datediff(dd,dateadd(dd,-1*datepart(dd,getdate())+1,getdate()),dateadd(dd,-1*datepart(dd,dateadd(mm,1,getdate())),dateadd(mm,1,getdate())))
replace any date with getdate to get the no of months in that particular date
DECLARE #Month INT=2,
#Year INT=1989
DECLARE #date DateTime=null
SET #date=CAST(CAST(#Year AS nvarchar) + '-' + CAST(#Month AS nvarchar) + '-' + '1' AS DATETIME);
DECLARE #noofDays TINYINT
DECLARE #CountForDate TINYINT
SET #noofDays = DATEPART(MONTH,#date )
SET #CountForDate = 28 + (#noofDays + floor(#noofDays/8)) % 2 + 2 % #noofDays + 2 * floor(1/#noofDays)
SET #noofDays= #CountForDate + CASE WHEN #CountForDate = 28 AND DATEPART(YEAR,#date)%4 =0 THEN 1 ELSE 0 END
PRINT #noofDays
DECLARE #date nvarchar(20)
SET #date ='2012-02-09 00:00:00'
SELECT DATEDIFF(day,cast(replace(cast(YEAR(#date) as char)+'-'+cast(MONTH(#date) as char)+'-01',' ','')+' 00:00:00' as datetime),dateadd(month,1,cast(replace(cast(YEAR(#date) as char)+'-'+cast(MONTH(#date) as char)+'-01',' ','')+' 00:00:00' as datetime)))
simple query in SQLServer2012 :
select day(('20-05-1951 22:00:00'))
i tested for many dates and it return always a correct result