Skip to content

TomoeMami/org-repeat-by-cron.el

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 

Repository files navigation

org-repeat-by-cron.el

https://melpastats-2761cf.gitlab.io/badges/org-repeat-by-cron-badge.svg

简体中文说明

An Org-mode task repeater based on Cron expressions.

Modified from org-reschedule-by-rule.

Key Differences

  1. Pure Elisp Parser: Implemented in pure Elisp with no dependency on external Python packages like croniter.
  2. Logic Control: Replaces the INTERVAL property with DAY_AND for sophisticated date matching (AND/OR logic for DOM and DOW).
  3. Dual Timestamp Support : Supports independent rescheduling for both SCHEDULED and DEADLINE timestamps simultaneously.
  4. Maximum Compatibility: Specifically designed to work with org-habit . It allows Org-mode to handle the repeat process natively, then automatically recalculating and setting the precise date based on Cron rules.

org-repeat-by-cron.el is a lightweight extension for Emacs Org-mode that allows you to repeat tasks using the power of Cron expressions.

Standard Org-mode repeaters (like +1d, ++1w) are relative to the current SCHEDULED or DEADLINE timestamp. In contrast, this tool provides repetition based on absolute time rules. You can easily set a task to repeat “on the last Friday of every month” or “on the first Monday of each quarter” without manual date calculations.

A core advantage is its pure Elisp implementation, ensuring it works out-of-the-box in any Emacs environment without external dependencies.

Features

  • Define complex repetition rules using standard 5-field Cron expressions (Minute, Hour, Day, Month, Day of Week).
  • Zero external dependencies; easy to install and efficient to run.
  • Supports advanced Cron extensions:
    • L: Represents the “Last” day of the month or the last specific weekday of the month.
    • W: Represents the “Nearest Weekday” (Mon-Fri) to a specific date.
    • LW : “Last Weekday” of the month.
    • #: Represents the “Nth weekday of the month” (e.g., 5#2 for the second Friday).
  • Toggle logic for “Day of Month” and “Day of Week” fields using the DAY_AND property (defaults to OR).
  • Supports English aliases for months (JAN-DEC) and days (SUN-SAT) for better readability.
  • You can choose to let it update the task’s SCHEDULED timestamp or DEADLINE timestamp, or update both independently (default is SCHEDULED ).

Installation & Configuration

Available via MELPA.

Recommended configuration:

(use-package org-repeat-by-cron
  :ensure t
  :config
  (global-org-repeat-by-cron-mode))

Usage

To make an Org task repeat according to a Cron rule, simply add the REPEAT_CRON property to its PROPERTIES drawer.

Tip: You do not need to wrap the REPEAT_CRON value in quotes.

Example

Suppose we have a weekly course held on weekends:

* TODO Weekend Course
:PROPERTIES:
:REPEAT_CRON: * * SAT,SUN 
:END:

When marked as DONE, it will automatically be rescheduled to the next matching date. For example, if today is Dec 9, 2025, it will be scheduled for the coming Saturday (Dec 13, 2025):

* TODO Weekend Course
SCHEDULED: <2025-12-13 Sat>
:PROPERTIES:
:REPEAT_CRON: * * SAT,SUN 
:REPEAT_ANCHOR: 2025-12-13 Sat
:END:

Marking it DONE again will calculate the next point based on the REPEAT_ANCHOR and the current time:

* TODO Weekend Course
SCHEDULED: <2025-12-14 Sun>
:PROPERTIES:
:REPEAT_CRON: * * SAT,SUN 
:REPEAT_ANCHOR: 2025-12-14 Sun
:END:

Note: If you do not want to specify a specific hour/minute, use the 3-field shorthand (discussed below).

