Thinking outside the box

Patron Saint of Lost Yaks
posts - 203, comments - 734, trackbacks - 4

My Links

Advertisement

News

Archives

Post Categories

Fastest LeapYear checker?

CREATE FUNCTION dbo.fnIsLeapYear
(
            @Year SMALLINT
)
RETURNS BIT
AS
BEGIN
            RETURN      CASE DATEPART(DAY, DATEADD(YEAR, @Year - 1904, '19040229'))
                                     WHEN 29 THEN 1
                                     ELSE 0
                         END
END

Print | posted on Wednesday, February 25, 2009 3:11 PM | Filed Under [ Optimization SQL Server 2008 Algorithms SQL Server 2005 SQL Server 2000 ]

Feedback

Gravatar

# re: Fastest LeapYear checker?

Here's one that is very similar. The difference in timing is not much.

CREATE FUNCTION Is_Leap_Year(@Year VARCHAR(4))
RETURNS BIT
AS
BEGIN
RETURN MONTH(CAST(@Year + '/2/28' AS DATETIME) + 1) - 3
END;
GO

2/25/2009 5:44 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

Should that be

DATEADD(YEAR, @Year - 1904, ...

If @Year == 1900, then you have
DATEADD(YEAR, 0, '19040229'))

which will tell you that 1900 was a leap year, when it was not.
2/25/2009 7:04 PM | James Curran
Gravatar

# re: Fastest LeapYear checker?

Here's another version that starts with an input date (and it does work for 1900 <g>):

DECLARE @start_date DATETIME
SET @start_date = '19000201'

SELECT
CASE DATEPART(dd, DATEADD(mm, 2, DATEADD(yy, DATEDIFF(yy, 0, @start_date), 0)) - 1)
WHEN 29 THEN 1
ELSE 0
END
2/25/2009 7:29 PM | Adam Machanic
Gravatar

# re: Fastest LeapYear checker?

Yes, it should read @Year - 1904.
Thank you for the correction.
2/25/2009 9:45 PM | Peso
Gravatar

# re: Fastest LeapYear checker?

I thought of that too Adam, but is has 4 functions. My suggestion has only two functions.

Here is another suggestion with 3 functions.

CREATE FUNCTION dbo.fnIsLeapYear2
(
@Year SMALLINT
)
RETURNS BIT
AS
BEGIN
RETURN CASE DATEPART(MONTH, DATEADD(DAY, 59, DATEADD(YEAR, @Year - 1900, 0)))
WHEN 3 THEN 0
ELSE 1
END
END
2/25/2009 9:51 PM | Peso
Gravatar

# re: Fastest LeapYear checker?

A slight modification of my earlier post. There is a day of year function, why not use it:

CREATE FUNCTION Is_Leap_Year(@Year VARCHAR(4))
RETURNS BIT
AS
BEGIN
RETURN DATEPART(y, CAST(@Year + '/3/1' AS DATETIME)) - 60;
END;
GO
2/25/2009 10:40 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

Minor correction. No reason for the explicit CAST, DATEPART will handle that for us:

CREATE FUNCTION Is_Leap_Year(@Year VARCHAR(4))
RETURNS BIT
AS
BEGIN
RETURN DATEPART(y, @Year + '/3/1') - 60;
END;
2/25/2009 10:43 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

Hi , this is another one:-)
create function dbo.fn_IsLeapYear (@year int)
returns bit
as
begin
return(select case datepart(mm, dateadd(dd, 1, cast((cast(@year as varchar(4)) + '0228') as datetime)))
when 2 then 1
else 0
end)
end
go
2/26/2009 7:27 AM | Uri Dimant
Gravatar

# re: Fastest LeapYear checker?

Here's a completely different method. I have no idea if it's faster or quicker.

CREATE FUNCTION Is_Leap_Year(@Year int)
RETURNS bit
AS
BEGIN
RETURN CASE
WHEN @Year % 400 = 0
OR ( @Year % 4 = 0
AND @Year % 100 <> 0) THEN 1
ELSE 0
END;
END;
2/26/2009 10:07 AM | Hugo Kornelis
Gravatar

# re: Fastest LeapYear checker?

I would not concentrate on 2/29 , we now that every forth year is leap year so why not just check the @year % 4?
2/26/2009 10:45 AM | Remote DBA
Gravatar

# re: Fastest LeapYear checker?

...and yet another one:

CREATE FUNCTION dbo.Is_Leap_Year(@Year int)
RETURNS bit
AS
BEGIN
RETURN ISDATE (@year * 10000 + 229)
END;
2/26/2009 12:45 PM | Frank Kalis
Gravatar

