From 38a927a35e14e726caafe6634a010b7444c54ff1 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 12 May 2026 17:38:56 +0300 Subject: [PATCH 1/4] Check if the date time contains milisecond Modified logic to convert convert utc date time --- app/filter/filtercontroller.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/filter/filtercontroller.cpp b/app/filter/filtercontroller.cpp index ec679c839..59bd2f1c0 100644 --- a/app/filter/filtercontroller.cpp +++ b/app/filter/filtercontroller.cpp @@ -262,7 +262,13 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) - expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDateTime().toUTC().toString( TIMESTAMP_FORMAT ) ) ); + { + const QDateTime utcDt = value.toDateTime().toUTC(); + const QString dtFormat = utcDt.time().msec() != 0 + ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) + : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); + expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDt.toString( dtFormat ) ) ); + } else expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); else @@ -358,7 +364,13 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons // QML always returns QDateTime so we can't differentiate based on type if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) - expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDateTime().toUTC().toString( TIMESTAMP_FORMAT ) ) ); + { + const QDateTime utcDt = value.toDateTime().toUTC(); + const QString dtFormat = utcDt.time().msec() != 0 + ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) + : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); + expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDt.toString( dtFormat ) ) ); + } else expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); else From 47c1ddb1fde4dd9b96cf2855c2357cec847313d4 Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Thu, 14 May 2026 12:35:56 +0300 Subject: [PATCH 2/4] Modified variable names to be more readable --- app/filter/filtercontroller.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/filter/filtercontroller.cpp b/app/filter/filtercontroller.cpp index 59bd2f1c0..d738e1e53 100644 --- a/app/filter/filtercontroller.cpp +++ b/app/filter/filtercontroller.cpp @@ -263,11 +263,11 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) { - const QDateTime utcDt = value.toDateTime().toUTC(); - const QString dtFormat = utcDt.time().msec() != 0 - ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) - : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); - expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDt.toString( dtFormat ) ) ); + const QDateTime utcDateTime = value.toDateTime().toUTC(); + const QString dateTimeFormat = utcDateTime.time().msec() != 0 + ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) + : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); + expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( dateTimeFormat ) ) ); } else expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); From 2de96e23f631a6d7f7bf38fae892eeb60683369d Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Thu, 14 May 2026 12:56:56 +0300 Subject: [PATCH 3/4] Added comment and renamed variables --- app/filter/filtercontroller.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/filter/filtercontroller.cpp b/app/filter/filtercontroller.cpp index d738e1e53..d9ee12edc 100644 --- a/app/filter/filtercontroller.cpp +++ b/app/filter/filtercontroller.cpp @@ -263,9 +263,10 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) { + // check if the DateTime contains milliseconds const QDateTime utcDateTime = value.toDateTime().toUTC(); const QString dateTimeFormat = utcDateTime.time().msec() != 0 - ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) + ? TIMESTAMP_FORMAT : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( dateTimeFormat ) ) ); } @@ -365,11 +366,12 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) { - const QDateTime utcDt = value.toDateTime().toUTC(); - const QString dtFormat = utcDt.time().msec() != 0 - ? QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ) - : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); - expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDt.toString( dtFormat ) ) ); + // check if the DateTime contains milliseconds + const QDateTime utcDateTime = value.toDateTime().toUTC(); + const QString dateTimeFormat = utcDateTime.time().msec() != 0 + ? TIMESTAMP_FORMAT + : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); + expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( dateTimeFormat ) ) ); } else expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); From bed2d9c6cee51584ca65fea8038387f5db6104ab Mon Sep 17 00:00:00 2001 From: Gabriel Bolbotina Date: Tue, 19 May 2026 21:07:19 +0300 Subject: [PATCH 4/4] Fixed edge case of 0 milliseconds Added constant for timestamp with no milliseconds --- app/filter/filtercontroller.cpp | 64 ++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/app/filter/filtercontroller.cpp b/app/filter/filtercontroller.cpp index d9ee12edc..a83c56bfc 100644 --- a/app/filter/filtercontroller.cpp +++ b/app/filter/filtercontroller.cpp @@ -23,6 +23,7 @@ #include "coreutils.h" const QString TIMESTAMP_FORMAT = QStringLiteral( "yyyy-MM-ddTHH:mm:ss.zzzZ" ); +const QString TIMESTAMP_FORMAT_NO_MS = QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); const QString DATE_FORMAT = QStringLiteral( "yyyy-MM-dd" ); FilterController::FilterController( QObject *parent ) @@ -263,17 +264,38 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons if ( value.typeId() == QMetaType::QDateTime ) if ( isDateFilterDateTime( filter.filterId ) ) { - // check if the DateTime contains milliseconds const QDateTime utcDateTime = value.toDateTime().toUTC(); - const QString dateTimeFormat = utcDateTime.time().msec() != 0 - ? TIMESTAMP_FORMAT - : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); - expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( dateTimeFormat ) ) ); + if ( utcDateTime.time().msec() != 0 ) + { + expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT ) ) ); + } + else + { + // if the msec() is 0, it could mean that either there are 0 milliseconds or none at all + // in this case add both filter queries and use 'OR' between them + QStringList dateTimeExpressions; + + // filter expression with milliseconds + QString exprWithMs( expressionCopy ); + exprWithMs.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT ) ) ); + dateTimeExpressions << QStringLiteral( "(%1)" ).arg( exprWithMs ); + + // filter expression without milliseconds + QString exprNoMs( expressionCopy ); + exprNoMs.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT_NO_MS ) ) ); + dateTimeExpressions << QStringLiteral( "(%1)" ).arg( exprNoMs ); + + expressionCopy = dateTimeExpressions.join( QStringLiteral( " OR " ) ); + } } else + { expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); + } else + { expressionCopy.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedValue( value ) ); + } } break; } @@ -364,21 +386,39 @@ QString FilterController::buildFieldExpression( const FieldFilter &filter ) cons { // QML always returns QDateTime so we can't differentiate based on type if ( value.typeId() == QMetaType::QDateTime ) + { if ( isDateFilterDateTime( filter.filterId ) ) { - // check if the DateTime contains milliseconds const QDateTime utcDateTime = value.toDateTime().toUTC(); - const QString dateTimeFormat = utcDateTime.time().msec() != 0 - ? TIMESTAMP_FORMAT - : QStringLiteral( "yyyy-MM-ddTHH:mm:ssZ" ); - expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( dateTimeFormat ) ) ); + if ( utcDateTime.time().msec() != 0 ) + { + expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT ) ) ); + expressions << QStringLiteral( "(%1)" ).arg( expressionTemplate ); + } + else + { + // if the msec() is 0, it could mean that either there are 0 milliseconds or none at all + // in this case, we create two filter expressions which are already joined by OR for multi-select + QString exprWithMs( expressionTemplate ); + exprWithMs.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT ) ) ); + expressions << QStringLiteral( "(%1)" ).arg( exprWithMs ); + + QString exprNoMs( expressionTemplate ); + exprNoMs.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( utcDateTime.toString( TIMESTAMP_FORMAT_NO_MS ) ) ); + expressions << QStringLiteral( "(%1)" ).arg( exprNoMs ); + } } else + { expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedString( value.toDate().toString( DATE_FORMAT ) ) ); + expressions << QStringLiteral( "(%1)" ).arg( expressionTemplate ); + } + } else + { expressionTemplate.replace( QStringLiteral( "@@value@@" ), QgsExpression::quotedValue( value ) ); - - expressions << QStringLiteral( "(%1)" ).arg( expressionTemplate ); + expressions << QStringLiteral( "(%1)" ).arg( expressionTemplate ); + } } } expressionCopy = expressions.join( QStringLiteral( " OR " ) );