Merge pull request #772 from whatdoineed2do/smartpl-dynamic-dates-context

[smartpl] generate dynamic dates for SMARTPL queries
This commit is contained in:
Christian Meffert 2019-08-03 08:51:57 +02:00 committed by GitHub
commit d996b1ff09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 138 deletions

View File

@ -150,6 +150,8 @@ One example of a valid date is a date in yyyy-mm-dd format:
There are also some special date keywords: There are also some special date keywords:
* `today`, `yesterday`, `last week`, `last month`, `last year` * `today`, `yesterday`, `last week`, `last month`, `last year`
These dates refer to the _start_ of that period; `today` means 00:00hrs of today, `last week` means the previous Monday 00:00hrs, `last month` is the first day of the previous month at 00:00hrs etc.
A valid date can also be made by applying an interval to a date. Intervals can be defined as `days`, `weeks`, `months`, `years`. A valid date can also be made by applying an interval to a date. Intervals can be defined as `days`, `weeks`, `months`, `years`.
As an example, a valid date might be: As an example, a valid date might be:
@ -173,7 +175,11 @@ This matches all songs added in the last 2 weeks.
} }
``` ```
This matches all audiobooks played in the last week. This matches all audiobooks played since the start of the last Monday 00:00AM.
All dates, except for `YYYY-DD-HH`, are relative to the day of when `forked-daapd` evaluates the smartpl query; `time_added after today` run on a Monday would match against items added since Monday 00:00hrs and evaluating the same smartpl on Friday would only match against added on Friday 00:00hrs.
Note that `time_added after 4 weeks ago` and `time_added after last month` are subtly different; the former is exactly 4 weeks ago (from today) whereas the latter is the first day of the previous month.
## Differences to mt-daapd smart playlists ## Differences to mt-daapd smart playlists

View File