Core Properties

  • REPEAT_CRON: (Required) A string containing the Cron expression.
    • 5-field format: Min Hour Dom Month Dow (e.g., 0 9 * * * for daily at 09:00). Generates a timestamp with HH:MM.
    • 3-field format: Dom Month Dow. Generates a date-only timestamp without HH:MM.
      • Why use 3 fields? In Org-mode, many tasks only need a date. If you want the task to appear in the “All Day” section of your Agenda, use the 3-field format. Use 5 fields only if you need a specific start time.
  • REPEAT_DAY_AND: (Optional) Defaults to nil (logic OR). Set to t to enable AND logic.
    • OR (Default): 13 * FRI triggers on the 13th of the month AND every Friday.
    • AND (Set to t): 13 * FRI triggers only on Friday the 13th.
  • REPEAT_DEADLINE: (Optional)
    • If set to t : The Cron rule in REPEAT_CRON updates the DEADLINE instead of SCHEDULED . (Existing SCHEDULED timestamps are cleared to prevent conflicts).
    • If set to a Cron expression (e.g. 0 18 * * 5 ): Enable independent repetition mode. The SCHEDULED time follows REPEAT_CRON , while the DEADLINE follows this specific expression.
  • REPEAT_ANCHOR & REPEAT_DEADLINE_ANCHOR : Automatically maintained by the plugin to store the last trigger base time. Users usually don’t need to edit this. If deleted, the plugin will use the current time as the base for the next calculation.

Workflow

  1. Create a PROPERTIES drawer under an Org heading.
  2. Add the REPEAT_CRON property with your expression.
  3. (Optional) Add REPEAT_DAY_AND or REPEAT_DEADLINE.
  4. Change the task state to DONE as usual.
  5. Automatically:
    • org-repeat-by-cron captures the state change and provides a temporary repeater to let Org-mode handle standard procedures (logging, keywords, etc.).
    • org-mode itself handles the repetition event.
    • org-repeat-by-cron then recalculates the next valid time based on the Cron rules.
    • It updates the task’s SCHEDULED or DEADLINE timestamp (or both) and removes the temporary repeater.

Note: Because the repetition is processed by Org-mode, this package is fully compatible with org-habit .

Cron Syntax

Standard Cron expressions consist of 5 fields separated by spaces.

FieldAllowed ValuesSpecial Characters
Minute0-59* , - /
Hour0-23* , - /
Day of Month1-31* , - / L W
Month1-12 or JAN-DEC* , - /
Day of Week0-7 (0 and 7 are Sunday) or SUN-SAT* , - / L #

Note: This parser treats * as the full range and supports logic switching via DAY_AND, so the special character ? is not required.

Standard Characters

CharDescriptionExample
*Any value* in Hour means “every hour”
,Value list1,15 in Day means “the 1st and 15th”
-RangeMON-FRI in Day of Week means “Monday to Friday”
/Step*/15 in Minute means “every 15 minutes”

Advanced Extensions

  • L
    • Last: In the Day field, L means the last day of the month. In the Day of Week field, 5L means the last Friday of the month.
    • L (Day) -> Jan 31st, Feb 28th, etc.
    • 6L (Day of Week) -> The last Saturday of the month.
  • W
    • Nearest Weekday: 15W means the closest weekday (Mon-Fri) to the 15th. If the 15th is a Saturday, it matches the 14th (Fri). If it’s a Sunday, it matches the 16th (Mon).
    • It will not cross month boundaries (e.g., if the 1st is Saturday, 1W matches the 3rd, Monday).
  • LW
    • Last Weekday of the month.
  • #
    • Nth Day of Week: Format is DOW#N.
    • 5#2 -> The second Friday of the month.
    • 1#1,1#3 -> The first and third Monday.

Examples

1. Weekly Report

Repeat every Friday at 5:00 PM.

* TODO Submit Weekly Report
SCHEDULED: <2025-09-12 Fri 17:00>
:PROPERTIES:
:REPEAT_CRON: 0 17 * * FRI
:END:

2. Monthly Bill (Last Day)

Reminder to pay bills on the last day of every month (using 3-field format).

* TODO Pay Credit Card Bill
SCHEDULED: <2025-09-30 Tue>
:PROPERTIES:
:REPEAT_CRON: L * * 
:END:

3. Bi-weekly Meeting

Meetings held on the 1st and 3rd Monday of the month.

* TODO Bi-weekly Sync Meeting
DEADLINE: <2025-10-06 Mon 10:00>
:PROPERTIES:
:REPEAT_CRON: 0 10 * * MON#1,MON#3
:REPEAT_DEADLINE: t
:END:

4. Quarterly Maintenance

First Monday of the first month of every quarter.

* TODO Server Quarterly Maintenance
SCHEDULED: <2025-10-06 Mon>
:PROPERTIES:
:REPEAT_CRON: * JAN,APR,JUL,OCT MON#1
:END:

