simple-dwd-weatherforecast 3.0.5__tar.gz → 3.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {simple_dwd_weatherforecast-3.0.5/simple_dwd_weatherforecast.egg-info → simple_dwd_weatherforecast-3.1.6}/PKG-INFO +1 -1
  2. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/setup.py +1 -1
  3. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast/dwdforecast.py +78 -33
  4. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6/simple_dwd_weatherforecast.egg-info}/PKG-INFO +1 -1
  5. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_is_in_timerange.py +9 -0
  6. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/LICENCE +0 -0
  7. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/README.md +0 -0
  8. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/setup.cfg +0 -0
  9. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast/__init__.py +0 -0
  10. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast/dwdmap.py +0 -0
  11. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast/stations.json +0 -0
  12. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast/uv_stations.json +0 -0
  13. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast.egg-info/SOURCES.txt +0 -0
  14. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast.egg-info/dependency_links.txt +0 -0
  15. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast.egg-info/requires.txt +0 -0
  16. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/simple_dwd_weatherforecast.egg-info/top_level.txt +0 -0
  17. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/__init__.py +0 -0
  18. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/dummy_data.py +0 -0
  19. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/dummy_data_full.py +0 -0
  20. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/dummy_uv.py +0 -0
  21. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_daily_avg.py +0 -0
  22. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_daily_condition.py +0 -0
  23. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_daily_max.py +0 -0
  24. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_daily_min.py +0 -0
  25. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_daily_sum.py +0 -0
  26. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_day_values.py +0 -0
  27. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_forecast_condition.py +0 -0
  28. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_forecast_data.py +0 -0
  29. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_station_name.py +0 -0
  30. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_avg.py +0 -0
  31. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_condition.py +0 -0
  32. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_max.py +0 -0
  33. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_min.py +0 -0
  34. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_sum.py +0 -0
  35. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_get_timeframe_values.py +0 -0
  36. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_is_valid_timeframe.py +0 -0
  37. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_location_tools.py +0 -0
  38. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_map.py +0 -0
  39. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_parsekml.py +0 -0
  40. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_region.py +0 -0
  41. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_reported_weather.py +0 -0
  42. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_station.py +0 -0
  43. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_stationsfile.py +0 -0
  44. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_update.py +0 -0
  45. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_update_hourly.py +0 -0
  46. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_uv_index.py +0 -0
  47. {simple_dwd_weatherforecast-3.0.5 → simple_dwd_weatherforecast-3.1.6}/tests/test_weather.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_dwd_weatherforecast
3
- Version: 3.0.5
3
+ Version: 3.1.6
4
4
  Summary: A simple tool to retrieve a weather forecast from DWD OpenData
5
5
  Home-page: https://github.com/FL550/simple_dwd_weatherforecast.git
6
6
  Author: Max Fermor
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="simple_dwd_weatherforecast",
8
- version="v3.0.5",
8
+ version="v3.1.6",
9
9
  author="Max Fermor",
10
10
  description="A simple tool to retrieve a weather forecast from DWD OpenData",
11
11
  long_description=long_description,
@@ -300,27 +300,44 @@ class Weather:
300
300
  return self.station["name"] # type: ignore
301
301
 
302
302
  def is_in_timerange(self, timestamp: datetime):
303
+ # Convert timestamp to UTC for comparison with forecast data keys
304
+ timestamp_utc = (
305
+ timestamp.astimezone(timezone.utc)
306
+ if timestamp.tzinfo
307
+ else timestamp.replace(tzinfo=timezone.utc)
308
+ )
303
309
  return (
304
310
  list(self.forecast_data.keys())[0] # type: ignore
305
- <= self.strip_to_hour_str(timestamp)
311
+ <= self.strip_to_hour_str(timestamp_utc)
306
312
  <= list(self.forecast_data.keys())[-1] # type: ignore
307
313
  )
308
314
 
309
315
  def is_in_timerange_day(self, timestamp: datetime):
