DateSerial Function for Microsoft SQL Server

AID: 670
  • Status: Published

4840 points

  • By
  • TypeGeneral
  • Posted on2009-05-19 at 14:07:53
Awards
  • Community Pick
If you are a fan of DateSerial() function in Microsoft Access or like me you deal with data where you only have year and month but would like to do date calculation/manipulation, then here is a replication of this functionality for Microsoft SQL Server utilizing a user defined function.

DateSerial() takes in a number of years, months, and days and returns the appropriate date allowing you vary inputs and use negatives without having to worry about crossing over months or years.  If you want more information on how the DateSerial() function worked within Access, see reference below.

Others may exist, but let's explore my version...


The Methodology

The main principle is to utilize simple DateAdd() function.  The DateAdd() function takes a datepart like "day", "month", or "year", etc. without the quotes (unlike MS Access); a signed integer indicating the number or days to add/subtract; the datetime to add/subtract number of datepart units from.

Basically, once a date is established by parsing the year sent to our DateSerial() function, we can simply add months and days using DateAdd().

This is very straight-forward and explained in code, so...


The Code

Adds some error handling for invalid start years has been added that can be adjusted per your own tastes.
CREATE FUNCTION dbo.DateSerial
(
  @year int ,
  @month int ,
  @day bigint
)
RETURNS datetime
AS
BEGIN
   DECLARE @date datetime
   -- catch invalid year entries and default appropriately
   SET @year = 
      CASE WHEN @year < 1900 THEN 1900
         WHEN @year > 9999 THEN year(getdate())
         ELSE @year
      END
   -- convert date by adding together like yyyymmdd
   SET @date = cast(@year * 10000 + 101 AS char(8))
   ;
      
   -- Add to date the proper months subtracting 1,
   -- since we used 1 as start instead of zero.    
   SET @date = dateadd(mm , @month - 1 , @date)
   -- Add to date the proper days subtracting 1,
   -- since we used 1 as start instead of zero.    
   SET @date = dateadd(dd , @day - 1 , @date)
   ;
      
   RETURN @date ;
END
;
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:

Select allOpen in new window


Here is the usage:
SELECT dbo.DateSerial(2009, 4, 1), dbo.DateSerial(2009, -1, 1)
;
                                    
1:
2:

Select allOpen in new window



Example usage above returns "2009-04-01 00:00:00.000" and "2008-11-01 00:00:00.000", respectively.  

The first example, dbo.DateSerial(2009, 4, 1), simply puts in a year, month, and day.  However, the second is a bit more involved.  

For the second example, we start with the year 2009.  Now for month, we have a -1, but what are we starting with?  Well, since January is month 1, then a month of 0 would be one month prior to January; therefore, a year of 2009 and month of 0 would start us at December 2008.  Now subtracting one month, we arrive at November 2008.  The last parameter sets the day to be the 1st; hence our result.

Clear as mud, right!


Well, here is a look at a scenario where this may be useful:

We have a labor entry system that stores a year and month of entry that we cannot change, but we need to utilize the two values as a date to make our where clause more easily able to determine entries for the last three months.  This is possible, otherwise, but can sometimes be ugly in syntax.  

The below snippet is one solution I have used...
SELECT
   WorkOrder ,
   PostYear ,
   PostMonth ,
   RunTimeHours
FROM
   WorkOrderLaborEntry
--WHERE {date is three months ago or newer}
WHERE ( 
   (YEAR(GETDATE())-PostYear)*12 
   + ( MONTH(GETDATE()) - PostMonth ) 
) BETWEEN 0 AND 3
;
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

Select allOpen in new window



Now here is the same code with DateSerial...
SELECT
   WorkOrder ,
   PostYear ,
   PostMonth ,
   RunTimeHours
FROM
   WorkOrderLaborEntry
--WHERE {date is three months ago or newer}
WHERE
   DATEDIFF(MONTH , 
      dbo.DateSerial(PostYear , PostMonth , 1),
      GETDATE()) BETWEEN 0 AND 3)
;
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:

Select allOpen in new window


Notice we were able to use standard date function as our DateSerial() tool returns a valid datetime value.

To explore this further, see this alternative allowing us to compare dates:
SELECT
   WorkOrder ,
   PostYear ,
   PostMonth ,
   RunTimeHours
FROM
   WorkOrderLaborEntry
--WHERE {date is three months ago or newer}
WHERE
   dbo.DateSerial(PostYear , PostMonth , 1) >= 
      DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-3, 0)
   AND dbo.DateSerial(PostYear , PostMonth , 1) < 
      DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())+1, 0)
;
                                    
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:

Select allOpen in new window


Very useful.

