How to get the year of a date, depending on the weeknumber - sql-server

I know how to get the year out of a date. But how can I get the year of a date, depending on the week number of that date?
Example: 28-12-2015 has week number 1 (ISO_WEEK). In this case, the week number does not belong to 2015, but 2016. Also: 1-1-2016: Weeknumber 53, so 1-1-2016 does not belong to 2016, but to 2015.
Is there a function to do this easily in SQL?
No there isn't, but I created it myself.
CREATE FUNCTION [dbo].[ISO_YEAR](#date DATETIME)
returns SMALLINT
AS
BEGIN
DECLARE #ISO_YEAR SMALLINT = CASE
WHEN DATEPART(ISO_WEEK, #date)=1
AND MONTH(#date)=12 THEN YEAR(#date)+1
WHEN DATEPART(ISO_WEEK, #date)=53
AND MONTH(#date)=1 THEN YEAR(#date)-1
WHEN DATEPART(ISO_WEEK, #date)=52
AND Month(#date)=1 THEN YEAR(#date)-1
ELSE YEAR(#date)
END;
RETURN #ISO_YEAR;
END;

As per your question it seems you have two different dates and you are not able to identify in which year this date belongs to.
I have a small example here:
DECLARE #Date datetime, #Date1 datetime
SET #Date = '2015-12-28'
SET #Date1 = '2016-01-01'
SELECT CASE WHEN datepart(Wk , #Date ) < datepart(Wk , #Date1 ) THEN (datepart(YEAR , #Date1 ) + 1) ELSE (datepart(YEAR , #Date1 )) END AS GETYEAR
OUTPUT:
2016
I storngly recommend that this is not a correct way to know the year. it is not a +/- 1 logic here. there are max 53 weeks every year and you should always get the year from date which you have.
refer fiddle:
http://sqlfiddle.com/#!3/9eecb7/4510

Related

Find End Date of month (Exclude Sunday and Saturday)

I have wrote a script (from the internet) but it's not working because of some logical error I guess.
ALTER FUNCTION EstDateCheck (#CoustDesireddate varchar(10))
RETURNS varchar(20)
AS
BEGIN
declare #testDate Date = (SELECT STUFF(#CoustDesireddate, 4, 0, '01/'));
declare #lastWorkDay date = iif(DATEPART(WEEKDAY, EOMONTH(#testDate)) <= 5
,EOMONTH(#testDate)
,DATEADD(DAY, -(7-DATEPART(WEEKDAY, EOMONTH(#testDate))),EOMONTH(#testDate)))
RETURN #lastWorkDay
END
This is how I execute
select dbo.EstDateCheck('07/2022')
I would, honestly, consider creating a calendar table. There are plenty of examples out there, so I'm not going to cover how to create one; plus what columns you need vary.
What you will have, however, is a column to denote if the day is a weekday (and probably a working day), as well as what month and year it belongs to. As a result you can then simply get the last weekday with the following query:
SELECT MAX(CalendarDate)
FROM dbo.CalendarTable
WHERE CalendarYear = 2022
AND CalendarMonth = 7
AND Weekday = 1;
If you therefore wanted to do this as a function, you could use an inline table value function:
CREATE FUNCTION dbo.MonthLastWeekday (#Year int, #Month int)
RETURNS table
AS RETURN
SELECT MAX(CalendarDate) AS LastWeekday
FROM dbo.CalendarTable
WHERE CalendarYear = #Year
AND CalendarMonth = #Month
AND Weekday = 1;
And then you would call the function as follows:
SELECT LastWeekday
FROM dbo.MonthLastWeekday(#Year,#Month);

How to get date from yyyy-mm-dd to yyyy-mm-dd in SQL?

I want to get date from yyyy-mm-dd to yyyy-mm-dd in SQL.
Example: I have two parameter #startdate : 2015-12-28 and #enddate : 2016-01-02, and database in SQLServer, datatype is varchar(10)
DATE_ORDER
28-12-2015
30-12-1996
29-12-2016
30-12-1997
24-12-2015
27-12-1993
03-01-2016
01-01-1992
02-01-2016
etc...
Ok,now I want to get data from #startdate : 2015-12-28 and #enddate : 2016-01-02. I use SELECT * FROM TABLE_X WHERE DATE_ORDER >= #startdate AND DATE_ORDER <= #enddate . But the results are not what I expected. Here are the results I want
28-12-2015
30-12-1996
29-12-2016
30-12-1997
01-01-1992
02-01-2016
I think to solve this problem, I need to do two things :
First, get date range from #startdate to #enddate , in here 28/12/2015, 29/12/2015, 30/12/2015, 31/12/2015, 01/01/2016, 02/01/2016.
The second: get the date in database same in range 28/12, 29/12, 30/12, 31/12, 01/01, 02/01, ignoring the year.
Can you give me some ideas about this ?
Your actual format is "105-italian" find details here.
You can convert your existing VARCHAR(10)-values with this line to real datetime
SELECT CONVERT(DATETIME,YourColumn,105)
Next thing to know is, that you should not use BETWEEN but rather >=StartDate AND < NakedDateOfTheFollowingDay to check date ranges
So to solve your need Get date-range from 2015-12-28 to 2016-01-02 you might do something like this:
DECLARE #Start DATETIME={d'2015-12-28'};
DECLARE #End DATETIME={d'2016-01-02'};
SELECT *
FROM YourTable
WHERE CONVERT(DATETIME,YourDateColumn,105)>=#Start AND CONVERT(DATETIME,YourDateColumn,105)<#End+1
Attention Be aware, that the conversion lets your expression be not sargable. No index will be used.
Better was to store your date as correctly typed data to avoid conversions...
Try this query
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
So, after the 2nd or 3rd edit, it slowly becomes clear, what you want (i hope).
So you REALLY WANT to get the dates with the year beeing ignored.
As someone pointed out already, date-values are stored internally not as string, but as internal datatype date (whatever that is in memory, i don't know).
If you want to compare DATES, you cannot do that with ignorance of any part. If you want to, you have to build a NEW date value of day and month of given row and a hard coded year (2000 or 1 or whatever) for EVERY row.
SELECT * FROM TABLE_X WHERE convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) >= #startdate AND convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) <= #enddate
If your startdate and enddate go OVER sylvester, you have to do 2 queries, on from startdate to 1231, one from 0101 to enddate.

Convert date in millisecond to date

In my table, I have a column that contains date in millisecond like this:
table a
dateinmili
1440301846096 //first six month date
1443589721039 //second six month date
I use that for my Android device and it works fine. When I want to use this time in a PROCEDURE in SQL Server and convert this time to human time (understandable for human) and date I have a problem.
I'm in Iran which uses UTC time in first six Persian date month 4.30 and 3.30 in second six month.
For convert date in PROCEDURE I use this code:
CONVERT(nVARCHAR(10),DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')),8) as date
DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')) as time
and here is my problem:
When I convert date in second six month and date registered in first six month of year, I get 1 hour difference between real time and converted time. I know that is because
DATEDIFF(mi, GETUTCDATE(), GETDATE())
method which return different between UTC time and local time when ever its called (in my example return 3:30 not 4:30 ) but I don't know how can I fix that?
I can add column which contain current UTC time but I am looking for another way.
update
I see this question and it's not my problem convert long to date.
My problem is in my country UTC time is not constant in whole year and change between 3.30 and 4.30, for example I have date registered in first six month (Persian six month) like 1440271800000 and convert it now which we are in second six month (Persian six month) and use this code for convert.
declare #unixTS bigint
set #unixTS = 1440271800000
select dateadd(ms, #unixTS%(3600*24*1000),
dateadd(day, #unixTS/(3600*24*1000), '1970-01-01 03:30:00.0')
)
I get this
2015-08-22 23:00:00.000
but it's not right date; the right date is:
2015-08-23 00:00:00.000
because when time registered UTC was 4.30 and not 3.30 but know when I convert
it UTC is 3.30.
I wish if there was a method in SQL which return past UTC time different; I mean put a date to that and return that time different between local time and gmt time my problem solved.
I hope you understand my problem.
In the US we have Daylight Savings Time in the summer, in most areas that means that we are also not fixed offset from UTC. Older versions of MS Dynamics CRM used to save everything in UTC, so when we wanted to export data in local time, we had a similar exercise. I created a set of SQL functions that would take the standard GMT offset and the datetime to I wanted to convert and figure out whether to apply the standard or DST offset and return the local datetime. If your offset follows a set of rules, then you can modify this:
CREATE function [dbo].[DC_GMTtoLocal]
(#OrigGMT datetime,
#StandardOffset int)
RETURNS datetime
AS
BEGIN
DECLARE #RevDate datetime
set #RevDate = CASE dbo.DC_DaylightSavingTime_IsInEffect(#OrigGMT)
WHEN 1 THEN DATEADD(hour, - #StandardOffset + 1, #OrigGMT) -- in DST
ELSE DATEADD(hour, - #StandardOffset, #OrigGMT) -- Not In DST
END
return #RevDate
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[DC_DaylightSavingTime_IsInEffect]
(#DtTime datetime)
RETURNS tinyint
AS
BEGIN
DECLARE #DLSStart datetime
, #DLSEnd datetime
, #DLSActive tinyint
SET #DLSActive = 0
If DATEADD(YEAR,3,GETDATE()) > #DtTime
BEGIN
SET #DLSStart =(SELECT dbo.DC_GetDaylightSavingsTimeStart(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
SET #DLSEnd =(SELECT dbo.DC_GetDaylightSavingsTimeEnd(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
IF #DtTime BETWEEN #DLSStart AND #DLSEnd
BEGIN
SET #DLSActive = 1
END
--SET #DLSActive = 0
END
RETURN #DLSActive
END
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeStart]
(#Year varchar(4))
RETURNS smalldatetime
as
--Start date: We evaluate the day of the week corresponding to the first day of the month and find the second Sunday of March using a Case statement
begin
declare #DTSStartWeek smalldatetime, #DTSEndWeek smalldatetime
set #DTSStartWeek = '03/01/' + convert(varchar,#Year)
return case datepart(dw,#DTSStartWeek)
when 1 then
dateadd(hour,170,#DTSStartWeek)
when 2 then
dateadd(hour,314,#DTSStartWeek)
when 3 then
dateadd(hour,290,#DTSStartWeek)
when 4 then
dateadd(hour,266,#DTSStartWeek)
when 5 then
dateadd(hour,242,#DTSStartWeek)
when 6 then
dateadd(hour,218,#DTSStartWeek)
when 7 then
dateadd(hour,194,#DTSStartWeek)
end
end
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeEnd]
(#Year varchar(4))
RETURNS smalldatetime
as
-- End date: We evaluate the day of the week corresponding to the first day of the month and find the first Sunday of March using a Case statement
begin
declare #DTSEndWeek smalldatetime
set #DTSEndWeek = '11/01/' + convert(varchar,#Year)
return case datepart(dw,dateadd(week,1,#DTSEndWeek))
when 1 then
dateadd(hour,2,#DTSEndWeek)
when 2 then
dateadd(hour,146,#DTSEndWeek)
when 3 then
dateadd(hour,122,#DTSEndWeek)
when 4 then
dateadd(hour,98,#DTSEndWeek)
when 5 then
dateadd(hour,74,#DTSEndWeek)
when 6 then
dateadd(hour,50,#DTSEndWeek)
when 7 then
dateadd(hour,26,#DTSEndWeek)
end
end
GO

How to calculate difference between two dates in workdays

I need to calculate difference in workdays between two dates. Is there a built in function for this in SQL Server? Can someone please provide an example on how to do this?
Here is something I wrote quickly. Just encapsulate it into a function or whatever you need.
declare #StartDate datetime
declare #EndDate datetime
declare #TotalDiff int
declare #NumberOfWeekends int
SET #StartDate = '3/12/2013'
SET #EndDate = '3/22/2013'
SET #NumberOfWeekends = 0
SET #TotalDiff = DATEDIFF(d,#StartDate, #EndDate)
If #TotalDiff > 7
SET #NumberOfWeekends = #TotalDiff / 7
else if DATEPART(dd, #EndDate) < DATEPART(DD, #StartDate)
SET #NumberOfWeekends = 1
select (#TotalDiff - 2*#NumberOfWeekends) as TotalWorkDays
No, there is nothing built in to SQL Server to directly give you number of working days between two dates, however there are a few built-in functions which will enable you to write one.
Firstly, a few caveats
The world cannot agree what a "Working Day" is. For most of us it's Saturday and Sunday. For most of the Middle East it's Friday & Saturday (with Sunday being a normal working day)
The world most certainly cannot agree on what constitutes a public holiday, which are almost always considered non-working days.
You have not specified how you would like to handle these cases so lets make some assumptions:
Saturday and Sunday will be non-working days
Public holidays will not be taken into acount
Now, determining if a particular days is saturday or sunday in sql is easy, given a #date of type DateTime:
IF DATENAME(dw,#date) IN ('Saturday','Sunday')
With that in mind, given a start and end date, you can just count incrementally from #startDate to #endDate
DECLARE #startDate DATETIME = '2013-01-01'
DECLARE #endDate DATETIME = '2013-01-20'
DECLARE #currDate DATETIME = #startDate
DECLARE #numDays INT = 0
WHILE #currDate<#endDate
BEGIN
IF DATENAME(dw,#currDate) NOT IN ('Saturday','Sunday')
SET #numDays = #numDays + 1
SET #currDate = DATEADD(day,1,#currDate)
END
SELECT #numDays
Note: This is non-inclsive so wont count #endDate. You could change it to be inclusive by changing WHILE #currDate<#endDate to WHILE #currDate<=#endDate
My solution does not count the #EndDate, so if you need to change that, just add 1 to #d2.
First, I calculate the number of days from an "initial" day (which happens to be 1/1/1900, a Monday) to #StartDate and #EndDate:
DECLARE #d1 int = DATEDIFF(Day, 0, #StartDate);
DECLARE #d2 int = DATEDIFF(Day, 0, #EndDate);
Then, the total number of days between #StartDate and #EndDate is:
#d2 - #d1
From this, I substract the number of Sundays and the number of Saturdays in the interval, each calculated as a difference simlar to the total days, but now for whole weeks (7 days). To get the number of whole weeks, I use integer division by 7 and the fact that the "initial" day (0) is a Monday. The number of Sundays in the interval is
#d2/7 - #d1/7
and the number of Saturdays is
(#d2+1)/7 - (#d1+1)/7
Putting all together, my solution is:
DECLARE #StartDate DATETIME = '20180101'
DECLARE #EndDate DATETIME = '20180201'
DECLARE #d1 int = DATEDIFF(Day, 0, #StartDate)
DECLARE #d2 int = DATEDIFF(Day, 0, #EndDate)
SELECT #d2 - #d1 - (#d2/7 - #d1/7) - ((#d2+1)/7 - (#d1+1)/7) AS workdays

In TSQL, how would you convert from an int to a datetime and give the age?

What would be the sql for the following,
I have a date of birth in an int field,
ie YYYYMMDD = 19600518
I would like to get the age.
None of the other answers are actually calculating age. They're calculating the number of year boundaries and not taking the birthday into account.
To calculate the age, you'll need to do something like this:
DECLARE #d DATETIME
SET #d = CONVERT(DATETIME, CONVERT(VARCHAR(8), 19600518), 112)
SELECT DATEDIFF(year, #d, GETDATE())
- CASE WHEN DATEADD(year, DATEDIFF(year, #d, GETDATE()), #d) <= GETDATE()
THEN 0 ELSE 1 END AS Age
Most of the other answers are not calculating age - just whole years (e.g. Jan 1 2009 is one "year" after Dec 31 2008). Thus, if you use most of the calculations on this page, you will return an incorrect age for half of the year, on average. Luke is the only person who has seen this but his answer strikes me as too complicated - there is an easier way:
Select CAST(DATEDIFF(hh, [birthdate], GETDATE()) / 8766 AS int) AS Age
(NOTE: Thanks go to 'Learning' for making a great catch on my original algorithm - this is a revision that uses hours instead of days)
Because the rounding here is very granular, this is almost perfectly accurate for every day of every year. The exceptions are so convoluted that they are almost humorous: every fourth year the age returned will be one year too young if we A) ask for the age before 6:00 AM, B) on the person's birthday and C) their birthday is after February 28th. Of course, depending on what time someone was born this might 'technically' be correct! In my setting, this is a perfectly acceptable compromise.
Here is a loop that prints out ages to show that this works.
Declare #age int;
Declare #BirthDate datetime;
Declare #Year int;
Set #Year = 2008;
WHILE (#Year > 1930)
BEGIN
-- Put today's date where you see '-03-18'
SET #BirthDate = CAST(Cast(#Year as varchar(4)) + '-03-18' AS DATETIME)
SELECT #age=CAST(DATEDIFF(hh, #BirthDate, GETDATE()) / 8766 AS int);
Print Cast(#Year as varchar) + ' Age: ' + Cast(#age as varchar);
Set #Year = #Year - 1;
END;
Finally, this is the version that will also convert Paul's integer date to a real date:
CAST(DATEDIFF(hh, Convert(Datetime, Convert(varchar(8), [birthdate]), 112), GETDATE()) / 8766 AS int) AS Age
DECLARE #dateSt VARCHAR(8)
DECLARE #startDt DATETIME
-- Set the start date string
SET #dateSt = '19600518'
-- Make it a DATETIME (the ISO way)
SET #startDt = CAST(SUBSTRING(#dateSt, 1, 4) + '-' +
SUBSTRING(#dateSt, 5, 2) + '-' +
SUBSTRING(#dateSt, 7, 2) AS DATETIME)
-- Age in Days
SELECT DATEDIFF(D, #startDt, getdate())
Age in years :
select datediff(YY, convert(datetime, convert(varchar, 19600518)), getdate())
[EDIT]
-- I forgot to declare the variables
declare #birthday datetime;
set #birthday = convert(datetime,convert(varchar, 19600518), 112);
declare #datetoday datetime;
set #datetoday = getdate();
select
(
CASE
WHEN dateadd(year, datediff (year, #birthday, #datetoday), #birthday) <= #datetoday
THEN datediff (year, #birthday, #datetoday)
ELSE datediff (year, #birthday, #datetoday) - 1
END) as age;
Here's a one-liner way to do it:
CONVERT(DATETIME, CONVERT(VARCHAR(8),19600518), 112)
But beware! This relies on T-SQL, and probably won't work in other SQL environments.
Please note that the "style" of 112 is simply the "ISO" date format of yyyymmdd. (Something I found in some CONVERT documentation.)
This is a reason why you should NOT ever store dates as anything except a datetime datatype. The best fix is to change your datatype and convert all the dates once (wouldn't be surprised if there are few invalid ones in there either). then you never have to do these workarounds again.
I worked it out and got same as #Learning
select dob, datediff(year, convert(datetime, convert(varchar(8),[dob])) ,getdate()) as age
from [mytable]
where IsDate(convert(varchar(8),[dob])) = 1
NB. I needed the IsDate as well as there were some invalid dates in the data.
Edit. Here is an article from SQLServerCentral on calculating age.

Resources