5
5
import re
6
6
import sqlite3
7
7
from textwrap import dedent
8
+ from datetime import datetime
8
9
9
10
10
11
# --------------------------------------------------
83
84
DATE_DEADLINE = "dueDate"
84
85
DATE_MODIFIED = "userModificationDate"
85
86
DATE_START = "startDate"
87
+ DATE_STOP = "stopDate"
86
88
87
89
# --------------------------------------------------
88
90
# Various filters
@@ -161,7 +163,7 @@ def __init__(self, filepath=None, print_sql=False):
161
163
# Automated migration to new database location in Things 3.12.6/3.13.1
162
164
# --------------------------------
163
165
try :
164
- with open (self .filepath ) as file :
166
+ with open (self .filepath , encoding = "utf-8" ) as file :
165
167
if "Your database file has been moved there" in file .readline ():
166
168
self .filepath = DEFAULT_FILEPATH
167
169
except (UnicodeDecodeError , FileNotFoundError , PermissionError ):
@@ -181,6 +183,7 @@ def get_tasks( # pylint: disable=R0914
181
183
heading = None ,
182
184
tag = None ,
183
185
start_date = None ,
186
+ stop_date = None ,
184
187
deadline = None ,
185
188
deadline_suppressed = None ,
186
189
trashed = False ,
@@ -246,6 +249,7 @@ def get_tasks( # pylint: disable=R0914
246
249
{ make_filter ("TASK.dueDateSuppressionDate" , deadline_suppressed )}
247
250
{ make_filter ("TAG.title" , tag )}
248
251
{ make_date_filter (f"TASK.{ DATE_START } " , start_date )}
252
+ { make_date_filter (f"TASK.{ DATE_STOP } " , stop_date )}
249
253
{ make_date_filter (f"TASK.{ DATE_DEADLINE } " , deadline )}
250
254
{ make_date_range_filter (f"TASK.{ DATE_CREATED } " , last )}
251
255
{ make_search_filter (search_query )}
@@ -635,9 +639,10 @@ def make_date_filter(date_column: str, value) -> str:
635
639
date_column : str
636
640
Name of the column that has date information on a task.
637
641
638
- value : bool, 'future', 'past', or None
642
+ value : bool, 'future', 'past', ISO 8601 date or None
639
643
`True` or `False` indicates whether a date is set or not.
640
644
`'future'` or `'past'` indicates a date in the future or past.
645
+ `ISO 8601` date is a string in the format `YYYY-MM-DD`.
641
646
`None` indicates any value.
642
647
643
648
Returns
@@ -657,6 +662,9 @@ def make_date_filter(date_column: str, value) -> str:
657
662
>>> make_date_filter('start_date', 'future')
658
663
"AND date(start_date, 'unixepoch', 'localtime') > date('now', 'localtime')"
659
664
665
+ >>> make_date_filter('stop_date', '2021-03-28')
666
+ "AND date(stop_date, 'unixepoch', 'localtime') >= date('2021-03-28', 'localtime')"
667
+
660
668
>>> make_date_filter('created', None)
661
669
''
662
670
@@ -668,10 +676,14 @@ def make_date_filter(date_column: str, value) -> str:
668
676
return make_filter (date_column , value )
669
677
670
678
# compare `date_column` to now.
671
- validate ("value" , value , ["future" , "past" ])
679
+ try :
680
+ now = f"date('{ datetime .fromisoformat (value )} ', 'localtime')"
681
+ operator = ">="
682
+ except ValueError :
683
+ validate ("value" , value , ["future" , "past" ])
684
+ operator = ">" if value == "future" else "<="
685
+ now = "date('now', 'localtime')"
672
686
date = f"date({ date_column } , 'unixepoch', 'localtime')"
673
- operator = ">" if value == "future" else "<="
674
- now = "date('now', 'localtime')"
675
687
676
688
return f"AND { date } { operator } { now } "
677
689
0 commit comments