# re: Fastest LeapYear checker?

I like that one!
2/26/2009 1:26 PM | Peso
Gravatar

# re: Fastest LeapYear checker?

@Frank

Sheesh, i didn't know about ISDATE(). But why * by 10000?
CREATE FUNCTION Is_Leap_Year(@Year VARCHAR(4))
RETURNS BIT
AS
BEGIN
RETURN ISDATE(@Year + '0229');
END;

@RemoteDBA The current rule is, every four years is a leap year, unless it is a hundreds year. So, 1900 was not a leap year. Unless, the hundreds year is divisible by 400, such as 2000 which was a leap year.

As the rule may change when people decide to fix the calendar, it is better to use the built-in date functionality.
2/26/2009 2:41 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

My last submission is missing the date separators. They are required for things like year 1 to work. Though, it isn't a SQL Server supported DATE anyway.

Regardless, all mine are bad anyway, resulting in errors or wrong answers when DATEFORMAT is changed. Everyone else's works. That is, the original submission, peso, uri, hugo, and frank's all work with the DATEFORMAT changed to each of its six values. Mine do not.

I learnt something from this exercise, thanx!
2/26/2009 3:31 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

More comments.

If the year is less than 4 characters, all FUNCTIONs only Hugo's FUNCTION works. The other throw conversion errors.

If the year has four characters, but is outside SQL Server's range, it returns an out of range error.

I would suggest that the FUNCTIONs do bound checking, or use Hugo's crude method.
2/26/2009 3:49 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

Brian,

good point about the * 10000. I copied Hugo's function where the @year parameter happened to be of int data type. And in order to get this running with ints you need to get this into the YYYYMMDD format which is @year * 10000 + the rest.

You changed it to varchar(4), so you just need to concatenate both parts which is just fine as well. I really like such collaborations. :-)

Btw, I might be wrong but SQL Server 2008 DOES support the ANSI-SQL date range which spans from 0001 to 9999, if I am not completely mistaken.
--
Cheers,
Frank
2/26/2009 4:02 PM | Frank Kalis
Gravatar

# re: Fastest LeapYear checker?

@Frank

Silly me. I'm playing with 2005 Express, so i'm stuck with the limitation of 1753-9999. I should have mentioned the version earlier.

In any case, going less than 4 characters may cause a problem, because the routines here rely on position, to some extent.

In 2005 Express, DATEFORMAT is SET at runtime, and that can break any CAST statement with DATETIME as well, because the routine has no control over it. The DATEPART() usage is much better, but i wonder if relying on a base date of 1900 is such a good idea. That is, will converting 0 to a date always be 1900?

2/26/2009 4:29 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

" but i wonder if relying on a base date of 1900 is such a good idea. That is, will converting 0 to a date always be 1900?"

Yes. To the best of my understanding everything in SQL Server's date arithmetics is based on the base date '1900-01-01' = 0.

This is made official by being mentioned in the Books Online:

"Values with the datetime data type are stored internally by the SQL Server 2005 Database Engine as two 4-byte integers. The first 4 bytes store the number of days before or after the base date: January 1, 1900. The base date is the system reference date...."

This is taken from the SQL Server 2005 BOL and I think it always was and still is true. Someone else may correct me if I'm wrong.
2/27/2009 8:34 AM | Frank Kalis
Gravatar

# re: Fastest LeapYear checker?

@Frank Excellent. If its documented, its reliable upon.

Except for the ISDATE() documentation. Two of their samples fail when DATEFORMAT is changed. I emailed in the correction. :)
2/27/2009 2:28 PM | Brian Tkatch
Gravatar

# re: Fastest LeapYear checker?

Whata about a data driven version with error handling?

CREATE FUNCTION Is_Leap_Year(@in_year INTEGER)
RETURNS INTEGER
AS
RETURN (
CASE WHEN @in_year NOT BETWEEN 0001 AND 9999
THEN -1 -- range error
WHEN @in_year
IN (SELECT leap_year
FROM VALUES (0004, 0008, etc.)
AS LeapYearList(leap_year
THEN 1 ELSE 0 END);




END;
3/3/2009 10:35 PM | Joe Celko
Gravatar

# re: Fastest LeapYear checker?


...just for fun


CREATE FUNCTION dbo.Another_Is_Leap_Year(@Year int)
RETURNS bit
AS
BEGIN
RETURN (1-SIGN(@Year % 400)) | (SIGN(@Year % 100) & (1-SIGN(@Year % 4)))
END;
10/3/2009 4:39 PM | Mark
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET