Repository: owncloud/ocis
Pull request #7261 (2023-09-11 09:22:49)

[tests-only][full-ci] removing the setresponse in given/then step in TrashbinContext and TagsContext
## Description
We have used setResponse() and $this->response in the Given/Then steps and some helper functions (maybe to reuse existing available methods). But storing responses from Given/Then steps and helper functions is not a good idea because it can lead to a false positive assertion in the Then steps.
So, check the use of setResponse() and $this->response in
- Given steps
- Then steps (Then steps can use $this->response but must prevent saving to it)
- Helper functions

So this pr make the above changes in `TrashbinContext.php` and `TagsContext.php`
## Related Issue
https://github.com/owncloud/ocis/issues/7082

## Motivation and Context
- To  remove setResponse() and $this->response in the Given/Then steps and some helper functions
- To avoid false positive assertions

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
- test environment:
- locally
- CI

## Screenshots (if appropriate):

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Technical debt
- [X] Tests only (no source changes)

## Checklist:
<!-- Tick the checkboxes when done. -->
<!-- Raise documentation ticket in owncloud.github.io/ -->
- [ ] Code changes
- [ ] Unit tests added
- [ ] Acceptance tests added
- [ ] Documentation ticket raised: <link> 

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=7261)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=7261&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=7261&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=7261&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce5af6ab1cd..6e45b6b7dd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -80,16 +80,31 @@ The following sections list the changes for unreleased.
    [KQL](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference)
    as the default oCIS search query language.
 
-   Some examples of a valid KQL query are:
-
-  * `Tag`: `tag:golden tag:"silver"`
-  * `Filename`: `name:file.txt name:"file.docx"`
-  * `Content`: `content:ahab content:"captain aha*"`
+   Simple queries:
+
+  * `tag:golden tag:"silver"`
+  * `name:file.txt name:"file.docx"`
+  * `content:ahab content:"captain aha*"`
+
+   Date/-range queries
+
+  * `Mtime:"2023-09-05T08:42:11.23554+02:00"`
+  * `Mtime>"2023-09-05T08:42:11.23554+02:00"`
+  * `Mtime>="2023-09-05T08:42:11.23554+02:00"`
+  * `Mtime<"2023-09-05T08:42:11.23554+02:00"`
+  * `Mtime<="2023-09-05T08:42:11.23554+02:00"`
+  * `Mtime:today` - range: start of today till end of today
+  * `Mtime:yesterday` - range: start of yesterday till end of yesterday
+  * `Mtime:"this week"` - range: start of this week till end of this week
+  * `Mtime:"this month"` - range: start of this month till end of this month
+  * `Mtime:"last month"` - range: start of last month till end of last month
+  * `Mtime:"this year"` - range: start of this year till end of this year
+  * `Mtime:"last year"` - range: start of last year till end of last year
 
    Conjunctive normal form queries:
 
-  * `Boolean`: `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver`
-  * `Group`: `(tag:book content:ahab*)`, `tag:(book pdf)`
+  * `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver`
+  * `(tag:book content:ahab*)`, `tag:(book pdf)`
 
    Complex queries:
 
@@ -105,6 +120,7 @@ The following sections list the changes for unreleased.
    https://github.com/owncloud/ocis/pull/7247
    https://github.com/owncloud/ocis/pull/7248
    https://github.com/owncloud/ocis/pull/7254
+   https://github.com/owncloud/ocis/pull/7262
    https://github.com/owncloud/web/pull/9653
    https://github.com/owncloud/web/pull/9672
 
diff --git a/changelog/unreleased/kql-search-query-language.md b/changelog/unreleased/kql-search-query-language.md
index 0749e09dff9..f1724114ca8 100644
--- a/changelog/unreleased/kql-search-query-language.md
+++ b/changelog/unreleased/kql-search-query-language.md
@@ -2,16 +2,31 @@ Enhancement: Keyword Query Language (KQL) search syntax
 
 We've introduced support for [KQL](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference) as the default oCIS search query language.
 
-Some examples of a valid KQL query are:
-
-* `Tag`: `tag:golden tag:"silver"`
-* `Filename`: `name:file.txt name:"file.docx"`
-* `Content`: `content:ahab content:"captain aha*"`
+Simple queries:
+
+* `tag:golden tag:"silver"`
+* `name:file.txt name:"file.docx"`
+* `content:ahab content:"captain aha*"`
+
+Date/-range queries
+
+* `Mtime:"2023-09-05T08:42:11.23554+02:00"`
+* `Mtime>"2023-09-05T08:42:11.23554+02:00"`
+* `Mtime>="2023-09-05T08:42:11.23554+02:00"`
+* `Mtime<"2023-09-05T08:42:11.23554+02:00"`
+* `Mtime<="2023-09-05T08:42:11.23554+02:00"`
+* `Mtime:today` - range: start of today till end of today
+* `Mtime:yesterday` - range: start of yesterday till end of yesterday
+* `Mtime:"this week"` - range: start of this week till end of this week
+* `Mtime:"this month"` - range: start of this month till end of this month
+* `Mtime:"last month"` - range: start of last month till end of last month
+* `Mtime:"this year"` - range: start of this year till end of this year
+* `Mtime:"last year"` - range: start of last year till end of last year
 
 Conjunctive normal form queries:
 
-* `Boolean`: `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver`
-* `Group`: `(tag:book content:ahab*)`, `tag:(book pdf)`
+* `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver`
+* `(tag:book content:ahab*)`, `tag:(book pdf)`
 
 Complex queries:
 
@@ -22,6 +37,7 @@ https://github.com/owncloud/ocis/pull/7043
 https://github.com/owncloud/ocis/pull/7247
 https://github.com/owncloud/ocis/pull/7248
 https://github.com/owncloud/ocis/pull/7254
+https://github.com/owncloud/ocis/pull/7262
 https://github.com/owncloud/web/pull/9653
 https://github.com/owncloud/web/pull/9672
 https://github.com/owncloud/ocis/issues/7042
diff --git a/go.mod b/go.mod
index a64f307ddba..20ceae213a6 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,6 @@ require (
 	github.com/Masterminds/semver v1.5.0
 	github.com/MicahParks/keyfunc v1.9.0
 	github.com/Nerzal/gocloak/v13 v13.8.0
-	github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
 	github.com/bbalet/stopwords v1.0.0
 	github.com/blevesearch/bleve/v2 v2.3.9
 	github.com/coreos/go-oidc v2.2.1+incompatible
@@ -50,6 +49,7 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0
 	github.com/jellydator/ttlcache/v2 v2.11.1
 	github.com/jellydator/ttlcache/v3 v3.1.0
+	github.com/jinzhu/now v1.1.5
 	github.com/justinas/alice v1.2.0
 	github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1
 	github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5
diff --git a/go.sum b/go.sum
index fbeb6004b5a..9af77a03b97 100644
--- a/go.sum
+++ b/go.sum
@@ -859,8 +859,6 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x
 github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
-github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
-github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
 github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
 github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -1525,6 +1523,8 @@ github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVK
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -1642,7 +1642,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@@ -1866,7 +1865,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo=
 github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
 github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -1898,7 +1896,6 @@ github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS
 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
 github.com/sciencemesh/meshdirectory-web v1.0.4 h1:1YSctF6PAXhoHUYCaeRTj7rHaF7b3rYrZf2R0VXBIbo=
 github.com/sciencemesh/meshdirectory-web v1.0.4/go.mod h1:fJSThTS3xf+sTdL0iXQoaQJssLI7tn7DetHMHUl4SRk=
-github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go
index d9710d35ce1..e3b9ab58106 100644
--- a/services/search/pkg/query/kql/cast.go
+++ b/services/search/pkg/query/kql/cast.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/araddon/dateparse"
+	"github.com/jinzhu/now"
 
 	"github.com/owncloud/ocis/v2/services/search/pkg/query/ast"
 )
@@ -21,21 +21,23 @@ func toNode[T ast.Node](in interface{}) (T, error) {
 
 func toNodes[T ast.Node](in interface{}) ([]T, error) {
 	switch v := in.(type) {
-	case []T:
-		return v, nil
 	case T:
 		return []T{v}, nil
+	case []T:
+		return v, nil
+	case []*ast.OperatorNode, []*ast.DateTimeNode:
+		return toNodes[T](v)
 	case []interface{}:
-		var ts []T
-		for _, inter := range v {
-			n, err := toNodes[T](inter)
+		var nodes []T
+		for _, el := range v {
+			node, err := toNodes[T](el)
 			if err != nil {
 				return nil, err
 			}
 
-			ts = append(ts, n...)
+			nodes = append(nodes, node...)
 		}
-		return ts, nil
+		return nodes, nil
 	case nil:
 		return nil, nil
 	default:
@@ -74,5 +76,53 @@ func toTime(in interface{}) (time.Time, error) {
 		return time.Time{}, err
 	}
 
-	return dateparse.ParseLocal(ts)
+	return now.Parse(ts)
+}
+
+func toTimeRange(in interface{}) (*time.Time, *time.Time, error) {
+	var from, to time.Time
+
+	value, err := toString(in)
+	if err != nil {
+		return &from, &to, UnsupportedTimeRangeError{}
+	}
+
+	c := &now.Config{
+		WeekStartDay: time.Monday,
+	}
+
+	n := c.With(timeNow())
+
+	switch value {
+	case "today":
+		from = n.BeginningOfDay()
+		to = n.EndOfDay()
+	case "yesterday":
+		yesterday := n.With(n.AddDate(0, 0, -1))
+		from = yesterday.BeginningOfDay()
+		to = yesterday.EndOfDay()
+	case "this week":
+		from = n.BeginningOfWeek()
+		to = n.EndOfWeek()
+	case "this month":
+		from = n.BeginningOfMonth()
+		to = n.EndOfMonth()
+	case "last month":
+		lastMonth := n.With(n.AddDate(0, -1, 0))
+		from = lastMonth.BeginningOfMonth()
+		to = lastMonth.EndOfMonth()
+	case "this year":
+		from = n.BeginningOfYear()
+		to = n.EndOfYear()
+	case "last year":
+		lastYear := n.With(n.AddDate(-1, 0, 0))
+		from = lastYear.BeginningOfYear()
+		to = lastYear.EndOfYear()
+	}
+
+	if from.IsZero() || to.IsZero() {
+		return nil, nil, UnsupportedTimeRangeError{}
+	}
+
+	return &from, &to, nil
 }
diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg
index ca9658dfa53..426e4d5091f 100644
--- a/services/search/pkg/query/kql/dictionary.peg
+++ b/services/search/pkg/query/kql/dictionary.peg
@@ -61,6 +61,12 @@ DateTimeRestrictionNode <-
         FullTime
     ) '"'? {
         return buildDateTimeNode(k, o, v, c.text, c.pos)
+    } /
+    k:Char+ (
+        OperatorEqualNode /
+        OperatorColonNode
+    ) '"'? v:NaturalLanguageDateTime '"'? {
+        return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos)
     }
 
 TextPropertyRestrictionNode <-
@@ -185,11 +191,22 @@ FullTime <-
         return c.text, nil
     }
 
