From d160d7a21c70131bee5283c21edf74fe059c85c7 Mon Sep 17 00:00:00 2001
From: Ernesto Rico Schmidt <ernesto@helsinki.at>
Date: Mon, 14 Feb 2022 12:46:48 -0400
Subject: [PATCH] Fix datetime usage

---
 program/management/commands/addnote.py        |  4 +--
 program/management/commands/export_showlog.py |  4 +--
 program/models.py                             | 16 +++++-----
 program/serializers.py                        |  2 +-
 program/utils.py                              | 16 +++++++++-
 program/views.py                              | 30 +++++++++----------
 6 files changed, 42 insertions(+), 30 deletions(-)

diff --git a/program/management/commands/addnote.py b/program/management/commands/addnote.py
index fafb33fc..7b8aff6a 100644
--- a/program/management/commands/addnote.py
+++ b/program/management/commands/addnote.py
@@ -1,10 +1,10 @@
 from django.core.management.base import BaseCommand, CommandError
 from django.core.exceptions import ValidationError
 
-from datetime import datetime
 import sys
 
 from program.models import Show, TimeSlot, Note
+from program.utils import parse_date
 
 
 class Command(BaseCommand):
@@ -30,7 +30,7 @@ class Command(BaseCommand):
             raise CommandError(dne)
 
         try:
-            start = datetime.strptime(start_date, '%Y-%m-%d')
+            start = parse_date(start_date)
         except ValueError as ve:
             raise CommandError(ve)
         else:
diff --git a/program/management/commands/export_showlog.py b/program/management/commands/export_showlog.py
index 41c20217..e5c86a83 100644
--- a/program/management/commands/export_showlog.py
+++ b/program/management/commands/export_showlog.py
@@ -25,8 +25,8 @@ class Command(BaseCommand):
 
         self.stdout.write(self.style.NOTICE, f"# Radio Helsinki Sendungslog {year}")
 
-        start = datetime.strptime('%d__01__01__00__00' % (year), '%Y__%m__%d__%H__%M')
-        end = datetime.strptime('%d__01__01__00__00' % (year+1), '%Y__%m__%d__%H__%M')
+        start = datetime(year, 1, 1, 0, 0)
+        end = datetime(year+1, 1, 1, 0, 0)
 
         currentDate = None
         for ts in TimeSlot.objects.filter(end__gt=start, start__lt=end).select_related('schedule').select_related('show'):
diff --git a/program/models.py b/program/models.py
index 306be0a9..fe9dafa7 100644
--- a/program/models.py
+++ b/program/models.py
@@ -18,9 +18,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-import json
 from datetime import datetime, time, timedelta
-from urllib.request import urlopen
 
 from dateutil.relativedelta import relativedelta
 from dateutil.rrule import rrule
@@ -34,7 +32,7 @@ from django.utils.translation import gettext_lazy as _
 from versatileimagefield.fields import VersatileImageField, PPOIField
 
 from steering.settings import THUMBNAIL_SIZES, AUTO_SET_UNTIL_DATE_TO_END_OF_YEAR, AUTO_SET_UNTIL_DATE_TO_DAYS_IN_FUTURE
-from .utils import parse_datetime
+from program.utils import parse_datetime, parse_date, parse_time
 
 
 class Type(models.Model):
@@ -321,7 +319,7 @@ class Schedule(models.Model):
         add_days_no = int(sdl['add_days_no']) if sdl['add_days_no'] > 0 else None
         add_business_days_only = True if 'add_business_days_only' in sdl and sdl['add_business_days_only'] is True else False
 
-        dstart = datetime.strptime(str(sdl['dstart']), '%Y-%m-%d').date()
+        dstart = parse_date(str(sdl['dstart']))
 
         # Schedule mustn't start in the past when adding
         if pk is None and dstart < timezone.now().date():