5. Black Friday Special (DAY_AND)

Only triggers when the month is the 13th and that day is Friday.

* TODO Black Friday Special
SCHEDULED: <2026-03-13 Fri>
:PROPERTIES:
:REPEAT_CRON: 13 * 5
:REPEAT_DAY_AND: t
:END:

6. Gym Schedule (with org-habit)

Workout Mon, Wed, Fri. Show habit tracking bars in Agenda, allowing a 2-day delay.

* TODO Strength Training
SCHEDULED: <2025-09-15 Mon .+1d/2d>
:PROPERTIES:
:STYLE:    habit
:REPEAT_CRON: * * MON,WED,FRI
:END:

Note: Always keep a repeater like .+1d/2d when using org-habit to preserve the consistency graph.

7. Independent Dual Timestamps (New in 1.1.2)

Suppose you have a weekly task, it should be due every Friday, but you will arrange to complete it on Thursday when free.

* TODO Weekly Key Project
SCHEDULED: <2025-09-08 Mon> DEADLINE: <2025-09-12 Fri>
:PROPERTIES:
:REPEAT_CRON: * * 4
:REPEAT_DEADLINE: * * FRI
:END:

Changelog

1.1.6 2026-03-03 Bug fix

Since the schedule/deadline timestamps obtained during rescheduling are from after the repetition was executed, they have been excluded from the base-time source to prevent the base-time from being incorrectly postponed.

1.1.5 2026-03-03 Bug fix

Fixed the issue where temporary repeaters could not be correctly created when there were no schedule/deadline timestamps.

1.1.4 - 2026-02-27 Logic Update

Updated internal logic to use temporary repeaters. This allows org-mode to natively handle todo-keywords and org-log, avoiding compatibility issues. The package now only resets timestamps after Org finishes its processing.

1.1.3 - 2026-02-26 Bug fix for org-repeat-by-cron-on-done trigger logic

To maintain compatibility, the previous version removed the previously used when-let*, which inadvertently triggered the org-repeat-by-cron-on-done function again.

1.1.2 - 2026-02-26 New scheduled/deadline parallel option

  • New: Supports independent DEADLINE repeat rules. Now the REPEAT_DEADLINE property can be filled with an independent Cron expression, realizing co-existence scheduling with SCHEDULE.
  • New: Introduced REPEAT_DEADLINE_ANCHOR to support the calculation baseline for independent deadlines.
  • Optimization: Internal code refactoring.

1.1.1 - 2026-02-25 Logic Fixes

Bug fix for org-repeat-by-cron-on-done trigger logic Fixed an issue where org-repeat-by-cron-on-done was incorrectly triggered on all headings. The function now correctly filters and only triggers on headings that possess the REPEAT_CRON property.

1.1.0 - 2026-02-24 Repeater Preservation

  1. Added support for native Org repeaters. org-repeat-by-cron now stashes the repeater, calculates the new date, and restores the cookie.
  2. Optimized internal logic.

1.0.5 - 2026-02-02 Timestamp Formatting

The rescheduled timestamp will include a time component (format %Y-%m-%d %a %H:%M ) if any of the following conditions are met (evaluated in a logical OR sequence):

  1. 5-field Cron: The REPEAT_CRON rule has 5 fields, implying a specific time is intended.
  2. Time in Anchor: The REPEAT_ANCHOR property already contains a time string ( HH:MM ).
  3. Time in existing timestamp: The current SCHEDULED or DEADLINE timestamp already contains a time string.

1.0.4 - 2026-01-18 Syntax Consistency

Changed the sequence of L and numbers from L5 to 5L to align with standard Cron conventions.

1.0.3 - 2025-12-25 Update Commentary

1.0.2 - 2025-10-28 Logic Fixes

  1. Correctly compares base-time and current-time to ensure forward scheduling.
  2. Switched the default trigger hook from org-after-todo-state-change-hook to org-trigger-hook for better compatibility.

1.0.1 - 2025-09-22 Update README

Updated README

1.0.0 - 2025-09-21 Initial Release

basic functionality.

About

An Org-mode task repeater based on Cron expressions.

Resources

License

Stars

Watchers

Forks

Packages