Skip to content

Commit c2715d7

Browse files
committedMay 7, 2020
[FEATURE][expressions] Add make_date, make_time and make_datetime functions
These functions allow for direct creation of date/time values. Previously this was only possible by going through the to_datetime/to_date/to_time functions, which are string based and accordingly frustrating/inefficient to use when you have numeric date/time component values.
1 parent 7a172b5 commit c2715d7

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed
 
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "make_date",
3+
"type": "function",
4+
"description": "Creates a date value from year, month and day numbers.",
5+
"arguments": [
6+
{"arg":"year","description":"Year number. Years 1 to 99 are interpreted as is. Year 0 is invalid."},
7+
{"arg":"month","description":"Month number, where 1=January"},
8+
{"arg":"day", "description":"Day number, beginning with 1 for the first day in the month"}
9+
],
10+
"examples": [
11+
{ "expression":"make_date(2020,5,4)", "returns":"date value 2020-05-04"}
12+
]
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "make_datetime",
3+
"type": "function",
4+
"description": "Creates a datetime value from year, month, day, hour, minute and second numbers.",
5+
"arguments": [
6+
{"arg":"year","description":"Year number. Years 1 to 99 are interpreted as is. Year 0 is invalid."},
7+
{"arg":"month","description":"Month number, where 1=January"},
8+
{"arg":"day", "description":"Day number, beginning with 1 for the first day in the month"},
9+
{"arg":"hour", "description":"Hour number"},
10+
{"arg":"minute", "description":"Minutes"},
11+
{"arg":"second", "description":"Seconds (fractional values include milliseconds)"}
12+
],
13+
"examples": [
14+
{ "expression":"make_datetime(2020,5,4,13,45,30.5)", "returns":"datetime value 2020-05-04 13:45:30.500"}
15+
]
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "make_time",
3+
"type": "function",
4+
"description": "Creates a time value from hour, minute and second numbers.",
5+
"arguments": [
6+
{"arg":"hour", "description":"Hour number"},
7+
{"arg":"minute", "description":"Minutes"},
8+
{"arg":"second", "description":"Seconds (fractional values include milliseconds)"}
9+
],
10+
"examples": [
11+
{ "expression":"make_time(13,45,30.5)", "returns":"time value 13:45:30.500"}
12+
]
13+
}

‎src/core/expression/qgsexpressionfunction.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,60 @@ static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionCo
11031103
return QVariant( datetime );
11041104
}
11051105

1106+
static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1107+
{
1108+
const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1109+
const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1110+
const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1111+
1112+
const QDate date( year, month, day );
1113+
if ( !date.isValid() )
1114+
{
1115+
parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1116+
return QVariant();
1117+
}
1118+
return QVariant( date );
1119+
}
1120+
1121+
static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1122+
{
1123+
const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1124+
const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1125+
const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1126+
1127+
const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1128+
if ( !time.isValid() )
1129+
{
1130+
parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1131+
return QVariant();
1132+
}
1133+
return QVariant( time );
1134+
}
1135+
1136+
static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1137+
{
1138+
const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1139+
const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1140+
const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1141+
const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1142+
const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1143+
const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1144+
1145+
const QDate date( year, month, day );
1146+
if ( !date.isValid() )
1147+
{
1148+
parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1149+
return QVariant();
1150+
}
1151+
const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1152+
if ( !time.isValid() )
1153+
{
1154+
parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1155+
return QVariant();
1156+
}
1157+
return QVariant( QDateTime( date, time ) );
1158+
}
1159+
11061160
static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
11071161
{
11081162
for ( const QVariant &value : values )
@@ -5738,6 +5792,21 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
57385792
<< new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
57395793
<< new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
57405794
<< new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
5795+
<< new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
5796+
<< QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
5797+
<< QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
5798+
fcnMakeDate, QStringLiteral( "Date and Time" ) )
5799+
<< new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
5800+
<< QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
5801+
<< QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
5802+
fcnMakeTime, QStringLiteral( "Date and Time" ) )
5803+
<< new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
5804+
<< QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
5805+
<< QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
5806+
<< QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
5807+
<< QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
5808+
<< QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
5809+
fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
57415810
<< new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
57425811
<< new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
57435812
<< new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )

‎tests/src/core/testqgsexpression.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,17 @@ class TestQgsExpression: public QObject
13341334
QTest::newRow( "try invalid without alternative" ) << "try(to_int('a'))" << false << QVariant();
13351335

13361336
// Datetime functions
1337+
QTest::newRow( "make date" ) << "make_date(2012,6,28)" << false << QVariant( QDate( 2012, 6, 28 ) );
1338+
QTest::newRow( "make date invalid" ) << "make_date('a',6,28)" << true << QVariant();
1339+
QTest::newRow( "make date invalid 2" ) << "make_date(2012,16,28)" << true << QVariant();
1340+
QTest::newRow( "make time" ) << "make_time(13,6,28)" << false << QVariant( QTime( 13, 6, 28 ) );
1341+
QTest::newRow( "make time with ms" ) << "make_time(13,6,28.5)" << false << QVariant( QTime( 13, 6, 28, 500 ) );
1342+
QTest::newRow( "make time invalid" ) << "make_time('a',6,28)" << true << QVariant();
1343+
QTest::newRow( "make time invalid 2" ) << "make_time(2012,16,28)" << true << QVariant();
1344+
QTest::newRow( "make datetime" ) << "make_datetime(2012,7,8,13,6,28)" << false << QVariant( QDateTime( QDate( 2012, 7, 8 ), QTime( 13, 6, 28 ) ) );
1345+
QTest::newRow( "make datetime with ms" ) << "make_datetime(2012,7,8,13,6,28.5)" << false << QVariant( QDateTime( QDate( 2012, 7, 8 ), QTime( 13, 6, 28, 500 ) ) );
1346+
QTest::newRow( "make datetime invalid" ) << "make_datetime(2012,7,8,'a',6,28)" << true << QVariant();
1347+
QTest::newRow( "make datetime invalid 2" ) << "make_datetime(2012,7,8,2012,16,28)" << true << QVariant();
13371348
QTest::newRow( "to date" ) << "todate('2012-06-28')" << false << QVariant( QDate( 2012, 6, 28 ) );
13381349
QTest::newRow( "to interval" ) << "tointerval('1 Year 1 Month 1 Week 1 Hour 1 Minute')" << false << QVariant::fromValue( QgsInterval( 34758060 ) );
13391350
QTest::newRow( "day with date" ) << "day('2012-06-28')" << false << QVariant( 28 );

0 commit comments

Comments
 (0)
Please sign in to comment.