@@ -330,18 +328,18 @@ class Schedule(models.Model):
         tstart = sdl['tstart'] + ':00' if len(str(sdl['tstart'])) == 5 else sdl['tstart']
         tend = sdl['tend'] + ':00' if len(str(sdl['tend'])) == 5 else sdl['tend']
 
-        tstart = datetime.strptime(str(tstart), '%H:%M:%S').time()
-        tend = datetime.strptime(str(tend), '%H:%M:%S').time()
+        tstart = parse_time(str(tstart))
+        tend = parse_time(str(tend))
 
         if sdl['until']:
-            until = datetime.strptime(str(sdl['until']), '%Y-%m-%d').date()
+            until = parse_date(str(sdl['until']))
         else:
             # If no until date was set
             # Set it to the end of the year
             # Or add x days
             if AUTO_SET_UNTIL_DATE_TO_END_OF_YEAR:
-                now = timezone.now()
-                until = datetime.strptime(str(now.year) + '-12-31', '%Y-%m-%d').date()
+                year = timezone.now().year
+                until = parse_date(f'{year}-12-31')
             else:
                 until = dstart + timedelta(days=+AUTO_SET_UNTIL_DATE_TO_DAYS_IN_FUTURE)
 
diff --git a/program/serializers.py b/program/serializers.py
index 8c138fce..5af1f180 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -28,7 +28,7 @@ from profile.serializers import ProfileSerializer
 from program.models import Show, Schedule, TimeSlot, Category, FundingCategory, Host, Topic, MusicFocus, Note, Type, Language, \
     RRule, Link
 from steering.settings import THUMBNAIL_SIZES
-from .utils import get_audio_url
+from program.utils import get_audio_url
 
 
 class UserSerializer(serializers.ModelSerializer):
diff --git a/program/utils.py b/program/utils.py
index 18d7d751..518c3499 100644
--- a/program/utils.py
+++ b/program/utils.py
@@ -18,7 +18,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-from datetime import datetime
+from datetime import datetime, date, time
 
 import requests
 from django.utils import timezone
@@ -38,6 +38,20 @@ def parse_datetime(date_string: str) -> datetime:
     return timezone.make_aware(parsed_datetime)
 
 
+def parse_date(date_string: str) -> date:
+    """
+    parse a date string and return a date object
+    """
+    return datetime.strptime(date_string, "%Y-%m-%d").date()
+
+
+def parse_time(date_string: str) -> time:
+    """
+    parse a time string and return a time object
+    """
+    return datetime.strptime(date_string, "%H:%M:%S").time()
+
+
 def get_audio_url(cba_id):
     """
     Retrieve the direct URL to the mp3 in CBA
diff --git a/program/views.py b/program/views.py
index 70520900..ead86f4a 100644
--- a/program/views.py
+++ b/program/views.py
@@ -36,15 +36,16 @@ from program.models import Type, MusicFocus, Language, Note, Show, Category, Fun
 from program.serializers import TypeSerializer, LanguageSerializer, MusicFocusSerializer, NoteSerializer, ShowSerializer, \
     ScheduleSerializer, CategorySerializer, FundingCategorySerializer, TopicSerializer, TimeSlotSerializer, HostSerializer, \
     UserSerializer
+from program.utils import parse_date
 
 logger = logging.getLogger(__name__)
 
 
 def json_day_schedule(request, year=None, month=None, day=None):
     if year is None and month is None and day is None:
-        today = timezone.make_aware(datetime.combine(date.today(), time(0, 0)))
+        today = timezone.make_aware(datetime.combine(timezone.now(), time(0, 0)))
     else:
-        today = timezone.make_aware(datetime.strptime('%s__%s__%s__00__00' % (year, month, day), '%Y__%m__%d__%H__%M'))
+        today = timezone.make_aware(datetime.combine(date(year, month, day), time(0, 0)))
 
     timeslots = TimeSlot.objects.get_24h_timeslots(today).select_related('schedule').select_related('show')
     schedule = []
@@ -75,16 +76,16 @@ def json_playout(request):
     """
 
     if request.GET.get('start') is None:
-        start = timezone.make_aware(datetime.combine(date.today(), time(0, 0)))
+        start = timezone.make_aware(datetime.combine(timezone.now(), time(0, 0)))
     else:
-        start = timezone.make_aware(datetime.combine(datetime.strptime(request.GET.get('start'), '%Y-%m-%d').date(), time(0, 0)))
+        start = timezone.make_aware(datetime.combine(parse_date(request.GET.get('start')), time(0, 0)))
 
     if request.GET.get('end') is None:
         # If no end was given, return the next week
         timeslots = TimeSlot.objects.get_7d_timeslots(start).select_related('schedule').select_related('show')
     else:
         # Otherwise return the given timerange
-        end = timezone.make_aware(datetime.combine(datetime.strptime(request.GET.get('end'), '%Y-%m-%d').date(), time(23, 59)))
+        end = timezone.make_aware(datetime.combine(parse_date(request.GET.get('end')), time(23, 59)))
         timeslots = TimeSlot.objects.get_timerange_timeslots(start, end).select_related('schedule').select_related('show')
 
     schedule = []
@@ -264,10 +265,10 @@ class APIShowViewSet(viewsets.ModelViewSet):
             # TODO: Really consider dstart? (=currently active, not just upcoming ones)
             # Add limit for future?
             show_ids = Schedule.objects.filter(Q(rrule_id__gt=1,
-                                                 dstart__lte=date.today(),
-                                                 until__gte=date.today()) |
+                                                 dstart__lte=timezone.now(),
+                                                 until__gte=timezone.now()) |
                                                Q(rrule_id=1,
-                                                 dstart__gte=date.today())
+                                                 dstart__gte=timezone.now())
                                                ).distinct().values_list('show_id', flat=True)
 
             # Filter active shows based on timeslots as well as on the is_active flag
@@ -539,12 +540,12 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         # Filters
 
         # Return next 60 days by default
-        start = timezone.make_aware(datetime.combine(date.today(), time(0, 0)))
+        start = timezone.make_aware(datetime.combine(timezone.now(), time(0, 0)))
         end = start + timedelta(days=60)
 
         if ('start' in self.request.query_params) and ('end' in self.request.query_params):
-            start = timezone.make_aware(datetime.combine(datetime.strptime(self.request.query_params.get('start'), '%Y-%m-%d').date(), time(0, 0)))
-            end = timezone.make_aware(datetime.combine(datetime.strptime(self.request.query_params.get('end'), '%Y-%m-%d').date(), time(23, 59)))
+            start = timezone.make_aware(datetime.combine(parse_date(self.request.query_params.get('start')), time(0, 0)))
+            end = timezone.make_aware(datetime.combine(parse_date(self.request.query_params.get('end')), time(23, 59)))
 
         default_order = '-start'
         order = self.request.query_params.get('order', default_order)
@@ -555,12 +556,11 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         if order.lstrip('-') not in model_fields:
             order = default_order
 
-
         if 'surrounding' in self.request.query_params:
-            today = timezone.make_aware(datetime.today())
+            now = timezone.now()
 
-            nearest_timeslots_in_future = TimeSlot.objects.filter(start__gte=today).order_by('start').values_list('id', flat=True)[:5]
-            nearest_timeslots_in_past = TimeSlot.objects.filter(start__lt=today).order_by('-start').values_list('id', flat=True)[:5]
+            nearest_timeslots_in_future = TimeSlot.objects.filter(start__gte=now).order_by('start').values_list('id', flat=True)[:5]
+            nearest_timeslots_in_past = TimeSlot.objects.filter(start__lt=now).order_by('-start').values_list('id', flat=True)[:5]
             relevant_timeslot_ids = list(nearest_timeslots_in_future) + list(nearest_timeslots_in_past)
 
             return TimeSlot.objects.filter(id__in=relevant_timeslot_ids).order_by(order)
-- 
GitLab