316
+ # Get the caller's timezone
317
+ caller_tz = timestamp.tzinfo or timezone.utc
318
+
319
+ # Convert forecast data boundaries to caller's timezone for comparison
320
+ first_entry_utc = arrow.get(
321
+ list(self.forecast_data.keys())[0], # type: ignore
322
+ "YYYY-MM-DDTHH:mm:ss.SSSZ", # type: ignore
323
+ ).datetime
324
+ last_entry_utc = arrow.get(
325
+ list(self.forecast_data.keys())[-1], # type: ignore
326
+ "YYYY-MM-DDTHH:mm:ss.SSSZ", # type: ignore
327
+ ).datetime
328
+
329
+ first_entry_local = first_entry_utc.astimezone(caller_tz)
330
+ last_entry_local = last_entry_utc.astimezone(caller_tz)
331
+ timestamp_local = (
332
+ timestamp.astimezone(caller_tz)
333
+ if timestamp.tzinfo
334
+ else timestamp.replace(tzinfo=caller_tz)
335
+ )
336
+
310
337
  return (
311
- self.strip_to_day(
312
- arrow.get(
313
- list(self.forecast_data.keys())[0], # type: ignore
314
- "YYYY-MM-DDTHH:mm:ss.SSSZ", # type: ignore
315
- ).datetime
316
- )
317
- <= self.strip_to_day(timestamp)
318
- <= self.strip_to_day(
319
- arrow.get(
320
- list(self.forecast_data.keys())[-1], # type: ignore
321
- "YYYY-MM-DDTHH:mm:ss.SSSZ", # type: ignore
322
- ).datetime
323
- )
338
+ self.strip_to_day(first_entry_local)
339
+ <= self.strip_to_day(timestamp_local)
340
+ <= self.strip_to_day(last_entry_local)
324
341
  )
325
342
 
326
343
  def is_valid_timeframe(self, timeframe: int) -> bool:
@@ -339,7 +356,12 @@ class Weather:
339
356
  if shouldUpdate:
340
357
  self.update()
341
358
  if self.is_in_timerange(timestamp):
