Best way to convert DateTime to “n Hours Ago” in SQL

前端 未结 7 1817
醉酒成梦
醉酒成梦 2021-01-18 03:52

I wrote a SQL function to convert a datetime value in SQL to a friendlier \"n Hours Ago\" or \"n Days Ago\" etc type of message. And I was wondering if there was a better wa

相关标签:
7条回答
  • 2021-01-18 04:03

    How about this? You could expand this pattern to do "years" messages, and you could put in a check for "1 day" or "1 hour" so it wouldn't say "1 days ago"...

    I like the CASE statement in SQL.

    drop function dbo.time_diff_message    
    GO
    
    create function dbo.time_diff_message (
        @input_date datetime
    )
    returns varchar(200)    
    as    
    begin    
    declare @msg varchar(200)    
    declare @hourdiff int
    
    set @hourdiff = datediff(hour, @input_date, getdate())    
    set @msg = case when @hourdiff < 0 then ' from now' else ' ago' end    
    set @hourdiff = abs(@hourdiff)    
    set @msg = case when @hourdiff > 24 then convert(varchar, @hourdiff/24) + ' days' + @msg
                    else convert(varchar, @hourdiff) + ' hours' + @msg
                end
    
    return @msg
    end
    
    GO    
    select dbo.time_diff_message('Dec 7 1941')
    
    0 讨论(0)
  • 2021-01-18 04:05

    My attempt - this is for MS SQL. It supports 'ago' and 'from now', pluralization and it doesn't use rounding or datediff, but truncation -- datediff gives 1 month diff between 8/30 and 9/1 which is probably not what you want. Rounding gives 1 month diff between 9/1 and 9/16. Again, probably not what you want.

    CREATE FUNCTION dbo.GetFriendlyDateTimeValue( @CompareDate DATETIME ) RETURNS NVARCHAR(48) AS BEGIN
    declare @s nvarchar(48)
    set @s='Now'
    select top 1 @s=convert(nvarchar,abs(n))+' '+s+case when abs(n)>1 then 's' else '' end+case when n>0 then ' ago' else ' from now' end from (
     select convert(int,(convert(float,(getdate()-@comparedate))*n)) as n, s from (
      select 1/365 as n, 'Year' as s union all
      select 1/30, 'Month' union all
      select 1, 'Day' union all
      select 7, 'Week' union all
      select 24, 'Hour' union all
      select 24*60, 'Minute' union all
      select 24*60*60, 'Second'
     ) k
    ) j where abs(n)>0 order by abs(n)
    return @s
    END
    
    0 讨论(0)
  • 2021-01-18 04:10

    The posts above gave me some good ideas so here is another function for anyone using SQL Server 2012.

        CREATE FUNCTION [dbo].[FN_TIME_ELAPSED] 
        (
            @TIMESTAMP DATETIME
        )
        RETURNS VARCHAR(50)
        AS
        BEGIN
    
        RETURN 
        (
            SELECT TIME_ELAPSED = 
                CASE
                    WHEN @TIMESTAMP IS NULL THEN NULL
                    WHEN MINUTES_AGO < 60 THEN CONCAT(MINUTES_AGO, ' minutes ago')
                    WHEN HOURS_AGO < 24 THEN CONCAT(HOURS_AGO, ' hours ago')
                    WHEN DAYS_AGO < 365 THEN CONCAT(DAYS_AGO, ' days ago')
                    ELSE CONCAT(YEARS_AGO, ' years ago') END
            FROM ( SELECT MINUTES_AGO = DATEDIFF(MINUTE, @TIMESTAMP, GETDATE()) ) TIMESPAN_MIN
            CROSS APPLY ( SELECT HOURS_AGO = DATEDIFF(HOUR, @TIMESTAMP, GETDATE()) ) TIMESPAN_HOUR
            CROSS APPLY ( SELECT DAYS_AGO = DATEDIFF(DAY, @TIMESTAMP, GETDATE()) ) TIMESPAN_DAY
            CROSS APPLY ( SELECT YEARS_AGO = DATEDIFF(YEAR, @TIMESTAMP, GETDATE()) ) TIMESPAN_YEAR
        )
        END
        GO
    

    And the implementation:

        SELECT TIME_ELAPSED = DBO.FN_TIME_ELAPSED(AUDIT_TIMESTAMP)
        FROM SOME_AUDIT_TABLE
    
    0 讨论(0)
  • 2021-01-18 04:11

    In Oracle:

    select
      CC.MOD_DATETIME,
      'Last modified ' ||
      case when (sysdate - cc.mod_datetime) < 1
           then round((sysdate - CC.MOD_DATETIME)*24) || ' hours ago'
           when (sysdate - CC.MOD_DATETIME) between 1 and 7
           then round(sysdate-CC.MOD_DATETIME) || ' days ago'
           when (sysdate - CC.MOD_DATETIME) between 8 and 365
           then round((sysdate - CC.MOD_DATETIME) / 7) || ' weeks ago'
           when (sysdate - CC.MOD_DATETIME) > 365   
           then round((sysdate - CC.MOD_DATETIME) / 365) || ' years ago'
           end
    from 
      customer_catalog CC
    
    0 讨论(0)
  • 2021-01-18 04:12

    Thanks for the various code posted above.

    As Hafthor pointed out there are limitations of the original code to do with rounding. I also found that some of the results his code kicked out didn't match with what I'd expect e.g. Friday afternoon -> Monday morning would show as '2 days ago'. I think we'd all call that 3 days ago, even though 3 complete 24 hour periods haven't elapsed.

    So I've amended the code (this is MS SQL). Disclaimer: I am a novice TSQL coder so this is quite hacky, but works!!

    I've done some overrides - e.g. anything up to 2 weeks is expressed in days. Anything over that up to 2 months is expressed in weeks. Anything over that is in months etc. Just seemed like the intuitive way to express it.

    CREATE FUNCTION [dbo].[GetFriendlyDateTimeValue]( @CompareDate DATETIME ) RETURNS NVARCHAR(48) AS BEGIN
    declare @s nvarchar(48)
    
    set @s='Now'
    select top 1 @s=convert(nvarchar,abs(n))+' '+s+case when abs(n)>1 then 's' else '' end+case when n>0 then ' ago' else ' from now' end from (
     select convert(int,(convert(float,(getdate()-@comparedate))*n)) as n, s from (
      select 1/365 as n, 'year' as s union all
      select 1/30, 'month' union all
      select 1/7, 'week' union all
      select 1, 'day' union all
      select 24, 'hour' union all
      select 24*60, 'minute' union all
      select 24*60*60, 'second'
     ) k
    ) j where abs(n)>0 order by abs(n)
    
    if @s like '%days%'
    BEGIN
     -- if over 2 months ago then express in months
     IF convert(nvarchar,DATEDIFF(MM, @CompareDate, GETDATE())) >= 2
     BEGIN
      select @s = convert(nvarchar,DATEDIFF(MM, @CompareDate, GETDATE())) + ' months ago'
     END
    
     -- if over 2 weeks ago then express in weeks, otherwise express as days
     ELSE IF convert(nvarchar,DATEDIFF(DD, @CompareDate, GETDATE())) >= 14
     BEGIN
      select @s = convert(nvarchar,DATEDIFF(WK, @CompareDate, GETDATE())) + ' weeks ago'
     END
    
     ELSE
      select @s = convert(nvarchar,DATEDIFF(DD, @CompareDate, GETDATE())) + ' days ago'
    END
    
    return @s
    END
    
    0 讨论(0)
  • 2021-01-18 04:18

    As you say, I probably wouldn't do it in SQL, but as a thought exercise have a MySQL implementation:

    CASE
        WHEN compare_date between date_sub(now(), INTERVAL 60 minute) and now() 
            THEN concat(minute(TIMEDIFF(now(), compare_date)), ' minutes ago')
    
        WHEN datediff(now(), compare_date) = 1 
            THEN 'Yesterday'
    
        WHEN compare_date between date_sub(now(), INTERVAL 24 hour) and now() 
            THEN concat(hour(TIMEDIFF(NOW(), compare_date)), ' hours ago')
    
        ELSE concat(datediff(now(), compare_date),' days ago')
    END
    

    Based on a similar sample seen on the MySQL Date and Time manual pages

    0 讨论(0)
提交回复
热议问题