We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
_tsf_reader.convert_base doesn't handle "10 minutes" frequency correctly
## Description
For Monash datasets with the "10 minutes" frequency, the frequency converter will convert it to a frequency 10 MonthEnd (10M), instead of the expect 10 Minutes (10T) frequency.

There is already code to properly handle the "minutely" frequency, but it checks for that string explicitly, so it doesn't catch the "10 minutes" case.

## To Reproduce
One dataset which has this frequency is the 10 minutes observation Solar dataset: https://zenodo.org/record/4656144
filename: `"solar_10_minutes_dataset.zip"`
record: `"4656132"`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/gluonts/dataset/repository/_tsf_reader.py`
Content:
```
1 # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License").
4 # You may not use this file except in compliance with the License.
5 # A copy of the License is located at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # or in the "license" file accompanying this file. This file is distributed
10 # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11 # express or implied. See the License for the specific language governing
12 # permissions and limitations under the License.
13 
14 from datetime import datetime
15 from distutils.util import strtobool
16 from multiprocessing import cpu_count
17 from types import SimpleNamespace
18 
19 import numpy as np
20 from toolz import compose_left
21 
22 from gluonts import json
23 from gluonts.nursery import glide
24 
25 parse_bool = compose_left(strtobool, bool)
26 
27 
28 def parse_attribute(ty, value: str):
29     if ty == "numeric":
30         return int(value)
31 
32     if ty == "string":
33         return value
34 
35     if ty == "date":
36         return datetime.strptime(value, "%Y-%m-%d %H-%M-%S")
37 
38     raise AttributeError(ty)
39 
40 
41 def frequency_converter(freq: str):
42     parts = freq.split("_")
43     if len(parts) == 1:
44         return convert_base(parts[0])
45     if len(parts) == 2:
46         return convert_multiple(parts[0]) + convert_base(parts[1])
47     raise ValueError(f"Invalid frequency string {freq}.")
48 
49 
50 def convert_base(text: str) -> str:
51     if text.lower() == "minutely":
52         return "T"
53     return text[0].upper()
54 
55 
56 def convert_multiple(text: str) -> str:
57     if text.isnumeric():
58         return text
59     if text == "half":
60         return "0.5"
61     raise ValueError(f"Unknown frequency multiple {text}.")
62 
63 
64 class TSFReader:
65     def __init__(
66         self,
67         path,
68         target_name="target",
69     ):
70         self.path = path
71         self.target_name = target_name
72 
73         self.meta = SimpleNamespace(columns={})
74 
75     def read(self):
76         with open(self.path, encoding="latin1") as in_file:
77             # strip whitespace
78             lines = map(str.strip, in_file)
79 
80             # ignore all lines starting with #
81             lines = filter(lambda line: not line.startswith("#"), lines)
82 
83             data_tag_found = self._read_header(lines)
84             assert data_tag_found, "Missing @data tag."
85             assert (
86                 self.meta.columns
87             ), "Missing attribute section. Attribute section must come before data."
88 
89             assert self.target_name not in self.meta.columns
90             self.meta.columns[self.target_name] = None
91 
92             data = self._read_data_section(lines)
93 
94             return self.meta, data
95 
96     def _read_header(self, lines):
97         for line in lines:
98             assert line.startswith("@")
99             stop = self._tag(line[1:])
100 
101             if stop:
102                 return True
103 
104         return False
105 
106     def _read_data_section(self, lines):
107         lines = list(lines)
108 
109         lines = glide.imap_unordered(
110             self._read_data, lines, num_workers=cpu_count(), batch_size=8092
111         )
112 
113         return list(lines)
114 
115     def _read_data(self, line):
116         parts = line.split(":")
117 
118         assert len(parts) == len(
119             self.meta.columns
120         ), "Missing attributes/values in series."
121 
122         *attributes, target = parts
123 
124         record = {}
125 
126         record[self.target_name] = self._data_target(target)
127 
128         for (column, ty), attr in zip(self.meta.columns.items(), attributes):
129             record[column] = parse_attribute(ty, attr)
130 
131         return record
132 
133     def _data_target(self, s):
134         s = s.replace("?", '"nan"')
135 
136         values = json.loads(f"[{s}]")
137         assert (
138             values
139         ), "A given series should contains a set of comma separated numeric values. At least one numeric value should be there in a series. Missing values should be indicated with ? symbol"
140 
141         return np.array(values, dtype=float)
142 
143     def _tag(self, line):
144         fn_by_tag = {
145             "attribute": self._tag_attribute,
146             "frequency": self._tag_frequency,
147             "horizon": self._tag_horizon,
148             "missing": self._tag_missing,
149             "equallength": self._tag_equallength,
150             "data": self._tag_data,
151         }
152         tag, *args = line.split(" ")
153 
154         if tag not in fn_by_tag:
155             return
156 
157         return fn_by_tag[tag](*args)
158 
159     def _tag_attribute(self, name, ty):
160         self.meta.columns[name] = ty
161 
162     def _tag_frequency(self, frequency):
163         self.meta.frequency = frequency
164 
165     def _tag_horizon(self, horizon):
166         self.meta.forecast_horizon = horizon
167 
168     def _tag_missing(self, missing):
169         self.meta.has_missing_values = parse_bool(missing)
170 
171     def _tag_equallength(self, equallength):
172         self.meta.has_equal_length = parse_bool(equallength)
173 
174     def _tag_data(self):
175         return True
176
```
--- 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/src/gluonts/dataset/repository/_tsf_reader.py b/src/gluonts/dataset/repository/_tsf_reader.py
--- a/src/gluonts/dataset/repository/_tsf_reader.py
+++ b/src/gluonts/dataset/repository/_tsf_reader.py
@@ -15,11 +15,13 @@
 from distutils.util import strtobool
 from multiprocessing import cpu_count
 from types import SimpleNamespace
+from typing import Dict
 
 import numpy as np
 from toolz import compose_left
 
 from gluonts import json
+from gluonts.exceptions import GluonTSDataError
 from gluonts.nursery import glide
 
 parse_bool = compose_left(strtobool, bool)
@@ -47,10 +49,32 @@
     raise ValueError(f"Invalid frequency string {freq}.")
 
 
+BASE_FREQ_TO_PANDAS_OFFSET: Dict[str, str] = {
+    "seconds": "S",
+    "minutely": "T",
+    "minutes": "T",
+    "hourly": "H",
+    "hours": "H",
+    "daily": "D",
+    "days": "D",
+    "weekly": "W",
+    "weeks": "W",
+    "monthly": "M",
+    "months": "M",
+    "quarterly": "Q",
+    "quarters": "Q",
+    "yearly": "Y",
+    "years": "Y",
+}
+
+
 def convert_base(text: str) -> str:
-    if text.lower() == "minutely":
-        return "T"
-    return text[0].upper()
+    try:
+        return BASE_FREQ_TO_PANDAS_OFFSET[text]
+    except KeyError:
+        raise GluonTSDataError(
+            f'"{text}" is not recognized as a frequency string'
+        )
 
 
 def convert_multiple(text: str) -> str:

{"golden_diff": "diff --git a/src/gluonts/dataset/repository/_tsf_reader.py b/src/gluonts/dataset/repository/_tsf_reader.py\n--- a/src/gluonts/dataset/repository/_tsf_reader.py\n+++ b/src/gluonts/dataset/repository/_tsf_reader.py\n@@ -15,11 +15,13 @@\n from distutils.util import strtobool\n from multiprocessing import cpu_count\n from types import SimpleNamespace\n+from typing import Dict\n \n import numpy as np\n from toolz import compose_left\n \n from gluonts import json\n+from gluonts.exceptions import GluonTSDataError\n from gluonts.nursery import glide\n \n parse_bool = compose_left(strtobool, bool)\n@@ -47,10 +49,32 @@\n     raise ValueError(f\"Invalid frequency string {freq}.\")\n \n \n+BASE_FREQ_TO_PANDAS_OFFSET: Dict[str, str] = {\n+    \"seconds\": \"S\",\n+    \"minutely\": \"T\",\n+    \"minutes\": \"T\",\n+    \"hourly\": \"H\",\n+    \"hours\": \"H\",\n+    \"daily\": \"D\",\n+    \"days\": \"D\",\n+    \"weekly\": \"W\",\n+    \"weeks\": \"W\",\n+    \"monthly\": \"M\",\n+    \"months\": \"M\",\n+    \"quarterly\": \"Q\",\n+    \"quarters\": \"Q\",\n+    \"yearly\": \"Y\",\n+    \"years\": \"Y\",\n+}\n+\n+\n def convert_base(text: str) -> str:\n-    if text.lower() == \"minutely\":\n-        return \"T\"\n-    return text[0].upper()\n+    try:\n+        return BASE_FREQ_TO_PANDAS_OFFSET[text]\n+    except KeyError:\n+        raise GluonTSDataError(\n+            f'\"{text}\" is not recognized as a frequency string'\n+        )\n \n \n def convert_multiple(text: str) -> str:\n", "issue": "_tsf_reader.convert_base doesn't handle \"10 minutes\" frequency correctly\n## Description\r\nFor Monash datasets with the \"10 minutes\" frequency, the frequency converter will convert it to a frequency 10 MonthEnd (10M), instead of the expect 10 Minutes (10T) frequency.\r\n\r\nThere is already code to properly handle the \"minutely\" frequency, but it checks for that string explicitly, so it doesn't catch the \"10 minutes\" case.\r\n\r\n## To Reproduce\r\nOne dataset which has this frequency is the 10 minutes observation Solar dataset: https://zenodo.org/record/4656144\r\nfilename: `\"solar_10_minutes_dataset.zip\"`\r\nrecord: `\"4656132\"`\n", "before_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\").\n# You may not use this file except in compliance with the License.\n# A copy of the License is located at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# or in the \"license\" file accompanying this file. This file is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n# express or implied. See the License for the specific language governing\n# permissions and limitations under the License.\n\nfrom datetime import datetime\nfrom distutils.util import strtobool\nfrom multiprocessing import cpu_count\nfrom types import SimpleNamespace\n\nimport numpy as np\nfrom toolz import compose_left\n\nfrom gluonts import json\nfrom gluonts.nursery import glide\n\nparse_bool = compose_left(strtobool, bool)\n\n\ndef parse_attribute(ty, value: str):\n    if ty == \"numeric\":\n        return int(value)\n\n    if ty == \"string\":\n        return value\n\n    if ty == \"date\":\n        return datetime.strptime(value, \"%Y-%m-%d %H-%M-%S\")\n\n    raise AttributeError(ty)\n\n\ndef frequency_converter(freq: str):\n    parts = freq.split(\"_\")\n    if len(parts) == 1:\n        return convert_base(parts[0])\n    if len(parts) == 2:\n        return convert_multiple(parts[0]) + convert_base(parts[1])\n    raise ValueError(f\"Invalid frequency string {freq}.\")\n\n\ndef convert_base(text: str) -> str:\n    if text.lower() == \"minutely\":\n        return \"T\"\n    return text[0].upper()\n\n\ndef convert_multiple(text: str) -> str:\n    if text.isnumeric():\n        return text\n    if text == \"half\":\n        return \"0.5\"\n    raise ValueError(f\"Unknown frequency multiple {text}.\")\n\n\nclass TSFReader:\n    def __init__(\n        self,\n        path,\n        target_name=\"target\",\n    ):\n        self.path = path\n        self.target_name = target_name\n\n        self.meta = SimpleNamespace(columns={})\n\n    def read(self):\n        with open(self.path, encoding=\"latin1\") as in_file:\n            # strip whitespace\n            lines = map(str.strip, in_file)\n\n            # ignore all lines starting with #\n            lines = filter(lambda line: not line.startswith(\"#\"), lines)\n\n            data_tag_found = self._read_header(lines)\n            assert data_tag_found, \"Missing @data tag.\"\n            assert (\n                self.meta.columns\n            ), \"Missing attribute section. Attribute section must come before data.\"\n\n            assert self.target_name not in self.meta.columns\n            self.meta.columns[self.target_name] = None\n\n            data = self._read_data_section(lines)\n\n            return self.meta, data\n\n    def _read_header(self, lines):\n        for line in lines:\n            assert line.startswith(\"@\")\n            stop = self._tag(line[1:])\n\n            if stop:\n                return True\n\n        return False\n\n    def _read_data_section(self, lines):\n        lines = list(lines)\n\n        lines = glide.imap_unordered(\n            self._read_data, lines, num_workers=cpu_count(), batch_size=8092\n        )\n\n        return list(lines)\n\n    def _read_data(self, line):\n        parts = line.split(\":\")\n\n        assert len(parts) == len(\n            self.meta.columns\n        ), \"Missing attributes/values in series.\"\n\n        *attributes, target = parts\n\n        record = {}\n\n        record[self.target_name] = self._data_target(target)\n\n        for (column, ty), attr in zip(self.meta.columns.items(), attributes):\n            record[column] = parse_attribute(ty, attr)\n\n        return record\n\n    def _data_target(self, s):\n        s = s.replace(\"?\", '\"nan\"')\n\n        values = json.loads(f\"[{s}]\")\n        assert (\n            values\n        ), \"A given series should contains a set of comma separated numeric values. At least one numeric value should be there in a series. Missing values should be indicated with ? symbol\"\n\n        return np.array(values, dtype=float)\n\n    def _tag(self, line):\n        fn_by_tag = {\n            \"attribute\": self._tag_attribute,\n            \"frequency\": self._tag_frequency,\n            \"horizon\": self._tag_horizon,\n            \"missing\": self._tag_missing,\n            \"equallength\": self._tag_equallength,\n            \"data\": self._tag_data,\n        }\n        tag, *args = line.split(\" \")\n\n        if tag not in fn_by_tag:\n            return\n\n        return fn_by_tag[tag](*args)\n\n    def _tag_attribute(self, name, ty):\n        self.meta.columns[name] = ty\n\n    def _tag_frequency(self, frequency):\n        self.meta.frequency = frequency\n\n    def _tag_horizon(self, horizon):\n        self.meta.forecast_horizon = horizon\n\n    def _tag_missing(self, missing):\n        self.meta.has_missing_values = parse_bool(missing)\n\n    def _tag_equallength(self, equallength):\n        self.meta.has_equal_length = parse_bool(equallength)\n\n    def _tag_data(self):\n        return True\n", "path": "src/gluonts/dataset/repository/_tsf_reader.py"}], "after_files": [{"content": "# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\").\n# You may not use this file except in compliance with the License.\n# A copy of the License is located at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# or in the \"license\" file accompanying this file. This file is distributed\n# on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n# express or implied. See the License for the specific language governing\n# permissions and limitations under the License.\n\nfrom datetime import datetime\nfrom distutils.util import strtobool\nfrom multiprocessing import cpu_count\nfrom types import SimpleNamespace\nfrom typing import Dict\n\nimport numpy as np\nfrom toolz import compose_left\n\nfrom gluonts import json\nfrom gluonts.exceptions import GluonTSDataError\nfrom gluonts.nursery import glide\n\nparse_bool = compose_left(strtobool, bool)\n\n\ndef parse_attribute(ty, value: str):\n    if ty == \"numeric\":\n        return int(value)\n\n    if ty == \"string\":\n        return value\n\n    if ty == \"date\":\n        return datetime.strptime(value, \"%Y-%m-%d %H-%M-%S\")\n\n    raise AttributeError(ty)\n\n\ndef frequency_converter(freq: str):\n    parts = freq.split(\"_\")\n    if len(parts) == 1:\n        return convert_base(parts[0])\n    if len(parts) == 2:\n        return convert_multiple(parts[0]) + convert_base(parts[1])\n    raise ValueError(f\"Invalid frequency string {freq}.\")\n\n\nBASE_FREQ_TO_PANDAS_OFFSET: Dict[str, str] = {\n    \"seconds\": \"S\",\n    \"minutely\": \"T\",\n    \"minutes\": \"T\",\n    \"hourly\": \"H\",\n    \"hours\": \"H\",\n    \"daily\": \"D\",\n    \"days\": \"D\",\n    \"weekly\": \"W\",\n    \"weeks\": \"W\",\n    \"monthly\": \"M\",\n    \"months\": \"M\",\n    \"quarterly\": \"Q\",\n    \"quarters\": \"Q\",\n    \"yearly\": \"Y\",\n    \"years\": \"Y\",\n}\n\n\ndef convert_base(text: str) -> str:\n    try:\n        return BASE_FREQ_TO_PANDAS_OFFSET[text]\n    except KeyError:\n        raise GluonTSDataError(\n            f'\"{text}\" is not recognized as a frequency string'\n        )\n\n\ndef convert_multiple(text: str) -> str:\n    if text.isnumeric():\n        return text\n    if text == \"half\":\n        return \"0.5\"\n    raise ValueError(f\"Unknown frequency multiple {text}.\")\n\n\nclass TSFReader:\n    def __init__(\n        self,\n        path,\n        target_name=\"target\",\n    ):\n        self.path = path\n        self.target_name = target_name\n\n        self.meta = SimpleNamespace(columns={})\n\n    def read(self):\n        with open(self.path, encoding=\"latin1\") as in_file:\n            # strip whitespace\n            lines = map(str.strip, in_file)\n\n            # ignore all lines starting with #\n            lines = filter(lambda line: not line.startswith(\"#\"), lines)\n\n            data_tag_found = self._read_header(lines)\n            assert data_tag_found, \"Missing @data tag.\"\n            assert (\n                self.meta.columns\n            ), \"Missing attribute section. Attribute section must come before data.\"\n\n            assert self.target_name not in self.meta.columns\n            self.meta.columns[self.target_name] = None\n\n            data = self._read_data_section(lines)\n\n            return self.meta, data\n\n    def _read_header(self, lines):\n        for line in lines:\n            assert line.startswith(\"@\")\n            stop = self._tag(line[1:])\n\n            if stop:\n                return True\n\n        return False\n\n    def _read_data_section(self, lines):\n        lines = list(lines)\n\n        lines = glide.imap_unordered(\n            self._read_data, lines, num_workers=cpu_count(), batch_size=8092\n        )\n\n        return list(lines)\n\n    def _read_data(self, line):\n        parts = line.split(\":\")\n\n        assert len(parts) == len(\n            self.meta.columns\n        ), \"Missing attributes/values in series.\"\n\n        *attributes, target = parts\n\n        record = {}\n\n        record[self.target_name] = self._data_target(target)\n\n        for (column, ty), attr in zip(self.meta.columns.items(), attributes):\n            record[column] = parse_attribute(ty, attr)\n\n        return record\n\n    def _data_target(self, s):\n        s = s.replace(\"?\", '\"nan\"')\n\n        values = json.loads(f\"[{s}]\")\n        assert (\n            values\n        ), \"A given series should contains a set of comma separated numeric values. At least one numeric value should be there in a series. Missing values should be indicated with ? symbol\"\n\n        return np.array(values, dtype=float)\n\n    def _tag(self, line):\n        fn_by_tag = {\n            \"attribute\": self._tag_attribute,\n            \"frequency\": self._tag_frequency,\n            \"horizon\": self._tag_horizon,\n            \"missing\": self._tag_missing,\n            \"equallength\": self._tag_equallength,\n            \"data\": self._tag_data,\n        }\n        tag, *args = line.split(\" \")\n\n        if tag not in fn_by_tag:\n            return\n\n        return fn_by_tag[tag](*args)\n\n    def _tag_attribute(self, name, ty):\n        self.meta.columns[name] = ty\n\n    def _tag_frequency(self, frequency):\n        self.meta.frequency = frequency\n\n    def _tag_horizon(self, horizon):\n        self.meta.forecast_horizon = horizon\n\n    def _tag_missing(self, missing):\n        self.meta.has_missing_values = parse_bool(missing)\n\n    def _tag_equallength(self, equallength):\n        self.meta.has_equal_length = parse_bool(equallength)\n\n    def _tag_data(self):\n        return True\n", "path": "src/gluonts/dataset/repository/_tsf_reader.py"}]}