We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cron fails to run
The weekly cron for `create_payday_issue` is no longer running successfully at the scheduled time.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `liberapay/cron.py`
Content:
```
1 from collections import namedtuple
2 from datetime import datetime, timedelta
3 import logging
4 import threading
5 from time import sleep
6 
7 
8 logger = logging.getLogger('liberapay.cron')
9 
10 
11 Daily = namedtuple('Daily', 'hour')
12 Weekly = namedtuple('Weekly', 'weekday hour')
13 
14 
15 class Cron(object):
16 
17     def __init__(self, website):
18         self.website = website
19         self.conn = None
20         self.has_lock = False
21         self.exclusive_jobs = []
22 
23     def __call__(self, period, func, exclusive=False):
24         if not self.website.env.run_cron_jobs:
25             return
26         if exclusive and not self.has_lock:
27             self.exclusive_jobs.append((period, func))
28             self._wait_for_lock()
29             return
30         def f():
31             if isinstance(period, Weekly):
32                 while True:
33                     now = datetime.utcnow()
34                     then = now.replace(hour=period.hour, minute=10, second=0)
35                     days = (now.isoweekday() - period.weekday) % 7
36                     if days:
37                         then += timedelta(days=days)
38                     seconds = (then - now).total_seconds()
39                     if seconds > 0:
40                         sleep(seconds)
41                     elif seconds < -60:
42                         sleep(86400 * 6)
43                         continue
44                     try:
45                         func()
46                     except Exception as e:
47                         self.website.tell_sentry(e, {})
48                     sleep(86400 * 6)
49             elif isinstance(period, Daily):
50                 while True:
51                     now = datetime.utcnow()
52                     then = now.replace(hour=period.hour, minute=5, second=0)
53                     seconds = (then - now).total_seconds()
54                     if seconds > 0:
55                         # later today
56                         sleep(seconds)
57                     elif seconds < -60:
58                         # tomorrow
59                         sleep(3600 * 24 + seconds)
60                     try:
61                         func()
62                     except Exception as e:
63                         self.website.tell_sentry(e, {})
64                         # retry in 5 minutes
65                         sleep(300)
66                     else:
67                         # success, sleep until tomorrow
68                         sleep(3600 * 23)
69             else:
70                 assert period >= 1
71                 while True:
72                     try:
73                         func()
74                     except Exception as e:
75                         self.website.tell_sentry(e, {})
76                     sleep(period)
77         t = threading.Thread(target=f)
78         t.daemon = True
79         t.start()
80 
81     def _wait_for_lock(self):
82         if self.conn:
83             return  # Already waiting
84         self.conn = self.website.db.get_connection().__enter__()
85         def f():
86             cursor = self.conn.cursor()
87             while True:
88                 if cursor.one("SELECT pg_try_advisory_lock(0)"):
89                     self.has_lock = True
90                     break
91                 sleep(300)
92             for job in self.exclusive_jobs:
93                 self(*job, exclusive=True)
94         t = threading.Thread(target=f)
95         t.daemon = True
96         t.start()
97
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/liberapay/cron.py b/liberapay/cron.py
--- a/liberapay/cron.py
+++ b/liberapay/cron.py
@@ -21,7 +21,7 @@
         self.exclusive_jobs = []
 
     def __call__(self, period, func, exclusive=False):
-        if not self.website.env.run_cron_jobs:
+        if not self.website.env.run_cron_jobs or not period:
             return
         if exclusive and not self.has_lock:
             self.exclusive_jobs.append((period, func))
@@ -32,7 +32,7 @@
                 while True:
                     now = datetime.utcnow()
                     then = now.replace(hour=period.hour, minute=10, second=0)
-                    days = (now.isoweekday() - period.weekday) % 7
+                    days = (period.weekday - now.isoweekday()) % 7
                     if days:
                         then += timedelta(days=days)
                     seconds = (then - now).total_seconds()

{"golden_diff": "diff --git a/liberapay/cron.py b/liberapay/cron.py\n--- a/liberapay/cron.py\n+++ b/liberapay/cron.py\n@@ -21,7 +21,7 @@\n         self.exclusive_jobs = []\n \n     def __call__(self, period, func, exclusive=False):\n-        if not self.website.env.run_cron_jobs:\n+        if not self.website.env.run_cron_jobs or not period:\n             return\n         if exclusive and not self.has_lock:\n             self.exclusive_jobs.append((period, func))\n@@ -32,7 +32,7 @@\n                 while True:\n                     now = datetime.utcnow()\n                     then = now.replace(hour=period.hour, minute=10, second=0)\n-                    days = (now.isoweekday() - period.weekday) % 7\n+                    days = (period.weekday - now.isoweekday()) % 7\n                     if days:\n                         then += timedelta(days=days)\n                     seconds = (then - now).total_seconds()\n", "issue": "Cron fails to run\nThe weekly cron for `create_payday_issue` is no longer running successfully at the scheduled time.\n", "before_files": [{"content": "from collections import namedtuple\nfrom datetime import datetime, timedelta\nimport logging\nimport threading\nfrom time import sleep\n\n\nlogger = logging.getLogger('liberapay.cron')\n\n\nDaily = namedtuple('Daily', 'hour')\nWeekly = namedtuple('Weekly', 'weekday hour')\n\n\nclass Cron(object):\n\n    def __init__(self, website):\n        self.website = website\n        self.conn = None\n        self.has_lock = False\n        self.exclusive_jobs = []\n\n    def __call__(self, period, func, exclusive=False):\n        if not self.website.env.run_cron_jobs:\n            return\n        if exclusive and not self.has_lock:\n            self.exclusive_jobs.append((period, func))\n            self._wait_for_lock()\n            return\n        def f():\n            if isinstance(period, Weekly):\n                while True:\n                    now = datetime.utcnow()\n                    then = now.replace(hour=period.hour, minute=10, second=0)\n                    days = (now.isoweekday() - period.weekday) % 7\n                    if days:\n                        then += timedelta(days=days)\n                    seconds = (then - now).total_seconds()\n                    if seconds > 0:\n                        sleep(seconds)\n                    elif seconds < -60:\n                        sleep(86400 * 6)\n                        continue\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                    sleep(86400 * 6)\n            elif isinstance(period, Daily):\n                while True:\n                    now = datetime.utcnow()\n                    then = now.replace(hour=period.hour, minute=5, second=0)\n                    seconds = (then - now).total_seconds()\n                    if seconds > 0:\n                        # later today\n                        sleep(seconds)\n                    elif seconds < -60:\n                        # tomorrow\n                        sleep(3600 * 24 + seconds)\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                        # retry in 5 minutes\n                        sleep(300)\n                    else:\n                        # success, sleep until tomorrow\n                        sleep(3600 * 23)\n            else:\n                assert period >= 1\n                while True:\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                    sleep(period)\n        t = threading.Thread(target=f)\n        t.daemon = True\n        t.start()\n\n    def _wait_for_lock(self):\n        if self.conn:\n            return  # Already waiting\n        self.conn = self.website.db.get_connection().__enter__()\n        def f():\n            cursor = self.conn.cursor()\n            while True:\n                if cursor.one(\"SELECT pg_try_advisory_lock(0)\"):\n                    self.has_lock = True\n                    break\n                sleep(300)\n            for job in self.exclusive_jobs:\n                self(*job, exclusive=True)\n        t = threading.Thread(target=f)\n        t.daemon = True\n        t.start()\n", "path": "liberapay/cron.py"}], "after_files": [{"content": "from collections import namedtuple\nfrom datetime import datetime, timedelta\nimport logging\nimport threading\nfrom time import sleep\n\n\nlogger = logging.getLogger('liberapay.cron')\n\n\nDaily = namedtuple('Daily', 'hour')\nWeekly = namedtuple('Weekly', 'weekday hour')\n\n\nclass Cron(object):\n\n    def __init__(self, website):\n        self.website = website\n        self.conn = None\n        self.has_lock = False\n        self.exclusive_jobs = []\n\n    def __call__(self, period, func, exclusive=False):\n        if not self.website.env.run_cron_jobs or not period:\n            return\n        if exclusive and not self.has_lock:\n            self.exclusive_jobs.append((period, func))\n            self._wait_for_lock()\n            return\n        def f():\n            if isinstance(period, Weekly):\n                while True:\n                    now = datetime.utcnow()\n                    then = now.replace(hour=period.hour, minute=10, second=0)\n                    days = (period.weekday - now.isoweekday()) % 7\n                    if days:\n                        then += timedelta(days=days)\n                    seconds = (then - now).total_seconds()\n                    if seconds > 0:\n                        sleep(seconds)\n                    elif seconds < -60:\n                        sleep(86400 * 6)\n                        continue\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                    sleep(86400 * 6)\n            elif isinstance(period, Daily):\n                while True:\n                    now = datetime.utcnow()\n                    then = now.replace(hour=period.hour, minute=5, second=0)\n                    seconds = (then - now).total_seconds()\n                    if seconds > 0:\n                        # later today\n                        sleep(seconds)\n                    elif seconds < -60:\n                        # tomorrow\n                        sleep(3600 * 24 + seconds)\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                        # retry in 5 minutes\n                        sleep(300)\n                    else:\n                        # success, sleep until tomorrow\n                        sleep(3600 * 23)\n            else:\n                assert period >= 1\n                while True:\n                    try:\n                        func()\n                    except Exception as e:\n                        self.website.tell_sentry(e, {})\n                    sleep(period)\n        t = threading.Thread(target=f)\n        t.daemon = True\n        t.start()\n\n    def _wait_for_lock(self):\n        if self.conn:\n            return  # Already waiting\n        self.conn = self.website.db.get_connection().__enter__()\n        def f():\n            cursor = self.conn.cursor()\n            while True:\n                if cursor.one(\"SELECT pg_try_advisory_lock(0)\"):\n                    self.has_lock = True\n                    break\n                sleep(300)\n            for job in self.exclusive_jobs:\n                self(*job, exclusive=True)\n        t = threading.Thread(target=f)\n        t.daemon = True\n        t.start()\n", "path": "liberapay/cron.py"}]}