342
- return self.forecast_data[self.strip_to_hour_str(timestamp)][ # type: ignore
359
+ timestamp_utc = (
360
+ timestamp.astimezone(timezone.utc)
361
+ if timestamp.tzinfo
362
+ else timestamp.replace(tzinfo=timezone.utc)
363
+ )
364
+ return self.forecast_data[self.strip_to_hour_str(timestamp_utc)][ # type: ignore
343
365
  weatherDataType.value[0]
344
366
  ]
345
367
  return None
@@ -349,9 +371,14 @@ class Weather:
349
371
  self.update()
350
372
 
351
373
  if self.is_in_timerange(timestamp):
374
+ timestamp_utc = (
375
+ timestamp.astimezone(timezone.utc)
376
+ if timestamp.tzinfo
377
+ else timestamp.replace(tzinfo=timezone.utc)
378
+ )
352
379
  return str(
353
380
  self.weather_codes[
354
- self.forecast_data[self.strip_to_hour_str(timestamp)][ # type: ignore
381
+ self.forecast_data[self.strip_to_hour_str(timestamp_utc)][ # type: ignore
355
382
  WeatherDataType.CONDITION.value[0]
356
383
  ]
357
384
  ][0]
@@ -610,7 +637,13 @@ class Weather:
610
637
  def get_timeframe_values(self, timestamp: datetime, timeframe: int):
611
638
  "timestamp has to be checked prior to be in timerange"
612
639
  result = []
613
- time_step = self.strip_to_hour(timestamp)
640
+ # Convert to UTC for internal operations since forecast data is stored in UTC
641
+ timestamp_utc = (
642
+ timestamp.astimezone(timezone.utc)
643
+ if timestamp.tzinfo
644
+ else timestamp.replace(tzinfo=timezone.utc)
645
+ )
646
+ time_step = self.strip_to_hour(timestamp_utc)
614
647
  for _ in range(timeframe):
615
648
  hour_str = self.strip_to_hour_str(time_step)
616
649
  time_step += timedelta(hours=1)
@@ -626,29 +659,41 @@ class Weather:
626
659
  next(iter(self.forecast_data)), # type: ignore
627
660
  "YYYY-MM-DDTHH:mm:ss.SSSZ", # type: ignore
628
661
  ).datetime # type: ignore
629
- if timestamp.day != first_entry_date.day:
630
- time_step = self.strip_to_day(timestamp)
662
+
663
+ # Convert to caller's timezone for comparison
664
+ caller_tz = timestamp.tzinfo or timezone.utc
665
+ first_entry_local = first_entry_date.astimezone(caller_tz)
666
+ timestamp_local = (
667
+ timestamp.astimezone(caller_tz)
668
+ if timestamp.tzinfo
669
+ else timestamp.replace(tzinfo=caller_tz)
670
+ )
671
+
672
+ if timestamp_local.date() != first_entry_local.date():
673
+ # Start from midnight in caller's timezone and convert to UTC for lookup
674
+ time_step_local = timestamp_local.replace(
675
+ hour=0, minute=0, second=0, microsecond=0
676
+ )
631
677
  for _ in range(24):
632
- hour_str = self.strip_to_hour_str(time_step)
678
+ time_step_utc = time_step_local.astimezone(timezone.utc)
679
+ hour_str = self.strip_to_hour_str(time_step_utc)
633
680
  if hour_str not in self.forecast_data: # type: ignore
634
681
  break
635
682
  result.append(self.forecast_data[hour_str]) # type: ignore
636
- time_step += timedelta(hours=1)
683
+ time_step_local += timedelta(hours=1)
637
684
  else:
685
+ # Use the first entry date and calculate until end of day in caller's timezone
638
686
  time_step = first_entry_date
639
- endtime = datetime(
640
- time_step.year,
641
- time_step.month,
642
- time_step.day,
643
- 0,
644
- 0,
645
- 0,
646
- 0,
647
- timezone.utc,
687
+ endtime_local = first_entry_local.replace(
688
+ hour=0, minute=0, second=0, microsecond=0
648
689
  ) + timedelta(days=1)
649
- timediff = endtime - time_step
650
- for _ in range(round(timediff.total_seconds() / 3600)):
651
- result.append(self.forecast_data[self.strip_to_hour_str(time_step)]) # type: ignore
690
+ endtime_utc = endtime_local.astimezone(timezone.utc)
691
+
692
+ while time_step < endtime_utc:
693
+ hour_str = self.strip_to_hour_str(time_step)
694
+ if hour_str not in self.forecast_data: # type: ignore
695
+ break
696
+ result.append(self.forecast_data[hour_str]) # type: ignore
652
697
  time_step += timedelta(hours=1)
653
698
  return result
654
699
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_dwd_weatherforecast
3
- Version: 3.0.5
3
+ Version: 3.1.6
4
4
  Summary: A simple tool to retrieve a weather forecast from DWD OpenData
5
5
  Home-page: https://github.com/FL550/simple_dwd_weatherforecast.git
6
6
  Author: Max Fermor
@@ -1,5 +1,6 @@
1
1
  import unittest
2
2
  from datetime import datetime
3
+ from zoneinfo import ZoneInfo
3
4
  from simple_dwd_weatherforecast import dwdforecast
4
5
  from dummy_data import parsed_data
5
6
 
@@ -32,3 +33,11 @@ class Weather_is_in_timerange(unittest.TestCase):
32
33
  def test_is_in_timerange_day_future(self):
33
34
  test_time = datetime(2102, 11, 5, 7, 30)
34
35
  self.assertFalse(self.dwd_weather.is_in_timerange(test_time))
36
+
37
+ def test_is_in_timerange_timezone(self):
38
+ test_time = datetime(2020, 11, 6, 5, 0, tzinfo=ZoneInfo("Europe/Berlin"))
39
+ self.assertTrue(self.dwd_weather.is_in_timerange(test_time))
40
+
41
+ def test_is_not_in_timerange_timezone(self):
42
+ test_time = datetime(2020, 11, 6, 4, 0, tzinfo=ZoneInfo("Europe/Berlin"))
43
+ self.assertFalse(self.dwd_weather.is_in_timerange(test_time))