Skip to content

Instantly share code, notes, and snippets.

@arctic-hen7
Created August 7, 2023 11:15
Show Gist options
  • Save arctic-hen7/35964ae56b1154622551cb9ddbf646a0 to your computer and use it in GitHub Desktop.
Save arctic-hen7/35964ae56b1154622551cb9ddbf646a0 to your computer and use it in GitHub Desktop.
Some simple code that parses Org mode timestamps in Python. No support for ranges yet, but works nicely with repeats from my testing.
import re
from datetime import datetime, timedelta, date
class Timestamp:
"""
A representation of a timestamp parsed from Org mode.
"""
def __init__(self, timestamp_str):
self.timestamp_str = timestamp_str
self.start_date = None
self.end_date = None
self.repeater = None
# Regular expression to match the Org mode timestamp pattern
timestamp_pattern = r'<(\d{4}-\d{2}-\d{2})(?: \w{3})?(?: \d{1,2}:\d{2})?(?:-\d{1,2}:\d{2})?(?: [+-]\d+[dwmy])?>'
match = re.match(timestamp_pattern, self.timestamp_str)
if not match:
raise ValueError("Invalid timestamp format")
date_str = match.group(1)
self.start_date = datetime.strptime(date_str, '%Y-%m-%d')
# Extract the repeater if present
repeater_pattern = r'([+-]\d+)([dwmy])'
repeater_match = re.search(repeater_pattern, self.timestamp_str)
if repeater_match:
count, unit = repeater_match.groups()
self.repeater_count = int(count)
self.repeater_unit = unit
else:
self.repeater_count = None
self.repeater_unit = None
# Extract the time range if present
time_range_pattern = r'(\d{1,2}:\d{2})(?:-(\d{1,2}:\d{2}))?'
time_range_match = re.search(time_range_pattern, self.timestamp_str)
if time_range_match:
start_time_str, end_time_str = time_range_match.groups()
self.start_date = self.start_date.replace(hour=int(start_time_str.split(':')[0]), minute=int(start_time_str.split(':')[1]))
if end_time_str:
self.end_date = self.start_date.replace(hour=int(end_time_str.split(':')[0]), minute=int(end_time_str.split(':')[1]))
def includes_date(self, target_date):
"""
Checks if this timestamp would include the given date. Be careful to
pass a date to this function, *not* a datetime.
"""
# Check if the target date falls within the range of the timestamp
if self.start_date.date() == target_date:
return True
# If a repeater is provided, check for recurring occurrences
if self.repeater_unit:
if self.repeater_unit == 'd':
# Repeats daily
days_diff = (target_date - self.start_date.date()).days
return days_diff % self.repeater_count == 0
elif self.repeater_unit == 'w':
# Repeats weekly
weeks_diff = (target_date - self.start_date.date()).days / 7
return weeks_diff % self.repeater_count == 0
elif self.repeater_unit == 'm':
# Repeats monthly
months_diff = (target_date.month - self.start_date.month)
is_same_day = target_date.day == self.start_date.day
return months_diff % self.repeater_count == 0 and is_same_day
elif self.repeater_unit == 'y':
# Repeats yearly
years_diff = (target_date.year - self.start_date.year)
is_same_month = target_date.month == self.start_date.month
is_same_day = target_date.day == self.start_date.day
return years_diff % self.repeater_count == 0 and is_same_month and is_same_day
return False
@arctic-hen7
Copy link
Author

Note that a large part of this was written in collaboration with ChatGPT: I highly recommend getting it to start writing something, fixing its mistakes (functionally and stylistically), giving it the updated code, and repeating this process to get something working. It handled the repeats terribly I'll admit (GPT-3.5), but that was fairly easily fixed manually after the fact. Just get it to write good tests!

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