@ -38,18 +38,43 @@ options {
} }
@members { @members {
static time_t startoftoday() static void append_date(pANTLR3_STRING result, const char *datval, char beforeorafter, const char *interval)
{ {
struct tm tm; if (strcmp((char *)datval, "today") == 0)
time_t now; {
result->append8(result, "strftime('\%s', datetime('now', 'start of day'");
}
else if (strcmp((char *)datval, "yesterday") == 0)
{
result->append8(result, "strftime('\%s', datetime('now', 'start of day', '-1 day'");
}
else if (strcmp((char *)datval, "last week") == 0)
{
result->append8(result, "strftime('\%s', datetime('now', 'start of day', 'weekday 0', '-13 days'");
}
else if (strcmp((char *)datval, "last month") == 0)
{
result->append8(result, "strftime('\%s', datetime('now', 'start of month', '-1 month'");
}
else if (strcmp((char *)datval, "last year") == 0)
{
result->append8(result, "strftime('\%s', datetime('now', 'start of year', '-1 year'");
}
else
{
result->append8(result, "strftime('\%s', datetime(\'");
result->append8(result, datval);
result->append8(result, "\'");
}
time(&now); if (beforeorafter)
localtime_r(&now, &tm); {
tm.tm_sec = 0; result->append8(result, ", '");
tm.tm_min = 0; result->addc(result, beforeorafter);
tm.tm_hour = 0; result->append8(result, interval);
result->addc(result, '\'');
return mktime(&tm); }
result->append8(result, ", 'utc'))");
} }
} }
@ -213,25 +238,19 @@ expression returns [ pANTLR3_STRING result, pANTLR3_STRING orderby, pANTLR3_STRI
} }
| DATETAG AFTER dateval | DATETAG AFTER dateval
{ {
char str[15];
sprintf(str, "\%d", $dateval.result);
$result = $DATETAG.text->factory->newRaw($DATETAG.text->factory); $result = $DATETAG.text->factory->newRaw($DATETAG.text->factory);
$result->append8($result, "f."); $result->append8($result, "f.");
$result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text)); $result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text));
$result->append8($result, " > "); $result->append8($result, " > ");
$result->append8($result, str); $result->append8($result, (const char*)$dateval.result->chars);
} }
| DATETAG BEFORE dateval | DATETAG BEFORE dateval
{ {
char str[15];
sprintf(str, "\%d", $dateval.result);
$result = $DATETAG.text->factory->newRaw($DATETAG.text->factory); $result = $DATETAG.text->factory->newRaw($DATETAG.text->factory);
$result->append8($result, "f."); $result->append8($result, "f.");
$result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text)); $result->appendS($result, $DATETAG.text->toUTF8($DATETAG.text));
$result->append8($result, " < "); $result->append8($result, " < ");
$result->append8($result, str); $result->append8($result, (const char*)$dateval.result->chars);
} }
| ENUMTAG IS ENUMVAL | ENUMTAG IS ENUMVAL
{ {
@ -328,150 +347,60 @@ ordertag returns [ pANTLR3_STRING result ]
} }
; ;
dateval returns [ int result ] dateval returns [ pANTLR3_STRING result ]
@init { $result = 0; } @init { $result = NULL; }
: DATE : DATE
{ {
pANTLR3_UINT8 datval; pANTLR3_UINT8 datval;
datval = $DATE.text->chars; datval = $DATE.text->chars;
$result = $DATE.text->factory->newRaw($DATE.text->factory);
if (strcmp((char *)datval, "today") == 0) append_date($result, (const char *)datval, 0, NULL);
{
$result = startoftoday();
}
else if (strcmp((char *)datval, "yesterday") == 0)
{
$result = startoftoday() - 24 * 3600;
}
else if (strcmp((char *)datval, "last week") == 0)
{
$result = startoftoday() - 24 * 3600 * 7;
}
else if (strcmp((char *)datval, "last month") == 0)
{
$result = startoftoday() - 24 * 3600 * 30;
}
else if (strcmp((char *)datval, "last year") == 0)
{
$result = startoftoday() - 24 * 3600 * 365;
}
else
{
struct tm tm;
char year[5];
char month[3];
char day[3];
memset((void*)&tm,0,sizeof(tm));
memset(year, 0, sizeof(year));
memset(month, 0, sizeof(month));
memset(day, 0, sizeof(day));
strncpy(year, (const char *)datval, 4);
strncpy(month, (const char *)datval + 5, 2);
strncpy(day, (const char *)datval + 8, 2);
tm.tm_year = atoi(year) - 1900;
tm.tm_mon = atoi(month) - 1;
tm.tm_mday = atoi(day);
$result = mktime(&tm);
}
} }
| interval BEFORE DATE | interval BEFORE DATE
{ {
pANTLR3_UINT8 datval; $result = $DATE.text->factory->newRaw($DATE.text->factory);
append_date($result, (const char *)$DATE.text->chars, '-', (const char *)$interval.result->chars);
datval = $DATE.text->chars;
if (strcmp((char *)datval, "yesterday") == 0)
{
$result = startoftoday() - 24 * 3600;
}
else if (strcmp((char *)datval, "last week") == 0)
{
$result = startoftoday() - 24 * 3600 * 7;
}
else if (strcmp((char *)datval, "last month") == 0)
{
$result = startoftoday() - 24 * 3600 * 30;
}
else if (strcmp((char *)datval, "last year") == 0)
{
$result = startoftoday() - 24 * 3600 * 365;
}
else
{
$result = startoftoday(NULL);
}
$result = $result - $interval.result;
} }
| interval AFTER DATE | interval AFTER DATE
{ {
pANTLR3_UINT8 datval; $result = $DATE.text->factory->newRaw($DATE.text->factory);
append_date($result, (const char *)$DATE.text->chars, '+', (const char *)$interval.result->chars);
datval = $DATE.text->chars;
if (strcmp((char *)datval, "yesterday") == 0)
{
$result = startoftoday() - 24 * 3600;
}
else if (strcmp((char *)datval, "last week") == 0)
{
$result = startoftoday() - 24 * 3600 * 7;
}
else if (strcmp((char *)datval, "last month") == 0)
{
$result = startoftoday() - 24 * 3600 * 30;
}
else if (strcmp((char *)datval, "last year") == 0)
{
$result = startoftoday() - 24 * 3600 * 365;
}
else
{
$result = startoftoday();
}
$result = $result + $interval.result;
} }
| interval AGO | interval AGO
{ {
$result = startoftoday() - $interval.result; $result = $AGO.text->factory->newRaw($AGO.text->factory);
append_date($result, "today", '-', (const char *)$interval.result->chars);
} }
; ;
interval returns [ int result ] interval returns [ pANTLR3_STRING result ]
@init { $result = 0; } @init { $result = NULL; }
: INT DATINTERVAL : INT DATINTERVAL
{ {
pANTLR3_UINT8 interval; pANTLR3_UINT8 interval;
int intval;
char buf[25];
$result = atoi((const char *)$INT.text->chars); $result = $DATINTERVAL.text->factory->newRaw($DATINTERVAL.text->factory);
// SQL doesnt have a modifer for 'week' but for day/hr/min/sec/month/yr
interval = $DATINTERVAL.text->chars; interval = $DATINTERVAL.text->chars;
if (strcmp((char *)interval, "weeks") == 0)
{
intval = atoi((const char *)$INT.text->chars) * 7;
snprintf(buf, sizeof(buf), "\%d days", intval);
if (strcmp((char *)interval, "days") == 0) $result->append8($result, buf);
{
$result = $result * 24 * 3600;
}
else if (strcmp((char *)interval, "weeks") == 0)
{
$result = $result * 24 * 3600 * 7;
}
else if (strcmp((char *)interval, "months") == 0)
{
$result = $result * 24 * 3600 * 30;
}
else if (strcmp((char *)interval, "weeks") == 0)
{
$result = $result * 24 * 3600 * 365;
} }
else else
{ {
$result = 0; $result->append8($result, (const char *)$INT.text->chars);
$result->append8($result, " ");
$result->append8($result, (const char *)$DATINTERVAL.text->chars);
} }
return $result;
} }
; ;