Search code examples
postgresqlplpgsqltimingsupabase

How to measure time difference in milliseconds in a function?


I would like to calculate the difference between the starting time (variable of type timestamptz) and the NOW() moment when my function finishes.

select * from _get_date_diff_ms(NOW(), NOW())

Using supabase.io with PG 13.3, I get this error message:

operator does not exist: timestamp with time zone - double precision"
CREATE OR REPLACE FUNCTION _get_date_diff_ms(p_begin_time timestamptz, p_end_time timestamptz)
RETURNS int AS
$$
    BEGIN
        RETURN ROUND ((
            EXTRACT (EPOCH FROM p_begin_time -
            EXTRACT (EPOCH FROM p_end_time)
        ) * 1000));
    END;
$$ LANGUAGE plpgsql;

Solution

  • operator does not exist: timestamp with time zone - double precision
    

    That's just because of wrong parentheses. You meant to write:

            RETURN ROUND ((
                EXTRACT (EPOCH FROM p_begin_time) -    -- !
                EXTRACT (EPOCH FROM p_end_time)
            ) * 1000);
    

    But the whole approach is broken. It's been pointed out by now that now() is a stable value within a Postgres transaction. See:

    Moreover, for your expressed purpose, it makes no sense to pass a "starting time" to begin with.

    • Either you mean the starting time of the transaction: then use now() anywhere in the function.
    • Or you mean the starting time of the function: then use clock_timestamp() at the start of the function.

    Assuming the latter, I suggest:

    CREATE OR REPLACE FUNCTION public._get_date_diff_ms() -- no parameter
     RETURNS numeric  -- !
     LANGUAGE plpgsql AS
    $func$
    DECLARE
       _start_ts timestamptz := clock_timestamp();  -- !
    BEGIN
       PERFORM pg_sleep(1);   --  do something here (1 sec example)
       RETURN round(1000 * EXTRACT(epoch FROM clock_timestamp() - _start_ts), 3);
    END                                                                                
    $func$;
    

    Call:

    test=> SELECT public._get_date_diff_ms();
     _get_date_diff_ms 
    -------------------
              1003.440
    

    I return numeric with 3 fractional digits instead of integer to capture microseconds. Else, it would just report 0 for many fast operations.

    See: