diff --git a/resources/function_help/json/format_date b/resources/function_help/json/format_date
index c694af9fc0b..6fe082c2b5f 100644
--- a/resources/function_help/json/format_date
+++ b/resources/function_help/json/format_date
@@ -4,8 +4,10 @@
"description": "Formats a date type or string into a custom string format. Uses Qt date/time format strings. See QDateTime::toString.",
"arguments": [
{"arg":"datetime","description":"date, time or datetime value"},
- {"arg":"format","description":"String template used to format the string.
Expression | Output |
---|
d | the day as number without a leading zero (1 to 31) |
dd | the day as number with a leading zero (01 to 31) |
ddd | the abbreviated localized day name (e.g. 'Mon' to 'Sun') |
dddd | the long localized day name (e.g. 'Monday' to 'Sunday') |
M | the month as number without a leading zero (1-12) |
MM | the month as number with a leading zero (01-12) |
MMM | the abbreviated localized month name (e.g. 'Jan' to 'Dec') |
MMMM | the long localized month name (e.g. 'January' to 'December') |
yy | the year as two digit number (00-99) |
yyyy | the year as four digit number |
These expressions may be used for the time part of the format string:
Expression | Output |
---|
h | the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
hh | the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
H | the hour without a leading zero (0 to 23, even with AM/PM display) |
HH | the hour with a leading zero (00 to 23, even with AM/PM display) |
m | the minute without a leading zero (0 to 59) |
mm | the minute with a leading zero (00 to 59) |
s | the second without a leading zero (0 to 59) |
ss | the second with a leading zero (00 to 59) |
z | the milliseconds without leading zeroes (0 to 999) |
zzz | the milliseconds with leading zeroes (000 to 999) |
AP or A | interpret as an AM/PM time. AP must be either \"AM\" or \"PM\". |
ap or a | Interpret as an AM/PM time. ap must be either \"am\" or \"pm\". |
"}
+ {"arg":"format","description":"String template used to format the string. Expression | Output |
---|
d | the day as number without a leading zero (1 to 31) |
dd | the day as number with a leading zero (01 to 31) |
ddd | the abbreviated localized day name (e.g. 'Mon' to 'Sun') |
dddd | the long localized day name (e.g. 'Monday' to 'Sunday') |
M | the month as number without a leading zero (1-12) |
MM | the month as number with a leading zero (01-12) |
MMM | the abbreviated localized month name (e.g. 'Jan' to 'Dec') |
MMMM | the long localized month name (e.g. 'January' to 'December') |
yy | the year as two digit number (00-99) |
yyyy | the year as four digit number |
These expressions may be used for the time part of the format string:
Expression | Output |
---|
h | the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) |
hh | the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) |
H | the hour without a leading zero (0 to 23, even with AM/PM display) |
HH | the hour with a leading zero (00 to 23, even with AM/PM display) |
m | the minute without a leading zero (0 to 59) |
mm | the minute with a leading zero (00 to 59) |
s | the second without a leading zero (0 to 59) |
ss | the second with a leading zero (00 to 59) |
z | the milliseconds without leading zeroes (0 to 999) |
zzz | the milliseconds with leading zeroes (000 to 999) |
AP or A | interpret as an AM/PM time. AP must be either \"AM\" or \"PM\". |
ap or a | Interpret as an AM/PM time. ap must be either \"am\" or \"pm\". |
"},
+ {"arg":"language","optional":true,"description":"language (lowercase, two- or three-letter, ISO 639 language code) used to format the date into a custom string"}
],
- "examples": [ { "expression":"format_date('2012-05-15','dd.MM.yyyy')", "returns":"'15.05.2012'"}
+ "examples": [ { "expression":"format_date('2012-05-15','dd.MM.yyyy')", "returns":"'15.05.2012'"},
+ { "expression":"format_date('2012-05-15','d MMMM yyyy','fr')", "returns":"'15 juin 2012'"}
]
}
diff --git a/resources/function_help/json/to_date b/resources/function_help/json/to_date
index 7f0177df9ba..26b6d560009 100644
--- a/resources/function_help/json/to_date
+++ b/resources/function_help/json/to_date
@@ -4,10 +4,12 @@
"description": "Converts a string into a date object. An optional format string can be provided to parse the string; see QDate::fromString for additional documentation on the format.",
"arguments": [
{"arg":"string","description":"string representing a date value"},
- {"arg":"format","optional":true,"description":"format used to convert the string into a date"}
+ {"arg":"format","optional":true,"description":"format used to convert the string into a date"},
+ {"arg":"language","optional":true,"description":"language (lowercase, two- or three-letter, ISO 639 language code) used to convert the string into a date"}
],
"examples": [
{ "expression":"to_date('2012-05-04')", "returns":"2012-05-04"},
- { "expression":"to_date('June 29, 2019','MMMM d, yyyy')", "returns":"2019-06-29"}
+ { "expression":"to_date('June 29, 2019','MMMM d, yyyy')", "returns":"2019-06-29"},
+ { "expression":"to_date('29 juin, 2019','d MMMM, yyyy','fr')", "returns":"2019-06-29"}
]
}
diff --git a/resources/function_help/json/to_datetime b/resources/function_help/json/to_datetime
index 27f7573b8e6..e10ad2dd1dc 100644
--- a/resources/function_help/json/to_datetime
+++ b/resources/function_help/json/to_datetime
@@ -4,10 +4,12 @@
"description": "Converts a string into a datetime object. An optional format string can be provided to parse the string; see QDateTime::fromString for additional documentation on the format.",
"arguments": [
{"arg":"string","description":"string representing a datetime value"},
- {"arg":"format","optional":true,"description":"format used to convert the string into a date"}
+ {"arg":"format","optional":true,"description":"format used to convert the string into a datetime"},
+ {"arg":"language","optional":true,"description":"language (lowercase, two- or three-letter, ISO 639 language code) used to convert the string into a datetime"}
],
"examples": [
{ "expression":"to_datetime('2012-05-04 12:50:00')", "returns":"2012-05-04T12:50:00"},
- { "expression":"to_datetime('June 29, 2019 @ 12:34','MMMM d, yyyy @ HH:mm')", "returns":"2019-06-29T12:34"}
+ { "expression":"to_datetime('June 29, 2019 @ 12:34','MMMM d, yyyy @ HH:mm')", "returns":"2019-06-29T12:34"},
+ { "expression":"to_datetime('29 juin, 2019 @ 12:34','d MMMM, yyyy @ HH:mm','fr')", "returns":"2019-06-29T12:34"}
]
}
diff --git a/resources/function_help/json/to_time b/resources/function_help/json/to_time
index 3c65088ab89..87368153850 100644
--- a/resources/function_help/json/to_time
+++ b/resources/function_help/json/to_time
@@ -4,10 +4,12 @@
"description": "Converts a string into a time object. An optional format string can be provided to parse the string; see QTime::fromString for additional documentation on the format.",
"arguments": [
{"arg":"string","description":"string representing a time value"},
- {"arg":"format","optional":true,"description":"format used to convert the string into a date"}
+ {"arg":"format","optional":true,"description":"format used to convert the string into a time"},
+ {"arg":"language","optional":true,"description":"language (lowercase, two- or three-letter, ISO 639 language code) used to convert the string into a time"}
],
"examples": [
{ "expression":"to_time('12:30:01')", "returns":"12:30:01"},
- { "expression":"to_time('12:34','HH:mm')", "returns":"12:34:00"}
+ { "expression":"to_time('12:34','HH:mm')", "returns":"12:34:00"},
+ { "expression":"to_time('12:34','HH:mm','fr')", "returns":"12:34:00"}
]
}
diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp
index 64aa3f66331..033ec039927 100644
--- a/src/core/expression/qgsexpressionfunction.cpp
+++ b/src/core/expression/qgsexpressionfunction.cpp
@@ -1025,11 +1025,24 @@ static QVariant fcnToString( const QVariantList &values, const QgsExpressionCont
static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
- if ( format.isEmpty() )
+ QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
+ if ( format.isEmpty() && !language.isEmpty() )
+ {
+ parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
+ return QVariant( QDateTime() );
+ }
+
+ if ( format.isEmpty() && language.isEmpty() )
return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
- QDateTime datetime = QDateTime::fromString( datetimestring, format );
+ QLocale locale = QLocale();
+ if ( !language.isEmpty() )
+ {
+ locale = QLocale( language );
+ }
+
+ QDateTime datetime = locale.toDateTime( datetimestring, format );
if ( !datetime.isValid() )
{
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
@@ -1744,11 +1757,24 @@ static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsE
static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
- if ( format.isEmpty() )
+ QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
+ if ( format.isEmpty() && !language.isEmpty() )
+ {
+ parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
+ return QVariant( QDate() );
+ }
+
+ if ( format.isEmpty() && language.isEmpty() )
return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
- QDate date = QDate::fromString( datestring, format );
+ QLocale locale = QLocale();
+ if ( !language.isEmpty() )
+ {
+ locale = QLocale( language );
+ }
+
+ QDate date = locale.toDate( datestring, format );
if ( !date.isValid() )
{
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
@@ -1760,11 +1786,24 @@ static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContex
static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
- if ( format.isEmpty() )
+ QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
+ if ( format.isEmpty() && !language.isEmpty() )
+ {
+ parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
+ return QVariant( QTime() );
+ }
+
+ if ( format.isEmpty() && language.isEmpty() )
return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
- QTime time = QTime::fromString( timestring, format );
+ QLocale locale = QLocale();
+ if ( !language.isEmpty() )
+ {
+ locale = QLocale( language );
+ }
+
+ QTime time = locale.toTime( timestring, format );
if ( !time.isValid() )
{
parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
@@ -3885,9 +3924,16 @@ static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpression
static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
- QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
+ QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
- return dt.toString( format );
+ QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
+
+ QLocale locale = QLocale();
+ if ( !language.isEmpty() )
+ {
+ locale = QLocale( language );
+ }
+ return locale.toString( datetime, format );
}
static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
@@ -5184,9 +5230,9 @@ const QList &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toint" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "toreal" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tostring" ) )
- << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todatetime" ) )
- << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todate" ) )
- << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "totime" ) )
+ << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todatetime" ) )
+ << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todate" ) )
+ << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "totime" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "tointerval" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todm" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet(), false, QStringList() << QStringLiteral( "todms" ) )
@@ -5335,7 +5381,7 @@ const QList &QgsExpression::Functions()
<< new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ) ), fcnFormatNumber, QStringLiteral( "String" ) )
- << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
+ << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp
index 2b3c1258189..b30531bda3e 100644
--- a/tests/src/core/testqgsexpression.cpp
+++ b/tests/src/core/testqgsexpression.cpp
@@ -1294,10 +1294,16 @@ class TestQgsExpression: public QObject
QTest::newRow( "epoch" ) << "epoch(to_datetime('2017-01-01T00:00:01+00:00'))" << false << QVariant( 1483228801000LL );
QTest::newRow( "epoch invalid date" ) << "epoch('invalid')" << true << QVariant();
QTest::newRow( "date from format" ) << "to_date('June 29, 2019','MMMM d, yyyy')" << false << QVariant( QDate( 2019, 6, 29 ) );
+ QTest::newRow( "date from format and language" ) << "to_date('29 juin, 2019','d MMMM, yyyy','fr')" << false << QVariant( QDate( 2019, 6, 29 ) );
QTest::newRow( "date from format, wrong string" ) << "to_date('wrong.string.here','yyyy.MM.dd')" << true << QVariant();
QTest::newRow( "date from format, wrong format" ) << "to_date('2019-01-01','wrong')" << true << QVariant();
+ QTest::newRow( "date from format, language missing format" ) << "to_date('2019-01-01',language:='fr')" << true << QVariant();
QTest::newRow( "datetime from format" ) << "to_datetime('June 29, 2019 @ 11:00','MMMM d, yyyy @ HH:mm')" << false << QVariant( QDateTime( QDate( 2019, 6, 29 ), QTime( 11, 0 ) ) );
+ QTest::newRow( "datetime from format and language" ) << "to_datetime('29 juin, 2019 @ 11:00','d MMMM, yyyy @ HH:mm','fr')" << false << QVariant( QDateTime( QDate( 2019, 6, 29 ), QTime( 11, 0 ) ) );
QTest::newRow( "time from format" ) << "to_time('12:34:56','HH:mm:ss')" << false << QVariant( QTime( 12, 34, 56 ) );
+ QTest::newRow( "time from format and language" ) << "to_time('12:34:56','HH:mm:ss','fr')" << false << QVariant( QTime( 12, 34, 56 ) );
+ QTest::newRow( "formatted string from date" ) << "format_date('2019-06-29','MMMM d, yyyy')" << false << QVariant( QString( "June 29, 2019" ) );
+ QTest::newRow( "formatted string from date with language" ) << "format_date('2019-06-29','d MMMM yyyy','fr')" << false << QVariant( QString( "29 juin 2019" ) );
// Color functions
QTest::newRow( "ramp color" ) << "ramp_color('Spectral',0.3)" << false << QVariant( "254,190,116,255" );