As a good alternative, my method without DateSerial() shown earlier is very clean as well for this particular case and was developed after creating this function, but as with anything else there is always a more complex need where this can come in handy like:
SELECT dbo.DateSerial(YEAR(GETDATE()), MONTH(GETDATE()), 1 - 35)
;
                                    
1:
2:

Select allOpen in new window


This will return the date 35 days prior to the first day of the current month in the current year!


Thanks for reading, and I hope you find my ramblings useful.  Always good to have a lot of tools in the arsenal, so just giving you another with our new found DateSerial() function.

Happy coding!

Best regards,

Kevin (aka MWVisa1)


References / Related Articles:
DateSerial in Access > http://office.microsoft.com/en-us/access/HA012288131033.aspx
TimeSerial in MS SQL > http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/A_669-TimeSerial-Function-for-Microsoft-SQL-Server.html
Asked On
2009-05-19 at 14:07:53ID670
Tags

SQL

,

SQL 2005

,

SQL 2008

,

SQL 2000

,

Date Functions

,

User Defined Functions

,

DateSerial

,

UDF

Topic

MS SQL Server

Views
3561

Comments

Author Comment

by: mwvisa1 on 2010-10-08 at 05:42:28ID: 20333

As an added resource, here is a nice reference on the SQL date and time data types:

Date and Time Functions (Transact-SQL)
http://msdn.microsoft.com/en-us/library/ms186724.aspx

As you can see in SQL 2008 and higher, you now have Date and DateTime2 which support a broader range of valid dates.  Still no DateSerial function, so this is still valid.

Thanks again for reading and voting above.

Kevin

Add your Comment

Please Sign up or Log in to comment on this article.

Join Experts Exchange Today

Gain Access to all our Tech Resources

Get personalized answers

Ask unlimited questions

Access Proven Solutions

Search 3.2 million solutions

Read In-Depth How-To Guides

1000+ articles, demos, & tips

Watch Step by Step Tutorials

Learn direct from top tech pros

And Much More!

Your complete tech resource

See Plans and Pricing

30-day free trial. Register in 60 seconds.

Loading Advertisement...

Top MS SQL Server Experts

  1. jogos

    246,566

    Guru

    1,668 points yesterday

    Profile
    Rank: Sage
  2. acperkins

    246,249

    Guru

    1,000 points yesterday

    Profile
    Rank: Genius
  3. lcohan

    194,990

    Guru

    2,000 points yesterday

    Profile
    Rank: Genius
  4. anujnb

    179,525

    Guru

    2,000 points yesterday

    Profile
    Rank: Wizard
  5. ScottPletcher

    154,405

    Guru

    6,500 points yesterday

    Profile
    Rank: Genius
  6. matthewspatrick

    131,392

    Master

    1,620 points yesterday

    Profile
    Rank: Savant
  7. ValentinoV

    126,429

    Master

    1,800 points yesterday

    Profile
    Rank: Genius
  8. EugeneZ

    120,790

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  9. TempDBA

    112,141

    Master

    1,168 points yesterday

    Profile
    Rank: Sage
  10. angelIII

    100,133

    Master

    0 points yesterday

    Profile
    Rank: Elite
  11. HainKurt

    93,046

    Master

    0 points yesterday

    Profile
    Rank: Genius
  12. mwvisa1

    88,585

    Master

    40 points yesterday

    Profile
    Rank: Genius
  13. dtodd

    88,114

    Master

    0 points yesterday

    Profile
    Rank: Genius
  14. huslayer

    81,392

    Master

    0 points yesterday

    Profile
    Rank: Sage
  15. ralmada

    75,583

    Master

    400 points yesterday

    Profile
    Rank: Genius
  16. BCUNNEY

    74,206

    Master

    0 points yesterday

    Profile
    Rank: Guru
  17. dqmq

    66,272

    Master

    0 points yesterday

    Profile
    Rank: Genius
  18. rajeevnandanmishra

    60,246

    Master

    2,000 points yesterday

    Profile
    Rank: Guru
  19. dbaduck

    58,208

    Master

    2,000 points yesterday

    Profile
    Rank: Sage
  20. CodeCruiser

    55,120

    Master

    0 points yesterday

    Profile
    Rank: Genius
  21. Qlemo

    53,598

    Master

    2,000 points yesterday

    Profile
    Rank: Genius
  22. ryanmccauley

    52,252

    Master

    0 points yesterday

    Profile
    Rank: Sage
  23. Cluskitt

    50,880

    Master

    800 points yesterday

    Profile
    Rank: Wizard
  24. sdstuber

    50,836

    Master

    0 points yesterday

    Profile
    Rank: Genius
  25. mark_wills

    49,374

    10 points yesterday

    Profile
    Rank: Genius

Hall Of Fame