|
8 | 8 | import plistlib
|
9 | 9 | import re
|
10 | 10 | import sqlite3
|
11 |
| -import weakref |
12 | 11 | from textwrap import dedent
|
13 | 12 | from typing import Optional, Union
|
| 13 | +import weakref |
14 | 14 |
|
15 | 15 |
|
16 | 16 | # --------------------------------------------------
|
|
75 | 75 | "heading_title",
|
76 | 76 | "project",
|
77 | 77 | "project_title",
|
| 78 | + "reminder_time", |
78 | 79 | "trashed",
|
79 | 80 | "tags",
|
80 | 81 | )
|
|
107 | 108 | # See `convert_isodate_sql_expression_to_thingsdate` for details.
|
108 | 109 | DATE_DEADLINE = "deadline" # INTEGER: YYYYYYYYYYYMMMMDDDDD0000000, in binary
|
109 | 110 | DATE_START = "startDate" # INTEGER: YYYYYYYYYYYMMMMDDDDD0000000, in binary
|
| 111 | +# See 'convert_thingstime_sql_expression_to_isotime' for details. |
| 112 | +REMINDER_TIME = "reminderTime" # INTEGER: hhhhhmmmmmm00000000000000000000, in binary |
110 | 113 |
|
111 | 114 | # --------------------------------------------------
|
112 | 115 | # Various filters
|
@@ -528,6 +531,9 @@ def make_tasks_sql_query(where_predicate=None, order_predicate=None):
|
528 | 531 | deadline_expression = convert_thingsdate_sql_expression_to_isodate(
|
529 | 532 | f"TASK.{DATE_DEADLINE}"
|
530 | 533 | )
|
| 534 | + reminder_time_expression = convert_thingstime_sql_expression_to_isotime( |
| 535 | + f"TASK.{REMINDER_TIME}" |
| 536 | + ) |
531 | 537 |
|
532 | 538 | return f"""
|
533 | 539 | SELECT DISTINCT
|
@@ -576,8 +582,9 @@ def make_tasks_sql_query(where_predicate=None, order_predicate=None):
|
576 | 582 | CASE
|
577 | 583 | WHEN CHECKLIST_ITEM.uuid IS NOT NULL THEN 1
|
578 | 584 | END AS checklist,
|
579 |
| - date({start_date_expression}) AS start_date, |
580 |
| - date({deadline_expression}) AS deadline, |
| 585 | + {start_date_expression} AS start_date, |
| 586 | + {deadline_expression} AS deadline, |
| 587 | + {reminder_time_expression} AS "reminder_time", |
581 | 588 | datetime(TASK.{DATE_STOP}, "unixepoch", "localtime") AS "stop_date",
|
582 | 589 | datetime(TASK.{DATE_CREATED}, "unixepoch", "localtime") AS created,
|
583 | 590 | datetime(TASK.{DATE_MODIFIED}, "unixepoch", "localtime") AS modified,
|
@@ -703,6 +710,49 @@ def convert_thingsdate_sql_expression_to_isodate(sql_expression):
|
703 | 710 | return f"CASE WHEN {thingsdate} THEN {isodate} ELSE {thingsdate} END"
|
704 | 711 |
|
705 | 712 |
|
| 713 | +def convert_thingstime_sql_expression_to_isotime(sql_expression: str) -> str: |
| 714 | + """ |
| 715 | + Return SQL Expression that decodes a Things time as a string. |
| 716 | +
|
| 717 | + A _Things time_ is an integer where the binary digits are |
| 718 | + hhhhhmmmmmm00000000000000000000; h is hours, m is minutes. |
| 719 | + Seconds are not encoded in a Things time. |
| 720 | +
|
| 721 | + For example, the ISO 8601 time '12:34:00' corresponds to the Things |
| 722 | + time 840957952 as integer; in binary that is: |
| 723 | + 0110010001000000000000000000000 |
| 724 | + hhhhhmmmmmm00000000000000000000 |
| 725 | + 12 34 00 |
| 726 | +
|
| 727 | + Parameters |
| 728 | + ---------- |
| 729 | + sql_expression : str |
| 730 | + A sql expression pointing to a "Things time" integer |
| 731 | + in format hhhhhmmmmmm00000000000000000000, in binary. |
| 732 | +
|
| 733 | + Example |
| 734 | + ------- |
| 735 | + >>> convert_thingstime_sql_expression_to_isotime('840957952') |
| 736 | + "CASE WHEN 840957952 THEN \ |
| 737 | + printf('%02d:%02d', (840957952 & 2080374784) >> 26, \ |
| 738 | + (840957952 & 66060288) >> 20) ELSE 840957952 END" |
| 739 | + >>> convert_thingstime_sql_expression_to_isotime('reminderTime') |
| 740 | + "CASE WHEN reminderTime THEN \ |
| 741 | + printf('%02d:%02d', (reminderTime & 2080374784) >> 26, \ |
| 742 | + (reminderTime & 66060288) >> 20) ELSE reminderTime END" |
| 743 | + """ |
| 744 | + h_mask = 0b1111100000000000000000000000000 |
| 745 | + m_mask = 0b0000011111100000000000000000000 |
| 746 | + |
| 747 | + thingstime = sql_expression |
| 748 | + hours = f"({thingstime} & {h_mask}) >> 26" |
| 749 | + minutes = f"({thingstime} & {m_mask}) >> 20" |
| 750 | + |
| 751 | + isotime = f"printf('%02d:%02d', {hours}, {minutes})" |
| 752 | + # when thingstime is NULL, return thingstime as-is |
| 753 | + return f"CASE WHEN {thingstime} THEN {isotime} ELSE {thingstime} END" |
| 754 | + |
| 755 | + |
706 | 756 | def dict_factory(cursor, row):
|
707 | 757 | """
|
708 | 758 | Convert SQL result into a dictionary.
|
|
0 commit comments