Repository: zulip/zulip

Issue:
Invalid timezone offset in `<time>` picker leads to 500
A message containing:
```
<time:1969-12-31T00:00:00+32:00>
```
..fails to send:
```
2021-09-01 20:41:56.250 ERR  [] Exception in Markdown parser; input (sanitized) was: '<xxxx:xxxx-xx-xxxxx:xx:xx+xx:xx>'
 (message unknown)
Traceback (most recent call last):
  File "/srv/zulip/zerver/lib/markdown/__init__.py", line 2466, in do_convert
    rendering_result.rendered_content = timeout(5, lambda: _md_engine.convert(content))
  File "/srv/zulip/zerver/lib/timeout.py", line 94, in timeout
    raise thread.exc_info[1].with_traceback(thread.exc_info[2])
  File "/srv/zulip/zerver/lib/timeout.py", line 53, in run
    self.result = func()
  File "/srv/zulip/zerver/lib/markdown/__init__.py", line 2466, in <lambda>
    rendering_result.rendered_content = timeout(5, lambda: _md_engine.convert(content))
  File "/srv/zulip-py3-venv/lib/python3.6/site-packages/markdown/core.py", line 268, in convert
    newRoot = treeprocessor.run(root)
  File "/srv/zulip-py3-venv/lib/python3.6/site-packages/markdown/treeprocessors.py", line 371, in run
    self.__handleInline(text), child
  File "/srv/zulip-py3-venv/lib/python3.6/site-packages/markdown/treeprocessors.py", line 132, in __handleInline
    self.inlinePatterns[patternIndex], data, patternIndex, startIndex
  File "/srv/zulip-py3-venv/lib/python3.6/site-packages/markdown/treeprocessors.py", line 286, in __applyPattern
    node = pattern.handleMatch(match)
  File "/srv/zulip/zerver/lib/markdown/__init__.py", line 1361, in handleMatch
    timestamp = timestamp.astimezone(datetime.timezone.utc)
ValueError: offset must be a timedelta strictly between -timedelta(hours=24) and timedelta(hours=24).
```

We should handle this more gracefully than a 500 error -- ideally, by not treating it as a smart timepicker, but just text.


Discussion:
Hello @zulip/server-dependencies, @zulip/server-markdown members, this issue was labeled with the "area: markdown", "area: dependencies" labels, so you may want to check it out!

<!-- areaLabelAddition -->

@chdinesh1089 @akshatdalton would either of you be interested in working on this?

Tagging as a priority because the markdown processor should never 500.
I might not be able to start working on this for a few days due to some other immediately pressing tasks. Will claim the issue when I get time to start working on this. I think it is fine if @akshatdalton or others interested work on this before I get a chance.
@zulipbot claim
Hello @jaiongit!

Thanks for your interest in Zulip! You have attempted to claim an issue without the labels "help wanted", "good first issue". Since you're a new contributor, you can only claim and submit pull requests for issues with the [help wanted](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+no%3Aassignee+label%3A%22help+wanted%22) or [good first issue](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+no%3Aassignee+label%3A%22good+first+issue%22) labels.

If this is your first time here, we recommend reading our [guide for new contributors](https://zulip.readthedocs.io/en/latest/overview/contributing.html) before getting started.

@zulipbot claim
Welcome to Zulip, @jaiongit! We just sent you an invite to collaborate on this repository at https://github.com/zulip/zulip/invitations. Please accept this invite in order to claim this issue and begin a fun, rewarding experience contributing to Zulip!

Here's some tips to get you off to a good start:

- Join me on the [Zulip developers' server](https://chat.zulip.org), to get help, chat about this issue, and meet the other developers.
- [Unwatch this repository](https://help.github.com/articles/unwatching-repositories/), so that you don't get 100 emails a day.

As you work on this issue, you'll also want to refer to the [Zulip code contribution guide](https://zulip.readthedocs.io/en/latest/contributing/index.html), as well as the rest of the developer documentation on that site.

See you on the other side (that is, the pull request side)!

Hello @jaiongit, you have been unassigned from this issue because you have not updated this issue or any referenced pull requests for over 14 days.

You can reclaim this issue or claim any other issue by commenting `@zulipbot claim` on that issue.

Thanks for your contributions, and hope to see you again soon!

I am working on it.
@timabbott We can't really do anything in this case (except putting try and except block or calling the `__str__` method which won't look good). The problem is in dateutil library which doesn't raise an error in the constructor. I have opened the issue here: https://github.com/dateutil/dateutil/issues/1231.
What would the try/except block option look like? 
@zulipbot claim
Welcome to Zulip, @tagyieh! We just sent you an invite to collaborate on this repository at https://github.com/zulip/zulip/invitations. Please accept this invite in order to claim this issue and begin a fun, rewarding experience contributing to Zulip!

Here's some tips to get you off to a good start:

- Join me on the [Zulip developers' server](https://chat.zulip.org), to get help, chat about this issue, and meet the other developers.
- [Unwatch this repository](https://help.github.com/articles/unwatching-repositories/), so that you don't get 100 emails a day.

As you work on this issue, you'll also want to refer to the [Zulip code contribution guide](https://zulip.readthedocs.io/en/latest/contributing/index.html), as well as the rest of the developer documentation on that site.

See you on the other side (that is, the pull request side)!

@tagyieh You have been unassigned from this issue because you have not made any updates for over 14 days. Please feel free to reclaim the issue if you decide to pick up again. Thanks!


Resolving patch:
diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py
--- a/zerver/lib/markdown/__init__.py
+++ b/zerver/lib/markdown/__init__.py
@@ -1417,7 +1417,15 @@ def handleMatch(self, match: Match[str]) -> Optional[Element]:
         # Use HTML5 <time> element for valid timestamps.
         time_element = Element("time")
         if timestamp.tzinfo:
-            timestamp = timestamp.astimezone(timezone.utc)
+            try:
+                timestamp = timestamp.astimezone(timezone.utc)
+            except ValueError:
+                error_element = Element("span")
+                error_element.set("class", "timestamp-error")
+                error_element.text = markdown.util.AtomicString(
+                    f"Invalid time format: {time_input_string}"
+                )
+                return error_element
         else:
             timestamp = timestamp.replace(tzinfo=timezone.utc)
         time_element.set("datetime", timestamp.isoformat().replace("+00:00", "Z"))


Test patch:
diff --git a/zerver/tests/fixtures/markdown_test_cases.json b/zerver/tests/fixtures/markdown_test_cases.json
--- a/zerver/tests/fixtures/markdown_test_cases.json
+++ b/zerver/tests/fixtures/markdown_test_cases.json
@@ -838,6 +838,13 @@
       "marked_expected_output": "<p><span>**hello world**</span></p>",
       "text_content": "Invalid time format: **hello world**"
     },
+    {
+      "name": "timestamp_invalid_timezone",
+      "input": "<time:1969-12-31T00:00:00+32:00>",
+      "expected_output": "<p><span class=\"timestamp-error\">Invalid time format: 1969-12-31T00:00:00+32:00</span></p>",
+      "marked_expected_output": "<p><span>1969-12-31T00:00:00+32:00</span></p>",
+      "text_content": "Invalid time format: 1969-12-31T00:00:00+32:00"
+    },
     {
       "name": "timestamp_unix",
       "input": "Let's meet at <time:1496701800>.",