• I feel it's easier truncating a CONVERT(..., ..., 121), then CASTing again and adding the unit if the two dates differ - you can easily add a flag for ceiling/truncating:

    CREATE FUNCTION dbo.DATECEILING( @f1 AS datetime, @part AS varchar(2), @is_ceiling AS bit )

    RETURNS datetime

    AS

    BEGIN

    DECLARE @s1 varchar(21), @s2 varchar(21), @f2 datetime

    select @s1 = REPLACE( CONVERT( varchar(23), @f1, 121 ), '-', '' )

    IF @part = 'yy' SET @s2 = LEFT( @s1, 4 ) + '0101'

    IF @part = 'mm' SET @s2 = LEFT( @s1, 6 ) + '01'

    IF @part = 'dd' SET @s2 = LEFT( @s1, 8 )

    IF @part = 'hh' SET @s2 = LEFT( @s1, 12 ) + '00:00'

    IF @part = 'mi' SET @s2 = LEFT( @s1, 15 ) + '00'

    IF @part = 'ss' SET @s2 = LEFT( @s1, 17 )

    SET @f2 = CAST( @s2 as datetime )

    IF @is_ceiling = 1 And @f1 > @f2

    BEGIN

    IF @part = 'yy' SET @f2 = DATEADD( year, 1, @f2 )

    IF @part = 'mm' SET @f2 = DATEADD( month, 1, @f2 )

    IF @part = 'dd' SET @f2 = DATEADD( day, 1, @f2 )

    IF @part = 'hh' SET @f2 = DATEADD( hour, 1, @f2 )

    IF @part = 'mi' SET @f2 = DATEADD( minute, 1, @f2 )

    IF @part = 'ss' SET @f2 = DATEADD( second, 1, @f2 )

    END

    RETURN @f2

    END