Skip to content

enh: validate datetime fields - #8388

Open
luka-nextcloud wants to merge 5 commits into
mainfrom
enhance-datetime-handling
Open

luka-nextcloud wants to merge 5 commits into
mainfrom
enhance-datetime-handling

Conversation

@luka-nextcloud

@luka-nextcloud luka-nextcloud commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Improve validation for date fields in card creation and update operations, ensuring that dates are properly formatted and do not exceed the year 9999.
  • Also enhance datetime fields handling on frontend to prevent invalid date selections.

Checklist

  • Code is properly formatted
  • Sign-off message is added to all commits
  • Tests (unit, integration, api and/or acceptance) are included
  • Documentation (manuals or wiki) has been updated or is not required

@github-project-automation github-project-automation Bot moved this to 🧭 Planning evaluation (don't pick) in 📝 Productivity team Sep 15, 2026
@luka-nextcloud luka-nextcloud moved this from 🧭 Planning evaluation (don't pick) to 👀 In review in 📝 Productivity team Sep 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🐢 Performance warning.
It looks like the query count of the integration tests increased with this PR.
Database query count is now 204658 was 198420 (+3.14%)
Please check your code again. If you added a new test this can be expected and the base value in tests/integration/base-query-count.txt can be increased.

@blizzz blizzz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already stored values are not repaired, right? One-time repair job could be considered, or command. But I guess it is an edge case, though would be nice for affected users/admins to get out of it?

Comment thread lib/Validators/BaseValidator.php Outdated
Comment thread src/components/card/DueDateSelector.vue Outdated
Comment thread lib/Service/CardService.php Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🐢 Performance warning.
It looks like the query count of the integration tests increased with this PR.
Database query count is now 206571 was 198420 (+4.1%)
Please check your code again. If you added a new test this can be expected and the base value in tests/integration/base-query-count.txt can be increased.

@luka-nextcloud
luka-nextcloud force-pushed the enhance-datetime-handling branch from 6b2db55 to d733901 Compare October 1, 2026 09:58
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

🐢 Performance warning.
It looks like the query count of the integration tests increased with this PR.
Database query count is now 206568 was 198420 (+4.1%)
Please check your code again. If you added a new test this can be expected and the base value in tests/integration/base-query-count.txt can be increased.

@blizzz blizzz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sqlite is not affected, right?

Json import and Trello imports, are they out of scope, or is it worth checking the year there as well?

Comment thread lib/Command/FixCardDate.php
parent::__construct();
}

protected function configure() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this is a one time thing to run. What is the motivation to go with a command, not a one-time repair job?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is because admin should input the default year to fix those fields.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, if you were an admin, what would you choose? Like, is there a reason to pick any other number than 9999? What would you pinpoint it at?

Comment on lines +160 to +165
$allowedFormats = [
'Y-m-d',
'Y-m-d H:i:s',
'Y-m-d\TH:i:sP',
'Y-m-d\TH:i:s.v\Z',
];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to we need to be restrictive and selective on formats? Would not passing them to \DateTime(Immutable) and evaluating the year be sufficient?

For, other clients (thinking about the Deck mobile app) might send different formats that have been accepted so far.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth to restrict the formats by allowing common formats. I will check the Deck android app to ensure the format sent from mobile is supported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blizzz Could you please provide the datetime formats and some sample values sent from the mobile app so I can ensure the validation logic correctly cover those formats? I am not able to run the app because I have no way to get the apk of https://github.com/stefan-niedermann/nextcloud-deck

@blizzz blizzz Oct 9, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With external clients it can also be someone's curl script that then breaks.

According to Clause, Android Deck does this:

[Certain] Android app source (stefan-niedermann/nextcloud-deck, main branch):

  • deprecated/app/.../GsonUTCInstantAdapter.java serialises with DateTimeFormatter.ISO_INSTANT, giving …T10:15:30Z. That is rejected.
  • data/remote/.../OffsetDateTimeAdapter.java uses ISO_OFFSET_DATE_TIME. That writes Z for UTC and drops :ss when seconds are zero. Both are rejected.

and further

[Likely] So after this PR the app gets a 400 on every card update that has a due date. Third-party clients (n8n, scripts) will hit the same thing. docs/API.md only says "ISO-8601", and it never listed these four formats.

Suggestion:

try {
        $year = (int)(new \DateTimeImmutable($value))->format('Y');
} catch (\Exception) {
        return false;
}
return $year >= 1 && $year <= 9999;

which still validates and is format agnostic.

Comment thread lib/Validators/BaseValidator.php Outdated
$datetime = \DateTime::createFromFormat($format, $value);
if ($datetime && $datetime->format($format) === $value) {
// Check if the year is within the valid range
if ((int)$datetime->format('Y') > 9999) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at the moment, createFromFormat would not accept years larger than 9999:

php > $format = 'Y-m-d';
php > $datetime = \DateTime::createFromFormat($format, '13731-10-01');
php > var_dump($datetime);
bool(false)

So we have dead code here. Passing this directly to DateTime or DateTimeImmutable would turn this active though:

php > $datetime2 = new \DateTime('13731-10-01');
php > var_dump($datetime2);
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2001-10-01 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

php > $datetime3 = new \DateTimeImmutable('13731-10-01');
php > var_dump($datetime3);
object(DateTimeImmutable)#2 (3) {
  ["date"]=>
  string(26) "2001-10-01 00:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

Personally I prefer DateTimeImmutable when it is not supposed to change, but it aint a stopper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right about the dead code. I have removed it.
BTW, I still prefer using \DateTime::createFromFormat(), as the fact that it does not accept years greater than 9999 makes more sense from perspective of datetime validation.

Comment thread lib/Command/FixCardDate.php
Comment thread lib/Command/FixCardDate.php
Signed-off-by: Luka Trovic <luka@nextcloud.com>
Signed-off-by: Luka Trovic <luka@nextcloud.com>
Signed-off-by: Luka Trovic <luka@nextcloud.com>
Signed-off-by: Luka Trovic <luka@nextcloud.com>
Signed-off-by: Luka Trovic <luka@nextcloud.com>
@luka-nextcloud
luka-nextcloud force-pushed the enhance-datetime-handling branch from d733901 to 6055ec4 Compare October 8, 2026 12:34
@luka-nextcloud

Copy link
Copy Markdown
Contributor Author

sqlite is not affected, right?

Json import and Trello imports, are they out of scope, or is it worth checking the year there as well?

I have just checked. The sqlite was affected. I have updated the command FixCardDate to make it work for sqlite as well.
The date fields are already validated by \DateTime::createFromFormat()
https://github.com/nextcloud/deck/blob/main/lib/Service/Importer/Systems/DeckJsonService.php#L295-L297
https://github.com/nextcloud/deck/blob/main/lib/Service/Importer/Systems/TrelloJsonService.php#L293

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

🐢 Performance warning.
It looks like the query count of the integration tests increased with this PR.
Database query count is now 205983 was 198420 (+3.81%)
Please check your code again. If you added a new test this can be expected and the base value in tests/integration/base-query-count.txt can be increased.

$qb->expr()->isNotNull('startdate'),
$qb->expr()->isNotNull('done'),
));
$cards = $qb->executeQuery()->fetchAllAssociative();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one more suggestion: do not fetch all cards, but cycle through them one by one. We may risk OOM issues on big instances and maybe limit what we fetch – just the necessary fields. Description could be large, but is not needed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Invalid extreme date time value accepted in PostgreSQL causes "Double time specification" error

2 participants