Repository: pvlib/pvlib-python (python)

Issue:
Error for `get_pvgis_tmy` with `outputformat='csv'` 
A recent `remote-data` CI run (https://github.com/pvlib/pvlib-python/actions/runs/12470840782/job/34806707387?pr=2258#step:6:78) experienced a test failure:



```python
>       pvgis_data = get_pvgis_tmy(45, 8, outputformat='csv', map_variables=False)

pvlib/tests/iotools/test_pvgis.py:488: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pvlib/iotools/pvgis.py:518: in get_pvgis_tmy
    data, months_selected, inputs, meta = _parse_pvgis_tmy_csv(src)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

src = <_io.BytesIO object at 0x7fe3530318f0>

    def _parse_pvgis_tmy_csv(src):
        # the first 3 rows are latitude, longitude, elevation
        inputs = {}
        # 'Latitude (decimal degrees): 45.000\r\n'
        inputs['latitude'] = float(src.readline().split(b':')[1])
        # 'Longitude (decimal degrees): 8.000\r\n'
        inputs['longitude'] = float(src.readline().split(b':')[1])
        # Elevation (m): 1389.0\r\n
        inputs['elevation'] = float(src.readline().split(b':')[1])
        # then there's a 13 row comma separated table with two columns: month, year
        # which contains the year used for that month in the
        src.readline()  # get "month,year\r\n"
        months_selected = []
        for month in range(12):
            months_selected.append(
>               {'month': month+1, 'year': int(src.readline().split(b',')[1])})
E           ValueError: invalid literal for int() with base 10: b'year\r\n'
```
Seems like PVGIS changed the format of their CSV TMY output.

Note that the default case in `get_pvgis_tmy` (`outputformat='json'`) works without issue, so this issue probably does not affect many users.  

Side note: why do we support all these formats?  Seems like we could simplify that module a lot by choosing whichever one we like best and deprecating the `outputformat` parameter.

Pull request:
Fix `get_pvgis_tmy` for `outputformat='csv'`
 - [x] Closes #2344
 - [x] I am familiar with the [contributing guidelines](https://pvlib-python.readthedocs.io/en/latest/contributing/index.html)
 - [x] Tests added
 - ~[ ] Updates entries in [`docs/sphinx/source/reference`](https://github.com/pvlib/pvlib-python/blob/main/docs/sphinx/source/reference) for API changes.~
 - [x] Adds description and name entries in the appropriate "what's new" file in [`docs/sphinx/source/whatsnew`](https://github.com/pvlib/pvlib-python/tree/main/docs/sphinx/source/whatsnew) for all changes. Includes link to the GitHub Issue with `` :issue:`num` `` or this Pull Request with `` :pull:`num` ``. Includes contributor name and/or GitHub username (link with `` :ghuser:`user` ``).
 - ~[ ] New code is fully documented. Includes [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) compliant docstrings, examples, and comments where necessary.~
 - [x] Pull request is nearly complete and ready for detailed review.
 - [x] Maintainer: Appropriate GitHub Labels (including `remote-data`) and Milestone are assigned to the Pull Request and linked Issue.


Patch:
diff --git a/docs/sphinx/source/whatsnew/v0.11.3.rst b/docs/sphinx/source/whatsnew/v0.11.3.rst
index 81ffcfc..62c7c29 100644
--- a/docs/sphinx/source/whatsnew/v0.11.3.rst
+++ b/docs/sphinx/source/whatsnew/v0.11.3.rst
@@ -31,6 +31,8 @@ Bug fixes
   if/when the time zone is updated, the tz attribute is now the single source
   of time-zone truth, is the single time-zone setter interface, and its getter 
   returns an IANA string. (:issue:`2340`, :pull:`2341`)
+* :py:func:`~pvlib.iotools.get_pvgis_tmy` with ``outputformat='csv'`` now
+  works with the updated data format returned by PVGIS. (:issue:`2344`, :pull:`2395`)
 
 Deprecations
 ~~~~~~~~~~~~
diff --git a/pvlib/iotools/pvgis.py b/pvlib/iotools/pvgis.py
index 3ddc3f6..3ab22c1 100644
--- a/pvlib/iotools/pvgis.py
+++ b/pvlib/iotools/pvgis.py
@@ -559,9 +559,17 @@ def _parse_pvgis_tmy_csv(src):
     inputs['longitude'] = float(src.readline().split(b':')[1])
     # Elevation (m): 1389.0\r\n
     inputs['elevation'] = float(src.readline().split(b':')[1])
+
+    # TMY has an extra line here: Irradiance Time Offset (h): 0.1761\r\n
+    line = src.readline()
+    if line.startswith(b'Irradiance Time Offset'):
+        inputs['irradiance time offset'] = float(line.split(b':')[1])
+        src.readline()  # skip over the "month,year\r\n"
+    else:
+        # `line` is already the "month,year\r\n" line, so nothing to do
+        pass
     # then there's a 13 row comma separated table with two columns: month, year
-    # which contains the year used for that month in the
-    src.readline()  # get "month,year\r\n"
+    # which contains the year used for that month in the TMY
     months_selected = []
     for month in range(12):
         months_selected.append(


Test patch:
diff --git a/tests/data/tmy_45.000_8.000_2005_2023.csv b/tests/data/tmy_45.000_8.000_2005_2023.csv
index 33cb0d5..353008e 100644
--- a/tests/data/tmy_45.000_8.000_2005_2023.csv
+++ b/tests/data/tmy_45.000_8.000_2005_2023.csv
@@ -1,6 +1,7 @@
 Latitude (decimal degrees): 45.000
 Longitude (decimal degrees): 8.000
 Elevation (m): 250.0
+Irradiance Time Offset (h): 0.1761
 month,year
 1,2018
 2,2007
diff --git a/tests/iotools/test_pvgis.py b/tests/iotools/test_pvgis.py
index a8290d7..7c025f4 100644
--- a/tests/iotools/test_pvgis.py
+++ b/tests/iotools/test_pvgis.py
@@ -328,7 +328,8 @@ def month_year_expected():
 @pytest.fixture
 def inputs_expected():
     return {
-        'location': {'latitude': 45.0, 'longitude': 8.0, 'elevation': 250.0},
+        'location': {'latitude': 45.0, 'longitude': 8.0, 'elevation': 250.0,
+                     'irradiance time offset': 0.1761},
         'meteo_data': {
             'radiation_db': 'PVGIS-SARAH3',
             'meteo_db': 'ERA5',
@@ -510,6 +511,9 @@ def _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
     assert inputs['latitude'] == inputs_expected['location']['latitude']
     assert inputs['longitude'] == inputs_expected['location']['longitude']
     assert inputs['elevation'] == inputs_expected['location']['elevation']
+    assert (inputs['irradiance time offset']
+            == inputs_expected['location']['irradiance time offset']
+            )
     for meta_value in meta:
         if not meta_value:
             continue