Here is some proper code that to provide elapsedTime and timeModifier 
functions.  I think I have fixed all the weird corner cases,  So I release this 
code into the public domain to do with what you will.  You need to wrap it in 
the extension code or otherwise add it to amalgamation and provide a hook to 
initialize it.  Note -- other headers may be required.

/* We need the math header for the fabs and fmod functions used in _elapsedTime 
function */

#include <math.h>

/*
** The elaspsedTime function will take either an input text string or an
** integer/floating point value.
**
** Input text in the d:h:m:s format will return a floating point value
** which is the total number of seconds represented.  Each of d/h/m/s may
** be arbitrary floating point numbers.
**
** Note that the d:h:m:s result will be calculated based on the absolute
** value of each field and that the sign of the leftmost field determines
** the sign of the result.
**
** Input numbers (integer or float) are a total number of seconds will 
** return the elapsed time string as d:hh:mm:ss.sss where d may be of 
** arbitrary length, hh will be zero filled and between 0 and 23, mm will 
** be zero filled and between 0 and 59.  ss.sss will be between 0.000 and 
** 59.999, zero filled, with exactly three decimal places.
**
** The timeModifier function will accept either an integer/floating point
** number of seconds or a text string as above.  It returns a text string
** of the format "+s.sss seconds" or "-s.sss seconds" suitable for use
** as an argument to the builtin datetime functions.
*/

static void _elapsedTime(sqlite3_context *context, int argc, sqlite3_value 
**argv) {

    double maxspan = 464269060799.999;

    switch (sqlite3_value_type(argv[0]))
    {
        case SQLITE_NULL:
        {
            return;
        }

        case SQLITE_TEXT:
        {
            double factors[4] = {86400.0, 3600.0, 60.0, 1.0};
            double total = 0.0;
            double sgn = 1.0;
            char *start, *end;

            /* Force conversion to utf-8 and make a copy of the text arg so we 
can modify it */
            sqlite3_value_text(argv[0]);
            start = sqlite3_malloc(sqlite3_value_bytes(argv[0]) + 1);
            strcpy(start, sqlite3_value_text(argv[0]));
            end = start + strlen(start);

            /* Compute totalseconds by parsing colon separated floats from the 
right */
            for (int j=3; j >= 0; j--)
            {
                double value;
                char *i;

                for (i=end; ((*i != ':') && (i >= start)); i--) ;
                value = atof(i + 1);
                total += fabs(value * factors[j]);
                sgn = (value < 0) || (*(i + 1) == '-') ? -1.0 : 1.0;
                if (i > start)
                    *i = 0;
                else
                    break;
            }
            sqlite3_free(start);
            if (total > maxspan)
                return;
            total *= sgn;

            /* Compute and return output based on user context */
            if ((intptr_t)sqlite3_user_data(context) == 0)
            {
                sqlite3_result_double(context, total);
            }
            else
            {
                char out[32];
                sqlite3_snprintf(sizeof(out), out, "%+.3f seconds", total);
                sqlite3_result_text(context, out, -1, SQLITE_TRANSIENT);
            }
            return;
        }

        default:
        {
            double s;
            int d, h, m;
            char out[32];
            char *sgn;

            /* Get our total seconds as a float */
            s = sqlite3_value_double(argv[0]);

            if (fabs(s) > maxspan)
                return;

            /* Return datetime modifier format */
            if ((intptr_t)sqlite3_user_data(context) == 1)
            {
                sqlite3_snprintf(sizeof(out), out, "%+.3f seconds", s);
                sqlite3_result_text(context, out, -1, SQLITE_TRANSIENT);
                return;
            }

            /* Save our sign and use only absolute value */
            sgn = s < 0 ? "-" : "";
            s = fabs(s);

            /* convert s to d/h/m/s */
            d = (int)(s / 86400.0);
            s = fmod(s, 86400.0);
            h = (int)(s / 3600.0);
            s = fmod(s, 3600.0);
            m = (int)(s / 60.0);
            s = fmod(s, 60.0);

            sqlite3_snprintf(sizeof(out), out, "%s%d:%02d:%02d:%06.3f", sgn, d, 
h, m, s);
            sqlite3_result_text(context, out, -1, SQLITE_TRANSIENT);
            return;
        }
    }
}

