Formatting Dates with 3 Character Months (SQL Spackle)

  • Thanks Jeff for the article. i learned something about the datename function and these langauages settings.

  • "You mean an 8% increase in performance with 44% less typing? Don't you wish you could do that everywhere?"

    SOLD!

  • My experience are just opposite. DATENAME is slower than CONVERT + DATEPART. See below:

    /*------------------------

    DECLARE @Bitbucket CHAR(4);

    PRINT '========== DATENAME Method of Conversion ==========';

    SET STATISTICS TIME ON;

    SELECT @Bitbucket = DATENAME(yy,SomeDate)

    FROM dbo.JBMTest

    SET STATISTICS TIME OFF;

    PRINT '========== Original Method of Conversion CONVERT ==========';

    SET STATISTICS TIME ON;

    SELECT @Bitbucket = CONVERT(CHAR(4), DATEPART(yy,SomeDate))

    FROM dbo.JBMTest;

    SET STATISTICS TIME OFF;

    PRINT '========== Original Method of Conversion CAST ==========';

    SET STATISTICS TIME ON;

    SELECT @Bitbucket = CAST(DATEPART(yy,SomeDate) AS CHAR(4))

    FROM dbo.JBMTest;

    SET STATISTICS TIME OFF;

    ------------------------*/

    ========== DATENAME Method of Conversion ==========

    SQL Server Execution Times:

    CPU time = 703 ms, elapsed time = 718 ms.

    ========== Original Method of Conversion CONVERT ==========

    SQL Server Execution Times:

    CPU time = 562 ms, elapsed time = 578 ms.

    ========== Original Method of Conversion CAST ==========

    SQL Server Execution Times:

    CPU time = 579 ms, elapsed time = 579 ms.

  • This post is slightly off topic. I'm sure I stole this code from somewhere (probably from this site :-)), but I have it in my toolbox of SQL examples. It shows examples all of the CONVERT style codes. I added the abbreviated month + yyyy example from this Spackle post:

    DECLARE @date DATETIME

    SET@date = GETDATE()

    --SET @date = '20090709T175449303'

    ;WITH cteDates (FormattedDate, Code, Style, SQL) AS

    (

    SELECT CONVERT(VARCHAR(35), @date, 0) AS FormattedDate,

    '0' AS Code,

    'Default' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 0)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 1) AS FormattedDate,

    '1' AS Code,

    'USA date - mm/dd/yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 1)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 2) AS FormattedDate,

    '2' AS Code,

    'ANSI date - yy.mm.dd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 2)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 3) AS FormattedDate,

    '3' AS Code,

    'UK/French date - dd/mm/yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 3)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 4) AS FormattedDate,

    '4' AS Code,

    'German date - dd.mm.yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 4)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 5) AS FormattedDate,

    '5' AS Code,

    'Italian date - dd-mm-yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 5)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 6) AS FormattedDate,

    '6' AS Code,

    'Abbreviated month - dd mmm yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 6)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 7) AS FormattedDate,

    '7' AS Code,

    'Abbreviated month - mmm dd, yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 7)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 8) AS FormattedDate,

    '8 OR 108' AS Code,

    '24 hour time - hh:mm:ss' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 8)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 9) AS FormattedDate,

    '9 OR 109' AS Code,

    'Default with seconds and milliseconds appended mmm dd yyyy hh:mm:ss:mmmXM' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 9)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 10) AS FormattedDate,

    '10' AS Code,

    'USA date with hyphens - mm-dd-yy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 10)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 11) AS FormattedDate,

    '11' AS Code,

    'Japanese date - yy/mm/dd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 11)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 12) AS FormattedDate,

    '12' AS Code,

    'ISO date - yymmdd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 12)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 13) AS FormattedDate,

    '13 OR 113' AS Code,

    'European default with seconds and milliseconds - dd mon yyyy hh:mm:ss:mmm' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 13)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 14) AS FormattedDate,

    '14 OR 114' AS Code,

    '24 hour time with milliseconds - hh:mm:ss:mmm' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 14)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 20) AS FormattedDate,

    '20 OR 120' AS Code,

    'ODBC canonical date and time - yyyy-mm-dd hh:mm:ss' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 20)' AS SQL

    UNION

    SELECT SUBSTRING(CONVERT(VARCHAR(35), @date, 20), 1, 10) AS FormattedDate,

    '20' AS Code,

    'yyyy-mm-dd' AS Style,

    'SELECT SUBSTRING(CONVERT(VARCHAR(35), @date, 20), 1, 10)' AS SQL

    UNION

    SELECT SUBSTRING(CONVERT(VARCHAR(35), @date, 20), 1, 16)

    AS FormattedDate,

    '20' AS Code,

    'yyyy-mm-dd hh:mm' AS Style,

    'SELECT SUBSTRING(CONVERT(VARCHAR(35), @date, 20), 1, 16)'

    AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 21) AS FormattedDate,

    '21 OR 121' AS Code,

    'ODBC canonical date and time with milliseconds - yyyy-mm-dd hh:mm:ss.mmm' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 21)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 101) AS FormattedDate,

    '101' AS Code,

    'USA date with century - mm/dd/yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 101)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 102) AS FormattedDate,

    '102' AS Code,

    'ANSI date with century - yyyy.mm.dd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 102)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 103) AS FormattedDate,

    '103' AS Code,

    'UK / French date with century - dd/mm/yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 103)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 104) AS FormattedDate,

    '104' AS Code,

    'German date with century - dd.mm.yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 104)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 105) AS FormattedDate,

    '105' AS Code,

    'Italian date with century - dd-mm-yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 105)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 106) AS FormattedDate,

    '106' AS Code,

    'Abbreviated month with century - dd mmm yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 106)' AS SQL

    UNION

    SELECT RIGHT(CONVERT(CHAR(11),@date, 106), 8) AS FormattedDate,

    '106' AS Code,

    'Abbreviated month - mmm yyyy' AS Style,

    'SELECT RIGHT(CONVERT(CHAR(11), @date, 106), 8)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 107) AS FormattedDate,

    '107' AS Code,

    'Abbreviated month with century - mmm dd, yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 107)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 110) AS FormattedDate,

    '110' AS Code,

    'USA date with hyphens and century - mm-dd-yyyy' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 110)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 111) AS FormattedDate,

    '111' AS Code,

    'Japanese date with century - yyyy/mm/dd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 111)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 112) AS FormattedDate,

    '112' AS Code,

    'ISO date with century - yyyymmdd' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 112)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 112) + ' ' + REPLACE(CONVERT(VARCHAR(35), @date, 108), ':', '')

    AS FormattedDate,

    '112 + 108' AS Code,

    'yyyymmdd hhmmss' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 112) + '' '' + REPLACE(CONVERT(VARCHAR(35), @date, 108), '':'', '''')'

    AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 112) + ' ' + REPLACE(CONVERT(VARCHAR(35), @date, 114), ':', '')

    AS FormattedDate,

    '112 + 114' AS Code,

    'yyyymmdd hhmmssfff' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 112) + '' '' + REPLACE(CONVERT(VARCHAR(35), @date, 114), '':'', '''')'

    AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 112) + ' ' + CONVERT(VARCHAR(35), @date, 114) AS FormattedDate,

    '112 + 114' AS Code,

    'yyyymmdd hh:mm:ss:fff' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 112) + '' '' + CONVERT(VARCHAR(35), @date, 114)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 126) AS FormattedDate,

    '126' AS Code,

    'ISO8601 - for use in XML - yyyy-mm-ddThh:mm:ss:mmm' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 126)' AS SQL

    UNION

    SELECT CONVERT(VARCHAR(35), @date, 127) AS FormattedDate,

    '127' AS Code,

    'ISO8601 with timezone Z - yyyy-mm-ddThh:mm:ss:mmmZ' AS Style,

    'SELECT CONVERT(VARCHAR(35), @date, 127)' AS SQL

    )

    --

    --SELECT * FROM cteDates AS cted ORDER BY cted.Code

    SELECT SortKey =

    CASE

    WHEN CHARINDEX(' ', cted.Code) = 0

    THEN cted.Code

    ELSE

    CAST(SUBSTRING(cted.Code, 1, CHARINDEX(' ', cted.Code)) AS INT)

    END,

    cted.FormattedDate, cted.Code, cted.Style, cted.SQL

    FROM cteDates AS cted

    ORDER BY SortKey, cted.FormattedDate

  • Thanks to David for pointing out the bug in my code. I've gotten so used to the SET LANGUAGE syntax without the string delimiters! I've fixed the code in my original posting just in case anyone else uses it.

    Best wishes,
    Phil Factor

  • @clark_button - (going slightly off topic too) You could also try this, that Robyn and I wrote a while back for Simple-Talk

    [font="Courier New"]DECLARE @types TABLE(

           [2 digit year] INT NULL,

           [4 digit year] INT NOT NULL,

           name VARCHAR(40))

    SET LANGUAGE british SET NOCOUNT ON

    --Each select statement is followed by an example output string using the style

    INSERT INTO @types  

    VALUES

         (NULL,100,'Default'),--Oct 17 2006  9:29PM

         (1,101, 'USA'),      --10/17/06 or 10/17/2006

         (2,102, 'ANSI'),     --06.10.17 or 2006.10.17

         (3,103, 'British/French'),--17/10/06 or 17/10/2006

         (4,104, 'German'),   --17.10.06 or 17.10.2006

         (5,105, 'Italian'),  --17-10-06 or 17-10-2006

         (6,106, 'dd mon yy'),--17 Oct 06 or 17 Oct 2006

         (7,107, 'Mon dd, yy'),--Oct 17, 06 or Oct 17, 2006

         (8,108, 'hh:mm:ss'), --21:29:45 or 21:29:45

         (NULL,109, 'Default + milliseconds'),--Oct 17 2006  9:29:45:500PM

         (10,110,'USA'),      --10-17-06 or 10-17-2006

         (11,111,'JAPAN'),    --06/10/17 or 2006/10/17

         (12,112,'ISO'),      --061017 or 20061017  

         (NULL,113,'Europe default(24h) + milliseconds'),--17 Oct 2006 21:29:45:500

         (14,114,'hh:mi:ss:mmm (24h)'), --21:29:45:500 or 21:29:45:500

         (NULL,120,'ODBC canonical (24h)'),--2006-10-17 21:29:45

         (NULL,121, 'ODBC canonical (24h)+ milliseconds'),--2006-10-17 21:29:45.500

         (NULL,126, 'ISO8601'),--2006-10-17T21:29:45.500

         (NULL,127, 'ISO8601 with time zone'), --SQL Server 2005 only!

         (NULL,130, 'Hijri'), --25 ????? 1427  9:33:21:340PM

         (NULL,131, 'Hijri')  --25/09/1427  9:29:45:500PM

    SELECT [name],

           [2 digit year]=COALESCE(CONVERT(NVARCHAR(3),[2 digit year]),'-'),

           [example]=CASE WHEN [2 digit year] IS NOT NULL

                     THEN CONVERT(NVARCHAR(30),GETDATE(),[2 digit year])

                     ELSE '-' END,

           [4 digit year]=COALESCE(CONVERT(NVARCHAR(3),[4 digit year]),'-'),

           [example]=CASE WHEN [4 digit year] IS NOT NULL

                     THEN CONVERT(NVARCHAR(30),GETDATE(),[4 digit year])

                     ELSE '-' END

    FROM @types

    [/font]

    Best wishes,
    Phil Factor

  • Well written AND entertaining. Nice. 🙂

    I've been using SqlSvr since the 6.5 days I still learn new stuff everyday. I have to admit I usually use DatePart - mostly because I had forgotten that DateName existed.

    So thanks for the reminder, Jeff. Hope you'll write some more articles in the future.

  • I use a "throw away" variable (@BitBucket)

    BWAA-HAA!!!

    Hmmmm

    The quotes aside .. Jeff a great article with more sample code than most of us deserve.

    Thanks, as I put this into my personal "toolbox"

    If everything seems to be going well, you have obviously overlooked something.

    Ron

    Please help us, help you -before posting a question please read[/url]
    Before posting a performance problem please read[/url]

  • Thanks for the tips on DATENAME (a function I think I'd used awhile back but had faded from my memory)

    I too would have done something like (limiting the convert output to 3 chars makes you not need to do Left/Substring):

    SELECT CONVERT(varchar(3),MyDateField,109)

    But the DATENAME method makes for more readable code. Luckily I don't have to deal with any language but English in my current job, but things like this are good to file away in case that changes down the road.

  • David McKinney (1/20/2011)


    I did a little more hunting and I came across sys.syslanguages which looks quite promising.

    To continue with the French exception, it has fields months and shortmonths with the comma separated values you see below.

    janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre

    janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc

    This merits consideration for a localised solution.

    That would seem to indicate that the CONVERT method may work correctly. I'll have to give it a try. Thanks again, David.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • marcin.motyl (1/20/2011)


    Different method

    DECLARE @Bitbucket CHAR(10);

    SET STATISTICS TIME ON;

    SELECT @Bitbucket = substring(convert(varchar(100), SomeDate,13) , 4,8)

    FROM dbo.JBMTest

    SET STATISTICS TIME OFF;

    and results:

    CPU time = 1078 ms, elapsed time = 1073

    Tiempo de CPU = 1047 ms, tiempo transcurrido = 1042 ms.

    CPU-Zeit = 1062 ms, verstrichene Zeit = 1056 ms.

    I only see three outputs there and the were 3 in the test code I provided. I also don't see the "markers" to identify which is which so it looks like you ran your code 3 different times with 3 different language settings.

    Because of the differences between machines and the fact that I did my testing on a 9 year single CPU machine, you need to add your code (hopefully with markers) to the test bed I provided so we have something to compare your method against.

    Thanks for the feedback and the code example. 🙂

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • surreydude. (1/20/2011)


    Interesting article. Mal-formed dates are the bane of my professional life; often this goes hand-in-hand with Excel data and/or lack of user discipline and validation.

    I've experienced date-related issues whilst working around the globe and in my experience, the US has the worst practices, whilst continental Europe has better disciplined users and validation. Whenever I am involved in the design/ETL phase of a project concerning data with dates, I go that bit extra to ensure that we have a least understood the magnitude of the problems around storage of such data. I favour always referencing dates in full ISO format, but it is important that people understand the format being used, rather than assume based on their locale. Where ambiguity can sneak in, I will use a format that is explicit and localised.

    One thing I did notice about the article, however, was the use of yy for year formats. It would seem that the "Y2K Bug" has not taught us very much afterall!

    I absolutely agree which is why I put the disclaimer/warning near the beginning of the article. Any time dates need to be transmitted as characters (ugh!) or integers (ugh!) in a "readable" file, I'll try to compell folks to use the ISO format in this global environment.

    So far as the "yy" thing goes... it's just a format selector like "106" is. I don't like to use words that have multiple code meanings like YEAR, MONTH, or DAY. Sometimes it can't be avoided but I try.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • nigel. (1/20/2011)


    David McKinney (1/20/2011)


    Très bien, Jeff! Except of course that you can't keep everybody happy!

    In France, June and July are Juin and Juillet respectively, and hence JUI and JUI when truncated to 3 characters.

    It gets even worse for languages such as Finnish, Estonian, & Czech (to name but a few).

    For instance Czech uses the Roman numerals (I,II,III,IV etc.) for short month names, and in Estonian the short month names vary in length from 3 to 5 characters.

    I haven't tried it because all of my work has been in English speaking countries but I believe the CONVERT method in the article may handle that with a slight modification using SUBSTRING(____, 3,20) instead of RIGHT(____,8)

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • is this a SQL 2008 only function or is it in SQL 2005 as well?

  • eric.hinrichsen (1/20/2011)


    Thanks for the tips on DATENAME (a function I think I'd used awhile back but had faded from my memory)

    I too would have done something like (limiting the convert output to 3 chars makes you not need to do Left/Substring):

    SELECT CONVERT(varchar(3),MyDateField,109)

    But the DATENAME method makes for more readable code. Luckily I don't have to deal with any language but English in my current job, but things like this are good to file away in case that changes down the road.

    As you can see from the previous posts, the DATENAME method and my CONVERT method doesn't quite cut it internationally. You'd need to use SUBSTRING(_____,3,20) on the convert method to get the short month name and year to come out in France (for example).

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 15 posts - 16 through 30 (of 108 total)

You must be logged in to reply to this topic. Login to reply