-DateTime
-  = FullDate "T" FullTime {
+DateTime  <-
+    FullDate "T" FullTime {
       return c.text, nil
     }
 
+NaturalLanguageDateTime  <-
+    "today" /
+    "yesterday" /
+    "this week" /
+    "this month" /
+    "last month" /
+    "this year" /
+    "last year"  {
+        return c.text, nil
+    }
+
 ////////////////////////////////////////////////////////
 // misc
 ////////////////////////////////////////////////////////
diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go
index afce6c5c85e..4bcd4f83b02 100644
--- a/services/search/pkg/query/kql/dictionary_gen.go
+++ b/services/search/pkg/query/kql/dictionary_gen.go
@@ -43,12 +43,12 @@ var g = &grammar{
 					pos: position{line: 19, col: 6, offset: 351},
 					exprs: []any{
 						&actionExpr{
-							pos: position{line: 213, col: 5, offset: 4364},
+							pos: position{line: 230, col: 5, offset: 4745},
 							run: (*parser).callonNodes3,
 							expr: &zeroOrMoreExpr{
-								pos: position{line: 213, col: 5, offset: 4364},
+								pos: position{line: 230, col: 5, offset: 4745},
 								expr: &charClassMatcher{
-									pos:        position{line: 213, col: 5, offset: 4364},
+									pos:        position{line: 230, col: 5, offset: 4745},
 									val:        "[ \\t]",
 									chars:      []rune{' ', '\t'},
 									ignoreCase: false,
@@ -86,10 +86,10 @@ var g = &grammar{
 									expr: &oneOrMoreExpr{
 										pos: position{line: 46, col: 7, offset: 1044},
 										expr: &actionExpr{
-											pos: position{line: 198, col: 5, offset: 4194},
+											pos: position{line: 215, col: 5, offset: 4575},
 											run: (*parser).callonNode7,
 											expr: &charClassMatcher{
-												pos:        position{line: 198, col: 5, offset: 4194},
+												pos:        position{line: 215, col: 5, offset: 4575},
 												val:        "[A-Za-z]",
 												ranges:     []rune{'A', 'Z', 'a', 'z'},
 												ignoreCase: false,
@@ -102,20 +102,20 @@ var g = &grammar{
 									pos: position{line: 46, col: 14, offset: 1051},
 									alternatives: []any{
 										&actionExpr{
-											pos: position{line: 114, col: 5, offset: 2721},
+											pos: position{line: 120, col: 5, offset: 2911},
 											run: (*parser).callonNode10,
 											expr: &litMatcher{
-												pos:        position{line: 114, col: 5, offset: 2721},
+												pos:        position{line: 120, col: 5, offset: 2911},
 												val:        ":",
 												ignoreCase: false,
 												want:       "\":\"",
 											},
 										},
 										&actionExpr{
-											pos: position{line: 119, col: 5, offset: 2807},
+											pos: position{line: 125, col: 5, offset: 2997},
 											run: (*parser).callonNode12,
 											expr: &litMatcher{
-												pos:        position{line: 119, col: 5, offset: 2807},
+												pos:        position{line: 125, col: 5, offset: 2997},
 												val:        "=",
 												ignoreCase: false,
 												want:       "\"=\"",
@@ -159,10 +159,10 @@ var g = &grammar{
 									expr: &oneOrMoreExpr{
 										pos: position{line: 51, col: 7, offset: 1205},
 										expr: &actionExpr{
-											pos: position{line: 198, col: 5, offset: 4194},
+											pos: position{line: 215, col: 5, offset: 4575},
 											run: (*parser).callonNode22,
 											expr: &charClassMatcher{
-												pos:        position{line: 198, col: 5, offset: 4194},
+												pos:        position{line: 215, col: 5, offset: 4575},
 												val:        "[A-Za-z]",
 												ranges:     []rune{'A', 'Z', 'a', 'z'},
 												ignoreCase: false,
@@ -178,60 +178,60 @@ var g = &grammar{
 										pos: position{line: 52, col: 9, offset: 1223},
 										alternatives: []any{
 											&actionExpr{
-												pos: position{line: 139, col: 5, offset: 3168},
+												pos: position{line: 145, col: 5, offset: 3358},
 												run: (*parser).callonNode26,
 												expr: &litMatcher{
-													pos:        position{line: 139, col: 5, offset: 3168},
+													pos:        position{line: 145, col: 5, offset: 3358},
 													val:        ">=",
 													ignoreCase: false,
 													want:       "\">=\"",
 												},
 											},
 											&actionExpr{
-												pos: position{line: 129, col: 5, offset: 2984},
+												pos: position{line: 135, col: 5, offset: 3174},
 												run: (*parser).callonNode28,
 												expr: &litMatcher{
-													pos:        position{line: 129, col: 5, offset: 2984},
+													pos:        position{line: 135, col: 5, offset: 3174},
 													val:        "<=",
 													ignoreCase: false,
 													want:       "\"<=\"",
 												},
 											},
 											&actionExpr{
-												pos: position{line: 134, col: 5, offset: 3073},
+												pos: position{line: 140, col: 5, offset: 3263},
 												run: (*parser).callonNode30,
 												expr: &litMatcher{
-													pos:        position{line: 134, col: 5, offset: 3073},
+													pos:        position{line: 140, col: 5, offset: 3263},
 													val:        ">",
 													ignoreCase: false,
 													want:       "\">\"",
 												},
 											},
 											&actionExpr{
-												pos: position{line: 124, col: 5, offset: 2892},
+												pos: position{line: 130, col: 5, offset: 3082},
 												run: (*parser).callonNode32,
 												expr: &litMatcher{
-													pos:        position{line: 124, col: 5, offset: 2892},
+													pos:        position{line: 130, col: 5, offset: 3082},
 													val:        "<",
 													ignoreCase: false,
 													want:       "\"<\"",
 												},
 											},
 											&actionExpr{
-												pos: position{line: 119, col: 5, offset: 2807},
+												pos: position{line: 125, col: 5, offset: 2997},
 												run: (*parser).callonNode34,
 												expr: &litMatcher{
-													pos:        position{line: 119, col: 5, offset: 2807},
+													pos:        position{line: 125, col: 5, offset: 2997},
 													val:        "=",
 													ignoreCase: false,
 													want:       "\"=\"",
 												},
 											},
 											&actionExpr{
-												pos: position{line: 114, col: 5, offset: 2721},
+												pos: position{line: 120, col: 5, offset: 2911},
 												run: (*parser).callonNode36,
 												expr: &litMatcher{
-													pos:        position{line: 114, col: 5, offset: 2721},
+													pos:        position{line: 120, col: 5, offset: 2911},
 													val:        ":",
 													ignoreCase: false,
 													want:       "\":\"",
@@ -256,28 +256,28 @@ var g = &grammar{
 										pos: position{line: 59, col: 9, offset: 1420},
 										alternatives: []any{
 											&actionExpr{
-												pos: position{line: 189, col: 5, offset: 4003},
+												pos: position{line: 195, col: 5, offset: 4197},
 												run: (*parser).callonNode42,
 												expr: &seqExpr{
-													pos: position{line: 189, col: 5, offset: 4003},
+													pos: position{line: 195, col: 5, offset: 4197},
 													exprs: []any{
 														&actionExpr{
-															pos: position{line: 179, col: 5, offset: 3770},
+															pos: position{line: 185, col: 5, offset: 3960},
 															run: (*parser).callonNode44,
 															expr: &seqExpr{
-																pos: position{line: 179, col: 5, offset: 3770},
+																pos: position{line: 185, col: 5, offset: 3960},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 149, col: 5, offset: 3370},
+																		pos: position{line: 155, col: 5, offset: 3560},
 																		run: (*parser).callonNode46,
 																		expr: &seqExpr{
-																			pos: position{line: 149, col: 5, offset: 3370},
+																			pos: position{line: 155, col: 5, offset: 3560},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode48,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -285,10 +285,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode50,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -296,10 +296,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode52,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -307,10 +307,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode54,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -321,22 +321,22 @@ var g = &grammar{
 																		},
 																	},
 																	&litMatcher{
-																		pos:        position{line: 179, col: 14, offset: 3779},
+																		pos:        position{line: 185, col: 14, offset: 3969},
 																		val:        "-",
 																		ignoreCase: false,
 																		want:       "\"-\"",
 																	},
 																	&actionExpr{
-																		pos: position{line: 154, col: 5, offset: 3447},
+																		pos: position{line: 160, col: 5, offset: 3637},
 																		run: (*parser).callonNode57,
 																		expr: &seqExpr{
-																			pos: position{line: 154, col: 5, offset: 3447},
+																			pos: position{line: 160, col: 5, offset: 3637},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode59,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -344,10 +344,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode61,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -358,22 +358,22 @@ var g = &grammar{
 																		},
 																	},
 																	&litMatcher{
-																		pos:        position{line: 179, col: 28, offset: 3793},
+																		pos:        position{line: 185, col: 28, offset: 3983},
 																		val:        "-",
 																		ignoreCase: false,
 																		want:       "\"-\"",
 																	},
 																	&actionExpr{
-																		pos: position{line: 159, col: 5, offset: 3510},
+																		pos: position{line: 165, col: 5, offset: 3700},
 																		run: (*parser).callonNode64,
 																		expr: &seqExpr{
-																			pos: position{line: 159, col: 5, offset: 3510},
+																			pos: position{line: 165, col: 5, offset: 3700},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode66,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -381,10 +381,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode68,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -398,28 +398,28 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 189, col: 14, offset: 4012},
+															pos:        position{line: 195, col: 14, offset: 4206},
 															val:        "T",
 															ignoreCase: false,
 															want:       "\"T\"",
 														},
 														&actionExpr{
-															pos: position{line: 184, col: 5, offset: 3857},
+															pos: position{line: 190, col: 5, offset: 4047},
 															run: (*parser).callonNode71,
 															expr: &seqExpr{
-																pos: position{line: 184, col: 5, offset: 3857},
+																pos: position{line: 190, col: 5, offset: 4047},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 164, col: 5, offset: 3574},
+																		pos: position{line: 170, col: 5, offset: 3764},
 																		run: (*parser).callonNode73,
 																		expr: &seqExpr{
-																			pos: position{line: 164, col: 5, offset: 3574},
+																			pos: position{line: 170, col: 5, offset: 3764},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode75,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -427,10 +427,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode77,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -441,22 +441,22 @@ var g = &grammar{
 																		},
 																	},
 																	&litMatcher{
-																		pos:        position{line: 184, col: 14, offset: 3866},
+																		pos:        position{line: 190, col: 14, offset: 4056},
 																		val:        ":",
 																		ignoreCase: false,
 																		want:       "\":\"",
 																	},
 																	&actionExpr{
-																		pos: position{line: 169, col: 5, offset: 3640},
+																		pos: position{line: 175, col: 5, offset: 3830},
 																		run: (*parser).callonNode80,
 																		expr: &seqExpr{
-																			pos: position{line: 169, col: 5, offset: 3640},
+																			pos: position{line: 175, col: 5, offset: 3830},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode82,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -464,10 +464,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode84,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -478,22 +478,22 @@ var g = &grammar{
 																		},
 																	},
 																	&litMatcher{
-																		pos:        position{line: 184, col: 29, offset: 3881},
+																		pos:        position{line: 190, col: 29, offset: 4071},
 																		val:        ":",
 																		ignoreCase: false,
 																		want:       "\":\"",
 																	},
 																	&actionExpr{
-																		pos: position{line: 174, col: 5, offset: 3706},
+																		pos: position{line: 180, col: 5, offset: 3896},
 																		run: (*parser).callonNode87,
 																		expr: &seqExpr{
-																			pos: position{line: 174, col: 5, offset: 3706},
+																			pos: position{line: 180, col: 5, offset: 3896},
 																			exprs: []any{
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode89,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -501,10 +501,10 @@ var g = &grammar{
 																					},
 																				},
 																				&actionExpr{
-																					pos: position{line: 208, col: 5, offset: 4313},
+																					pos: position{line: 225, col: 5, offset: 4694},
 																					run: (*parser).callonNode91,
 																					expr: &charClassMatcher{
-																						pos:        position{line: 208, col: 5, offset: 4313},
+																						pos:        position{line: 225, col: 5, offset: 4694},
 																						val:        "[0-9]",
 																						ranges:     []rune{'0', '9'},
 																						ignoreCase: false,
@@ -515,23 +515,23 @@ var g = &grammar{
 																		},
 																	},
 																	&zeroOrOneExpr{
-																		pos: position{line: 184, col: 44, offset: 3896},
+																		pos: position{line: 190, col: 44, offset: 4086},
 																		expr: &seqExpr{
-																			pos: position{line: 184, col: 45, offset: 3897},
+																			pos: position{line: 190, col: 45, offset: 4087},
 																			exprs: []any{
 																				&litMatcher{
-																					pos:        position{line: 184, col: 45, offset: 3897},
+																					pos:        position{line: 190, col: 45, offset: 4087},
 																					val:        ".",
 																					ignoreCase: false,
 																					want:       "\".\"",
 																				},
 																				&oneOrMoreExpr{
-																					pos: position{line: 184, col: 49, offset: 3901},
+																					pos: position{line: 190, col: 49, offset: 4091},
 																					expr: &actionExpr{
-																						pos: position{line: 208, col: 5, offset: 4313},
+																						pos: position{line: 225, col: 5, offset: 4694},
 																						run: (*parser).callonNode97,
 																						expr: &charClassMatcher{
-																							pos:        position{line: 208, col: 5, offset: 4313},
+																							pos:        position{line: 225, col: 5, offset: 4694},
 																							val:        "[0-9]",
 																							ranges:     []rune{'0', '9'},
 																							ignoreCase: false,
@@ -543,35 +543,35 @@ var g = &grammar{
 																		},
 																	},
 																	&choiceExpr{
-																		pos: position{line: 184, col: 59, offset: 3911},
+																		pos: position{line: 190, col: 59, offset: 4101},
 																		alternatives: []any{
 																			&litMatcher{
-																				pos:        position{line: 184, col: 59, offset: 3911},
+																				pos:        position{line: 190, col: 59, offset: 4101},
 																				val:        "Z",
 																				ignoreCase: false,
 																				want:       "\"Z\"",
 																			},
 																			&seqExpr{
-																				pos: position{line: 184, col: 65, offset: 3917},
+																				pos: position{line: 190, col: 65, offset: 4107},
 																				exprs: []any{
 																					&charClassMatcher{
-																						pos:        position{line: 184, col: 66, offset: 3918},
+																						pos:        position{line: 190, col: 66, offset: 4108},
 																						val:        "[+-]",
 																						chars:      []rune{'+', '-'},
 																						ignoreCase: false,
 																						inverted:   false,
 																					},
 																					&actionExpr{
-																						pos: position{line: 164, col: 5, offset: 3574},
+																						pos: position{line: 170, col: 5, offset: 3764},
 																						run: (*parser).callonNode103,
 																						expr: &seqExpr{
-																							pos: position{line: 164, col: 5, offset: 3574},
+																							pos: position{line: 170, col: 5, offset: 3764},
 																							exprs: []any{
 																								&actionExpr{
-																									pos: position{line: 208, col: 5, offset: 4313},
+																									pos: position{line: 225, col: 5, offset: 4694},
 																									run: (*parser).callonNode105,
 																									expr: &charClassMatcher{
-																										pos:        position{line: 208, col: 5, offset: 4313},
+																										pos:        position{line: 225, col: 5, offset: 4694},
 																										val:        "[0-9]",
 																										ranges:     []rune{'0', '9'},
 																										ignoreCase: false,
@@ -579,10 +579,10 @@ var g = &grammar{
 																									},
 																								},
 																								&actionExpr{
-																									pos: position{line: 208, col: 5, offset: 4313},
+																									pos: position{line: 225, col: 5, offset: 4694},
 																									run: (*parser).callonNode107,
 																									expr: &charClassMatcher{
-																										pos:        position{line: 208, col: 5, offset: 4313},
+																										pos:        position{line: 225, col: 5, offset: 4694},
 																										val:        "[0-9]",
 																										ranges:     []rune{'0', '9'},
 																										ignoreCase: false,
@@ -593,22 +593,22 @@ var g = &grammar{
 																						},
 																					},
 																					&litMatcher{
-																						pos:        position{line: 184, col: 86, offset: 3938},
+																						pos:        position{line: 190, col: 86, offset: 4128},
 																						val:        ":",
 																						ignoreCase: false,
 																						want:       "\":\"",
 																					},
 																					&actionExpr{
-																						pos: position{line: 169, col: 5, offset: 3640},
+																						pos: position{line: 175, col: 5, offset: 3830},
 																						run: (*parser).callonNode110,
 																						expr: &seqExpr{
-																							pos: position{line: 169, col: 5, offset: 3640},
+																							pos: position{line: 175, col: 5, offset: 3830},
 																							exprs: []any{
 																								&actionExpr{
-																									pos: position{line: 208, col: 5, offset: 4313},
+																									pos: position{line: 225, col: 5, offset: 4694},
 																									run: (*parser).callonNode112,
 																									expr: &charClassMatcher{
-																										pos:        position{line: 208, col: 5, offset: 4313},
+																										pos:        position{line: 225, col: 5, offset: 4694},
 																										val:        "[0-9]",
 																										ranges:     []rune{'0', '9'},
 																										ignoreCase: false,
@@ -616,10 +616,10 @@ var g = &grammar{
 																									},
 																								},
 																								&actionExpr{
-																									pos: position{line: 208, col: 5, offset: 4313},
+																									pos: position{line: 225, col: 5, offset: 4694},
 																									run: (*parser).callonNode114,
 																									expr: &charClassMatcher{
-																										pos:        position{line: 208, col: 5, offset: 4313},
+																										pos:        position{line: 225, col: 5, offset: 4694},
 																										val:        "[0-9]",
 																										ranges:     []rune{'0', '9'},
 																										ignoreCase: false,
@@ -640,22 +640,22 @@ var g = &grammar{
 												},
 											},
 											&actionExpr{
-												pos: position{line: 179, col: 5, offset: 3770},
+												pos: position{line: 185, col: 5, offset: 3960},
 												run: (*parser).callonNode116,
 												expr: &seqExpr{
-													pos: position{line: 179, col: 5, offset: 3770},
+													pos: position{line: 185, col: 5, offset: 3960},
 													exprs: []any{
 														&actionExpr{
-															pos: position{line: 149, col: 5, offset: 3370},
+															pos: position{line: 155, col: 5, offset: 3560},
 															run: (*parser).callonNode118,
 															expr: &seqExpr{
-																pos: position{line: 149, col: 5, offset: 3370},
+																pos: position{line: 155, col: 5, offset: 3560},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode120,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -663,10 +663,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode122,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -674,10 +674,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode124,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -685,10 +685,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode126,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -699,22 +699,22 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 179, col: 14, offset: 3779},
+															pos:        position{line: 185, col: 14, offset: 3969},
 															val:        "-",
 															ignoreCase: false,
 															want:       "\"-\"",
 														},
 														&actionExpr{
-															pos: position{line: 154, col: 5, offset: 3447},
+															pos: position{line: 160, col: 5, offset: 3637},
 															run: (*parser).callonNode129,
 															expr: &seqExpr{
-																pos: position{line: 154, col: 5, offset: 3447},
+																pos: position{line: 160, col: 5, offset: 3637},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode131,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -722,10 +722,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode133,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -736,22 +736,22 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 179, col: 28, offset: 3793},
+															pos:        position{line: 185, col: 28, offset: 3983},
 															val:        "-",
 															ignoreCase: false,
 															want:       "\"-\"",
 														},
 														&actionExpr{
-															pos: position{line: 159, col: 5, offset: 3510},
+															pos: position{line: 165, col: 5, offset: 3700},
 															run: (*parser).callonNode136,
 															expr: &seqExpr{
-																pos: position{line: 159, col: 5, offset: 3510},
+																pos: position{line: 165, col: 5, offset: 3700},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode138,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -759,10 +759,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode140,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -776,22 +776,22 @@ var g = &grammar{
 												},
 											},
 											&actionExpr{
-												pos: position{line: 184, col: 5, offset: 3857},
+												pos: position{line: 190, col: 5, offset: 4047},
 												run: (*parser).callonNode142,
 												expr: &seqExpr{
-													pos: position{line: 184, col: 5, offset: 3857},
+													pos: position{line: 190, col: 5, offset: 4047},
 													exprs: []any{
 														&actionExpr{
-															pos: position{line: 164, col: 5, offset: 3574},
+															pos: position{line: 170, col: 5, offset: 3764},
 															run: (*parser).callonNode144,
 															expr: &seqExpr{
-																pos: position{line: 164, col: 5, offset: 3574},
+																pos: position{line: 170, col: 5, offset: 3764},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode146,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -799,10 +799,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode148,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -813,22 +813,22 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 184, col: 14, offset: 3866},
+															pos:        position{line: 190, col: 14, offset: 4056},
 															val:        ":",
 															ignoreCase: false,
 															want:       "\":\"",
 														},
 														&actionExpr{
-															pos: position{line: 169, col: 5, offset: 3640},
+															pos: position{line: 175, col: 5, offset: 3830},
 															run: (*parser).callonNode151,
 															expr: &seqExpr{
-																pos: position{line: 169, col: 5, offset: 3640},
+																pos: position{line: 175, col: 5, offset: 3830},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode153,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -836,10 +836,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode155,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -850,22 +850,22 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 184, col: 29, offset: 3881},
+															pos:        position{line: 190, col: 29, offset: 4071},
 															val:        ":",
 															ignoreCase: false,
 															want:       "\":\"",
 														},
 														&actionExpr{
-															pos: position{line: 174, col: 5, offset: 3706},
+															pos: position{line: 180, col: 5, offset: 3896},
 															run: (*parser).callonNode158,
 															expr: &seqExpr{
-																pos: position{line: 174, col: 5, offset: 3706},
+																pos: position{line: 180, col: 5, offset: 3896},
 																exprs: []any{
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode160,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -873,10 +873,10 @@ var g = &grammar{
 																		},
 																	},
 																	&actionExpr{
-																		pos: position{line: 208, col: 5, offset: 4313},
+																		pos: position{line: 225, col: 5, offset: 4694},
 																		run: (*parser).callonNode162,
 																		expr: &charClassMatcher{
-																			pos:        position{line: 208, col: 5, offset: 4313},
+																			pos:        position{line: 225, col: 5, offset: 4694},
 																			val:        "[0-9]",
 																			ranges:     []rune{'0', '9'},
 																			ignoreCase: false,
@@ -887,23 +887,23 @@ var g = &grammar{
 															},
 														},
 														&zeroOrOneExpr{
-															pos: position{line: 184, col: 44, offset: 3896},
+															pos: position{line: 190, col: 44, offset: 4086},
 															expr: &seqExpr{
-																pos: position{line: 184, col: 45, offset: 3897},
+																pos: position{line: 190, col: 45, offset: 4087},
 																exprs: []any{
 																	&litMatcher{
-																		pos:        position{line: 184, col: 45, offset: 3897},
+																		pos:        position{line: 190, col: 45, offset: 4087},
 																		val:        ".",
 																		ignoreCase: false,
 																		want:       "\".\"",
 																	},
 																	&oneOrMoreExpr{
-																		pos: position{line: 184, col: 49, offset: 3901},
+																		pos: position{line: 190, col: 49, offset: 4091},
 																		expr: &actionExpr{
-																			pos: position{line: 208, col: 5, offset: 4313},
+																			pos: position{line: 225, col: 5, offset: 4694},
 																			run: (*parser).callonNode168,
 																			expr: &charClassMatcher{
-																				pos:        position{line: 208, col: 5, offset: 4313},
+																				pos:        position{line: 225, col: 5, offset: 4694},
 																				val:        "[0-9]",
 																				ranges:     []rune{'0', '9'},
 																				ignoreCase: false,
@@ -915,35 +915,35 @@ var g = &grammar{
 															},
 														},
 														&choiceExpr{
-															pos: position{line: 184, col: 59, offset: 3911},
+															pos: position{line: 190, col: 59, offset: 4101},
 															alternatives: []any{
 																&litMatcher{
-																	pos:        position{line: 184, col: 59, offset: 3911},
+																	pos:        position{line: 190, col: 59, offset: 4101},
 																	val:        "Z",
 																	ignoreCase: false,
 																	want:       "\"Z\"",
 																},
 																&seqExpr{
-																	pos: position{line: 184, col: 65, offset: 3917},
+																	pos: position{line: 190, col: 65, offset: 4107},
 																	exprs: []any{
 																		&charClassMatcher{
-																			pos:        position{line: 184, col: 66, offset: 3918},
+																			pos:        position{line: 190, col: 66, offset: 4108},
 																			val:        "[+-]",
 																			chars:      []rune{'+', '-'},
 																			ignoreCase: false,
 																			inverted:   false,
 																		},
 																		&actionExpr{
-																			pos: position{line: 164, col: 5, offset: 3574},
+																			pos: position{line: 170, col: 5, offset: 3764},
 																			run: (*parser).callonNode174,
 																			expr: &seqExpr{
-																				pos: position{line: 164, col: 5, offset: 3574},
+																				pos: position{line: 170, col: 5, offset: 3764},
 																				exprs: []any{
 																					&actionExpr{
-																						pos: position{line: 208, col: 5, offset: 4313},
+																						pos: position{line: 225, col: 5, offset: 4694},
 																						run: (*parser).callonNode176,
 																						expr: &charClassMatcher{
-																							pos:        position{line: 208, col: 5, offset: 4313},
+																							pos:        position{line: 225, col: 5, offset: 4694},
 																							val:        "[0-9]",
 																							ranges:     []rune{'0', '9'},
 																							ignoreCase: false,
@@ -951,10 +951,10 @@ var g = &grammar{
 																						},
 																					},
 																					&actionExpr{
-																						pos: position{line: 208, col: 5, offset: 4313},
+																						pos: position{line: 225, col: 5, offset: 4694},
 																						run: (*parser).callonNode178,
 																						expr: &charClassMatcher{
-																							pos:        position{line: 208, col: 5, offset: 4313},
+																							pos:        position{line: 225, col: 5, offset: 4694},
 																							val:        "[0-9]",
 																							ranges:     []rune{'0', '9'},
 																							ignoreCase: false,
@@ -965,22 +965,22 @@ var g = &grammar{
 																			},
 																		},
 																		&litMatcher{
-																			pos:        position{line: 184, col: 86, offset: 3938},
+																			pos:        position{line: 190, col: 86, offset: 4128},
 																			val:        ":",
 																			ignoreCase: false,
 																			want:       "\":\"",
 																		},
 																		&actionExpr{
-																			pos: position{line: 169, col: 5, offset: 3640},
+																			pos: position{line: 175, col: 5, offset: 3830},
 																			run: (*parser).callonNode181,
 																			expr: &seqExpr{
-																				pos: position{line: 169, col: 5, offset: 3640},
+																				pos: position{line: 175, col: 5, offset: 3830},
 																				exprs: []any{
 																					&actionExpr{
-																						pos: position{line: 208, col: 5, offset: 4313},
+																						pos: position{line: 225, col: 5, offset: 4694},
 																						run: (*parser).callonNode183,
 																						expr: &charClassMatcher{
-																							pos:        position{line: 208, col: 5, offset: 4313},
+																							pos:        position{line: 225, col: 5, offset: 4694},
 																							val:        "[0-9]",
 																							ranges:     []rune{'0', '9'},
 																							ignoreCase: false,
@@ -988,10 +988,10 @@ var g = &grammar{
 																						},
 																					},
 																					&actionExpr{
-																						pos: position{line: 208, col: 5, offset: 4313},
+																						pos: position{line: 225, col: 5, offset: 4694},
 																						run: (*parser).callonNode185,
 																						expr: &charClassMatcher{
-																							pos:        position{line: 208, col: 5, offset: 4313},
+																							pos:        position{line: 225, col: 5, offset: 4694},
 																							val:        "[0-9]",
 																							ranges:     []rune{'0', '9'},
 																							ignoreCase: false,
@@ -1024,21 +1024,21 @@ var g = &grammar{
 						},
 					},
 					&actionExpr{
-						pos: position{line: 67, col: 5, offset: 1579},
+						pos: position{line: 65, col: 5, offset: 1549},
 						run: (*parser).callonNode189,
 						expr: &seqExpr{
-							pos: position{line: 67, col: 5, offset: 1579},
+							pos: position{line: 65, col: 5, offset: 1549},
 							exprs: []any{
 								&labeledExpr{
-									pos:   position{line: 67, col: 5, offset: 1579},
+									pos:   position{line: 65, col: 5, offset: 1549},
 									label: "k",
 									expr: &oneOrMoreExpr{
-										pos: position{line: 67, col: 7, offset: 1581},
+										pos: position{line: 65, col: 7, offset: 1551},
 										expr: &actionExpr{
-											pos: position{line: 198, col: 5, offset: 4194},
+											pos: position{line: 215, col: 5, offset: 4575},
 											run: (*parser).callonNode193,
 											expr: &charClassMatcher{
-												pos:        position{line: 198, col: 5, offset: 4194},
+												pos:        position{line: 215, col: 5, offset: 4575},
 												val:        "[A-Za-z]",
 												ranges:     []rune{'A', 'Z', 'a', 'z'},
 												ignoreCase: false,
@@ -1048,23 +1048,148 @@ var g = &grammar{
 									},
 								},
 								&choiceExpr{
-									pos: position{line: 67, col: 14, offset: 1588},
+									pos: position{line: 66, col: 9, offset: 1567},
 									alternatives: []any{
 										&actionExpr{
-											pos: position{line: 114, col: 5, offset: 2721},
+											pos: position{line: 125, col: 5, offset: 2997},
 											run: (*parser).callonNode196,
 											expr: &litMatcher{
-												pos:        position{line: 114, col: 5, offset: 2721},
+												pos:        position{line: 125, col: 5, offset: 2997},
+												val:        "=",
+												ignoreCase: false,
+												want:       "\"=\"",
+											},
+										},
+										&actionExpr{
+											pos: position{line: 120, col: 5, offset: 2911},
+											run: (*parser).callonNode198,
+											expr: &litMatcher{
+												pos:        position{line: 120, col: 5, offset: 2911},
+												val:        ":",
+												ignoreCase: false,
+												want:       "\":\"",
+											},
+										},
+									},
+								},
+								&zeroOrOneExpr{
+									pos: position{line: 68, col: 7, offset: 1619},
+									expr: &litMatcher{
+										pos:        position{line: 68, col: 7, offset: 1619},
+										val:        "\"",
+										ignoreCase: false,
+										want:       "\"\\\"\"",
+									},
+								},
+								&labeledExpr{
+									pos:   position{line: 68, col: 12, offset: 1624},
+									label: "v",
+									expr: &choiceExpr{
+										pos: position{line: 200, col: 5, offset: 4285},
+										alternatives: []any{
+											&litMatcher{
+												pos:        position{line: 200, col: 5, offset: 4285},
+												val:        "today",
+												ignoreCase: false,
+												want:       "\"today\"",
+											},
+											&litMatcher{
+												pos:        position{line: 201, col: 5, offset: 4299},
+												val:        "yesterday",
+												ignoreCase: false,
+												want:       "\"yesterday\"",
+											},
+											&litMatcher{
+												pos:        position{line: 202, col: 5, offset: 4317},
+												val:        "this week",
+												ignoreCase: false,
+												want:       "\"this week\"",
+											},
+											&litMatcher{
+												pos:        position{line: 203, col: 5, offset: 4335},
+												val:        "this month",
+												ignoreCase: false,
+												want:       "\"this month\"",
+											},
+											&litMatcher{
+												pos:        position{line: 204, col: 5, offset: 4354},
+												val:        "last month",
+												ignoreCase: false,
+												want:       "\"last month\"",
+											},
+											&litMatcher{
+												pos:        position{line: 205, col: 5, offset: 4373},
+												val:        "this year",
+												ignoreCase: false,
+												want:       "\"this year\"",
+											},
+											&actionExpr{
+												pos: position{line: 206, col: 5, offset: 4391},
+												run: (*parser).callonNode210,
+												expr: &litMatcher{
+													pos:        position{line: 206, col: 5, offset: 4391},
+													val:        "last year",
+													ignoreCase: false,
+													want:       "\"last year\"",
+												},
+											},
+										},
+									},
+								},
+								&zeroOrOneExpr{
+									pos: position{line: 68, col: 38, offset: 1650},
+									expr: &litMatcher{
+										pos:        position{line: 68, col: 38, offset: 1650},
+										val:        "\"",
+										ignoreCase: false,
+										want:       "\"\\\"\"",
+									},
+								},
+							},
+						},
+					},
+					&actionExpr{
+						pos: position{line: 73, col: 5, offset: 1769},
+						run: (*parser).callonNode214,
+						expr: &seqExpr{
+							pos: position{line: 73, col: 5, offset: 1769},
+							exprs: []any{
+								&labeledExpr{
+									pos:   position{line: 73, col: 5, offset: 1769},
+									label: "k",
+									expr: &oneOrMoreExpr{
+										pos: position{line: 73, col: 7, offset: 1771},
+										expr: &actionExpr{
+											pos: position{line: 215, col: 5, offset: 4575},
+											run: (*parser).callonNode218,
+											expr: &charClassMatcher{
+												pos:        position{line: 215, col: 5, offset: 4575},
+												val:        "[A-Za-z]",
+												ranges:     []rune{'A', 'Z', 'a', 'z'},
+												ignoreCase: false,
+												inverted:   false,
+											},
+										},
+									},
+								},
+								&choiceExpr{
+									pos: position{line: 73, col: 14, offset: 1778},
+									alternatives: []any{
+										&actionExpr{
+											pos: position{line: 120, col: 5, offset: 2911},
+											run: (*parser).callonNode221,
+											expr: &litMatcher{
+												pos:        position{line: 120, col: 5, offset: 2911},
 												val:        ":",
 												ignoreCase: false,
 												want:       "\":\"",
 											},
 										},
 										&actionExpr{
-											pos: position{line: 119, col: 5, offset: 2807},
-											run: (*parser).callonNode198,
+											pos: position{line: 125, col: 5, offset: 2997},
+											run: (*parser).callonNode223,
 											expr: &litMatcher{
-												pos:        position{line: 119, col: 5, offset: 2807},
+												pos:        position{line: 125, col: 5, offset: 2997},
 												val:        "=",
 												ignoreCase: false,
 												want:       "\"=\"",
@@ -1073,30 +1198,30 @@ var g = &grammar{
 									},
 								},
 								&labeledExpr{
-									pos:   position{line: 67, col: 53, offset: 1627},
+									pos:   position{line: 73, col: 53, offset: 1817},
 									label: "v",
 									expr: &choiceExpr{
-										pos: position{line: 67, col: 56, offset: 1630},
+										pos: position{line: 73, col: 56, offset: 1820},
 										alternatives: []any{
 											&actionExpr{
-												pos: position{line: 203, col: 5, offset: 4253},
-												run: (*parser).callonNode202,
+												pos: position{line: 220, col: 5, offset: 4634},
+												run: (*parser).callonNode227,
 												expr: &seqExpr{
-													pos: position{line: 203, col: 5, offset: 4253},
+													pos: position{line: 220, col: 5, offset: 4634},
 													exprs: []any{
 														&litMatcher{
-															pos:        position{line: 203, col: 5, offset: 4253},
+															pos:        position{line: 220, col: 5, offset: 4634},
 															val:        "\"",
 															ignoreCase: false,
 															want:       "\"\\\"\"",
 														},
 														&labeledExpr{
-															pos:   position{line: 203, col: 9, offset: 4257},
+															pos:   position{line: 220, col: 9, offset: 4638},
 															label: "v",
 															expr: &zeroOrMoreExpr{
-																pos: position{line: 203, col: 11, offset: 4259},
+																pos: position{line: 220, col: 11, offset: 4640},
 																expr: &charClassMatcher{
-																	pos:        position{line: 203, col: 11, offset: 4259},
+																	pos:        position{line: 220, col: 11, offset: 4640},
 																	val:        "[^\"]",
 																	chars:      []rune{'"'},
 																	ignoreCase: false,
@@ -1105,7 +1230,7 @@ var g = &grammar{
 															},
 														},
 														&litMatcher{
-															pos:        position{line: 203, col: 17, offset: 4265},
+															pos:        position{line: 220, col: 17, offset: 4646},
 															val:        "\"",
 															ignoreCase: false,
 															want:       "\"\\\"\"",
@@ -1114,9 +1239,9 @@ var g = &grammar{
 												},
 											},
 											&oneOrMoreExpr{
-												pos: position{line: 67, col: 65, offset: 1639},
+												pos: position{line: 73, col: 65, offset: 1829},
 												expr: &charClassMatcher{
-													pos:        position{line: 67, col: 65, offset: 1639},
+													pos:        position{line: 73, col: 65, offset: 1829},
 													val:        "[^ ()]",
 													chars:      []rune{' ', '(', ')'},
 													ignoreCase: false,
@@ -1130,19 +1255,19 @@ var g = &grammar{
 						},
 					},
 					&actionExpr{
-						pos: position{line: 99, col: 5, offset: 2431},
-						run: (*parser).callonNode211,
+						pos: position{line: 105, col: 5, offset: 2621},
+						run: (*parser).callonNode236,
 						expr: &choiceExpr{
-							pos: position{line: 99, col: 6, offset: 2432},
+							pos: position{line: 105, col: 6, offset: 2622},
 							alternatives: []any{
 								&litMatcher{
-									pos:        position{line: 99, col: 6, offset: 2432},
+									pos:        position{line: 105, col: 6, offset: 2622},
 									val:        "AND",
 									ignoreCase: false,
 									want:       "\"AND\"",
 								},
 								&litMatcher{
-									pos:        position{line: 99, col: 14, offset: 2440},
+									pos:        position{line: 105, col: 14, offset: 2630},
 									val:        "+",
 									ignoreCase: false,
 									want:       "\"+\"",
@@ -1151,19 +1276,19 @@ var g = &grammar{
 						},
 					},
 					&actionExpr{
-						pos: position{line: 104, col: 5, offset: 2532},
-						run: (*parser).callonNode215,
+						pos: position{line: 110, col: 5, offset: 2722},
+						run: (*parser).callonNode240,
 						expr: &choiceExpr{
-							pos: position{line: 104, col: 6, offset: 2533},
+							pos: position{line: 110, col: 6, offset: 2723},
 							alternatives: []any{
 								&litMatcher{
-									pos:        position{line: 104, col: 6, offset: 2533},
+									pos:        position{line: 110, col: 6, offset: 2723},
 									val:        "NOT",
 									ignoreCase: false,
 									want:       "\"NOT\"",
 								},
 								&litMatcher{
-									pos:        position{line: 104, col: 14, offset: 2541},
+									pos:        position{line: 110, col: 14, offset: 2731},
 									val:        "-",
 									ignoreCase: false,
 									want:       "\"-\"",
@@ -1172,28 +1297,28 @@ var g = &grammar{
 						},
 					},
 					&actionExpr{
-						pos: position{line: 109, col: 5, offset: 2632},
-						run: (*parser).callonNode219,
+						pos: position{line: 115, col: 5, offset: 2822},
+						run: (*parser).callonNode244,
 						expr: &litMatcher{
-							pos:        position{line: 109, col: 6, offset: 2633},
+							pos:        position{line: 115, col: 6, offset: 2823},
 							val:        "OR",
 							ignoreCase: false,
 							want:       "\"OR\"",
 						},
 					},
 					&actionExpr{
-						pos: position{line: 80, col: 6, offset: 1919},
-						run: (*parser).callonNode221,
+						pos: position{line: 86, col: 6, offset: 2109},
+						run: (*parser).callonNode246,
 						expr: &seqExpr{
-							pos: position{line: 80, col: 6, offset: 1919},
+							pos: position{line: 86, col: 6, offset: 2109},
 							exprs: []any{
 								&zeroOrOneExpr{
-									pos: position{line: 80, col: 6, offset: 1919},
+									pos: position{line: 86, col: 6, offset: 2109},
 									expr: &actionExpr{
-										pos: position{line: 114, col: 5, offset: 2721},
-										run: (*parser).callonNode224,
+										pos: position{line: 120, col: 5, offset: 2911},
+										run: (*parser).callonNode249,
 										expr: &litMatcher{
-											pos:        position{line: 114, col: 5, offset: 2721},
+											pos:        position{line: 120, col: 5, offset: 2911},
 											val:        ":",
 											ignoreCase: false,
 											want:       "\":\"",
@@ -1201,12 +1326,12 @@ var g = &grammar{
 									},
 								},
 								&actionExpr{
-									pos: position{line: 213, col: 5, offset: 4364},
-									run: (*parser).callonNode226,
+									pos: position{line: 230, col: 5, offset: 4745},
+									run: (*parser).callonNode251,
 									expr: &zeroOrMoreExpr{
-										pos: position{line: 213, col: 5, offset: 4364},
+										pos: position{line: 230, col: 5, offset: 4745},
 										expr: &charClassMatcher{
-											pos:        position{line: 213, col: 5, offset: 4364},
+											pos:        position{line: 230, col: 5, offset: 4745},
 											val:        "[ \\t]",
 											chars:      []rune{' ', '\t'},
 											ignoreCase: false,
@@ -1215,27 +1340,27 @@ var g = &grammar{
 									},
 								},
 								&labeledExpr{
-									pos:   position{line: 80, col: 27, offset: 1940},
+									pos:   position{line: 86, col: 27, offset: 2130},
 									label: "v",
 									expr: &actionExpr{
-										pos: position{line: 203, col: 5, offset: 4253},
-										run: (*parser).callonNode230,
+										pos: position{line: 220, col: 5, offset: 4634},
+										run: (*parser).callonNode255,
 										expr: &seqExpr{
-											pos: position{line: 203, col: 5, offset: 4253},
+											pos: position{line: 220, col: 5, offset: 4634},
 											exprs: []any{
 												&litMatcher{
-													pos:        position{line: 203, col: 5, offset: 4253},
+													pos:        position{line: 220, col: 5, offset: 4634},
 													val:        "\"",
 													ignoreCase: false,
 													want:       "\"\\\"\"",
 												},
 												&labeledExpr{
-													pos:   position{line: 203, col: 9, offset: 4257},
+													pos:   position{line: 220, col: 9, offset: 4638},
 													label: "v",
 													expr: &zeroOrMoreExpr{
-														pos: position{line: 203, col: 11, offset: 4259},
+														pos: position{line: 220, col: 11, offset: 4640},
 														expr: &charClassMatcher{
-															pos:        position{line: 203, col: 11, offset: 4259},
+															pos:        position{line: 220, col: 11, offset: 4640},
 															val:        "[^\"]",
 															chars:      []rune{'"'},
 															ignoreCase: false,
@@ -1244,7 +1369,7 @@ var g = &grammar{
 													},
 												},
 												&litMatcher{
-													pos:        position{line: 203, col: 17, offset: 4265},
+													pos:        position{line: 220, col: 17, offset: 4646},
 													val:        "\"",
 													ignoreCase: false,
 													want:       "\"\\\"\"",
@@ -1254,12 +1379,12 @@ var g = &grammar{
 									},
 								},
 								&actionExpr{
-									pos: position{line: 213, col: 5, offset: 4364},
-									run: (*parser).callonNode237,
+									pos: position{line: 230, col: 5, offset: 4745},
+									run: (*parser).callonNode262,
 									expr: &zeroOrMoreExpr{
-										pos: position{line: 213, col: 5, offset: 4364},
+										pos: position{line: 230, col: 5, offset: 4745},
 										expr: &charClassMatcher{
-											pos:        position{line: 213, col: 5, offset: 4364},
+											pos:        position{line: 230, col: 5, offset: 4745},
 											val:        "[ \\t]",
 											chars:      []rune{' ', '\t'},
 											ignoreCase: false,
@@ -1268,12 +1393,12 @@ var g = &grammar{
 									},
 								},
 								&zeroOrOneExpr{
-									pos: position{line: 80, col: 38, offset: 1951},
+									pos: position{line: 86, col: 38, offset: 2141},
 									expr: &actionExpr{
-										pos: position{line: 114, col: 5, offset: 2721},
-										run: (*parser).callonNode241,
+										pos: position{line: 120, col: 5, offset: 2911},
+										run: (*parser).callonNode266,
 										expr: &litMatcher{
-											pos:        position{line: 114, col: 5, offset: 2721},
+											pos:        position{line: 120, col: 5, offset: 2911},
 											val:        ":",
 											ignoreCase: false,
 											want:       "\":\"",
@@ -1284,18 +1409,18 @@ var g = &grammar{
 						},
 					},
 					&actionExpr{
-						pos: position{line: 85, col: 6, offset: 2049},
-						run: (*parser).callonNode243,
+						pos: position{line: 91, col: 6, offset: 2239},
+						run: (*parser).callonNode268,
 						expr: &seqExpr{
-							pos: position{line: 85, col: 6, offset: 2049},
+							pos: position{line: 91, col: 6, offset: 2239},
 							exprs: []any{
 								&zeroOrOneExpr{
-									pos: position{line: 85, col: 6, offset: 2049},
+									pos: position{line: 91, col: 6, offset: 2239},
 									expr: &actionExpr{
-										pos: position{line: 114, col: 5, offset: 2721},
-										run: (*parser).callonNode246,
+										pos: position{line: 120, col: 5, offset: 2911},
+										run: (*parser).callonNode271,
 										expr: &litMatcher{
-											pos:        position{line: 114, col: 5, offset: 2721},
+											pos:        position{line: 120, col: 5, offset: 2911},
 											val:        ":",
 											ignoreCase: false,
 											want:       "\":\"",
@@ -1303,12 +1428,12 @@ var g = &grammar{
 									},
 								},
 								&actionExpr{
-									pos: position{line: 213, col: 5, offset: 4364},
-									run: (*parser).callonNode248,
+									pos: position{line: 230, col: 5, offset: 4745},
+									run: (*parser).callonNode273,
 									expr: &zeroOrMoreExpr{
-										pos: position{line: 213, col: 5, offset: 4364},
+										pos: position{line: 230, col: 5, offset: 4745},
 										expr: &charClassMatcher{
-											pos:        position{line: 213, col: 5, offset: 4364},
+											pos:        position{line: 230, col: 5, offset: 4745},
 											val:        "[ \\t]",
 											chars:      []rune{' ', '\t'},
 											ignoreCase: false,
@@ -1317,12 +1442,12 @@ var g = &grammar{
 									},
 								},
 								&labeledExpr{
-									pos:   position{line: 85, col: 27, offset: 2070},
+									pos:   position{line: 91, col: 27, offset: 2260},
 									label: "v",
 									expr: &oneOrMoreExpr{
-										pos: position{line: 85, col: 29, offset: 2072},
+										pos: position{line: 91, col: 29, offset: 2262},
 										expr: &charClassMatcher{
-											pos:        position{line: 85, col: 29, offset: 2072},
+											pos:        position{line: 91, col: 29, offset: 2262},
 											val:        "[^ :()]",
 											chars:      []rune{' ', ':', '(', ')'},
 											ignoreCase: false,
@@ -1331,12 +1456,12 @@ var g = &grammar{
 									},
 								},
 								&actionExpr{
-									pos: position{line: 213, col: 5, offset: 4364},
-									run: (*parser).callonNode254,
+									pos: position{line: 230, col: 5, offset: 4745},
+									run: (*parser).callonNode279,
 									expr: &zeroOrMoreExpr{
-										pos: position{line: 213, col: 5, offset: 4364},
+										pos: position{line: 230, col: 5, offset: 4745},
 										expr: &charClassMatcher{
-											pos:        position{line: 213, col: 5, offset: 4364},
+											pos:        position{line: 230, col: 5, offset: 4745},
 											val:        "[ \\t]",
 											chars:      []rune{' ', '\t'},
 											ignoreCase: false,
@@ -1345,12 +1470,12 @@ var g = &grammar{
 									},
 								},
 								&zeroOrOneExpr{
-									pos: position{line: 85, col: 40, offset: 2083},
+									pos: position{line: 91, col: 40, offset: 2273},
 									expr: &actionExpr{
-										pos: position{line: 114, col: 5, offset: 2721},
-										run: (*parser).callonNode258,
+										pos: position{line: 120, col: 5, offset: 2911},
+										run: (*parser).callonNode283,
 										expr: &litMatcher{
-											pos:        position{line: 114, col: 5, offset: 2721},
+											pos:        position{line: 120, col: 5, offset: 2911},
 											val:        ":",
 											ignoreCase: false,
 											want:       "\":\"",
@@ -1380,10 +1505,10 @@ var g = &grammar{
 								expr: &oneOrMoreExpr{
 									pos: position{line: 32, col: 8, offset: 615},
 									expr: &actionExpr{
-										pos: position{line: 198, col: 5, offset: 4194},
+										pos: position{line: 215, col: 5, offset: 4575},
 										run: (*parser).callonGroupNode6,
 										expr: &charClassMatcher{
-											pos:        position{line: 198, col: 5, offset: 4194},
+											pos:        position{line: 215, col: 5, offset: 4575},
 											val:        "[A-Za-z]",
 											ranges:     []rune{'A', 'Z', 'a', 'z'},
 											ignoreCase: false,
@@ -1399,20 +1524,20 @@ var g = &grammar{
 								pos: position{line: 32, col: 17, offset: 624},
 								alternatives: []any{
 									&actionExpr{
-										pos: position{line: 114, col: 5, offset: 2721},
+										pos: position{line: 120, col: 5, offset: 2911},
 										run: (*parser).callonGroupNode10,
 										expr: &litMatcher{
-											pos:        position{line: 114, col: 5, offset: 2721},
+											pos:        position{line: 120, col: 5, offset: 2911},
 											val:        ":",
 											ignoreCase: false,
 											want:       "\":\"",
 										},
 									},
 									&actionExpr{
-										pos: position{line: 119, col: 5, offset: 2807},
+										pos: position{line: 125, col: 5, offset: 2997},
 										run: (*parser).callonGroupNode12,
 										expr: &litMatcher{
-											pos:        position{line: 119, col: 5, offset: 2807},
+											pos:        position{line: 125, col: 5, offset: 2997},
 											val:        "=",
 											ignoreCase: false,
 											want:       "\"=\"",
@@ -2284,19 +2409,19 @@ func (p *parser) callonNode198() (any, error) {
 	return p.cur.onNode198()
 }
 
-func (c *current) onNode202(v any) (any, error) {
-	return v, nil
+func (c *current) onNode210() (any, error) {
+	return c.text, nil
 
 }
 
-func (p *parser) callonNode202() (any, error) {
+func (p *parser) callonNode210() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode202(stack["v"])
+	return p.cur.onNode210()
 }
 
 func (c *current) onNode189(k, v any) (any, error) {
-	return buildStringNode(k, v, c.text, c.pos)
+	return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos)
 
 }
 
@@ -2306,158 +2431,213 @@ func (p *parser) callonNode189() (any, error) {
 	return p.cur.onNode189(stack["k"], stack["v"])
 }
 
-func (c *current) onNode211() (any, error) {
+func (c *current) onNode218() (any, error) {
+	return c.text, nil
+
+}
+
+func (p *parser) callonNode218() (any, error) {
+	stack := p.vstack[len(p.vstack)-1]
+	_ = stack
+	return p.cur.onNode218()
+}
+
+func (c *current) onNode221() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode211() (any, error) {
+func (p *parser) callonNode221() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode211()
+	return p.cur.onNode221()
 }
 
-func (c *current) onNode215() (any, error) {
+func (c *current) onNode223() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode215() (any, error) {
+func (p *parser) callonNode223() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode215()
+	return p.cur.onNode223()
 }
 
-func (c *current) onNode219() (any, error) {
+func (c *current) onNode227(v any) (any, error) {
+	return v, nil
+
+}
+
+func (p *parser) callonNode227() (any, error) {
+	stack := p.vstack[len(p.vstack)-1]
+	_ = stack
+	return p.cur.onNode227(stack["v"])
+}
+
+func (c *current) onNode214(k, v any) (any, error) {
+	return buildStringNode(k, v, c.text, c.pos)
+
+}
+
+func (p *parser) callonNode214() (any, error) {
+	stack := p.vstack[len(p.vstack)-1]
+	_ = stack
+	return p.cur.onNode214(stack["k"], stack["v"])
+}
+
+func (c *current) onNode236() (any, error) {
+	return buildOperatorNode(c.text, c.pos)
+
+}
+
+func (p *parser) callonNode236() (any, error) {
+	stack := p.vstack[len(p.vstack)-1]
+	_ = stack
+	return p.cur.onNode236()
+}
+
+func (c *current) onNode240() (any, error) {
+	return buildOperatorNode(c.text, c.pos)
+
+}
+
+func (p *parser) callonNode240() (any, error) {
+	stack := p.vstack[len(p.vstack)-1]
+	_ = stack
+	return p.cur.onNode240()
+}
+
+func (c *current) onNode244() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode219() (any, error) {
+func (p *parser) callonNode244() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode219()
+	return p.cur.onNode244()
 }
 
-func (c *current) onNode224() (any, error) {
+func (c *current) onNode249() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode224() (any, error) {
+func (p *parser) callonNode249() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode224()
+	return p.cur.onNode249()
 }
 
-func (c *current) onNode226() (any, error) {
+func (c *current) onNode251() (any, error) {
 	return nil, nil
 
 }
 
-func (p *parser) callonNode226() (any, error) {
+func (p *parser) callonNode251() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode226()
+	return p.cur.onNode251()
 }
 
-func (c *current) onNode230(v any) (any, error) {
+func (c *current) onNode255(v any) (any, error) {
 	return v, nil
 
 }
 
-func (p *parser) callonNode230() (any, error) {
+func (p *parser) callonNode255() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode230(stack["v"])
+	return p.cur.onNode255(stack["v"])
 }
 
-func (c *current) onNode237(v any) (any, error) {
+func (c *current) onNode262(v any) (any, error) {
 	return nil, nil
 
 }
 
-func (p *parser) callonNode237() (any, error) {
+func (p *parser) callonNode262() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode237(stack["v"])
+	return p.cur.onNode262(stack["v"])
 }
 
-func (c *current) onNode241() (any, error) {
+func (c *current) onNode266() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode241() (any, error) {
+func (p *parser) callonNode266() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode241()
+	return p.cur.onNode266()
 }
 
-func (c *current) onNode221(v any) (any, error) {
+func (c *current) onNode246(v any) (any, error) {
 	return buildStringNode("", v, c.text, c.pos)
 
 }
 
-func (p *parser) callonNode221() (any, error) {
+func (p *parser) callonNode246() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode221(stack["v"])
+	return p.cur.onNode246(stack["v"])
 }
 
-func (c *current) onNode246() (any, error) {
+func (c *current) onNode271() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode246() (any, error) {
+func (p *parser) callonNode271() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode246()
+	return p.cur.onNode271()
 }
 
-func (c *current) onNode248() (any, error) {
+func (c *current) onNode273() (any, error) {
 	return nil, nil
 
 }
 
-func (p *parser) callonNode248() (any, error) {
+func (p *parser) callonNode273() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode248()
+	return p.cur.onNode273()
 }
 
-func (c *current) onNode254(v any) (any, error) {
+func (c *current) onNode279(v any) (any, error) {
 	return nil, nil
 
 }
 
-func (p *parser) callonNode254() (any, error) {
+func (p *parser) callonNode279() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode254(stack["v"])
+	return p.cur.onNode279(stack["v"])
 }
 
-func (c *current) onNode258() (any, error) {
+func (c *current) onNode283() (any, error) {
 	return buildOperatorNode(c.text, c.pos)
 
 }
 
-func (p *parser) callonNode258() (any, error) {
+func (p *parser) callonNode283() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode258()
+	return p.cur.onNode283()
 }
 
-func (c *current) onNode243(v any) (any, error) {
+func (c *current) onNode268(v any) (any, error) {
 	return buildStringNode("", v, c.text, c.pos)
 
 }
 
-func (p *parser) callonNode243() (any, error) {
+func (p *parser) callonNode268() (any, error) {
 	stack := p.vstack[len(p.vstack)-1]
 	_ = stack
-	return p.cur.onNode243(stack["v"])
+	return p.cur.onNode268(stack["v"])
 }
 
 func (c *current) onGroupNode6() (any, error) {
diff --git a/services/search/pkg/query/kql/doc.go b/services/search/pkg/query/kql/doc.go
index a887affc32f..f578d171f2f 100644
--- a/services/search/pkg/query/kql/doc.go
+++ b/services/search/pkg/query/kql/doc.go
@@ -10,7 +10,6 @@ The following spec parts are supported and tested:
   - 2.1.8 OR Operator
   - 2.1.12 Parentheses
   - 2.3.5 Date Tokens
-  - Human tokens not implemented
   - 3.1.11 Implicit Operator
   - 3.1.12 Parentheses
   - 3.1.2 AND Operator
diff --git a/services/search/pkg/query/kql/error.go b/services/search/pkg/query/kql/error.go
index c12cbfe9d00..5f171d77fdb 100644
--- a/services/search/pkg/query/kql/error.go
+++ b/services/search/pkg/query/kql/error.go
@@ -28,3 +28,12 @@ func (e NamedGroupInvalidNodesError) Error() string {
 		ast.NodeValue(e.Node),
 	).Error()
 }
+
+// UnsupportedTimeRangeError records an error and the value that caused it.
+type UnsupportedTimeRangeError struct {
+	Value interface{}
+}
+
+func (e UnsupportedTimeRangeError) Error() string {
+	return fmt.Sprintf("unable to convert '%v' to a time range", e.Value)
+}
diff --git a/services/search/pkg/query/kql/factory.go b/services/search/pkg/query/kql/factory.go
index ff9044886a9..0eb858dd97a 100644
--- a/services/search/pkg/query/kql/factory.go
+++ b/services/search/pkg/query/kql/factory.go
@@ -101,6 +101,39 @@ func buildDateTimeNode(k, o, v interface{}, text []byte, pos position) (*ast.Dat
 		Value:    value,
 	}, nil
 }
+func buildNaturalLanguageDateTimeNodes(k, v interface{}, text []byte, pos position) ([]ast.Node, error) {
+	b, err := base(text, pos)
+	if err != nil {
+		return nil, err
+	}
+
+	key, err := toString(k)
+	if err != nil {
+		return nil, err
+	}
+
+	from, to, err := toTimeRange(v)
+	if err != nil {
+		return nil, err
+	}
+
+	return []ast.Node{
+		&ast.DateTimeNode{
+			Base:     b,
+			Value:    *from,
+			Key:      key,
+			Operator: &ast.OperatorNode{Value: ">="},
+		},
+		&ast.OperatorNode{Value: BoolAND},
+		&ast.DateTimeNode{
+			Base:     b,
+			Value:    *to,
+			Key:      key,
+			Operator: &ast.OperatorNode{Value: "<="},
+		},
+	}, nil
+
+}
 
 func buildBooleanNode(k, v interface{}, text []byte, pos position) (*ast.BooleanNode, error) {
 	b, err := base(text, pos)
diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go
index 8719223b8d2..2cf03a7f066 100644
--- a/services/search/pkg/query/kql/kql.go
+++ b/services/search/pkg/query/kql/kql.go
@@ -3,6 +3,7 @@ package kql
 
 import (
 	"errors"
+	"time"
 
 	"github.com/owncloud/ocis/v2/services/search/pkg/query/ast"
 )
@@ -42,3 +43,7 @@ func (b Builder) Build(q string) (*ast.Ast, error) {
 
 	return f.(*ast.Ast), nil
 }
+
+// timeNow mirrors time.Now by default, the only reason why this exists
+// is to monkey patch it from the tests. See PatchTimeNow
+var timeNow = time.Now
diff --git a/vendor/github.com/araddon/dateparse/.travis.yml b/vendor/github.com/araddon/dateparse/.travis.yml
deleted file mode 100644
index 3b4b17777fb..00000000000
--- a/vendor/github.com/araddon/dateparse/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: go
-
-go:
-  - 1.13.x
-
-before_install:
-  - go get -t -v ./...
-
-script:
-  - go test -race -coverprofile=coverage.txt -covermode=atomic
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/araddon/dateparse/README.md b/vendor/github.com/araddon/dateparse/README.md
deleted file mode 100644
index fe682dd5652..00000000000
--- a/vendor/github.com/araddon/dateparse/README.md
+++ /dev/null
@@ -1,323 +0,0 @@
-Go Date Parser 
----------------------------
-
-Parse many date strings without knowing format in advance.  Uses a scanner to read bytes and use a state machine to find format.  Much faster than shotgun based parse methods.  See [bench_test.go](https://github.com/araddon/dateparse/blob/master/bench_test.go) for performance comparison.
-
-
-[![Code Coverage](https://codecov.io/gh/araddon/dateparse/branch/master/graph/badge.svg)](https://codecov.io/gh/araddon/dateparse)
-[![GoDoc](https://godoc.org/github.com/araddon/dateparse?status.svg)](http://godoc.org/github.com/araddon/dateparse)
-[![Build Status](https://travis-ci.org/araddon/dateparse.svg?branch=master)](https://travis-ci.org/araddon/dateparse)
-[![Go ReportCard](https://goreportcard.com/badge/araddon/dateparse)](https://goreportcard.com/report/araddon/dateparse)
-
-**MM/DD/YYYY VS DD/MM/YYYY** Right now this uses mm/dd/yyyy WHEN ambiguous if this is not desired behavior, use `ParseStrict` which will fail on ambiguous date strings.
-
-**Timezones** The location your server is configured affects the results!  See example or https://play.golang.org/p/IDHRalIyXh and last paragraph here https://golang.org/pkg/time/#Parse.
-
-
-```go
-
-// Normal parse.  Equivalent Timezone rules as time.Parse()
-t, err := dateparse.ParseAny("3/1/2014")
-
-// Parse Strict, error on ambigous mm/dd vs dd/mm dates
-t, err := dateparse.ParseStrict("3/1/2014")
-> returns error 
-
-// Return a string that represents the layout to parse the given date-time.
-layout, err := dateparse.ParseFormat("May 8, 2009 5:57:51 PM")
-> "Jan 2, 2006 3:04:05 PM"
-
-```
-
-cli tool for testing dateformats
-----------------------------------
-
-[Date Parse CLI](https://github.com/araddon/dateparse/blob/master/dateparse)
-
-
-Extended example
--------------------
-
-https://github.com/araddon/dateparse/blob/master/example/main.go
-
-```go
-package main
-
-import (
-	"flag"
-	"fmt"
-	"time"
-
-	"github.com/scylladb/termtables"
-	"github.com/araddon/dateparse"
-)
-
-var examples = []string{
-	"May 8, 2009 5:57:51 PM",
-	"oct 7, 1970",
-	"oct 7, '70",
-	"oct. 7, 1970",
-	"oct. 7, 70",
-	"Mon Jan  2 15:04:05 2006",
-	"Mon Jan  2 15:04:05 MST 2006",
-	"Mon Jan 02 15:04:05 -0700 2006",
-	"Monday, 02-Jan-06 15:04:05 MST",
-	"Mon, 02 Jan 2006 15:04:05 MST",
-	"Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
-	"Mon, 02 Jan 2006 15:04:05 -0700",
-	"Mon 30 Sep 2018 09:09:09 PM UTC",
-	"Mon Aug 10 15:44:11 UTC+0100 2015",
-	"Thu, 4 Jan 2018 17:53:36 +0000",
-	"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
-	"Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00)",
-	"September 17, 2012 10:09am",
-	"September 17, 2012 at 10:09am PST-08",
-	"September 17, 2012, 10:10:09",
-	"October 7, 1970",
-	"October 7th, 1970",
-	"12 Feb 2006, 19:17",
-	"12 Feb 2006 19:17",
-	"14 May 2019 19:11:40.164",
-	"7 oct 70",
-	"7 oct 1970",
-	"03 February 2013",
-	"1 July 2013",
-	"2013-Feb-03",
-	// dd/Mon/yyy  alpha Months
-	"06/Jan/2008:15:04:05 -0700",
-	"06/Jan/2008 15:04:05 -0700",
-	//   mm/dd/yy
-	"3/31/2014",
-	"03/31/2014",
-	"08/21/71",
-	"8/1/71",
-	"4/8/2014 22:05",
-	"04/08/2014 22:05",
-	"4/8/14 22:05",
-	"04/2/2014 03:00:51",
-	"8/8/1965 12:00:00 AM",
-	"8/8/1965 01:00:01 PM",
-	"8/8/1965 01:00 PM",
-	"8/8/1965 1:00 PM",
-	"8/8/1965 12:00 AM",
-	"4/02/2014 03:00:51",
-	"03/19/2012 10:11:59",
-	"03/19/2012 10:11:59.3186369",
-	// yyyy/mm/dd
-	"2014/3/31",
-	"2014/03/31",
-	"2014/4/8 22:05",
-	"2014/04/08 22:05",
-	"2014/04/2 03:00:51",
-	"2014/4/02 03:00:51",
-	"2012/03/19 10:11:59",
-	"2012/03/19 10:11:59.3186369",
-	// yyyy:mm:dd
-	"2014:3:31",
-	"2014:03:31",
-	"2014:4:8 22:05",
-	"2014:04:08 22:05",
-	"2014:04:2 03:00:51",
-	"2014:4:02 03:00:51",
-	"2012:03:19 10:11:59",
-	"2012:03:19 10:11:59.3186369",
-	// Chinese
-	"2014年04月08日",
-	//   yyyy-mm-ddThh
-	"2006-01-02T15:04:05+0000",
-	"2009-08-12T22:15:09-07:00",
-	"2009-08-12T22:15:09",
-	"2009-08-12T22:15:09.988",
-	"2009-08-12T22:15:09Z",
-	"2017-07-19T03:21:51:897+0100",
-	"2019-05-29T08:41-04", // no seconds, 2 digit TZ offset
-	//   yyyy-mm-dd hh:mm:ss
-	"2014-04-26 17:24:37.3186369",
-	"2012-08-03 18:31:59.257000000",
-	"2014-04-26 17:24:37.123",
-	"2013-04-01 22:43",
-	"2013-04-01 22:43:22",
-	"2014-12-16 06:20:00 UTC",
-	"2014-12-16 06:20:00 GMT",
-	"2014-04-26 05:24:37 PM",
-	"2014-04-26 13:13:43 +0800",
-	"2014-04-26 13:13:43 +0800 +08",
-	"2014-04-26 13:13:44 +09:00",
-	"2012-08-03 18:31:59.257000000 +0000 UTC",
-	"2015-09-30 18:48:56.35272715 +0000 UTC",
-	"2015-02-18 00:12:00 +0000 GMT",
-	"2015-02-18 00:12:00 +0000 UTC",
-	"2015-02-08 03:02:00 +0300 MSK m=+0.000000001",
-	"2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001",
-	"2017-07-19 03:21:51+00:00",
-	"2014-04-26",
-	"2014-04",
-	"2014",
-	"2014-05-11 08:20:13,787",
-	// yyyy-mm-dd-07:00
-	"2020-07-20+08:00",
-	// mm.dd.yy
-	"3.31.2014",
-	"03.31.2014",
-	"08.21.71",
-	"2014.03",
-	"2014.03.30",
-	//  yyyymmdd and similar
-	"20140601",
-	"20140722105203",
-	// yymmdd hh:mm:yy  mysql log
-	// 080313 05:21:55 mysqld started
-	"171113 14:14:20",
-	// unix seconds, ms, micro, nano
-	"1332151919",
-	"1384216367189",
-	"1384216367111222",
-	"1384216367111222333",
-}
-
-var (
-	timezone = ""
-)
-
-func main() {
-	flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
-	flag.Parse()
-
-	if timezone != "" {
-		// NOTE:  This is very, very important to understand
-		// time-parsing in go
-		loc, err := time.LoadLocation(timezone)
-		if err != nil {
-			panic(err.Error())
-		}
-		time.Local = loc
-	}
-
-	table := termtables.CreateTable()
-
-	table.AddHeaders("Input", "Parsed, and Output as %v")
-	for _, dateExample := range examples {
-		t, err := dateparse.ParseLocal(dateExample)
-		if err != nil {
-			panic(err.Error())
-		}
-		table.AddRow(dateExample, fmt.Sprintf("%v", t))
-	}
-	fmt.Println(table.Render())
-}
-
-/*
-+-------------------------------------------------------+-----------------------------------------+
-| Input                                                 | Parsed, and Output as %v                |
-+-------------------------------------------------------+-----------------------------------------+
-| May 8, 2009 5:57:51 PM                                | 2009-05-08 17:57:51 +0000 UTC           |
-| oct 7, 1970                                           | 1970-10-07 00:00:00 +0000 UTC           |
-| oct 7, '70                                            | 1970-10-07 00:00:00 +0000 UTC           |
-| oct. 7, 1970                                          | 1970-10-07 00:00:00 +0000 UTC           |
-| oct. 7, 70                                            | 1970-10-07 00:00:00 +0000 UTC           |
-| Mon Jan  2 15:04:05 2006                              | 2006-01-02 15:04:05 +0000 UTC           |
-| Mon Jan  2 15:04:05 MST 2006                          | 2006-01-02 15:04:05 +0000 MST           |
-| Mon Jan 02 15:04:05 -0700 2006                        | 2006-01-02 15:04:05 -0700 -0700         |
-| Monday, 02-Jan-06 15:04:05 MST                        | 2006-01-02 15:04:05 +0000 MST           |
-| Mon, 02 Jan 2006 15:04:05 MST                         | 2006-01-02 15:04:05 +0000 MST           |
-| Tue, 11 Jul 2017 16:28:13 +0200 (CEST)                | 2017-07-11 16:28:13 +0200 +0200         |
-| Mon, 02 Jan 2006 15:04:05 -0700                       | 2006-01-02 15:04:05 -0700 -0700         |
-| Mon 30 Sep 2018 09:09:09 PM UTC                       | 2018-09-30 21:09:09 +0000 UTC           |
-| Mon Aug 10 15:44:11 UTC+0100 2015                     | 2015-08-10 15:44:11 +0000 UTC           |
-| Thu, 4 Jan 2018 17:53:36 +0000                        | 2018-01-04 17:53:36 +0000 UTC           |
-| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT           |
-| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00)            | 2021-01-03 00:12:23 +0800 +0800         |
-| September 17, 2012 10:09am                            | 2012-09-17 10:09:00 +0000 UTC           |
-| September 17, 2012 at 10:09am PST-08                  | 2012-09-17 10:09:00 -0800 PST           |
-| September 17, 2012, 10:10:09                          | 2012-09-17 10:10:09 +0000 UTC           |
-| October 7, 1970                                       | 1970-10-07 00:00:00 +0000 UTC           |
-| October 7th, 1970                                     | 1970-10-07 00:00:00 +0000 UTC           |
-| 12 Feb 2006, 19:17                                    | 2006-02-12 19:17:00 +0000 UTC           |
-| 12 Feb 2006 19:17                                     | 2006-02-12 19:17:00 +0000 UTC           |
-| 14 May 2019 19:11:40.164                              | 2019-05-14 19:11:40.164 +0000 UTC       |
-| 7 oct 70                                              | 1970-10-07 00:00:00 +0000 UTC           |
-| 7 oct 1970                                            | 1970-10-07 00:00:00 +0000 UTC           |
-| 03 February 2013                                      | 2013-02-03 00:00:00 +0000 UTC           |
-| 1 July 2013                                           | 2013-07-01 00:00:00 +0000 UTC           |
-| 2013-Feb-03                                           | 2013-02-03 00:00:00 +0000 UTC           |
-| 06/Jan/2008:15:04:05 -0700                            | 2008-01-06 15:04:05 -0700 -0700         |
-| 06/Jan/2008 15:04:05 -0700                            | 2008-01-06 15:04:05 -0700 -0700         |
-| 3/31/2014                                             | 2014-03-31 00:00:00 +0000 UTC           |
-| 03/31/2014                                            | 2014-03-31 00:00:00 +0000 UTC           |
-| 08/21/71                                              | 1971-08-21 00:00:00 +0000 UTC           |
-| 8/1/71                                                | 1971-08-01 00:00:00 +0000 UTC           |
-| 4/8/2014 22:05                                        | 2014-04-08 22:05:00 +0000 UTC           |
-| 04/08/2014 22:05                                      | 2014-04-08 22:05:00 +0000 UTC           |
-| 4/8/14 22:05                                          | 2014-04-08 22:05:00 +0000 UTC           |
-| 04/2/2014 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 8/8/1965 12:00:00 AM                                  | 1965-08-08 00:00:00 +0000 UTC           |
-| 8/8/1965 01:00:01 PM                                  | 1965-08-08 13:00:01 +0000 UTC           |
-| 8/8/1965 01:00 PM                                     | 1965-08-08 13:00:00 +0000 UTC           |
-| 8/8/1965 1:00 PM                                      | 1965-08-08 13:00:00 +0000 UTC           |
-| 8/8/1965 12:00 AM                                     | 1965-08-08 00:00:00 +0000 UTC           |
-| 4/02/2014 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 03/19/2012 10:11:59                                   | 2012-03-19 10:11:59 +0000 UTC           |
-| 03/19/2012 10:11:59.3186369                           | 2012-03-19 10:11:59.3186369 +0000 UTC   |
-| 2014/3/31                                             | 2014-03-31 00:00:00 +0000 UTC           |
-| 2014/03/31                                            | 2014-03-31 00:00:00 +0000 UTC           |
-| 2014/4/8 22:05                                        | 2014-04-08 22:05:00 +0000 UTC           |
-| 2014/04/08 22:05                                      | 2014-04-08 22:05:00 +0000 UTC           |
-| 2014/04/2 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 2014/4/02 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 2012/03/19 10:11:59                                   | 2012-03-19 10:11:59 +0000 UTC           |
-| 2012/03/19 10:11:59.3186369                           | 2012-03-19 10:11:59.3186369 +0000 UTC   |
-| 2014:3:31                                             | 2014-03-31 00:00:00 +0000 UTC           |
-| 2014:03:31                                            | 2014-03-31 00:00:00 +0000 UTC           |
-| 2014:4:8 22:05                                        | 2014-04-08 22:05:00 +0000 UTC           |
-| 2014:04:08 22:05                                      | 2014-04-08 22:05:00 +0000 UTC           |
-| 2014:04:2 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 2014:4:02 03:00:51                                    | 2014-04-02 03:00:51 +0000 UTC           |
-| 2012:03:19 10:11:59                                   | 2012-03-19 10:11:59 +0000 UTC           |
-| 2012:03:19 10:11:59.3186369                           | 2012-03-19 10:11:59.3186369 +0000 UTC   |
-| 2014年04月08日                                        | 2014-04-08 00:00:00 +0000 UTC           |
-| 2006-01-02T15:04:05+0000                              | 2006-01-02 15:04:05 +0000 UTC           |
-| 2009-08-12T22:15:09-07:00                             | 2009-08-12 22:15:09 -0700 -0700         |
-| 2009-08-12T22:15:09                                   | 2009-08-12 22:15:09 +0000 UTC           |
-| 2009-08-12T22:15:09.988                               | 2009-08-12 22:15:09.988 +0000 UTC       |
-| 2009-08-12T22:15:09Z                                  | 2009-08-12 22:15:09 +0000 UTC           |
-| 2017-07-19T03:21:51:897+0100                          | 2017-07-19 03:21:51.897 +0100 +0100     |
-| 2019-05-29T08:41-04                                   | 2019-05-29 08:41:00 -0400 -0400         |
-| 2014-04-26 17:24:37.3186369                           | 2014-04-26 17:24:37.3186369 +0000 UTC   |
-| 2012-08-03 18:31:59.257000000                         | 2012-08-03 18:31:59.257 +0000 UTC       |
-| 2014-04-26 17:24:37.123                               | 2014-04-26 17:24:37.123 +0000 UTC       |
-| 2013-04-01 22:43                                      | 2013-04-01 22:43:00 +0000 UTC           |
-| 2013-04-01 22:43:22                                   | 2013-04-01 22:43:22 +0000 UTC           |
-| 2014-12-16 06:20:00 UTC                               | 2014-12-16 06:20:00 +0000 UTC           |
-| 2014-12-16 06:20:00 GMT                               | 2014-12-16 06:20:00 +0000 UTC           |
-| 2014-04-26 05:24:37 PM                                | 2014-04-26 17:24:37 +0000 UTC           |
-| 2014-04-26 13:13:43 +0800                             | 2014-04-26 13:13:43 +0800 +0800         |
-| 2014-04-26 13:13:43 +0800 +08                         | 2014-04-26 13:13:43 +0800 +0800         |
-| 2014-04-26 13:13:44 +09:00                            | 2014-04-26 13:13:44 +0900 +0900         |
-| 2012-08-03 18:31:59.257000000 +0000 UTC               | 2012-08-03 18:31:59.257 +0000 UTC       |
-| 2015-09-30 18:48:56.35272715 +0000 UTC                | 2015-09-30 18:48:56.35272715 +0000 UTC  |
-| 2015-02-18 00:12:00 +0000 GMT                         | 2015-02-18 00:12:00 +0000 UTC           |
-| 2015-02-18 00:12:00 +0000 UTC                         | 2015-02-18 00:12:00 +0000 UTC           |
-| 2015-02-08 03:02:00 +0300 MSK m=+0.000000001          | 2015-02-08 03:02:00 +0300 +0300         |
-| 2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001      | 2015-02-08 03:02:00.001 +0300 +0300     |
-| 2017-07-19 03:21:51+00:00                             | 2017-07-19 03:21:51 +0000 UTC           |
-| 2014-04-26                                            | 2014-04-26 00:00:00 +0000 UTC           |
-| 2014-04                                               | 2014-04-01 00:00:00 +0000 UTC           |
-| 2014                                                  | 2014-01-01 00:00:00 +0000 UTC           |
-| 2014-05-11 08:20:13,787                               | 2014-05-11 08:20:13.787 +0000 UTC       |
-| 2020-07-20+08:00                                      | 2020-07-20 00:00:00 +0800 +0800         |
-| 3.31.2014                                             | 2014-03-31 00:00:00 +0000 UTC           |
-| 03.31.2014                                            | 2014-03-31 00:00:00 +0000 UTC           |
-| 08.21.71                                              | 1971-08-21 00:00:00 +0000 UTC           |
-| 2014.03                                               | 2014-03-01 00:00:00 +0000 UTC           |
-| 2014.03.30                                            | 2014-03-30 00:00:00 +0000 UTC           |
-| 20140601                                              | 2014-06-01 00:00:00 +0000 UTC           |
-| 20140722105203                                        | 2014-07-22 10:52:03 +0000 UTC           |
-| 171113 14:14:20                                       | 2017-11-13 14:14:20 +0000 UTC           |
-| 1332151919                                            | 2012-03-19 10:11:59 +0000 UTC           |
-| 1384216367189                                         | 2013-11-12 00:32:47.189 +0000 UTC       |
-| 1384216367111222                                      | 2013-11-12 00:32:47.111222 +0000 UTC    |
-| 1384216367111222333                                   | 2013-11-12 00:32:47.111222333 +0000 UTC |
-+-------------------------------------------------------+-----------------------------------------+
-*/
-
-```
diff --git a/vendor/github.com/araddon/dateparse/parseany.go b/vendor/github.com/araddon/dateparse/parseany.go
deleted file mode 100644
index b9668b21bd4..00000000000
--- a/vendor/github.com/araddon/dateparse/parseany.go
+++ /dev/null
@@ -1,2189 +0,0 @@
-// Package dateparse parses date-strings without knowing the format
-// in advance, using a fast lex based approach to eliminate shotgun
-// attempts.  It leans towards US style dates when there is a conflict.
-package dateparse
-
-import (
-	"fmt"
-	"strconv"
-	"strings"
-	"time"
-	"unicode"
-	"unicode/utf8"
-)
-
-// func init() {
-// 	gou.SetupLogging("debug")
-// 	gou.SetColorOutput()
-// }
-
-var days = []string{
-	"mon",
-	"tue",
-	"wed",
-	"thu",
-	"fri",
-	"sat",
-	"sun",
-	"monday",
-	"tuesday",
-	"wednesday",
-	"thursday",
-	"friday",
-	"saturday",
-	"sunday",
-}
-
-var months = []string{
-	"january",
-	"february",
-	"march",
-	"april",
-	"may",
-	"june",
-	"july",
-	"august",
-	"september",
-	"october",
-	"november",
-	"december",
-}
-
-type dateState uint8
-type timeState uint8
-
-const (
-	dateStart dateState = iota // 0
-	dateDigit
-	dateDigitSt
-	dateYearDash
-	dateYearDashAlphaDash
-	dateYearDashDash
-	dateYearDashDashWs // 5
-	dateYearDashDashT
-	dateYearDashDashOffset
-	dateDigitDash
-	dateDigitDashAlpha
-	dateDigitDashAlphaDash // 10
-	dateDigitDot
-	dateDigitDotDot
-	dateDigitSlash
-	dateDigitYearSlash
-	dateDigitSlashAlpha // 15
-	dateDigitColon
-	dateDigitChineseYear
-	dateDigitChineseYearWs
-	dateDigitWs
-	dateDigitWsMoYear // 20
-	dateDigitWsMolong
-	dateAlpha
-	dateAlphaWs
-	dateAlphaWsDigit
-	dateAlphaWsDigitMore // 25
-	dateAlphaWsDigitMoreWs
-	dateAlphaWsDigitMoreWsYear
-	dateAlphaWsMonth
-	dateAlphaWsDigitYearmaybe
-	dateAlphaWsMonthMore
-	dateAlphaWsMonthSuffix
-	dateAlphaWsMore
-	dateAlphaWsAtTime
-	dateAlphaWsAlpha
-	dateAlphaWsAlphaYearmaybe // 35
-	dateAlphaPeriodWsDigit
-	dateWeekdayComma
-	dateWeekdayAbbrevComma
-)
-const (
-	// Time state
-	timeIgnore timeState = iota // 0
-	timeStart
-	timeWs
-	timeWsAlpha
-	timeWsAlphaWs
-	timeWsAlphaZoneOffset // 5
-	timeWsAlphaZoneOffsetWs
-	timeWsAlphaZoneOffsetWsYear
-	timeWsAlphaZoneOffsetWsExtra
-	timeWsAMPMMaybe
-	timeWsAMPM // 10
-	timeWsOffset
-	timeWsOffsetWs // 12
-	timeWsOffsetColonAlpha
-	timeWsOffsetColon
-	timeWsYear // 15
-	timeOffset
-	timeOffsetColon
-	timeAlpha
-	timePeriod
-	timePeriodOffset // 20
-	timePeriodOffsetColon
-	timePeriodOffsetColonWs
-	timePeriodWs
-	timePeriodWsAlpha
-	timePeriodWsOffset // 25
-	timePeriodWsOffsetWs
-	timePeriodWsOffsetWsAlpha
-	timePeriodWsOffsetColon
-	timePeriodWsOffsetColonAlpha
-	timeZ
-	timeZDigit
-)
-
-var (
-	// ErrAmbiguousMMDD for date formats such as 04/02/2014 the mm/dd vs dd/mm are
-	// ambiguous, so it is an error for strict parse rules.
-	ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format")
-)
-
-func unknownErr(datestr string) error {
-	return fmt.Errorf("Could not find format for %q", datestr)
-}
-
-// ParseAny parse an unknown date format, detect the layout.
-// Normal parse.  Equivalent Timezone rules as time.Parse().
-// NOTE:  please see readme on mmdd vs ddmm ambiguous dates.
-func ParseAny(datestr string, opts ...ParserOption) (time.Time, error) {
-	p, err := parseTime(datestr, nil, opts...)
-	if err != nil {
-		return time.Time{}, err
-	}
-	return p.parse()
-}
-
-// ParseIn with Location, equivalent to time.ParseInLocation() timezone/offset
-// rules.  Using location arg, if timezone/offset info exists in the
-// datestring, it uses the given location rules for any zone interpretation.
-// That is, MST means one thing when using America/Denver and something else
-// in other locations.
-func ParseIn(datestr string, loc *time.Location, opts ...ParserOption) (time.Time, error) {
-	p, err := parseTime(datestr, loc, opts...)
-	if err != nil {
-		return time.Time{}, err
-	}
-	return p.parse()
-}
-
-// ParseLocal Given an unknown date format, detect the layout,
-// using time.Local, parse.
-//
-// Set Location to time.Local.  Same as ParseIn Location but lazily uses
-// the global time.Local variable for Location argument.
-//
-//     denverLoc, _ := time.LoadLocation("America/Denver")
-//     time.Local = denverLoc
-//
-//     t, err := dateparse.ParseLocal("3/1/2014")
-//
-// Equivalent to:
-//
-//     t, err := dateparse.ParseIn("3/1/2014", denverLoc)
-//
-func ParseLocal(datestr string, opts ...ParserOption) (time.Time, error) {
-	p, err := parseTime(datestr, time.Local, opts...)
-	if err != nil {
-		return time.Time{}, err
-	}
-	return p.parse()
-}
-
-// MustParse  parse a date, and panic if it can't be parsed.  Used for testing.
-// Not recommended for most use-cases.
-func MustParse(datestr string, opts ...ParserOption) time.Time {
-	p, err := parseTime(datestr, nil, opts...)
-	if err != nil {
-		panic(err.Error())
-	}
-	t, err := p.parse()
-	if err != nil {
-		panic(err.Error())
-	}
-	return t
-}
-
-// ParseFormat parse's an unknown date-time string and returns a layout
-// string that can parse this (and exact same format) other date-time strings.
-//
-//     layout, err := dateparse.ParseFormat("2013-02-01 00:00:00")
-//     // layout = "2006-01-02 15:04:05"
-//
-func ParseFormat(datestr string, opts ...ParserOption) (string, error) {
-	p, err := parseTime(datestr, nil, opts...)
-	if err != nil {
-		return "", err
-	}
-	_, err = p.parse()
-	if err != nil {
-		return "", err
-	}
-	return string(p.format), nil
-}
-
-// ParseStrict parse an unknown date format.  IF the date is ambigous
-// mm/dd vs dd/mm then return an error. These return errors:   3.3.2014 , 8/8/71 etc
-func ParseStrict(datestr string, opts ...ParserOption) (time.Time, error) {
-	p, err := parseTime(datestr, nil, opts...)
-	if err != nil {
-		return time.Time{}, err
-	}
-	if p.ambiguousMD {
-		return time.Time{}, ErrAmbiguousMMDD
-	}
-	return p.parse()
-}
-
-func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (p *parser, err error) {
-
-	p = newParser(datestr, loc, opts...)
-	if p.retryAmbiguousDateWithSwap {
-		// month out of range signifies that a day/month swap is the correct solution to an ambiguous date
-		// this is because it means that a day is being interpreted as a month and overflowing the valid value for that
-		// by retrying in this case, we can fix a common situation with no assumptions
-		defer func() {
-			if p != nil && p.ambiguousMD {
-				// if it errors out with the following error, swap before we
-				// get out of this function to reduce scope it needs to be applied on
-				_, err := p.parse()
-				if err != nil && strings.Contains(err.Error(), "month out of range") {
-					// create the option to reverse the preference
-					preferMonthFirst := PreferMonthFirst(!p.preferMonthFirst)
-					// turn off the retry to avoid endless recursion
-					retryAmbiguousDateWithSwap := RetryAmbiguousDateWithSwap(false)
-					modifiedOpts := append(opts, preferMonthFirst, retryAmbiguousDateWithSwap)
-					p, err = parseTime(datestr, time.Local, modifiedOpts...)
-				}
-			}
-
-		}()
-	}
-
-	i := 0
-
-	// General strategy is to read rune by rune through the date looking for
-	// certain hints of what type of date we are dealing with.
-	// Hopefully we only need to read about 5 or 6 bytes before
-	// we figure it out and then attempt a parse
-iterRunes:
-	for ; i < len(datestr); i++ {
-		//r := rune(datestr[i])
-		r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:])
-		if bytesConsumed > 1 {
-			i += (bytesConsumed - 1)
-		}
-
-		// gou.Debugf("i=%d r=%s state=%d   %s", i, string(r), p.stateDate, datestr)
-		switch p.stateDate {
-		case dateStart:
-			if unicode.IsDigit(r) {
-				p.stateDate = dateDigit
-			} else if unicode.IsLetter(r) {
-				p.stateDate = dateAlpha
-			} else {
-				return nil, unknownErr(datestr)
-			}
-		case dateDigit:
-
-			switch r {
-			case '-', '\u2212':
-				// 2006-01-02
-				// 2013-Feb-03
-				// 13-Feb-03
-				// 29-Jun-2016
-				if i == 4 {
-					p.stateDate = dateYearDash
-					p.yeari = 0
-					p.yearlen = i
-					p.moi = i + 1
-					p.set(0, "2006")
-				} else {
-					p.stateDate = dateDigitDash
-				}
-			case '/':
-				// 08/May/2005
-				// 03/31/2005
-				// 2014/02/24
-				p.stateDate = dateDigitSlash
-				if i == 4 {
-					// 2014/02/24  -  Year first /
-					p.yearlen = i // since it was start of datestr, i=len
-					p.moi = i + 1
-					p.setYear()
-					p.stateDate = dateDigitYearSlash
-				} else {
-					// Either Ambiguous dd/mm vs mm/dd  OR dd/month/yy
-					// 08/May/2005
-					// 03/31/2005
-					// 31/03/2005
-					if i+2 < len(p.datestr) && unicode.IsLetter(rune(datestr[i+1])) {
-						// 08/May/2005
-						p.stateDate = dateDigitSlashAlpha
-						p.moi = i + 1
-						p.daylen = 2
-						p.dayi = 0
-						p.setDay()
-						continue
-					}
-					// Ambiguous dd/mm vs mm/dd the bane of date-parsing
-					// 03/31/2005
-					// 31/03/2005
-					p.ambiguousMD = true
-					if p.preferMonthFirst {
-						if p.molen == 0 {
-							// 03/31/2005
-							p.molen = i
-							p.setMonth()
-							p.dayi = i + 1
-						}
-					} else {
-						if p.daylen == 0 {
-							p.daylen = i
-							p.setDay()
-							p.moi = i + 1
-						}
-					}
-
-				}
-
-			case ':':
-				// 03/31/2005
-				// 2014/02/24
-				p.stateDate = dateDigitColon
-				if i == 4 {
-					p.yearlen = i
-					p.moi = i + 1
-					p.setYear()
-				} else {
-					p.ambiguousMD = true
-					if p.preferMonthFirst {
-						if p.molen == 0 {
-							p.molen = i
-							p.setMonth()
-							p.dayi = i + 1
-						}
-					}
-				}
-
-			case '.':
-				// 3.31.2014
-				// 08.21.71
-				// 2014.05
-				p.stateDate = dateDigitDot
-				if i == 4 {
-					p.yearlen = i
-					p.moi = i + 1
-					p.setYear()
-				} else {
-					p.ambiguousMD = true
-					p.moi = 0
-					p.molen = i
-					p.setMonth()
-					p.dayi = i + 1
-				}
-
-			case ' ':
-				// 18 January 2018
-				// 8 January 2018
-				// 8 jan 2018
-				// 02 Jan 2018 23:59
-				// 02 Jan 2018 23:59:34
-				// 12 Feb 2006, 19:17
-				// 12 Feb 2006, 19:17:22
-				if i == 6 {
-					p.stateDate = dateDigitSt
-				} else {
-					p.stateDate = dateDigitWs
-					p.dayi = 0
-					p.daylen = i
-				}
-			case '年':
-				// Chinese Year
-				p.stateDate = dateDigitChineseYear
-			case ',':
-				return nil, unknownErr(datestr)
-			default:
-				continue
-			}
-			p.part1Len = i
-
-		case dateDigitSt:
-			p.set(0, "060102")
-			i = i - 1
-			p.stateTime = timeStart
-			break iterRunes
-		case dateYearDash:
-			// dateYearDashDashT
-			//  2006-01-02T15:04:05Z07:00
-			//  2020-08-17T17:00:00:000+0100
-			// dateYearDashDashWs
-			//  2013-04-01 22:43:22
-			// dateYearDashAlphaDash
-			//   2013-Feb-03
-			switch r {
-			case '-':
-				p.molen = i - p.moi
-				p.dayi = i + 1
-				p.stateDate = dateYearDashDash
-				p.setMonth()
-			default:
-				if unicode.IsLetter(r) {
-					p.stateDate = dateYearDashAlphaDash
-				}
-			}
-
-		case dateYearDashDash:
-			// dateYearDashDashT
-			//  2006-01-02T15:04:05Z07:00
-			// dateYearDashDashWs
-			//  2013-04-01 22:43:22
-			// dateYearDashDashOffset
-			//  2020-07-20+00:00
-			switch r {
-			case '+', '-':
-				p.offseti = i
-				p.daylen = i - p.dayi
-				p.stateDate = dateYearDashDashOffset
-				p.setDay()
-			case ' ':
-				p.daylen = i - p.dayi
-				p.stateDate = dateYearDashDashWs
-				p.stateTime = timeStart
-				p.setDay()
-				break iterRunes
-			case 'T':
-				p.daylen = i - p.dayi
-				p.stateDate = dateYearDashDashT
-				p.stateTime = timeStart
-				p.setDay()
-				break iterRunes
-			}
-
-		case dateYearDashDashT:
-			// dateYearDashDashT
-			//  2006-01-02T15:04:05Z07:00
-			//  2020-08-17T17:00:00:000+0100
-
-		case dateYearDashDashOffset:
-			//  2020-07-20+00:00
-			switch r {
-			case ':':
-				p.set(p.offseti, "-07:00")
-				// case ' ':
-				// 	return nil, unknownErr(datestr)
-			}
-
-		case dateYearDashAlphaDash:
-			// 2013-Feb-03
-			switch r {
-			case '-':
-				p.molen = i - p.moi
-				p.set(p.moi, "Jan")
-				p.dayi = i + 1
-			}
-		case dateDigitDash:
-			// 13-Feb-03
-			// 29-Jun-2016
-			if unicode.IsLetter(r) {
-				p.stateDate = dateDigitDashAlpha
-				p.moi = i
-			} else {
-				return nil, unknownErr(datestr)
-			}
-		case dateDigitDashAlpha:
-			// 13-Feb-03
-			// 28-Feb-03
-			// 29-Jun-2016
-			switch r {
-			case '-':
-				p.molen = i - p.moi
-				p.set(p.moi, "Jan")
-				p.yeari = i + 1
-				p.stateDate = dateDigitDashAlphaDash
-			}
-
-		case dateDigitDashAlphaDash:
-			// 13-Feb-03   ambiguous
-			// 28-Feb-03   ambiguous
-			// 29-Jun-2016  dd-month(alpha)-yyyy
-			switch r {
-			case ' ':
-				// we need to find if this was 4 digits, aka year
-				// or 2 digits which makes it ambiguous year/day
-				length := i - (p.moi + p.molen + 1)
-				if length == 4 {
-					p.yearlen = 4
-					p.set(p.yeari, "2006")
-					// We now also know that part1 was the day
-					p.dayi = 0
-					p.daylen = p.part1Len
-					p.setDay()
-				} else if length == 2 {
-					// We have no idea if this is
-					// yy-mon-dd   OR  dd-mon-yy
-					//
-					// We are going to ASSUME (bad, bad) that it is dd-mon-yy  which is a horible assumption
-					p.ambiguousMD = true
-					p.yearlen = 2
-					p.set(p.yeari, "06")
-					// We now also know that part1 was the day
-					p.dayi = 0
-					p.daylen = p.part1Len
-					p.setDay()
-				}
-				p.stateTime = timeStart
-				break iterRunes
-			}
-
-		case dateDigitYearSlash:
-			// 2014/07/10 06:55:38.156283
-			// I honestly don't know if this format ever shows up as yyyy/
-
-			switch r {
-			case ' ', ':':
-				p.stateTime = timeStart
-				if p.daylen == 0 {
-					p.daylen = i - p.dayi
-					p.setDay()
-				}
-				break iterRunes
-			case '/':
-				if p.molen == 0 {
-					p.molen = i - p.moi
-					p.setMonth()
-					p.dayi = i + 1
-				}
-			}
-
-		case dateDigitSlashAlpha:
-			// 06/May/2008
-
-			switch r {
-			case '/':
-				//       |
-				// 06/May/2008
-				if p.molen == 0 {
-					p.set(p.moi, "Jan")
-					p.yeari = i + 1
-				}
-				// We aren't breaking because we are going to re-use this case
-				// to find where the date starts, and possible time begins
-			case ' ', ':':
-				p.stateTime = timeStart
-				if p.yearlen == 0 {
-					p.yearlen = i - p.yeari
-					p.setYear()
-				}
-				break iterRunes
-			}
-
-		case dateDigitSlash:
-			// 03/19/2012 10:11:59
-			// 04/2/2014 03:00:37
-			// 3/1/2012 10:11:59
-			// 4/8/2014 22:05
-			// 3/1/2014
-			// 10/13/2014
-			// 01/02/2006
-			// 1/2/06
-
-			switch r {
-			case '/':
-				// This is the 2nd / so now we should know start pts of all of the dd, mm, yy
-				if p.preferMonthFirst {
-					if p.daylen == 0 {
-						p.daylen = i - p.dayi
-						p.setDay()
-						p.yeari = i + 1
-					}
-				} else {
-					if p.molen == 0 {
-						p.molen = i - p.moi
-						p.setMonth()
-						p.yeari = i + 1
-					}
-				}
-				// Note no break, we are going to pass by and re-enter this dateDigitSlash
-				// and look for ending (space) or not (just date)
-			case ' ':
-				p.stateTime = timeStart
-				if p.yearlen == 0 {
-					p.yearlen = i - p.yeari
-					p.setYear()
-				}
-				break iterRunes
-			}
-
-		case dateDigitColon:
-			// 2014:07:10 06:55:38.156283
-			// 03:19:2012 10:11:59
-			// 04:2:2014 03:00:37
-			// 3:1:2012 10:11:59
-			// 4:8:2014 22:05
-			// 3:1:2014
-			// 10:13:2014
-			// 01:02:2006
-			// 1:2:06
-
-			switch r {
-			case ' ':
-				p.stateTime = timeStart
-				if p.yearlen == 0 {
-					p.yearlen = i - p.yeari
-					p.setYear()
-				} else if p.daylen == 0 {
-					p.daylen = i - p.dayi
-					p.setDay()
-				}
-				break iterRunes
-			case ':':
-				if p.yearlen > 0 {
-					// 2014:07:10 06:55:38.156283
-					if p.molen == 0 {
-						p.molen = i - p.moi
-						p.setMonth()
-						p.dayi = i + 1
-					}
-				} else if p.preferMonthFirst {
-					if p.daylen == 0 {
-						p.daylen = i - p.dayi
-						p.setDay()
-						p.yeari = i + 1
-					}
-				}
-			}
-
-		case dateDigitWs:
-			// 18 January 2018
-			// 8 January 2018
-			// 8 jan 2018
-			// 1 jan 18
-			// 02 Jan 2018 23:59
-			// 02 Jan 2018 23:59:34
-			// 12 Feb 2006, 19:17
-			// 12 Feb 2006, 19:17:22
-			switch r {
-			case ' ':
-				p.yeari = i + 1
-				//p.yearlen = 4
-				p.dayi = 0
-				p.daylen = p.part1Len
-				p.setDay()
-				p.stateTime = timeStart
-				if i > p.daylen+len(" Sep") { //  November etc
-					// If len greather than space + 3 it must be full month
-					p.stateDate = dateDigitWsMolong
-				} else {
-					// If len=3, the might be Feb or May?  Ie ambigous abbreviated but
-					// we can parse may with either.  BUT, that means the
-					// format may not be correct?
-					// mo := strings.ToLower(datestr[p.daylen+1 : i])
-					p.moi = p.daylen + 1
-					p.molen = i - p.moi
-					p.set(p.moi, "Jan")
-					p.stateDate = dateDigitWsMoYear
-				}
-			}
-
-		case dateDigitWsMoYear:
-			// 8 jan 2018
-			// 02 Jan 2018 23:59
-			// 02 Jan 2018 23:59:34
-			// 12 Feb 2006, 19:17
-			// 12 Feb 2006, 19:17:22
-			switch r {
-			case ',':
-				p.yearlen = i - p.yeari
-				p.setYear()
-				i++
-				break iterRunes
-			case ' ':
-				p.yearlen = i - p.yeari
-				p.setYear()
-				break iterRunes
-			}
-		case dateDigitWsMolong:
-			// 18 January 2018
-			// 8 January 2018
-
-		case dateDigitChineseYear:
-			// dateDigitChineseYear
-			//   2014年04月08日
-			//               weekday  %Y年%m月%e日 %A %I:%M %p
-			// 2013年07月18日 星期四 10:27 上午
-			if r == ' ' {
-				p.stateDate = dateDigitChineseYearWs
-				break
-			}
-		case dateDigitDot:
-			// This is the 2nd period
-			// 3.31.2014
-			// 08.21.71
-			// 2014.05
-			// 2018.09.30
-			if r == '.' {
-				if p.moi == 0 {
-					// 3.31.2014
-					p.daylen = i - p.dayi
-					p.yeari = i + 1
-					p.setDay()
-					p.stateDate = dateDigitDotDot
-				} else {
-					// 2018.09.30
-					//p.molen = 2
-					p.molen = i - p.moi
-					p.dayi = i + 1
-					p.setMonth()
-					p.stateDate = dateDigitDotDot
-				}
-			}
-		case dateDigitDotDot:
-			// iterate all the way through
-		case dateAlpha:
-			// dateAlphaWS
-			//  Mon Jan _2 15:04:05 2006
-			//  Mon Jan _2 15:04:05 MST 2006
-			//  Mon Jan 02 15:04:05 -0700 2006
-			//  Mon Aug 10 15:44:11 UTC+0100 2015
-			//  Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
-			//  dateAlphaWSDigit
-			//    May 8, 2009 5:57:51 PM
-			//    oct 1, 1970
-			//  dateAlphaWsMonth
-			//    April 8, 2009
-			//  dateAlphaWsMore
-			//    dateAlphaWsAtTime
-			//      January 02, 2006 at 3:04pm MST-07
-			//
-			//  dateAlphaPeriodWsDigit
-			//    oct. 1, 1970
-			// dateWeekdayComma
-			//   Monday, 02 Jan 2006 15:04:05 MST
-			//   Monday, 02-Jan-06 15:04:05 MST
-			//   Monday, 02 Jan 2006 15:04:05 -0700
-			//   Monday, 02 Jan 2006 15:04:05 +0100
-			// dateWeekdayAbbrevComma
-			//   Mon, 02 Jan 2006 15:04:05 MST
-			//   Mon, 02 Jan 2006 15:04:05 -0700
-			//   Thu, 13 Jul 2017 08:58:40 +0100
-			//   Tue, 11 Jul 2017 16:28:13 +0200 (CEST)
-			//   Mon, 02-Jan-06 15:04:05 MST
-			switch {
-			case r == ' ':
-				//      X
-				// April 8, 2009
-				if i > 3 {
-					// Check to see if the alpha is name of month?  or Day?
-					month := strings.ToLower(datestr[0:i])
-					if isMonthFull(month) {
-						p.fullMonth = month
-						// len(" 31, 2018")   = 9
-						if len(datestr[i:]) < 10 {
-							// April 8, 2009
-							p.stateDate = dateAlphaWsMonth
-						} else {
-							p.stateDate = dateAlphaWsMore
-						}
-						p.dayi = i + 1
-						break
-					}
-
-				} else {
-					// This is possibly ambiguous?  May will parse as either though.
-					// So, it could return in-correct format.
-					// dateAlphaWs
-					//   May 05, 2005, 05:05:05
-					//   May 05 2005, 05:05:05
-					//   Jul 05, 2005, 05:05:05
-					//   May 8 17:57:51 2009
-					//   May  8 17:57:51 2009
-					// skip & return to dateStart
-					//   Tue 05 May 2020, 05:05:05
-					//   Mon Jan  2 15:04:05 2006
-
-					maybeDay := strings.ToLower(datestr[0:i])
-					if isDay(maybeDay) {
-						// using skip throws off indices used by other code; saner to restart
-						return parseTime(datestr[i+1:], loc)
-					}
-					p.stateDate = dateAlphaWs
-				}
-
-			case r == ',':
-				// Mon, 02 Jan 2006
-
-				if i == 3 {
-					p.stateDate = dateWeekdayAbbrevComma
-					p.set(0, "Mon")
-				} else {
-					p.stateDate = dateWeekdayComma
-					p.skip = i + 2
-					i++
-					// TODO:  lets just make this "skip" as we don't need
-					// the mon, monday, they are all superfelous and not needed
-					// just lay down the skip, no need to fill and then skip
-				}
-			case r == '.':
-				// sept. 28, 2017
-				// jan. 28, 2017
-				p.stateDate = dateAlphaPeriodWsDigit
-				if i == 3 {
-					p.molen = i
-					p.set(0, "Jan")
-				} else if i == 4 {
-					// gross
-					datestr = datestr[0:i-1] + datestr[i:]
-					return parseTime(datestr, loc, opts...)
-				} else {
-					return nil, unknownErr(datestr)
-				}
-			}
-
-		case dateAlphaWs:
-			// dateAlphaWsAlpha
-			//   Mon Jan _2 15:04:05 2006
-			//   Mon Jan _2 15:04:05 MST 2006
-			//   Mon Jan 02 15:04:05 -0700 2006
-			//   Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
-			//   Mon Aug 10 15:44:11 UTC+0100 2015
-			// dateAlphaWsDigit
-			//   May 8, 2009 5:57:51 PM
-			//   May 8 2009 5:57:51 PM
-			//   May 8 17:57:51 2009
-			//   May  8 17:57:51 2009
-			//   May 08 17:57:51 2009
-			//   oct 1, 1970
-			//   oct 7, '70
-			switch {
-			case unicode.IsLetter(r):
-				p.set(0, "Mon")
-				p.stateDate = dateAlphaWsAlpha
-				p.set(i, "Jan")
-			case unicode.IsDigit(r):
-				p.set(0, "Jan")
-				p.stateDate = dateAlphaWsDigit
-				p.dayi = i
-			}
-
-		case dateAlphaWsDigit:
-			// May 8, 2009 5:57:51 PM
-			// May 8 2009 5:57:51 PM
-			// oct 1, 1970
-			// oct 7, '70
-			// oct. 7, 1970
-			// May 8 17:57:51 2009
-			// May  8 17:57:51 2009
-			// May 08 17:57:51 2009
-			if r == ',' {
-				p.daylen = i - p.dayi
-				p.setDay()
-				p.stateDate = dateAlphaWsDigitMore
-			} else if r == ' ' {
-				p.daylen = i - p.dayi
-				p.setDay()
-				p.yeari = i + 1
-				p.stateDate = dateAlphaWsDigitYearmaybe
-				p.stateTime = timeStart
-			} else if unicode.IsLetter(r) {
-				p.stateDate = dateAlphaWsMonthSuffix
-				i--
-			}
-		case dateAlphaWsDigitYearmaybe:
-			//       x
-			// May 8 2009 5:57:51 PM
-			// May 8 17:57:51 2009
-			// May  8 17:57:51 2009
-			// May 08 17:57:51 2009
-			// Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
-			if r == ':' {
-				// Guessed wrong; was not a year
-				i = i - 3
-				p.stateDate = dateAlphaWsDigit
-				p.yeari = 0
-				break iterRunes
-			} else if r == ' ' {
-				// must be year format, not 15:04
-				p.yearlen = i - p.yeari
-				p.setYear()
-				break iterRunes
-			}
-		case dateAlphaWsDigitMore:
-			//       x
-			// May 8, 2009 5:57:51 PM
-			// May 05, 2005, 05:05:05
-			// May 05 2005, 05:05:05
-			// oct 1, 1970
-			// oct 7, '70
-			if r == ' ' {
-				p.yeari = i + 1
-				p.stateDate = dateAlphaWsDigitMoreWs
-			}
-		case dateAlphaWsDigitMoreWs:
-			//            x
-			// May 8, 2009 5:57:51 PM
-			// May 05, 2005, 05:05:05
-			// oct 1, 1970
-			// oct 7, '70
-			switch r {
-			case '\'':
-				p.yeari = i + 1
-			case ' ', ',':
-				//            x
-				// May 8, 2009 5:57:51 PM
-				//            x
-				// May 8, 2009, 5:57:51 PM
-				p.stateDate = dateAlphaWsDigitMoreWsYear
-				p.yearlen = i - p.yeari
-				p.setYear()
-				p.stateTime = timeStart
-				break iterRunes
-			}
-
-		case dateAlphaWsMonth:
-			// April 8, 2009
-			// April 8 2009
-			switch r {
-			case ' ', ',':
-				//       x
-				// June 8, 2009
-				//       x
-				// June 8 2009
-				if p.daylen == 0 {
-					p.daylen = i - p.dayi
-					p.setDay()
-				}
-			case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N':
-				// st, rd, nd, st
-				i--
-				p.stateDate = dateAlphaWsMonthSuffix
-			default:
-				if p.daylen > 0 && p.yeari == 0 {
-					p.yeari = i
-				}
-			}
-		case dateAlphaWsMonthMore:
-			//                  X
-			// January 02, 2006, 15:04:05
-			// January 02 2006, 15:04:05
-			// January 02, 2006 15:04:05
-			// January 02 2006 15:04:05
-			switch r {
-			case ',':
-				p.yearlen = i - p.yeari
-				p.setYear()
-				p.stateTime = timeStart
-				i++
-				break iterRunes
-			case ' ':
-				p.yearlen = i - p.yeari
-				p.setYear()
-				p.stateTime = timeStart
-				break iterRunes
-			}
-		case dateAlphaWsMonthSuffix:
-			//        x
-			// April 8th, 2009
-			// April 8th 2009
-			switch r {
-			case 't', 'T':
-				if p.nextIs(i, 'h') || p.nextIs(i, 'H') {
-					if len(datestr) > i+2 {
-						return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...)
-					}
-				}
-			case 'n', 'N':
-				if p.nextIs(i, 'd') || p.nextIs(i, 'D') {
-					if len(datestr) > i+2 {
-						return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...)
-					}
-				}
-			case 's', 'S':
-				if p.nextIs(i, 't') || p.nextIs(i, 'T') {
-					if len(datestr) > i+2 {
-						return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...)
-					}
-				}
-			case 'r', 'R':
-				if p.nextIs(i, 'd') || p.nextIs(i, 'D') {
-					if len(datestr) > i+2 {
-						return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...)
-					}
-				}
-			}
-		case dateAlphaWsMore:
-			// January 02, 2006, 15:04:05
-			// January 02 2006, 15:04:05
-			// January 2nd, 2006, 15:04:05
-			// January 2nd 2006, 15:04:05
-			// September 17, 2012 at 5:00pm UTC-05
-			switch {
-			case r == ',':
-				//           x
-				// January 02, 2006, 15:04:05
-				if p.nextIs(i, ' ') {
-					p.daylen = i - p.dayi
-					p.setDay()
-					p.yeari = i + 2
-					p.stateDate = dateAlphaWsMonthMore
-					i++
-				}
-
-			case r == ' ':
-				//           x
-				// January 02 2006, 15:04:05
-				p.daylen = i - p.dayi
-				p.setDay()
-				p.yeari = i + 1
-				p.stateDate = dateAlphaWsMonthMore
-			case unicode.IsDigit(r):
-				//         XX
-				// January 02, 2006, 15:04:05
-				continue
-			case unicode.IsLetter(r):
-				//          X
-				// January 2nd, 2006, 15:04:05
-				p.daylen = i - p.dayi
-				p.setDay()
-				p.stateDate = dateAlphaWsMonthSuffix
-				i--
-			}
-
-		case dateAlphaPeriodWsDigit:
-			//    oct. 7, '70
-			switch {
-			case r == ' ':
-				// continue
-			case unicode.IsDigit(r):
-				p.stateDate = dateAlphaWsDigit
-				p.dayi = i
-			default:
-				return p, unknownErr(datestr)
-			}
-		case dateWeekdayComma:
-			// Monday, 02 Jan 2006 15:04:05 MST
-			// Monday, 02 Jan 2006 15:04:05 -0700
-			// Monday, 02 Jan 2006 15:04:05 +0100
-			// Monday, 02-Jan-06 15:04:05 MST
-			if p.dayi == 0 {
-				p.dayi = i
-			}
-			switch r {
-			case ' ', '-':
-				if p.moi == 0 {
-					p.moi = i + 1
-					p.daylen = i - p.dayi
-					p.setDay()
-				} else if p.yeari == 0 {
-					p.yeari = i + 1
-					p.molen = i - p.moi
-					p.set(p.moi, "Jan")
-				} else {
-					p.stateTime = timeStart
-					break iterRunes
-				}
-			}
-		case dateWeekdayAbbrevComma:
-			// Mon, 02 Jan 2006 15:04:05 MST
-			// Mon, 02 Jan 2006 15:04:05 -0700
-			// Thu, 13 Jul 2017 08:58:40 +0100
-			// Thu, 4 Jan 2018 17:53:36 +0000
-			// Tue, 11 Jul 2017 16:28:13 +0200 (CEST)
-			// Mon, 02-Jan-06 15:04:05 MST
-			switch r {
-			case ' ', '-':
-				if p.dayi == 0 {
-					p.dayi = i + 1
-				} else if p.moi == 0 {
-					p.daylen = i - p.dayi
-					p.setDay()
-					p.moi = i + 1
-				} else if p.yeari == 0 {
-					p.molen = i - p.moi
-					p.set(p.moi, "Jan")
-					p.yeari = i + 1
-				} else {
-					p.yearlen = i - p.yeari
-					p.setYear()
-					p.stateTime = timeStart
-					break iterRunes
-				}
-			}
-
-		default:
-			break iterRunes
-		}
-	}
-	p.coalesceDate(i)
-	if p.stateTime == timeStart {
-		// increment first one, since the i++ occurs at end of loop
-		if i < len(p.datestr) {
-			i++
-		}
-		// ensure we skip any whitespace prefix
-		for ; i < len(datestr); i++ {
-			r := rune(datestr[i])
-			if r != ' ' {
-				break
-			}
-		}
-
-	iterTimeRunes:
-		for ; i < len(datestr); i++ {
-			r := rune(datestr[i])
-
-			// gou.Debugf("i=%d r=%s state=%d iterTimeRunes  %s %s", i, string(r), p.stateTime, p.ds(), p.ts())
-
-			switch p.stateTime {
-			case timeStart:
-				// 22:43:22
-				// 22:43
-				// timeComma
-				//   08:20:13,787
-				// timeWs
-				//   05:24:37 PM
-				//   06:20:00 UTC
-				//   06:20:00 UTC-05
-				//   00:12:00 +0000 UTC
-				//   22:18:00 +0000 UTC m=+0.000000001
-				//   15:04:05 -0700
-				//   15:04:05 -07:00
-				//   15:04:05 2008
-				// timeOffset
-				//   03:21:51+00:00
-				//   19:55:00+0100
-				// timePeriod
-				//   17:24:37.3186369
-				//   00:07:31.945167
-				//   18:31:59.257000000
-				//   00:00:00.000
-				//   timePeriodOffset
-				//     19:55:00.799+0100
-				//     timePeriodOffsetColon
-				//       15:04:05.999-07:00
-				//   timePeriodWs
-				//     timePeriodWsOffset
-				//       00:07:31.945167 +0000
-				//       00:00:00.000 +0000
-				//     timePeriodWsOffsetAlpha
-				//       00:07:31.945167 +0000 UTC
-				//       22:18:00.001 +0000 UTC m=+0.000000001
-				//       00:00:00.000 +0000 UTC
-				//     timePeriodWsAlpha
-				//       06:20:00.000 UTC
-				if p.houri == 0 {
-					p.houri = i
-				}
-				switch r {
-				case ',':
-					// hm, lets just swap out comma for period.  for some reason go
-					// won't parse it.
-					// 2014-05-11 08:20:13,787
-					ds := []byte(p.datestr)
-					ds[i] = '.'
-					return parseTime(string(ds), loc, opts...)
-				case '-', '+':
-					//   03:21:51+00:00
-					p.stateTime = timeOffset
-					if p.seci == 0 {
-						// 22:18+0530
-						p.minlen = i - p.mini
-					} else {
-						if p.seclen == 0 {
-							p.seclen = i - p.seci
-						}
-						if p.msi > 0 && p.mslen == 0 {
-							p.mslen = i - p.msi
-						}
-					}
-					p.offseti = i
-				case '.':
-					p.stateTime = timePeriod
-					p.seclen = i - p.seci
-					p.msi = i + 1
-				case 'Z':
-					p.stateTime = timeZ
-					if p.seci == 0 {
-						p.minlen = i - p.mini
-					} else {
-						p.seclen = i - p.seci
-					}
-					// (Z)ulu time
-					p.loc = time.UTC
-				case 'a', 'A':
-					if p.nextIs(i, 't') || p.nextIs(i, 'T') {
-						//                    x
-						// September 17, 2012 at 5:00pm UTC-05
-						i++ // skip t
-						if p.nextIs(i, ' ') {
-							//                      x
-							// September 17, 2012 at 5:00pm UTC-05
-							i++         // skip '
-							p.houri = 0 // reset hour
-						}
-					} else {
-						switch {
-						case r == 'a' && p.nextIs(i, 'm'):
-							p.coalesceTime(i)
-							p.set(i, "am")
-						case r == 'A' && p.nextIs(i, 'M'):
-							p.coalesceTime(i)
-							p.set(i, "PM")
-						}
-					}
-
-				case 'p', 'P':
-					// Could be AM/PM
-					switch {
-					case r == 'p' && p.nextIs(i, 'm'):
-						p.coalesceTime(i)
-						p.set(i, "pm")
-					case r == 'P' && p.nextIs(i, 'M'):
-						p.coalesceTime(i)
-						p.set(i, "PM")
-					}
-				case ' ':
-					p.coalesceTime(i)
-					p.stateTime = timeWs
-				case ':':
-					if p.mini == 0 {
-						p.mini = i + 1
-						p.hourlen = i - p.houri
-					} else if p.seci == 0 {
-						p.seci = i + 1
-						p.minlen = i - p.mini
-					} else if p.seci > 0 {
-						// 18:31:59:257    ms uses colon, wtf
-						p.seclen = i - p.seci
-						p.set(p.seci, "05")
-						p.msi = i + 1
-
-						// gross, gross, gross.   manipulating the datestr is horrible.
-						// https://github.com/araddon/dateparse/issues/117
-						// Could not get the parsing to work using golang time.Parse() without
-						// replacing that colon with period.
-						p.set(i, ".")
-						datestr = datestr[0:i] + "." + datestr[i+1:]
-						p.datestr = datestr
-					}
-				}
-			case timeOffset:
-				// 19:55:00+0100
-				// timeOffsetColon
-				//   15:04:05+07:00
-				//   15:04:05-07:00
-				if r == ':' {
-					p.stateTime = timeOffsetColon
-				}
-			case timeWs:
-				// timeWsAlpha
-				//   06:20:00 UTC
-				//   06:20:00 UTC-05
-				//   15:44:11 UTC+0100 2015
-				//   18:04:07 GMT+0100 (GMT Daylight Time)
-				//   17:57:51 MST 2009
-				//   timeWsAMPMMaybe
-				//     05:24:37 PM
-				// timeWsOffset
-				//   15:04:05 -0700
-				//   00:12:00 +0000 UTC
-				//   timeWsOffsetColon
-				//     15:04:05 -07:00
-				//     17:57:51 -0700 2009
-				//     timeWsOffsetColonAlpha
-				//       00:12:00 +00:00 UTC
-				// timeWsYear
-				//     00:12:00 2008
-				// timeZ
-				//   15:04:05.99Z
-				switch r {
-				case 'A', 'P':
-					// Could be AM/PM or could be PST or similar
-					p.tzi = i
-					p.stateTime = timeWsAMPMMaybe
-				case '+', '-':
-					p.offseti = i
-					p.stateTime = timeWsOffset
-				default:
-					if unicode.IsLetter(r) {
-						// 06:20:00 UTC
-						// 06:20:00 UTC-05
-						// 15:44:11 UTC+0100 2015
-						// 17:57:51 MST 2009
-						p.tzi = i
-						p.stateTime = timeWsAlpha
-					} else if unicode.IsDigit(r) {
-						// 00:12:00 2008
-						p.stateTime = timeWsYear
-						p.yeari = i
-					}
-				}
-			case timeWsAlpha:
-				// 06:20:00 UTC
-				// 06:20:00 UTC-05
-				// timeWsAlphaWs
-				//   17:57:51 MST 2009
-				// timeWsAlphaZoneOffset
-				// timeWsAlphaZoneOffsetWs
-				//   timeWsAlphaZoneOffsetWsExtra
-				//     18:04:07 GMT+0100 (GMT Daylight Time)
-				//   timeWsAlphaZoneOffsetWsYear
-				//     15:44:11 UTC+0100 2015
-				switch r {
-				case '+', '-':
-					p.tzlen = i - p.tzi
-					if p.tzlen == 4 {
-						p.set(p.tzi, " MST")
-					} else if p.tzlen == 3 {
-						p.set(p.tzi, "MST")
-					}
-					p.stateTime = timeWsAlphaZoneOffset
-					p.offseti = i
-				case ' ':
-					// 17:57:51 MST 2009
-					// 17:57:51 MST
-					p.tzlen = i - p.tzi
-					if p.tzlen == 4 {
-						p.set(p.tzi, " MST")
-					} else if p.tzlen == 3 {
-						p.set(p.tzi, "MST")
-					}
-					p.stateTime = timeWsAlphaWs
-					p.yeari = i + 1
-				}
-			case timeWsAlphaWs:
-				//   17:57:51 MST 2009
-
-			case timeWsAlphaZoneOffset:
-				// 06:20:00 UTC-05
-				// timeWsAlphaZoneOffset
-				// timeWsAlphaZoneOffsetWs
-				//   timeWsAlphaZoneOffsetWsExtra
-				//     18:04:07 GMT+0100 (GMT Daylight Time)
-				//   timeWsAlphaZoneOffsetWsYear
-				//     15:44:11 UTC+0100 2015
-				switch r {
-				case ' ':
-					p.set(p.offseti, "-0700")
-					if p.yeari == 0 {
-						p.yeari = i + 1
-					}
-					p.stateTime = timeWsAlphaZoneOffsetWs
-				}
-			case timeWsAlphaZoneOffsetWs:
-				// timeWsAlphaZoneOffsetWs
-				//   timeWsAlphaZoneOffsetWsExtra
-				//     18:04:07 GMT+0100 (GMT Daylight Time)
-				//   timeWsAlphaZoneOffsetWsYear
-				//     15:44:11 UTC+0100 2015
-				if unicode.IsDigit(r) {
-					p.stateTime = timeWsAlphaZoneOffsetWsYear
-				} else {
-					p.extra = i - 1
-					p.stateTime = timeWsAlphaZoneOffsetWsExtra
-				}
-			case timeWsAlphaZoneOffsetWsYear:
-				// 15:44:11 UTC+0100 2015
-				if unicode.IsDigit(r) {
-					p.yearlen = i - p.yeari + 1
-					if p.yearlen == 4 {
-						p.setYear()
-					}
-				}
-			case timeWsAMPMMaybe:
-				// timeWsAMPMMaybe
-				//   timeWsAMPM
-				//     05:24:37 PM
-				//   timeWsAlpha
-				//     00:12:00 PST
-				//     15:44:11 UTC+0100 2015
-				if r == 'M' {
-					//return parse("2006-01-02 03:04:05 PM", datestr, loc)
-					p.stateTime = timeWsAMPM
-					p.set(i-1, "PM")
-					if p.hourlen == 2 {
-						p.set(p.houri, "03")
-					} else if p.hourlen == 1 {
-						p.set(p.houri, "3")
-					}
-				} else {
-					p.stateTime = timeWsAlpha
-				}
-
-			case timeWsOffset:
-				// timeWsOffset
-				//   15:04:05 -0700
-				//   timeWsOffsetWsOffset
-				//     17:57:51 -0700 -07
-				//   timeWsOffsetWs
-				//     17:57:51 -0700 2009
-				//     00:12:00 +0000 UTC
-				//   timeWsOffsetColon
-				//     15:04:05 -07:00
-				//     timeWsOffsetColonAlpha
-				//       00:12:00 +00:00 UTC
-				switch r {
-				case ':':
-					p.stateTime = timeWsOffsetColon
-				case ' ':
-					p.set(p.offseti, "-0700")
-					p.yeari = i + 1
-					p.stateTime = timeWsOffsetWs
-				}
-			case timeWsOffsetWs:
-				// 17:57:51 -0700 2009
-				// 00:12:00 +0000 UTC
-				// 22:18:00.001 +0000 UTC m=+0.000000001
-				// w Extra
-				//   17:57:51 -0700 -07
-				switch r {
-				case '=':
-					// eff you golang
-					if datestr[i-1] == 'm' {
-						p.extra = i - 2
-						p.trimExtra()
-						break
-					}
-				case '+', '-', '(':
-					// This really doesn't seem valid, but for some reason when round-tripping a go date
-					// their is an extra +03 printed out.  seems like go bug to me, but, parsing anyway.
-					// 00:00:00 +0300 +03
-					// 00:00:00 +0300 +0300
-					p.extra = i - 1
-					p.stateTime = timeWsOffset
-					p.trimExtra()
-					break
-				default:
-					switch {
-					case unicode.IsDigit(r):
-						p.yearlen = i - p.yeari + 1
-						if p.yearlen == 4 {
-							p.setYear()
-						}
-					case unicode.IsLetter(r):
-						// 15:04:05 -0700 MST
-						if p.tzi == 0 {
-							p.tzi = i
-						}
-					}
-				}
-
-			case timeWsOffsetColon:
-				// timeWsOffsetColon
-				//   15:04:05 -07:00
-				//   timeWsOffsetColonAlpha
-				//     2015-02-18 00:12:00 +00:00 UTC
-				if unicode.IsLetter(r) {
-					// 2015-02-18 00:12:00 +00:00 UTC
-					p.stateTime = timeWsOffsetColonAlpha
-					break iterTimeRunes
-				}
-			case timePeriod:
-				// 15:04:05.999999999+07:00
-				// 15:04:05.999999999-07:00
-				// 15:04:05.999999+07:00
-				// 15:04:05.999999-07:00
-				// 15:04:05.999+07:00
-				// 15:04:05.999-07:00
-				// timePeriod
-				//   17:24:37.3186369
-				//   00:07:31.945167
-				//   18:31:59.257000000
-				//   00:00:00.000
-				//   timePeriodOffset
-				//     19:55:00.799+0100
-				//     timePeriodOffsetColon
-				//       15:04:05.999-07:00
-				//   timePeriodWs
-				//     timePeriodWsOffset
-				//       00:07:31.945167 +0000
-				//       00:00:00.000 +0000
-				//       With Extra
-				//         00:00:00.000 +0300 +03
-				//     timePeriodWsOffsetAlpha
-				//       00:07:31.945167 +0000 UTC
-				//       00:00:00.000 +0000 UTC
-				//       22:18:00.001 +0000 UTC m=+0.000000001
-				//     timePeriodWsAlpha
-				//       06:20:00.000 UTC
-				switch r {
-				case ' ':
-					p.mslen = i - p.msi
-					p.stateTime = timePeriodWs
-				case '+', '-':
-					// This really shouldn't happen
-					p.mslen = i - p.msi
-					p.offseti = i
-					p.stateTime = timePeriodOffset
-				default:
-					if unicode.IsLetter(r) {
-						// 06:20:00.000 UTC
-						p.mslen = i - p.msi
-						p.stateTime = timePeriodWsAlpha
-					}
-				}
-			case timePeriodOffset:
-				// timePeriodOffset
-				//   19:55:00.799+0100
-				//   timePeriodOffsetColon
-				//     15:04:05.999-07:00
-				//     13:31:51.999-07:00 MST
-				if r == ':' {
-					p.stateTime = timePeriodOffsetColon
-				}
-			case timePeriodOffsetColon:
-				// timePeriodOffset
-				//   timePeriodOffsetColon
-				//     15:04:05.999-07:00
-				//     13:31:51.999 -07:00 MST
-				switch r {
-				case ' ':
-					p.set(p.offseti, "-07:00")
-					p.stateTime = timePeriodOffsetColonWs
-					p.tzi = i + 1
-				}
-			case timePeriodOffsetColonWs:
-				// continue
-			case timePeriodWs:
-				// timePeriodWs
-				//   timePeriodWsOffset
-				//     00:07:31.945167 +0000
-				//     00:00:00.000 +0000
-				//   timePeriodWsOffsetAlpha
-				//     00:07:31.945167 +0000 UTC
-				//     00:00:00.000 +0000 UTC
-				//   timePeriodWsOffsetColon
-				//     13:31:51.999 -07:00 MST
-				//   timePeriodWsAlpha
-				//     06:20:00.000 UTC
-				if p.offseti == 0 {
-					p.offseti = i
-				}
-				switch r {
-				case '+', '-':
-					p.mslen = i - p.msi - 1
-					p.stateTime = timePeriodWsOffset
-				default:
-					if unicode.IsLetter(r) {
-						//     00:07:31.945167 +0000 UTC
-						//     00:00:00.000 +0000 UTC
-						p.stateTime = timePeriodWsOffsetWsAlpha
-						break iterTimeRunes
-					}
-				}
-
-			case timePeriodWsOffset:
-				// timePeriodWs
-				//   timePeriodWsOffset
-				//     00:07:31.945167 +0000
-				//     00:00:00.000 +0000
-				//     With Extra
-				//       00:00:00.000 +0300 +03
-				//   timePeriodWsOffsetAlpha
-				//     00:07:31.945167 +0000 UTC
-				//     00:00:00.000 +0000 UTC
-				//     03:02:00.001 +0300 MSK m=+0.000000001
-				//   timePeriodWsOffsetColon
-				//     13:31:51.999 -07:00 MST
-				//   timePeriodWsAlpha
-				//     06:20:00.000 UTC
-				switch r {
-				case ':':
-					p.stateTime = timePeriodWsOffsetColon
-				case ' ':
-					p.set(p.offseti, "-0700")
-				case '+', '-':
-					// This really doesn't seem valid, but for some reason when round-tripping a go date
-					// their is an extra +03 printed out.  seems like go bug to me, but, parsing anyway.
-					// 00:00:00.000 +0300 +03
-					// 00:00:00.000 +0300 +0300
-					p.extra = i - 1
-					p.trimExtra()
-					break
-				default:
-					if unicode.IsLetter(r) {
-						// 00:07:31.945167 +0000 UTC
-						// 00:00:00.000 +0000 UTC
-						// 03:02:00.001 +0300 MSK m=+0.000000001
-						p.stateTime = timePeriodWsOffsetWsAlpha
-					}
-				}
-			case timePeriodWsOffsetWsAlpha:
-				// 03:02:00.001 +0300 MSK m=+0.000000001
-				// eff you golang
-				if r == '=' && datestr[i-1] == 'm' {
-					p.extra = i - 2
-					p.trimExtra()
-					break
-				}
-
-			case timePeriodWsOffsetColon:
-				// 13:31:51.999 -07:00 MST
-				switch r {
-				case ' ':
-					p.set(p.offseti, "-07:00")
-				default:
-					if unicode.IsLetter(r) {
-						// 13:31:51.999 -07:00 MST
-						p.tzi = i
-						p.stateTime = timePeriodWsOffsetColonAlpha
-					}
-				}
-			case timePeriodWsOffsetColonAlpha:
-				// continue
-			case timeZ:
-				// timeZ
-				//   15:04:05.99Z
-				// With a time-zone at end after Z
-				// 2006-01-02T15:04:05.999999999Z07:00
-				// 2006-01-02T15:04:05Z07:00
-				// RFC3339     = "2006-01-02T15:04:05Z07:00"
-				// RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
-				if unicode.IsDigit(r) {
-					p.stateTime = timeZDigit
-				}
-
-			}
-		}
-
-		switch p.stateTime {
-		case timeWsAlpha:
-			switch len(p.datestr) - p.tzi {
-			case 3:
-				// 13:31:51.999 +01:00 CET
-				p.set(p.tzi, "MST")
-			case 4:
-				p.set(p.tzi, "MST")
-				p.extra = len(p.datestr) - 1
-				p.trimExtra()
-			}
-
-		case timeWsAlphaWs:
-			p.yearlen = i - p.yeari
-			p.setYear()
-		case timeWsYear:
-			p.yearlen = i - p.yeari
-			p.setYear()
-		case timeWsAlphaZoneOffsetWsExtra:
-			p.trimExtra()
-		case timeWsAlphaZoneOffset:
-			// 06:20:00 UTC-05
-			if i-p.offseti < 4 {
-				p.set(p.offseti, "-07")
-			} else {
-				p.set(p.offseti, "-0700")
-			}
-
-		case timePeriod:
-			p.mslen = i - p.msi
-		case timeOffset:
-
-			switch len(p.datestr) - p.offseti {
-			case 0, 1, 2, 4:
-				return p, fmt.Errorf("TZ offset not recognized %q near %q (must be 2 or 4 digits optional colon)", datestr, string(datestr[p.offseti:]))
-			case 3:
-				// 19:55:00+01
-				p.set(p.offseti, "-07")
-			case 5:
-				// 19:55:00+0100
-				p.set(p.offseti, "-0700")
-			}
-
-		case timeWsOffset:
-			p.set(p.offseti, "-0700")
-		case timeWsOffsetWs:
-			// 17:57:51 -0700 2009
-			// 00:12:00 +0000 UTC
-			if p.tzi > 0 {
-				switch len(p.datestr) - p.tzi {
-				case 3:
-					// 13:31:51.999 +01:00 CET
-					p.set(p.tzi, "MST")
-				case 4:
-					// 13:31:51.999 +01:00 CEST
-					p.set(p.tzi, "MST ")
-				}
-
-			}
-		case timeWsOffsetColon:
-			// 17:57:51 -07:00
-			p.set(p.offseti, "-07:00")
-		case timeOffsetColon:
-			// 15:04:05+07:00
-			p.set(p.offseti, "-07:00")
-		case timePeriodOffset:
-			// 19:55:00.799+0100
-			p.set(p.offseti, "-0700")
-		case timePeriodOffsetColon:
-			p.set(p.offseti, "-07:00")
-		case timePeriodWsOffsetColonAlpha:
-			p.tzlen = i - p.tzi
-			switch p.tzlen {
-			case 3:
-				p.set(p.tzi, "MST")
-			case 4:
-				p.set(p.tzi, "MST ")
-			}
-		case timePeriodWsOffset:
-			p.set(p.offseti, "-0700")
-		}
-		p.coalesceTime(i)
-	}
-
-	switch p.stateDate {
-	case dateDigit:
-		// unixy timestamps ish
-		//  example              ct type
-		//  1499979655583057426  19 nanoseconds
-		//  1499979795437000     16 micro-seconds
-		//  20180722105203       14 yyyyMMddhhmmss
-		//  1499979795437        13 milliseconds
-		//  1332151919           10 seconds
-		//  20140601             8  yyyymmdd
-		//  2014                 4  yyyy
-		t := time.Time{}
-		if len(datestr) == len("1499979655583057426") { // 19
-			// nano-seconds
-			if nanoSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
-				t = time.Unix(0, nanoSecs)
-			}
-		} else if len(datestr) == len("1499979795437000") { // 16
-			// micro-seconds
-			if microSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
-				t = time.Unix(0, microSecs*1000)
-			}
-		} else if len(datestr) == len("yyyyMMddhhmmss") { // 14
-			// yyyyMMddhhmmss
-			p.format = []byte("20060102150405")
-			return p, nil
-		} else if len(datestr) == len("1332151919000") { // 13
-			if miliSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
-				t = time.Unix(0, miliSecs*1000*1000)
-			}
-		} else if len(datestr) == len("1332151919") { //10
-			if secs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
-				t = time.Unix(secs, 0)
-			}
-		} else if len(datestr) == len("20140601") {
-			p.format = []byte("20060102")
-			return p, nil
-		} else if len(datestr) == len("2014") {
-			p.format = []byte("2006")
-			return p, nil
-		} else if len(datestr) < 4 {
-			return nil, fmt.Errorf("unrecognized format, too short %v", datestr)
-		}
-		if !t.IsZero() {
-			if loc == nil {
-				p.t = &t
-				return p, nil
-			}
-			t = t.In(loc)
-			p.t = &t
-			return p, nil
-		}
-	case dateDigitSt:
-		// 171113 14:14:20
-		return p, nil
-
-	case dateYearDash:
-		// 2006-01
-		return p, nil
-
-	case dateYearDashDash:
-		// 2006-01-02
-		// 2006-1-02
-		// 2006-1-2
-		// 2006-01-2
-		return p, nil
-
-	case dateYearDashDashOffset:
-		///  2020-07-20+00:00
-		switch len(p.datestr) - p.offseti {
-		case 5:
-			p.set(p.offseti, "-0700")
-		case 6:
-			p.set(p.offseti, "-07:00")
-		}
-		return p, nil
-
-	case dateYearDashAlphaDash:
-		// 2013-Feb-03
-		// 2013-Feb-3
-		p.daylen = i - p.dayi
-		p.setDay()
-		return p, nil
-
-	case dateYearDashDashWs:
-		// 2013-04-01
-		return p, nil
-
-	case dateYearDashDashT:
-		return p, nil
-
-	case dateDigitDashAlphaDash:
-		// 13-Feb-03   ambiguous
-		// 28-Feb-03   ambiguous
-		// 29-Jun-2016
-		length := len(datestr) - (p.moi + p.molen + 1)
-		if length == 4 {
-			p.yearlen = 4
-			p.set(p.yeari, "2006")
-			// We now also know that part1 was the day
-			p.dayi = 0
-			p.daylen = p.part1Len
-			p.setDay()
-		} else if length == 2 {
-			// We have no idea if this is
-			// yy-mon-dd   OR  dd-mon-yy
-			//
-			// We are going to ASSUME (bad, bad) that it is dd-mon-yy  which is a horible assumption
-			p.ambiguousMD = true
-			p.yearlen = 2
-			p.set(p.yeari, "06")
-			// We now also know that part1 was the day
-			p.dayi = 0
-			p.daylen = p.part1Len
-			p.setDay()
-		}
-
-		return p, nil
-
-	case dateDigitDot:
-		// 2014.05
-		p.molen = i - p.moi
-		p.setMonth()
-		return p, nil
-
-	case dateDigitDotDot:
-		// 03.31.1981
-		// 3.31.2014
-		// 3.2.1981
-		// 3.2.81
-		// 08.21.71
-		// 2018.09.30
-		return p, nil
-
-	case dateDigitWsMoYear:
-		// 2 Jan 2018
-		// 2 Jan 18
-		// 2 Jan 2018 23:59
-		// 02 Jan 2018 23:59
-		// 12 Feb 2006, 19:17
-		return p, nil
-
-	case dateDigitWsMolong:
-		// 18 January 2018
-		// 8 January 2018
-		if p.daylen == 2 {
-			p.format = []byte("02 January 2006")
-			return p, nil
-		}
-		p.format = []byte("2 January 2006")
-		return p, nil // parse("2 January 2006", datestr, loc)
-
-	case dateAlphaWsMonth:
-		p.yearlen = i - p.yeari
-		p.setYear()
-		return p, nil
-
-	case dateAlphaWsMonthMore:
-		return p, nil
-
-	case dateAlphaWsDigitMoreWs:
-		// oct 1, 1970
-		p.yearlen = i - p.yeari
-		p.setYear()
-		return p, nil
-
-	case dateAlphaWsDigitMoreWsYear:
-		// May 8, 2009 5:57:51 PM
-		// Jun 7, 2005, 05:57:51
-		return p, nil
-
-	case dateAlphaWsAlpha:
-		return p, nil
-
-	case dateAlphaWsDigit:
-		return p, nil
-
-	case dateAlphaWsDigitYearmaybe:
-		return p, nil
-
-	case dateDigitSlash:
-		// 3/1/2014
-		// 10/13/2014
-		// 01/02/2006
-		return p, nil
-
-	case dateDigitSlashAlpha:
-		// 03/Jun/2014
-		return p, nil
-
-	case dateDigitYearSlash:
-		// 2014/10/13
-		return p, nil
-
-	case dateDigitColon:
-		// 3:1:2014
-		// 10:13:2014
-		// 01:02:2006
-		// 2014:10:13
-		return p, nil
-
-	case dateDigitChineseYear:
-		// dateDigitChineseYear
-		//   2014年04月08日
-		p.format = []byte("2006年01月02日")
-		return p, nil
-
-	case dateDigitChineseYearWs:
-		p.format = []byte("2006年01月02日 15:04:05")
-		return p, nil
-
-	case dateWeekdayComma:
-		// Monday, 02 Jan 2006 15:04:05 -0700
-		// Monday, 02 Jan 2006 15:04:05 +0100
-		// Monday, 02-Jan-06 15:04:05 MST
-		return p, nil
-
-	case dateWeekdayAbbrevComma:
-		// Mon, 02-Jan-06 15:04:05 MST
-		// Mon, 02 Jan 2006 15:04:05 MST
-		return p, nil
-
-	}
-
-	return nil, unknownErr(datestr)
-}
-
-type parser struct {
-	loc                        *time.Location
-	preferMonthFirst           bool
-	retryAmbiguousDateWithSwap bool
-	ambiguousMD                bool
-	stateDate                  dateState
-	stateTime                  timeState
-	format                     []byte
-	datestr                    string
-	fullMonth                  string
-	skip                       int
-	extra                      int
-	part1Len                   int
-	yeari                      int
-	yearlen                    int
-	moi                        int
-	molen                      int
-	dayi                       int
-	daylen                     int
-	houri                      int
-	hourlen                    int
-	mini                       int
-	minlen                     int
-	seci                       int
-	seclen                     int
-	msi                        int
-	mslen                      int
-	offseti                    int
-	offsetlen                  int
-	tzi                        int
-	tzlen                      int
-	t                          *time.Time
-}
-
-// ParserOption defines a function signature implemented by options
-// Options defined like this accept the parser and operate on the data within
-type ParserOption func(*parser) error
-
-// PreferMonthFirst is an option that allows preferMonthFirst to be changed from its default
-func PreferMonthFirst(preferMonthFirst bool) ParserOption {
-	return func(p *parser) error {
-		p.preferMonthFirst = preferMonthFirst
-		return nil
-	}
-}
-
-// RetryAmbiguousDateWithSwap is an option that allows retryAmbiguousDateWithSwap to be changed from its default
-func RetryAmbiguousDateWithSwap(retryAmbiguousDateWithSwap bool) ParserOption {
-	return func(p *parser) error {
-		p.retryAmbiguousDateWithSwap = retryAmbiguousDateWithSwap
-		return nil
-	}
-}
-
-func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser {
-	p := &parser{
-		stateDate:                  dateStart,
-		stateTime:                  timeIgnore,
-		datestr:                    dateStr,
-		loc:                        loc,
-		preferMonthFirst:           true,
-		retryAmbiguousDateWithSwap: false,
-	}
-	p.format = []byte(dateStr)
-
-	// allow the options to mutate the parser fields from their defaults
-	for _, option := range opts {
-		option(p)
-	}
-	return p
-}
-
-func (p *parser) nextIs(i int, b byte) bool {
-	if len(p.datestr) > i+1 && p.datestr[i+1] == b {
-		return true
-	}
-	return false
-}
-
-func (p *parser) set(start int, val string) {
-	if start < 0 {
-		return
-	}
-	if len(p.format) < start+len(val) {
-		return
-	}
-	for i, r := range val {
-		p.format[start+i] = byte(r)
-	}
-}
-func (p *parser) setMonth() {
-	if p.molen == 2 {
-		p.set(p.moi, "01")
-	} else if p.molen == 1 {
-		p.set(p.moi, "1")
-	}
-}
-
-func (p *parser) setDay() {
-	if p.daylen == 2 {
-		p.set(p.dayi, "02")
-	} else if p.daylen == 1 {
-		p.set(p.dayi, "2")
-	}
-}
-func (p *parser) setYear() {
-	if p.yearlen == 2 {
-		p.set(p.yeari, "06")
-	} else if p.yearlen == 4 {
-		p.set(p.yeari, "2006")
-	}
-}
-func (p *parser) coalesceDate(end int) {
-	if p.yeari > 0 {
-		if p.yearlen == 0 {
-			p.yearlen = end - p.yeari
-		}
-		p.setYear()
-	}
-	if p.moi > 0 && p.molen == 0 {
-		p.molen = end - p.moi
-		p.setMonth()
-	}
-	if p.dayi > 0 && p.daylen == 0 {
-		p.daylen = end - p.dayi
-		p.setDay()
-	}
-}
-func (p *parser) ts() string {
-	return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen)
-}
-func (p *parser) ds() string {
-	return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen)
-}
-func (p *parser) coalesceTime(end int) {
-	// 03:04:05
-	// 15:04:05
-	// 3:04:05
-	// 3:4:5
-	// 15:04:05.00
-	if p.houri > 0 {
-		if p.hourlen == 2 {
-			p.set(p.houri, "15")
-		} else if p.hourlen == 1 {
-			p.set(p.houri, "3")
-		}
-	}
-	if p.mini > 0 {
-		if p.minlen == 0 {
-			p.minlen = end - p.mini
-		}
-		if p.minlen == 2 {
-			p.set(p.mini, "04")
-		} else {
-			p.set(p.mini, "4")
-		}
-	}
-	if p.seci > 0 {
-		if p.seclen == 0 {
-			p.seclen = end - p.seci
-		}
-		if p.seclen == 2 {
-			p.set(p.seci, "05")
-		} else {
-			p.set(p.seci, "5")
-		}
-	}
-
-	if p.msi > 0 {
-		for i := 0; i < p.mslen; i++ {
-			p.format[p.msi+i] = '0'
-		}
-	}
-}
-func (p *parser) setFullMonth(month string) {
-	if p.moi == 0 {
-		p.format = []byte(fmt.Sprintf("%s%s", "January", p.format[len(month):]))
-	}
-}
-
-func (p *parser) trimExtra() {
-	if p.extra > 0 && len(p.format) > p.extra {
-		p.format = p.format[0:p.extra]
-		p.datestr = p.datestr[0:p.extra]
-	}
-}
-
-// func (p *parser) remove(i, length int) {
-// 	if len(p.format) > i+length {
-// 		//append(a[:i], a[j:]...)
-// 		p.format = append(p.format[0:i], p.format[i+length:]...)
-// 	}
-// 	if len(p.datestr) > i+length {
-// 		//append(a[:i], a[j:]...)
-// 		p.datestr = fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+length:])
-// 	}
-// }
-
-func (p *parser) parse() (time.Time, error) {
-	if p.t != nil {
-		return *p.t, nil
-	}
-	if len(p.fullMonth) > 0 {
-		p.setFullMonth(p.fullMonth)
-	}
-	if p.skip > 0 && len(p.format) > p.skip {
-		p.format = p.format[p.skip:]
-		p.datestr = p.datestr[p.skip:]
-	}
-
-	if p.loc == nil {
-		// gou.Debugf("parse layout=%q input=%q   \ntx, err := time.Parse(%q, %q)", string(p.format), p.datestr, string(p.format), p.datestr)
-		return time.Parse(string(p.format), p.datestr)
-	}
-	//gou.Debugf("parse layout=%q input=%q   \ntx, err := time.ParseInLocation(%q, %q, %v)", string(p.format), p.datestr, string(p.format), p.datestr, p.loc)
-	return time.ParseInLocation(string(p.format), p.datestr, p.loc)
-}
-func isDay(alpha string) bool {
-	for _, day := range days {
-		if alpha == day {
-			return true
-		}
-	}
-	return false
-}
-func isMonthFull(alpha string) bool {
-	for _, month := range months {
-		if alpha == month {
-			return true
-		}
-	}
-	return false
-}
diff --git a/vendor/github.com/jinzhu/now/Guardfile b/vendor/github.com/jinzhu/now/Guardfile
new file mode 100644
index 00000000000..0b860b0653c
--- /dev/null
+++ b/vendor/github.com/jinzhu/now/Guardfile
@@ -0,0 +1,3 @@
+guard 'gotest' do
+  watch(%r{\.go$})
+end
diff --git a/vendor/github.com/araddon/dateparse/LICENSE b/vendor/github.com/jinzhu/now/License
similarity index 95%
rename from vendor/github.com/araddon/dateparse/LICENSE
rename to vendor/github.com/jinzhu/now/License
index f675ed313ac..037e1653e69 100644
--- a/vendor/github.com/araddon/dateparse/LICENSE
+++ b/vendor/github.com/jinzhu/now/License
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2015-2017 Aaron Raddon
+Copyright (c) 2013-NOW  Jinzhu <wosmvp@gmail.com>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/github.com/jinzhu/now/README.md b/vendor/github.com/jinzhu/now/README.md
new file mode 100644
index 00000000000..e81d31de6a7
--- /dev/null
+++ b/vendor/github.com/jinzhu/now/README.md
@@ -0,0 +1,137 @@
+## Now
+
+Now is a time toolkit for golang
+
+[![go report card](https://goreportcard.com/badge/github.com/jinzhu/now "go report card")](https://goreportcard.com/report/github.com/jinzhu/now)
+[![test status](https://github.com/jinzhu/now/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/now/actions)
+[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
+
+## Install
+
+```
+go get -u github.com/jinzhu/now
+```
+
+## Usage
+
+Calculating time based on current time
+
+```go
+import "github.com/jinzhu/now"
+
+time.Now() // 2013-11-18 17:51:49.123456789 Mon
+
+now.BeginningOfMinute()        // 2013-11-18 17:51:00 Mon
+now.BeginningOfHour()          // 2013-11-18 17:00:00 Mon
+now.BeginningOfDay()           // 2013-11-18 00:00:00 Mon
+now.BeginningOfWeek()          // 2013-11-17 00:00:00 Sun
+now.BeginningOfMonth()         // 2013-11-01 00:00:00 Fri
+now.BeginningOfQuarter()       // 2013-10-01 00:00:00 Tue
+now.BeginningOfYear()          // 2013-01-01 00:00:00 Tue
+
+now.EndOfMinute()              // 2013-11-18 17:51:59.999999999 Mon
+now.EndOfHour()                // 2013-11-18 17:59:59.999999999 Mon
+now.EndOfDay()                 // 2013-11-18 23:59:59.999999999 Mon
+now.EndOfWeek()                // 2013-11-23 23:59:59.999999999 Sat
+now.EndOfMonth()               // 2013-11-30 23:59:59.999999999 Sat
+now.EndOfQuarter()             // 2013-12-31 23:59:59.999999999 Tue
+now.EndOfYear()                // 2013-12-31 23:59:59.999999999 Tue
+
+now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
+now.EndOfWeek()                // 2013-11-24 23:59:59.999999999 Sun
+```
+
+Calculating time based on another time
+
+```go
+t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location())
+now.With(t).EndOfMonth()   // 2013-02-28 23:59:59.999999999 Thu
+```
+
+Calculating time based on configuration
+
+```go
+location, err := time.LoadLocation("Asia/Shanghai")
+
+myConfig := &now.Config{
+	WeekStartDay: time.Monday,
+	TimeLocation: location,
+	TimeFormats: []string{"2006-01-02 15:04:05"},
+}
+
+t := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.Now().Location()) // // 2013-11-18 17:51:49.123456789 Mon
+myConfig.With(t).BeginningOfWeek()         // 2013-11-18 00:00:00 Mon
+
+myConfig.Parse("2002-10-12 22:14:01")     // 2002-10-12 22:14:01
+myConfig.Parse("2002-10-12 22:14")        // returns error 'can't parse string as time: 2002-10-12 22:14'
+```
+
+### Monday/Sunday
+
+Don't be bothered with the `WeekStartDay` setting, you can use `Monday`, `Sunday`
+
+```go
+now.Monday()              // 2013-11-18 00:00:00 Mon
+now.Monday("17:44")       // 2013-11-18 17:44:00 Mon
+now.Sunday()              // 2013-11-24 00:00:00 Sun (Next Sunday)
+now.Sunday("18:19:24")    // 2013-11-24 18:19:24 Sun (Next Sunday)
+now.EndOfSunday()         // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday)
+
+t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun
+now.With(t).Monday()              // 2013-11-18 00:00:00 Mon (Last Monday if today is Sunday)
+now.With(t).Monday("17:44")       // 2013-11-18 17:44:00 Mon (Last Monday if today is Sunday)
+now.With(t).Sunday()              // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday)
+now.With(t).Sunday("18:19:24")    // 2013-11-24 18:19:24 Sun (Beginning Of Today if today is Sunday)
+now.With(t).EndOfSunday()         // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday)
+```
+
+### Parse String to Time
+
+```go
+time.Now() // 2013-11-18 17:51:49.123456789 Mon
+
+// Parse(string) (time.Time, error)
+t, err := now.Parse("2017")                // 2017-01-01 00:00:00, nil
+t, err := now.Parse("2017-10")             // 2017-10-01 00:00:00, nil
+t, err := now.Parse("2017-10-13")          // 2017-10-13 00:00:00, nil
+t, err := now.Parse("1999-12-12 12")       // 1999-12-12 12:00:00, nil
+t, err := now.Parse("1999-12-12 12:20")    // 1999-12-12 12:20:00, nil
+t, err := now.Parse("1999-12-12 12:20:21") // 1999-12-12 12:20:21, nil
+t, err := now.Parse("10-13")               // 2013-10-13 00:00:00, nil
+t, err := now.Parse("12:20")               // 2013-11-18 12:20:00, nil
+t, err := now.Parse("12:20:13")            // 2013-11-18 12:20:13, nil
+t, err := now.Parse("14")                  // 2013-11-18 14:00:00, nil
+t, err := now.Parse("99:99")               // 2013-11-18 12:20:00, Can't parse string as time: 99:99
+
+// MustParse must parse string to time or it will panic
+now.MustParse("2013-01-13")             // 2013-01-13 00:00:00
+now.MustParse("02-17")                  // 2013-02-17 00:00:00
+now.MustParse("2-17")                   // 2013-02-17 00:00:00
+now.MustParse("8")                      // 2013-11-18 08:00:00
+now.MustParse("2002-10-12 22:14")       // 2002-10-12 22:14:00
+now.MustParse("99:99")                  // panic: Can't parse string as time: 99:99
+```
+
+Extend `now` to support more formats is quite easy, just update `now.TimeFormats` with other time layouts, e.g:
+
+```go
+now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04")
+```
+
+Please send me pull requests if you want a format to be supported officially
+
+## Contributing
+
+You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do.
+
+# Author
+
+**jinzhu**
+
+* <http://github.com/jinzhu>
+* <wosmvp@gmail.com>
+* <http://twitter.com/zhangjinzhu>
+
+## License
+
+Released under the [MIT License](http://www.opensource.org/licenses/MIT).
diff --git a/vendor/github.com/jinzhu/now/main.go b/vendor/github.com/jinzhu/now/main.go
new file mode 100644
index 00000000000..8f78bc75219
--- /dev/null
+++ b/vendor/github.com/jinzhu/now/main.go
@@ -0,0 +1,200 @@
+// Package now is a time toolkit for golang.
+//
+// More details README here: https://github.com/jinzhu/now
+//
+//  import "github.com/jinzhu/now"
+//
+//  now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
+//  now.BeginningOfDay()    // 2013-11-18 00:00:00 Mon
+//  now.EndOfDay()          // 2013-11-18 23:59:59.999999999 Mon
+package now
+
+import "time"
+
+// WeekStartDay set week start day, default is sunday
+var WeekStartDay = time.Sunday
+
+// TimeFormats default time formats will be parsed as
+var TimeFormats = []string{
+	"2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2",
+	"15:4:5", "15:4", "15",
+	"15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST", "2006-01-02T15:04:05Z0700", "2006-01-02T15:04:05Z07",
+	"2006.1.2", "2006.1.2 15:04:05", "2006.01.02", "2006.01.02 15:04:05", "2006.01.02 15:04:05.999999999",
+	"1/2/2006", "1/2/2006 15:4:5", "2006/01/02", "20060102", "2006/01/02 15:04:05",
+	time.ANSIC, time.UnixDate, time.RubyDate, time.RFC822, time.RFC822Z, time.RFC850,
+	time.RFC1123, time.RFC1123Z, time.RFC3339, time.RFC3339Nano,
+	time.Kitchen, time.Stamp, time.StampMilli, time.StampMicro, time.StampNano,
+}
+
+// Config configuration for now package
+type Config struct {
+	WeekStartDay time.Weekday
+	TimeLocation *time.Location
+	TimeFormats  []string
+}
+
+// DefaultConfig default config
+var DefaultConfig *Config
+
+// New initialize Now based on configuration
+func (config *Config) With(t time.Time) *Now {
+	return &Now{Time: t, Config: config}
+}
+
+// Parse parse string to time based on configuration
+func (config *Config) Parse(strs ...string) (time.Time, error) {
+	if config.TimeLocation == nil {
+		return config.With(time.Now()).Parse(strs...)
+	} else {
+		return config.With(time.Now().In(config.TimeLocation)).Parse(strs...)
+	}
+}
+
+// MustParse must parse string to time or will panic
+func (config *Config) MustParse(strs ...string) time.Time {
+	if config.TimeLocation == nil {
+		return config.With(time.Now()).MustParse(strs...)
+	} else {
+		return config.With(time.Now().In(config.TimeLocation)).MustParse(strs...)
+	}
+}
+
+// Now now struct
+type Now struct {
+	time.Time
+	*Config
+}
+
+// With initialize Now with time
+func With(t time.Time) *Now {
+	config := DefaultConfig
+	if config == nil {
+		config = &Config{
+			WeekStartDay: WeekStartDay,
+			TimeFormats:  TimeFormats,
+		}
+	}
+
+	return &Now{Time: t, Config: config}
+}
+
+// New initialize Now with time
+func New(t time.Time) *Now {
+	return With(t)
+}
+
+// BeginningOfMinute beginning of minute
+func BeginningOfMinute() time.Time {
+	return With(time.Now()).BeginningOfMinute()
+}
+
+// BeginningOfHour beginning of hour
+func BeginningOfHour() time.Time {
+	return With(time.Now()).BeginningOfHour()
+}
+
+// BeginningOfDay beginning of day
+func BeginningOfDay() time.Time {
+	return With(time.Now()).BeginningOfDay()
+}
+
+// BeginningOfWeek beginning of week
+func BeginningOfWeek() time.Time {
+	return With(time.Now()).BeginningOfWeek()
+}
+
+// BeginningOfMonth beginning of month
+func BeginningOfMonth() time.Time {
+	return With(time.Now()).BeginningOfMonth()
+}
+
+// BeginningOfQuarter beginning of quarter
+func BeginningOfQuarter() time.Time {
+	return With(time.Now()).BeginningOfQuarter()
+}
+
+// BeginningOfYear beginning of year
+func BeginningOfYear() time.Time {
+	return With(time.Now()).BeginningOfYear()
+}
+
+// EndOfMinute end of minute
+func EndOfMinute() time.Time {
+	return With(time.Now()).EndOfMinute()
+}
+
+// EndOfHour end of hour
+func EndOfHour() time.Time {
+	return With(time.Now()).EndOfHour()
+}
+
+// EndOfDay end of day
+func EndOfDay() time.Time {
+	return With(time.Now()).EndOfDay()
+}
+
+// EndOfWeek end of week
+func EndOfWeek() time.Time {
+	return With(time.Now()).EndOfWeek()
+}
+
+// EndOfMonth end of month
+func EndOfMonth() time.Time {
+	return With(time.Now()).EndOfMonth()
+}
+
+// EndOfQuarter end of quarter
+func EndOfQuarter() time.Time {
+	return With(time.Now()).EndOfQuarter()
+}
+
+// EndOfYear end of year
+func EndOfYear() time.Time {
+	return With(time.Now()).EndOfYear()
+}
+
+// Monday monday
+
+func Monday(strs ...string) time.Time {
+	return With(time.Now()).Monday(strs...)
+}
+
+// Sunday sunday
+func Sunday(strs ...string) time.Time {
+	return With(time.Now()).Sunday(strs...)
+}
+
+// EndOfSunday end of sunday
+func EndOfSunday() time.Time {
+	return With(time.Now()).EndOfSunday()
+}
+
+// Quarter returns the yearly quarter
+func Quarter() uint {
+	return With(time.Now()).Quarter()
+}
+
+// Parse parse string to time
+func Parse(strs ...string) (time.Time, error) {
+	return With(time.Now()).Parse(strs...)
+}
+
+// ParseInLocation parse string to time in location
+func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) {
+	return With(time.Now().In(loc)).Parse(strs...)
+}
+
+// MustParse must parse string to time or will panic
+func MustParse(strs ...string) time.Time {
+	return With(time.Now()).MustParse(strs...)
+}
+
+// MustParseInLocation must parse string to time in location or will panic
+func MustParseInLocation(loc *time.Location, strs ...string) time.Time {
+	return With(time.Now().In(loc)).MustParse(strs...)
+}
+
+// Between check now between the begin, end time or not
+func Between(time1, time2 string) bool {
+	return With(time.Now()).Between(time1, time2)
+}
diff --git a/vendor/github.com/jinzhu/now/now.go b/vendor/github.com/jinzhu/now/now.go
new file mode 100644
index 00000000000..2f524cc8d4b
--- /dev/null
+++ b/vendor/github.com/jinzhu/now/now.go
@@ -0,0 +1,245 @@
+package now
+
+import (
+	"errors"
+	"regexp"
+	"time"
+)
+
+// BeginningOfMinute beginning of minute
+func (now *Now) BeginningOfMinute() time.Time {
+	return now.Truncate(time.Minute)
+}
+
+// BeginningOfHour beginning of hour
+func (now *Now) BeginningOfHour() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location())
+}
+
+// BeginningOfDay beginning of day
+func (now *Now) BeginningOfDay() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location())
+}
+
+// BeginningOfWeek beginning of week
+func (now *Now) BeginningOfWeek() time.Time {
+	t := now.BeginningOfDay()
+	weekday := int(t.Weekday())
+
+	if now.WeekStartDay != time.Sunday {
+		weekStartDayInt := int(now.WeekStartDay)
+
+		if weekday < weekStartDayInt {
+			weekday = weekday + 7 - weekStartDayInt
+		} else {
+			weekday = weekday - weekStartDayInt
+		}
+	}
+	return t.AddDate(0, 0, -weekday)
+}
+
+// BeginningOfMonth beginning of month
+func (now *Now) BeginningOfMonth() time.Time {
+	y, m, _ := now.Date()
+	return time.Date(y, m, 1, 0, 0, 0, 0, now.Location())
+}
+
+// BeginningOfQuarter beginning of quarter
+func (now *Now) BeginningOfQuarter() time.Time {
+	month := now.BeginningOfMonth()
+	offset := (int(month.Month()) - 1) % 3
+	return month.AddDate(0, -offset, 0)
+}
+
+// BeginningOfHalf beginning of half year
+func (now *Now) BeginningOfHalf() time.Time {
+	month := now.BeginningOfMonth()
+	offset := (int(month.Month()) - 1) % 6
+	return month.AddDate(0, -offset, 0)
+}
+
+// BeginningOfYear BeginningOfYear beginning of year
+func (now *Now) BeginningOfYear() time.Time {
+	y, _, _ := now.Date()
+	return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location())
+}
+
+// EndOfMinute end of minute
+func (now *Now) EndOfMinute() time.Time {
+	return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond)
+}
+
+// EndOfHour end of hour
+func (now *Now) EndOfHour() time.Time {
+	return now.BeginningOfHour().Add(time.Hour - time.Nanosecond)
+}
+
+// EndOfDay end of day
+func (now *Now) EndOfDay() time.Time {
+	y, m, d := now.Date()
+	return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location())
+}
+
+// EndOfWeek end of week
+func (now *Now) EndOfWeek() time.Time {
+	return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
+}
+
+// EndOfMonth end of month
+func (now *Now) EndOfMonth() time.Time {
+	return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
+}
+
+// EndOfQuarter end of quarter
+func (now *Now) EndOfQuarter() time.Time {
+	return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
+}
+
+// EndOfHalf end of half year
+func (now *Now) EndOfHalf() time.Time {
+	return now.BeginningOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond)
+}
+
+// EndOfYear end of year
+func (now *Now) EndOfYear() time.Time {
+	return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
+}
+
+// Monday monday
+/*
+func (now *Now) Monday() time.Time {
+	t := now.BeginningOfDay()
+	weekday := int(t.Weekday())
+	if weekday == 0 {
+		weekday = 7
+	}
+	return t.AddDate(0, 0, -weekday+1)
+}
+*/
+
+func (now *Now) Monday(strs ...string) time.Time {
+	var parseTime time.Time
+	var err error
+	if len(strs) > 0 {
+		parseTime, err = now.Parse(strs...)
+		if err != nil {
+			panic(err)
+		}
+	} else {
+		parseTime = now.BeginningOfDay()
+	}
+	weekday := int(parseTime.Weekday())
+	if weekday == 0 {
+		weekday = 7
+	}
+	return parseTime.AddDate(0, 0, -weekday+1)
+}
+
+func (now *Now) Sunday(strs ...string) time.Time {
+	var parseTime time.Time
+	var err error
+	if len(strs) > 0 {
+		parseTime, err = now.Parse(strs...)
+		if err != nil {
+			panic(err)
+		}
+	} else {
+		parseTime = now.BeginningOfDay()
+	}
+	weekday := int(parseTime.Weekday())
+	if weekday == 0 {
+		weekday = 7
+	}
+	return parseTime.AddDate(0, 0, (7 - weekday))
+}
+
+// EndOfSunday end of sunday
+func (now *Now) EndOfSunday() time.Time {
+	return New(now.Sunday()).EndOfDay()
+}
+
+// Quarter returns the yearly quarter
+func (now *Now) Quarter() uint {
+	return (uint(now.Month())-1)/3 + 1
+}
+
+func (now *Now) parseWithFormat(str string, location *time.Location) (t time.Time, err error) {
+	for _, format := range now.TimeFormats {
+		t, err = time.ParseInLocation(format, str, location)
+
+		if err == nil {
+			return
+		}
+	}
+	err = errors.New("Can't parse string as time: " + str)
+	return
+}
+
+var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*|T)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))(\s*$|[Z+-])`) // match 15:04:05, 15:04:05.000, 15:04:05.000000 15, 2017-01-01 15:04, 2021-07-20T00:59:10Z, 2021-07-20T00:59:10+08:00, 2021-07-20T00:00:10-07:00 etc
+var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`)            // match 15:04:05, 15, 15:04:05.000, 15:04:05.000000, etc
+
+// Parse parse string to time
+func (now *Now) Parse(strs ...string) (t time.Time, err error) {
+	var (
+		setCurrentTime  bool
+		parseTime       []int
+		currentLocation = now.Location()
+		onlyTimeInStr   = true
+		currentTime     = formatTimeToList(now.Time)
+	)
+
+	for _, str := range strs {
+		hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15
+		onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str)
+		if t, err = now.parseWithFormat(str, currentLocation); err == nil {
+			location := t.Location()
+			parseTime = formatTimeToList(t)
+
+			for i, v := range parseTime {
+				// Don't reset hour, minute, second if current time str including time
+				if hasTimeInStr && i <= 3 {
+					continue
+				}
+
+				// If value is zero, replace it with current time
+				if v == 0 {
+					if setCurrentTime {
+						parseTime[i] = currentTime[i]
+					}
+				} else {
+					setCurrentTime = true
+				}
+
+				// if current time only includes time, should change day, month to current time
+				if onlyTimeInStr {
+					if i == 4 || i == 5 {
+						parseTime[i] = currentTime[i]
+						continue
+					}
+				}
+			}
+
+			t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location)
+			currentTime = formatTimeToList(t)
+		}
+	}
+	return
+}
+
+// MustParse must parse string to time or it will panic
+func (now *Now) MustParse(strs ...string) (t time.Time) {
+	t, err := now.Parse(strs...)
+	if err != nil {
+		panic(err)
+	}
+	return t
+}
+
+// Between check time between the begin, end time or not
+func (now *Now) Between(begin, end string) bool {
+	beginTime := now.MustParse(begin)
+	endTime := now.MustParse(end)
+	return now.After(beginTime) && now.Before(endTime)
+}
diff --git a/vendor/github.com/jinzhu/now/time.go b/vendor/github.com/jinzhu/now/time.go
new file mode 100644
index 00000000000..52dd8b2a098
--- /dev/null
+++ b/vendor/github.com/jinzhu/now/time.go
@@ -0,0 +1,9 @@
+package now
+
+import "time"
+
+func formatTimeToList(t time.Time) []int {
+	hour, min, sec := t.Clock()
+	year, month, day := t.Date()
+	return []int{t.Nanosecond(), sec, min, hour, day, int(month), year}
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 254a1cd480f..bcb4678afcf 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -78,9 +78,6 @@ github.com/alexedwards/argon2id
 # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
 ## explicit
 github.com/amoghe/go-crypt
-# github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
-## explicit; go 1.12
-github.com/araddon/dateparse
 # github.com/armon/go-metrics v0.4.1
 ## explicit; go 1.12
 github.com/armon/go-metrics
@@ -1172,6 +1169,9 @@ github.com/jellydator/ttlcache/v2
 # github.com/jellydator/ttlcache/v3 v3.1.0
 ## explicit; go 1.18
 github.com/jellydator/ttlcache/v3
+# github.com/jinzhu/now v1.1.5
+## explicit; go 1.12
+github.com/jinzhu/now
 # github.com/jmespath/go-jmespath v0.4.0
 ## explicit; go 1.14
 github.com/jmespath/go-jmespath


Test patch:
diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go
index 7e86456a999..05e3cf56135 100644
--- a/services/search/pkg/query/kql/dictionary_test.go
+++ b/services/search/pkg/query/kql/dictionary_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/araddon/dateparse"
+	"github.com/jinzhu/now"
 	tAssert "github.com/stretchr/testify/assert"
 
 	"github.com/owncloud/ocis/v2/services/search/pkg/query/ast"
@@ -13,66 +13,18 @@ import (
 	"github.com/owncloud/ocis/v2/services/search/pkg/query/kql"
 )
 
-var mustParseTime = func(t *testing.T, ts string) time.Time {
-	tp, err := dateparse.ParseLocal(ts)
-	if err != nil {
-		t.Fatalf("time.Parse(...) error = %v", err)
-	}
-
-	return tp
-}
-
-var mustJoin = func(v []string) string {
-	return strings.Join(v, " ")
-}
-
-var FullDictionary = []string{
-	`federated search`,
-	`federat* search`,
-	`search fed*`,
-	`author:"John Smith"`,
-	`filetype:docx`,
-	`filename:budget.xlsx`,
-	`author: "John Smith"`,
-	`author :"John Smith"`,
-	`author : "John Smith"`,
-	`author "John Smith"`,
-	`author "John Smith"`,
-	`author:Shakespear`,
-	`author:Paul`,
-	`author:Shakesp*`,
-	`title:"Advanced Search"`,
-	`title:"Advanced Sear*"`,
-	`title:"Advan* Search"`,
-	`title:"*anced Search"`,
-	`author:"John Smith" OR author:"Jane Smith"`,
-	`author:"John Smith" AND filetype:docx`,
-	`author:("John Smith" "Jane Smith")`,
-	`author:("John Smith" OR "Jane Smith")`,
-	`(DepartmentId:* OR RelatedHubSites:*) AND contentclass:sts_site NOT IsHubSite:false`,
-	`author:"John Smith" (filetype:docx title:"Advanced Search")`,
-}
-
-func TestParse(t *testing.T) {
-	tests := []struct {
-		name          string
-		skip          bool
-		givenQuery    string
-		expectedAst   *ast.Ast
-		expectedError error
-	}{
-		// SPEC //////////////////////////////////////////////////////////////////////////////
-		//
-		// https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf
-		// https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0
-		// https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference
-		//
-		// ++
+func TestParse_Spec(t *testing.T) {
+	// SPEC //////////////////////////////////////////////////////////////////////////////
+	//
+	// https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf
+	// https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0
+	// https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference
+	tests := []testCase{
 		// 2.1.2 AND Operator
 		// 3.1.2 AND Operator
 		{
 			name: `cat AND dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -82,22 +34,21 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `AND`,
-			expectedError: kql.StartsWithBinaryOperatorError{
+			error: kql.StartsWithBinaryOperatorError{
 				Node: &ast.OperatorNode{Value: kql.BoolAND},
 			},
 		},
 		{
 			name: `AND cat AND dog`,
-			expectedError: kql.StartsWithBinaryOperatorError{
+			error: kql.StartsWithBinaryOperatorError{
 				Node: &ast.OperatorNode{Value: kql.BoolAND},
 			},
 		},
-		// ++
 		// 2.1.6 NOT Operator
 		// 3.1.6 NOT Operator
 		{
 			name: `cat NOT dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -108,19 +59,18 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `NOT dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.OperatorNode{Value: kql.BoolNOT},
 					&ast.StringNode{Value: "dog"},
 				},
 			},
 		},
-		// ++
 		// 2.1.8 OR Operator
 		// 3.1.8 OR Operator
 		{
 			name: `cat OR dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolOR},
@@ -130,21 +80,20 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `OR`,
-			expectedError: kql.StartsWithBinaryOperatorError{
+			error: kql.StartsWithBinaryOperatorError{
 				Node: &ast.OperatorNode{Value: kql.BoolOR},
 			},
 		},
 		{
 			name: `OR cat AND dog`,
-			expectedError: kql.StartsWithBinaryOperatorError{
+			error: kql.StartsWithBinaryOperatorError{
 				Node: &ast.OperatorNode{Value: kql.BoolOR},
 			},
 		},
-		// ++
 		// 3.1.11 Implicit Operator
 		{
 			name: `cat dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -154,7 +103,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat AND (dog OR fox)`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -168,7 +117,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat (dog OR fox)`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -180,12 +129,11 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		// ++
 		// 2.1.12 Parentheses
 		// 3.1.12 Parentheses
 		{
 			name: `(cat OR dog) AND fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{Nodes: []ast.Node{
 						&ast.StringNode{Value: "cat"},
@@ -197,11 +145,10 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		// ++
 		// 3.2.3 Implicit Operator for Property Restriction
 		{
 			name: `author:"John Smith" filetype:docx`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Key: "author", Value: "John Smith"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -211,7 +158,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `author:"John Smith" AND filetype:docx`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Key: "author", Value: "John Smith"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -221,7 +168,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `author:"John Smith" author:"Jane Smith"`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Key: "author", Value: "John Smith"},
 					&ast.OperatorNode{Value: kql.BoolOR},
@@ -231,7 +178,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `author:"John Smith" OR author:"Jane Smith"`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Key: "author", Value: "John Smith"},
 					&ast.OperatorNode{Value: kql.BoolOR},
@@ -241,7 +188,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat filetype:docx`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -251,7 +198,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat AND filetype:docx`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -259,11 +206,10 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		// ++
 		// 3.3.1.1.1 Implicit AND Operator
 		{
 			name: `cat +dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -273,7 +219,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat AND dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -283,7 +229,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat -dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -294,7 +240,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat AND NOT dog`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -305,7 +251,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat +dog -fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -318,7 +264,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat AND dog AND NOT fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -331,7 +277,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat dog +fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -343,7 +289,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `fox OR (fox AND (cat OR dog))`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "fox"},
 					&ast.OperatorNode{Value: kql.BoolOR},
@@ -361,7 +307,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat dog -fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -374,7 +320,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `(NOT fox) AND (cat OR dog)`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{Nodes: []ast.Node{
 						&ast.OperatorNode{Value: kql.BoolNOT},
@@ -391,7 +337,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `cat +dog -fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -404,7 +350,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `(NOT fox) AND (dog OR (dog AND cat))`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{Nodes: []ast.Node{
 						&ast.OperatorNode{Value: kql.BoolNOT},
@@ -423,12 +369,11 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		// ++
 		// 2.3.5 Date Tokens
 		// 3.3.5 Date Tokens
 		{
 			name: `Modified:2023-09-05`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.DateTimeNode{
 						Key:      "Modified",
@@ -440,7 +385,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `Modified:"2008-01-29"`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.DateTimeNode{
 						Key:      "Modified",
@@ -451,15 +396,400 @@ func TestParse(t *testing.T) {
 			},
 		},
 		{
-			name: `Modified:today`,
-			skip: true,
+			name:  `Modified:today`,
+			patch: patchNow(t, "2023-09-10"),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Modified",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-10"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Modified",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-10 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			testKQL(t, tc)
+		})
+	}
+}
+
+func TestParse_DateTimeRestrictionNode(t *testing.T) {
+	tests := []testCase{
+		{
+			name: "format",
+			query: join([]string{
+				`Mtime:"2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime:2023-09-05T08:42:11.23554+02:00`,
+				`Mtime="2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime=2023-09-05T08:42:11.23554+02:00`,
+				`Mtime<"2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime<2023-09-05T08:42:11.23554+02:00`,
+				`Mtime<="2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime<=2023-09-05T08:42:11.23554+02:00`,
+				`Mtime>"2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime>2023-09-05T08:42:11.23554+02:00`,
+				`Mtime>="2023-09-05T08:42:11.23554+02:00"`,
+				`Mtime>=2023-09-05T08:42:11.23554+02:00`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ":"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ":"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">"},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - today",
+			patch: setWorldClock(t, "2023-09-10"),
+			query: join([]string{
+				`Mtime:today`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-10"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-10 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - yesterday",
+			patch: setWorldClock(t, "2023-09-10"),
+			query: join([]string{
+				`Mtime:yesterday`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-09"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-09 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - yesterday - the beginning of the month",
+			patch: setWorldClock(t, "2023-09-01"),
+			query: join([]string{
+				`Mtime:yesterday`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-08-31"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-08-31 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - this week",
+			patch: setWorldClock(t, "2023-09-06"),
+			query: join([]string{
+				`Mtime:"this week"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-04"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-10 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - this month",
+			patch: setWorldClock(t, "2023-09-02"),
+			query: join([]string{
+				`Mtime:"this month"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-09-01"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-09-30 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - last month",
+			patch: setWorldClock(t, "2023-09-02"),
+			query: join([]string{
+				`Mtime:"last month"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-08-01"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-08-31 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - last month - the beginning of the year",
+			patch: setWorldClock(t, "2023-01-01"),
+			query: join([]string{
+				`Mtime:"last month"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2022-12-01"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2022-12-31 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - this year",
+			patch: setWorldClock(t, "2023-06-18"),
+			query: join([]string{
+				`Mtime:"this year"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2023-01-01"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2023-12-31 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+		{
+			name:  "NaturalLanguage DateTimeNode - last year",
+			patch: setWorldClock(t, "2023-01-01"),
+			query: join([]string{
+				`Mtime:"last year"`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: ">="},
+						Value:    mustParseTime(t, "2022-01-01"),
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.DateTimeNode{
+						Key:      "Mtime",
+						Operator: &ast.OperatorNode{Value: "<="},
+						Value:    mustParseTime(t, "2022-12-31 23:59:59.999999999"),
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			testKQL(t, tc)
+		})
+	}
+}
+
+func TestParse_Errors(t *testing.T) {
+	tests := []testCase{
+		{
+			query: "animal:(mammal:cat mammal:dog reptile:turtle)",
+			error: kql.NamedGroupInvalidNodesError{
+				Node: &ast.StringNode{Key: "mammal", Value: "cat"},
+			},
+		},
+		{
+			query: "animal:(cat mammal:dog turtle)",
+			error: kql.NamedGroupInvalidNodesError{
+				Node: &ast.StringNode{Key: "mammal", Value: "dog"},
+			},
+		},
+		{
+			query: "animal:(AND cat)",
+			error: kql.StartsWithBinaryOperatorError{
+				Node: &ast.OperatorNode{Value: kql.BoolAND},
+			},
+		},
+		{
+			query: "animal:(OR cat)",
+			error: kql.StartsWithBinaryOperatorError{
+				Node: &ast.OperatorNode{Value: kql.BoolOR},
+			},
+		},
+		{
+			query: "(AND cat)",
+			error: kql.StartsWithBinaryOperatorError{
+				Node: &ast.OperatorNode{Value: kql.BoolAND},
+			},
+		},
+		{
+			query: "(OR cat)",
+			error: kql.StartsWithBinaryOperatorError{
+				Node: &ast.OperatorNode{Value: kql.BoolOR},
+			},
+		},
+		{
+			query: `cat dog`,
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.StringNode{Value: "cat"},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.StringNode{Value: "dog"},
+				},
+			},
 		},
-		//////////////////////////////////////////////////////////////////////////////////////
-		// everything else
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			testKQL(t, tc)
+		})
+	}
+}
+
+func TestParse_Stress(t *testing.T) {
+	tests := []testCase{
 		{
-			name:       "FullDictionary",
-			givenQuery: mustJoin(FullDictionary),
-			expectedAst: &ast.Ast{
+			name:  "FullDictionary",
+			query: join(FullDictionary),
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "federated"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -565,13 +895,13 @@ func TestParse(t *testing.T) {
 			},
 		},
 		{
-			name: "Group",
-			givenQuery: mustJoin([]string{
+			name: "complex",
+			query: join([]string{
 				`(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`,
 				`author:("John Smith" Jane)`,
 				`author:("John Smith" OR Jane)`,
 			}),
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Nodes: []ast.Node{
@@ -608,7 +938,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "author",
@@ -627,7 +957,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `author:("John Smith" OR Jane)`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "author",
@@ -642,7 +972,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `NOT "John Smith" NOT Jane`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.OperatorNode{Value: kql.BoolNOT},
 					&ast.StringNode{Value: "John Smith"},
@@ -654,7 +984,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.OperatorNode{Value: kql.BoolNOT},
 					&ast.StringNode{Key: "author", Value: "John Smith"},
@@ -669,7 +999,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `scope:"<uuid>/new folder/subfolder" file`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{
 						Key:   "scope",
@@ -684,7 +1014,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `	😂 "*😀 😁*" name:😂💁👌🎍😍 name:😂💁👌 😍`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{
 						Value: "😂",
@@ -710,121 +1040,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		{
-			name: "DateTimeRestrictionNode",
-			givenQuery: mustJoin([]string{
-				`Mtime:"2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime:2023-09-05T08:42:11.23554+02:00`,
-				`Mtime="2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime=2023-09-05T08:42:11.23554+02:00`,
-				`Mtime<"2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime<2023-09-05T08:42:11.23554+02:00`,
-				`Mtime<="2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime<=2023-09-05T08:42:11.23554+02:00`,
-				`Mtime>"2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime>2023-09-05T08:42:11.23554+02:00`,
-				`Mtime>="2023-09-05T08:42:11.23554+02:00"`,
-				`Mtime>=2023-09-05T08:42:11.23554+02:00`,
-			}),
-			expectedAst: &ast.Ast{
-				Nodes: []ast.Node{
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ":"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ":"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "<"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "<"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "<="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: "<="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ">"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ">"},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ">="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.DateTimeNode{
-						Key:      "Mtime",
-						Operator: &ast.OperatorNode{Value: ">="},
-						Value:    mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"),
-					},
-				},
-			},
-		},
-		{
-			name: "id",
-			givenQuery: mustJoin([]string{
-				`id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`,
-				`ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`,
-			}),
-			expectedAst: &ast.Ast{
-				Nodes: []ast.Node{
-					&ast.StringNode{
-						Key:   "id",
-						Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c",
-					},
-					&ast.OperatorNode{Value: kql.BoolOR},
-					&ast.StringNode{
-						Key:   "ID",
-						Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c",
-					},
-				},
-			},
-		},
 		{
 			name: "animal:(cat dog turtle)",
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "animal",
@@ -847,7 +1065,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: "(cat dog turtle)",
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Nodes: []ast.Node{
@@ -867,55 +1085,9 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
-		{
-			name: "animal:(mammal:cat mammal:dog reptile:turtle)",
-			expectedError: kql.NamedGroupInvalidNodesError{
-				Node: &ast.StringNode{Key: "mammal", Value: "cat"},
-			},
-		},
-		{
-			name: "animal:(cat mammal:dog turtle)",
-			expectedError: kql.NamedGroupInvalidNodesError{
-				Node: &ast.StringNode{Key: "mammal", Value: "dog"},
-			},
-		},
-		{
-			name: "animal:(AND cat)",
-			expectedError: kql.StartsWithBinaryOperatorError{
-				Node: &ast.OperatorNode{Value: kql.BoolAND},
-			},
-		},
-		{
-			name: "animal:(OR cat)",
-			expectedError: kql.StartsWithBinaryOperatorError{
-				Node: &ast.OperatorNode{Value: kql.BoolOR},
-			},
-		},
-		{
-			name: "(AND cat)",
-			expectedError: kql.StartsWithBinaryOperatorError{
-				Node: &ast.OperatorNode{Value: kql.BoolAND},
-			},
-		},
-		{
-			name: "(OR cat)",
-			expectedError: kql.StartsWithBinaryOperatorError{
-				Node: &ast.OperatorNode{Value: kql.BoolOR},
-			},
-		},
-		{
-			name: `cat dog`,
-			expectedAst: &ast.Ast{
-				Nodes: []ast.Node{
-					&ast.StringNode{Value: "cat"},
-					&ast.OperatorNode{Value: kql.BoolAND},
-					&ast.StringNode{Value: "dog"},
-				},
-			},
-		},
 		{
 			name: `cat dog fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.StringNode{Value: "cat"},
 					&ast.OperatorNode{Value: kql.BoolAND},
@@ -927,7 +1099,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `(cat dog) fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Nodes: []ast.Node{
@@ -943,7 +1115,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `(mammal:cat mammal:dog) fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Nodes: []ast.Node{
@@ -959,7 +1131,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `mammal:(cat dog) fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "mammal",
@@ -976,7 +1148,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `mammal:(cat dog) mammal:fox`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "mammal",
@@ -993,7 +1165,7 @@ func TestParse(t *testing.T) {
 		},
 		{
 			name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`,
-			expectedAst: &ast.Ast{
+			ast: &ast.Ast{
 				Nodes: []ast.Node{
 					&ast.GroupNode{
 						Key: "title",
@@ -1015,48 +1187,171 @@ func TestParse(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "ids",
+			query: join([]string{
+				`id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`,
+				`ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`,
+			}),
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.StringNode{
+						Key:   "id",
+						Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c",
+					},
+					&ast.OperatorNode{Value: kql.BoolOR},
+					&ast.StringNode{
+						Key:   "ID",
+						Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c",
+					},
+				},
+			},
+		},
+		{
+			name: `"test:test" test:"test:test" "more:*+#!/°^§$%&&/()=?<><<more" more:"more:*+#!/°^§$%&&/()=?<><<more"`,
+			ast: &ast.Ast{
+				Nodes: []ast.Node{
+					&ast.StringNode{
+						Value: "test:test",
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.StringNode{
+						Key:   "test",
+						Value: "test:test",
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.StringNode{
+						Value: "more:*+#!/°^§$%&&/()=?<><<more",
+					},
+					&ast.OperatorNode{Value: kql.BoolAND},
+					&ast.StringNode{
+						Key:   "more",
+						Value: "more:*+#!/°^§$%&&/()=?<><<more",
+					},
+				},
+			},
+		},
 	}
 
-	assert := tAssert.New(t)
-
-	for _, tt := range tests {
-		tt := tt
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			testKQL(t, tc)
+		})
+	}
+}
 
-		t.Run(tt.name, func(t *testing.T) {
-			if tt.skip {
-				t.Skip()
-			}
+func BenchmarkParse(b *testing.B) {
+	b.ReportAllocs()
+	for n := 0; n < b.N; n++ {
+		if _, err := kql.Parse("", []byte(strings.Join(FullDictionary, " "))); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
 
-			q := tt.name
+type testCase struct {
+	name  string
+	query string
+	ast   *ast.Ast
+	error error
+	skip  bool
+	patch func() func()
+}
 
-			if tt.givenQuery != "" {
-				q = tt.givenQuery
-			}
+var mustParseTime = func(t *testing.T, ts string) time.Time {
+	tp, err := now.Parse(ts)
+	if err != nil {
+		t.Fatalf("time.Parse(...) error = %v", err)
+	}
 
-			parsedAST, err := kql.Builder{}.Build(q)
+	return tp
+}
 
-			if tt.expectedError != nil {
-				if tt.expectedError.Error() != "" {
-					assert.Equal(err.Error(), tt.expectedError.Error())
-				} else {
-					assert.NotNil(err)
-				}
+var setWorldClock = func(t *testing.T, ts string) func() func() {
+	return func() func() {
+		kql.PatchTimeNow(func() time.Time {
+			return mustParseTime(t, ts)
+		})
 
-				return
-			}
+		return func() {
+			kql.PatchTimeNow(time.Now)
+		}
+	}
+}
 
-			if diff := test.DiffAst(tt.expectedAst, parsedAST); diff != "" {
-				t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", q, diff)
-			}
+var patchNow = func(t *testing.T, ts string) func() func() {
+	return func() func() {
+		kql.PatchTimeNow(func() time.Time {
+			return mustParseTime(t, ts)
 		})
+
+		return func() {
+			kql.PatchTimeNow(time.Now)
+		}
 	}
 }
 
-func BenchmarkParse(b *testing.B) {
-	b.ReportAllocs()
-	for n := 0; n < b.N; n++ {
-		if _, err := kql.Parse("", []byte(strings.Join(FullDictionary, " "))); err != nil {
-			b.Fatal(err)
+var join = func(v []string) string {
+	return strings.Join(v, " ")
+}
+
+func testKQL(t *testing.T, tc testCase) {
+	if tc.skip {
+		t.Skip()
+	}
+
+	if tc.patch != nil {
+		revert := tc.patch()
+		defer revert()
+	}
+
+	query := tc.name
+	if tc.query != "" {
+		query = tc.query
+	}
+
+	astResult, err := kql.Builder{}.Build(query)
+	assert := tAssert.New(t)
+
+	if tc.error != nil {
+		if expectedError := tc.error.Error(); expectedError != "" {
+			assert.Equal(err.Error(), expectedError)
+		} else {
+			assert.NotNil(err)
 		}
+
+		return
+	}
+
+	if diff := test.DiffAst(tc.ast, astResult); diff != "" {
+		t.Fatalf("AST mismatch \nquery: '%s' \n(-expected +got): %s", query, diff)
 	}
 }
+
+var FullDictionary = []string{
+	`federated search`,
+	`federat* search`,
+	`search fed*`,
+	`author:"John Smith"`,
+	`filetype:docx`,
+	`filename:budget.xlsx`,
+	`author: "John Smith"`,
+	`author :"John Smith"`,
+	`author : "John Smith"`,
+	`author "John Smith"`,
+	`author "John Smith"`,
+	`author:Shakespear`,
+	`author:Paul`,
+	`author:Shakesp*`,
+	`title:"Advanced Search"`,
+	`title:"Advanced Sear*"`,
+	`title:"Advan* Search"`,
+	`title:"*anced Search"`,
+	`author:"John Smith" OR author:"Jane Smith"`,
+	`author:"John Smith" AND filetype:docx`,
+	`author:("John Smith" "Jane Smith")`,
+	`author:("John Smith" OR "Jane Smith")`,
+	`(DepartmentId:* OR RelatedHubSites:*) AND contentclass:sts_site NOT IsHubSite:false`,
+	`author:"John Smith" (filetype:docx title:"Advanced Search")`,
+}
diff --git a/services/search/pkg/query/kql/engine_suite_test.go b/services/search/pkg/query/kql/engine_suite_test.go
new file mode 100644
index 00000000000..44a37b93179
--- /dev/null
+++ b/services/search/pkg/query/kql/engine_suite_test.go
@@ -0,0 +1,11 @@
+package kql
+
+import (
+	"time"
+)
+
+// PatchTimeNow is here to path the package time now func,
+// this only exists for the tests context
+func PatchTimeNow(t func() time.Time) {
+	timeNow = t
+}
diff --git a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
index 71e071abce8..3a90a282c71 100644
--- a/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
+++ b/tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md
@@ -22,7 +22,7 @@ The expected failures in this file are from features in the owncloud/ocis repo.
 
 - [apiSpacesShares/changingFilesShare.feature:14](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSpacesShares/changingFilesShare.feature#L14)
 
-### [copy to overwrite (file and folder) from Personal to Shares Jail behaves differently](https://github.com/owncloud/ocis/issues/4393)
+### [Shared mount folder gets deleted when overwritten by a file from personal space](https://github.com/owncloud/ocis/issues/7208)
 
 - [apiSpacesShares/copySpaces.feature:528](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSpacesShares/copySpaces.feature#L528)
 - [apiSpacesShares/copySpaces.feature:542](https://github.com/owncloud/ocis/blob/master/tests/acceptance/features/apiSpacesShares/copySpaces.feature#L542)
diff --git a/tests/acceptance/features/apiSpacesShares/copySpaces.feature b/tests/acceptance/features/apiSpacesShares/copySpaces.feature
index 208f4a02fb1..885ba4fd8ad 100644
--- a/tests/acceptance/features/apiSpacesShares/copySpaces.feature
+++ b/tests/acceptance/features/apiSpacesShares/copySpaces.feature
@@ -524,7 +524,7 @@ Feature: copy file
     Then the HTTP status code should be "403"
     And for user "Alice" the content of the file "/testshare/overwritethis.txt" of the space "Shares" should be "ownCloud test text file 1"
 
-  @issue-4393
+  @issue-7208
   Scenario: copy a file over the top of an existing folder received as a user share
     Given using spaces DAV path
     And user "Alice" has uploaded file with content "ownCloud test text file 1" to "/textfile1.txt"
@@ -538,7 +538,7 @@ Feature: copy file
     And as "Alice" file "/textfile1.txt" should exist
     And user "Alice" should not have any received shares
 
-  @issue-4393
+  @issue-7208
   Scenario: copy a folder over the top of an existing file received as a user share
     Given using spaces DAV path
     And user "Alice" has created folder "/FOLDER"
diff --git a/tests/acceptance/features/bootstrap/TagContext.php b/tests/acceptance/features/bootstrap/TagContext.php
index 1aeb2efefa1..e608a3e801a 100644
--- a/tests/acceptance/features/bootstrap/TagContext.php
+++ b/tests/acceptance/features/bootstrap/TagContext.php
@@ -24,6 +24,7 @@ use Behat\Behat\Hook\Scope\BeforeScenarioScope;
 use Behat\Gherkin\Node\TableNode;
 use PHPUnit\Framework\Assert;
 use TestHelpers\GraphHelper;
+use \Psr\Http\Message\ResponseInterface;
 
 require_once 'bootstrap.php';
 
@@ -53,18 +54,16 @@ class TagContext implements Context {
 	}
 
 	/**
-	 * @When /^user "([^"]*)" creates the following tags for (folder|file)\s?"([^"]*)" of space "([^"]*)":$/
-	 *
 	 * @param string $user
 	 * @param string $fileOrFolder   (file|folder)
 	 * @param string $resource
 	 * @param string $space
 	 * @param TableNode $table
 	 *
-	 * @return void
+	 * @return ResponseInterface
 	 * @throws Exception
 	 */
-	public function theUserCreatesFollowingTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
+	public function createTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):ResponseInterface {
 		$tagNameArray = [];
 		foreach ($table->getRows() as $value) {
 			$tagNameArray[] = $value[0];
@@ -75,7 +74,7 @@ class TagContext implements Context {
 			$resourceId = $this->spacesContext->getFileId($user, $space, $resource);
 		}
 
-		$response = GraphHelper::createTags(
+		return GraphHelper::createTags(
 			$this->featureContext->getBaseUrl(),
 			$this->featureContext->getStepLineRef(),
 			$user,
@@ -83,6 +82,22 @@ class TagContext implements Context {
 			$resourceId,
 			$tagNameArray
 		);
+	}
+
+	/**
+	 * @When /^user "([^"]*)" creates the following tags for (folder|file) "([^"]*)" of space "([^"]*)":$/
+	 *
+	 * @param string $user
+	 * @param string $fileOrFolder   (file|folder)
+	 * @param string $resource
+	 * @param string $space
+	 * @param TableNode $table
+	 *
+	 * @return void
+	 * @throws Exception
+	 */
+	public function theUserCreatesFollowingTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
+		$response = $this->createTags($user, $fileOrFolder, $resource, $space, $table);
 		$this->featureContext->setResponse($response);
 	}
 
@@ -99,8 +114,8 @@ class TagContext implements Context {
 	 * @throws Exception
 	 */
 	public function theUserHasCreatedFollowingTags(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
-		$this->theUserCreatesFollowingTags($user, $fileOrFolder, $resource, $space, $table);
-		$this->featureContext->theHttpStatusCodeShouldBe(200);
+		$response = $this->createTags($user, $fileOrFolder, $resource, $space, $table);
+		$this->featureContext->theHttpStatusCodeShouldBe(200, "", $response);
 	}
 
 	/**
@@ -175,18 +190,16 @@ class TagContext implements Context {
 	}
 
 	/**
-	 * @When /^user "([^"]*)" removes the following tags for (folder|file)\s?"([^"]*)" of space "([^"]*)":$/
-	 *
 	 * @param string $user
 	 * @param string $fileOrFolder   (file|folder)
 	 * @param string $resource
 	 * @param string $space
 	 * @param TableNode $table
 	 *
-	 * @return void
+	 * @return ResponseInterface
 	 * @throws Exception
 	 */
-	public function userRemovesTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
+	public function removeTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):ResponseInterface {
 		$tagNameArray = [];
 		foreach ($table->getRows() as $value) {
 			$tagNameArray[] = $value[0];
@@ -198,7 +211,7 @@ class TagContext implements Context {
 			$resourceId = $this->spacesContext->getFileId($user, $space, $resource);
 		}
 
-		$response = GraphHelper::deleteTags(
+		return GraphHelper::deleteTags(
 			$this->featureContext->getBaseUrl(),
 			$this->featureContext->getStepLineRef(),
 			$user,
@@ -206,6 +219,22 @@ class TagContext implements Context {
 			$resourceId,
 			$tagNameArray
 		);
+	}
+
+	/**
+	 * @When /^user "([^"]*)" removes the following tags for (folder|file) "([^"]*)" of space "([^"]*)":$/
+	 *
+	 * @param string $user
+	 * @param string $fileOrFolder   (file|folder)
+	 * @param string $resource
+	 * @param string $space
+	 * @param TableNode $table
+	 *
+	 * @return void
+	 * @throws Exception
+	 */
+	public function userRemovesTagsFromResourceOfTheSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
+		$response = $this->removeTagsFromResourceOfTheSpace($user, $fileOrFolder, $resource, $space, $table);
 		$this->featureContext->setResponse($response);
 	}
 
@@ -222,7 +251,7 @@ class TagContext implements Context {
 	 * @throws Exception
 	 */
 	public function userHAsRemovedTheFollowingTagsForFileOfSpace(string $user, string $fileOrFolder, string $resource, string $space, TableNode $table):void {
-		$this->userRemovesTagsFromResourceOfTheSpace($user, $fileOrFolder, $resource, $space, $table);
-		$this->featureContext->theHttpStatusCodeShouldBe(200);
+		$response = $this->removeTagsFromResourceOfTheSpace($user, $fileOrFolder, $resource, $space, $table);
+		$this->featureContext->theHttpStatusCodeShouldBe(200, "", $response);
 	}
 }
diff --git a/tests/acceptance/features/bootstrap/TrashbinContext.php b/tests/acceptance/features/bootstrap/TrashbinContext.php
index ef098f3d125..c237a25be9e 100644
--- a/tests/acceptance/features/bootstrap/TrashbinContext.php
+++ b/tests/acceptance/features/bootstrap/TrashbinContext.php
@@ -37,8 +37,6 @@ class TrashbinContext implements Context {
 	private FeatureContext $featureContext;
 
 	/**
-	 * @When user :user empties the trashbin using the trashbin API
-	 *
 	 * @param string|null $user user
 	 *
 	 * @return ResponseInterface
@@ -46,7 +44,7 @@ class TrashbinContext implements Context {
 	public function emptyTrashbin(?string $user):ResponseInterface {
 		$user = $this->featureContext->getActualUsername($user);
 		$davPathVersion = $this->featureContext->getDavPathVersion();
-		$response = WebDavHelper::makeDavRequest(
+		return WebDavHelper::makeDavRequest(
 			$this->featureContext->getBaseUrl(),
 			$user,
 			$this->featureContext->getPasswordForUser($user),
@@ -58,11 +56,18 @@ class TrashbinContext implements Context {
 			$davPathVersion,
 			'trash-bin'
 		);
-
-		$this->featureContext->setResponse($response);
-		return $response;
 	}
 
+	/**
+	 * @When user :user empties the trashbin using the trashbin API
+	 *
+	 * @param string $user user
+	 *
+	 * @return void
+	 */
+	public function userEmptiesTrashbin(string $user): void {
+		$this->featureContext->setResponse($this->emptyTrashbin($user));
+	}
 	/**
 	 * @Given user :user has emptied the trashbin
 	 *
@@ -72,12 +77,7 @@ class TrashbinContext implements Context {
 	 */
 	public function userHasEmptiedTrashbin(string $user):void {
 		$response = $this->emptyTrashbin($user);
-
-		Assert::assertEquals(
-			204,
-			$response->getStatusCode(),
-			__METHOD__ . " Expected status code was '204' but got '" . $response->getStatusCode() . "'"
-		);
+		$this->featureContext->theHTTPStatusCodeShouldBe(204, '', $response);
 	}
 
 	/**
@@ -547,7 +547,8 @@ class TrashbinContext implements Context {
 	public function userTriesToRestoreFromTrashbinOfUser(?string $asUser, ?string $path, ?string $user):void {
 		$user = $this->featureContext->getActualUsername($user);
 		$asUser = $this->featureContext->getActualUsername($asUser);
-		$this->restoreElement($user, $path, null, true, $asUser);
+		$response = $this->restoreElement($user, $path, null, true, $asUser);
+		$this->featureContext->setResponse($response);
 	}
 
 	/**
@@ -565,7 +566,8 @@ class TrashbinContext implements Context {
 	public function userTriesToRestoreFromTrashbinOfUserUsingPassword(?string $asUser, ?string $path, ?string $user, ?string $password):void {
 		$asUser = $this->featureContext->getActualUsername($asUser);
 		$user = $this->featureContext->getActualUsername($user);
-		$this->restoreElement($user, $path, null, true, $asUser, $password);
+		$response = $this->restoreElement($user, $path, null, true, $asUser, $password);
+		$this->featureContext->setResponse($response);
 	}
 
 	/**
@@ -752,18 +754,18 @@ class TrashbinContext implements Context {
 	 * @param string|null $asUser - To send request as another user
 	 * @param string|null $password
 	 *
-	 * @return void
+	 * @return ResponseInterface
 	 * @throws JsonException
 	 * @throws GuzzleException
 	 */
-	private function sendUndeleteRequest(string $user, string $trashItemHRef, string $destinationPath, ?string $asUser = null, ?string $password = null):void {
+	private function sendUndeleteRequest(string $user, string $trashItemHRef, string $destinationPath, ?string $asUser = null, ?string $password = null):ResponseInterface {
 		$asUser = $asUser ?? $user;
 		$destinationPath = \trim($destinationPath, '/');
 		$destinationValue = $this->featureContext->getBaseUrl() . "/remote.php/dav/files/$user/$destinationPath";
 
 		$trashItemHRef = $this->convertTrashbinHref($trashItemHRef);
 		$headers['Destination'] = $destinationValue;
-		$response = $this->featureContext->makeDavRequest(
+		return $this->featureContext->makeDavRequest(
 			$asUser,
 			'MOVE',
 			$trashItemHRef,
@@ -776,7 +778,6 @@ class TrashbinContext implements Context {
 			[],
 			$user
 		);
-		$this->featureContext->setResponse($response);
 	}
 
 	/**
@@ -787,11 +788,11 @@ class TrashbinContext implements Context {
 	 * @param string|null $asUser - To send request as another user
 	 * @param string|null $password
 	 *
-	 * @return void
+	 * @return ResponseInterface
 	 * @throws JsonException
 	 * @throws GuzzleException
 	 */
-	private function restoreElement(string $user, string $originalPath, ?string $destinationPath = null, bool $throwExceptionIfNotFound = true, ?string $asUser = null, ?string $password = null):void {
+	private function restoreElement(string $user, string $originalPath, ?string $destinationPath = null, bool $throwExceptionIfNotFound = true, ?string $asUser = null, ?string $password = null):ResponseInterface {
 		$asUser = $asUser ?? $user;
 		$listing = $this->listTrashbinFolder($user);
 		$originalPath = \trim($originalPath, '/');
@@ -800,14 +801,13 @@ class TrashbinContext implements Context {
 		}
 		foreach ($listing as $entry) {
 			if ($entry['original-location'] === $originalPath) {
-				$this->sendUndeleteRequest(
+				return $this->sendUndeleteRequest(
 					$user,
 					$entry['href'],
 					$destinationPath,
 					$asUser,
 					$password
 				);
-				return;
 			}
 		}
 		// The requested element to restore was not even in the trashbin.
@@ -907,7 +907,8 @@ class TrashbinContext implements Context {
 	 * @throws Exception
 	 */
 	public function userTriesToRestoreElementInTrash(string $user, string $originalPath):void {
-		$this->restoreElement($user, $originalPath, null, false);
+		$response = $this->restoreElement($user, $originalPath, null, false);
+		$this->featureContext->setResponse($response);
 	}
 
 	/**
@@ -922,7 +923,7 @@ class TrashbinContext implements Context {
 	 */
 	public function elementInTrashIsRestored(?string $user, string $originalPath):void {
 		$user = $this->featureContext->getActualUsername($user);
-		$this->restoreElement($user, $originalPath);
+		$this->featureContext->setResponse($this->restoreElement($user, $originalPath));
 	}
 
 	/**
@@ -955,7 +956,8 @@ class TrashbinContext implements Context {
 	 * @throws Exception
 	 */
 	public function elementInTrashHasBeenRestored(string $user, string $originalPath):void {
-		$this->restoreElement($user, $originalPath);
+		$response = $this->restoreElement($user, $originalPath);
+		$this->featureContext->theHTTPStatusCodeShouldBe(201, "", $response);
 		if ($this->isInTrash($user, $originalPath)) {
 			throw new Exception("File previously located at $originalPath is still in the trashbin");
 		}
@@ -978,7 +980,7 @@ class TrashbinContext implements Context {
 		?string $destinationPath
 	):void {
 		$user = $this->featureContext->getActualUsername($user);
-		$this->restoreElement($user, $originalPath, $destinationPath);
+		$this->featureContext->setResponse($this->restoreElement($user, $originalPath, $destinationPath));
 	}
 
 	/**