And the sqlite3_create_function calls to use:

    sqlite3_create_function(db, "elapsedTime",     1, 
SQLITE_UTF8|SQLITE_DETERMINISTIC,       0, _elapsedTime,    0, 0);
    sqlite3_create_function(db, "timeModifier",    1, 
SQLITE_UTF8|SQLITE_DETERMINISTIC,(void*)1, _elapsedTime,    0, 0);

-- 
The fact that there's a Highway to Hell but only a Stairway to Heaven says a 
lot about anticipated traffic volume.

>-----Original Message-----
>From: sqlite-users <sqlite-users-boun...@mailinglists.sqlite.org> On
>Behalf Of Dominique Devienne
>Sent: Tuesday, 10 September, 2019 02:36
>To: General Discussion of SQLite Database <sqlite-
>us...@mailinglists.sqlite.org>
>Subject: [sqlite] Why aren't there date/time parsing built-in functions
>in SQLite
>
>Hi,
>
>There are functions to format numbers as text times and/or dates,
>but I can't find equivalent ones to parse those text times/dates back to
>numbers.
>
>I wanted to sum durations expressed as HH:MM:SS.SS strings, and I was
>expecting
>a function parsing such a string into a number of seconds for example,
>but
>couldn't
>find one. Sure you can take apart the string with substr() and cast then
>do
>the math
>easily via verbose SQL, but why?
>
>I ended up massaging those text durations as CSV in VIM to decompose them
>and
>then did what's below, but my question is really why the "reverse" of
>strftime()
>is not part of SQLite itself? Seems to me so "natural" it should be, I
>wonder if I'm
>not missing some obvious way to do this more easily with SQLite?
>
>Thanks, --DD
>
>sqlite> create table vs (id number primary key, hh, mm, ss, cs);
>sqlite> .mode csv
>sqlite> .import time-elapsed.txt vs
>sqlite> .mode col
>sqlite> .header on
>sqlite> select * from vs limit 10;
>id          hh          mm          ss          cs
>----------  ----------  ----------  ----------  ----------
>1           00          00          02          68
>14          00          00          00          78
>12          00          00          02          31
>4           00          00          06          36
>5           00          00          08          01
>8           00          00          09          36
>9           00          00          09          79
>11          00          00          13          62
>10          00          00          17          50
>33          00          00          07          86
>sqlite> select id, hh||':'||mm||':'||ss||'.'||cs as "elapsed",
>cast(hh*(60*60)+mm*(60)+ss as number)+cs/100.0 from vs limit 10;
>id          elapsed      cast(hh*(60*60)+mm*(60)+ss as number)+cs/100.0
>----------  -----------  ----------------------------------------------
>1           00:00:02.68  2.68
>14          00:00:00.78  0.78
>12          00:00:02.31  2.31
>4           00:00:06.36  6.36
>5           00:00:08.01  8.01
>8           00:00:09.36  9.36
>9           00:00:09.79  9.79
>11          00:00:13.62  13.62
>10          00:00:17.50  17.5
>33          00:00:07.86  7.86
>sqlite> select id, hh||':'||mm||':'||ss||'.'||cs as "elapsed",
>hh*(60*60)+mm*(60)+ss+cs/100.0 from vs limit 10;
>id          elapsed      hh*(60*60)+mm*(60)+ss+cs/100.0
>----------  -----------  ------------------------------
>1           00:00:02.68  2.68
>14          00:00:00.78  0.78
>12          00:00:02.31  2.31
>4           00:00:06.36  6.36
>5           00:00:08.01  8.01
>8           00:00:09.36  9.36
>9           00:00:09.79  9.79
>11          00:00:13.62  13.62
>10          00:00:17.50  17.5
>33          00:00:07.86  7.86
>sqlite> select sum(hh*(60*60)+mm*(60)+ss+cs/100.0) as total from vs;
>total
>----------
>7338.85
>sqlite> select time(sum(hh*(60*60)+mm*(60)+ss+cs/100.0)) as total from
>vs;
>total
>----------
>08:24:00
>sqlite> select max(hh*(60*60)+mm*(60)+ss+cs/100.0) as total from vs;
>total
>----------
>211.95
>sqlite>
>_______________________________________________
>sqlite-users mailing list
>sqlite-users@mailinglists.sqlite.org
>http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users



_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to