Skip to content

Commit

Permalink
Merge pull request thauber#41 from Pavlov123/master
Browse files Browse the repository at this point in the history
Fix issue bartekgorny#14.
  • Loading branch information
llazzaro committed Jan 28, 2014
2 parents bf5a260 + 280f8e7 commit 89c2a68
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 49 deletions.
161 changes: 113 additions & 48 deletions schedule/periods.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.conf import settings
import pytz
import datetime

from django.conf import settings
from django.template.defaultfilters import date as date_filter
from django.utils.translation import ugettext
from django.utils.dates import WEEKDAYS, WEEKDAYS_ABBR
Expand Down Expand Up @@ -31,19 +32,29 @@ class Period(object):
"""
def __init__(self, events, start, end, parent_persisted_occurrences=None,
occurrence_pool=None, tzinfo=pytz.utc):
self.start = start
self.end = end

self.utc_start = self._normalize_timezone_to_utc(start, tzinfo)

self.utc_end = self._normalize_timezone_to_utc(end, tzinfo)

self.events = events
self.tzinfo = self._get_tzinfo(tzinfo)
self.occurrence_pool = occurrence_pool
if parent_persisted_occurrences is not None:
self._persisted_occurrences = parent_persisted_occurrences

def _normalize_timezone_to_utc(self, point_in_time, tzinfo):
if point_in_time.tzinfo is not None:
return point_in_time.astimezone(pytz.utc)
if tzinfo is not None:
return tzinfo.localize(point_in_time).astimezone(pytz.utc)
return pytz.utc.localize(point_in_time)

def __eq__(self, period):
return self.start == period.start and self.end == period.end and self.events == period.events
return self.utc_start == period.utc_start and self.utc_end == period.utc_end and self.events == period.events

def __ne__(self, period):
return self.start != period.start or self.end != period.end or self.events != period.events
return self.utc_start != period.utc_start or self.utc_end != period.utc_end or self.events != period.events

def _get_tzinfo(self, tzinfo):
return tzinfo if settings.USE_TZ else None
Expand All @@ -52,7 +63,7 @@ def _get_sorted_occurrences(self):
occurrences = []
if hasattr(self, "occurrence_pool") and self.occurrence_pool is not None:
for occurrence in self.occurrence_pool:
if occurrence.start <= self.end and occurrence.end >= self.start:
if occurrence.start <= self.utc_end and occurrence.end >= self.utc_start:
occurrences.append(occurrence)
return occurrences
for event in self.events:
Expand Down Expand Up @@ -82,9 +93,9 @@ def classify_occurrence(self, occurrence):
return None
started = False
ended = False
if self.start <= occurrence.start < self.end:
if self.utc_start <= occurrence.start < self.utc_end:
started = True
if self.start <= occurrence.end < self.end:
if self.utc_start <= occurrence.end < self.utc_end:
ended = True
if started and ended:
return {'occurrence': occurrence, 'class': 1}
Expand Down Expand Up @@ -115,42 +126,66 @@ def get_time_slot(self, start, end):
return Period(self.events, start, end)
return None

def create_sub_period(self, cls, start=None):
def create_sub_period(self, cls, start=None, tzinfo=None):
if tzinfo is None:
tzinfo = self.tzinfo
start = start or self.start
return cls(self.events, start, self.get_persisted_occurrences(), self.occurrences)
return cls(self.events, start, self.get_persisted_occurrences(), self.occurrences, tzinfo)

def get_periods(self, cls):
def get_periods(self, cls, tzinfo=None):
if tzinfo is None:
tzinfo = self.tzinfo
period = self.create_sub_period(cls)
while period.start < self.end:
yield self.create_sub_period(cls, period.start)
yield self.create_sub_period(cls, period.start, tzinfo)
period = period.next()

@property
def start(self):
if self.tzinfo is not None:
return self.utc_start.astimezone(self.tzinfo)
return self.utc_start.replace(tzinfo=None)

@property
def end(self):
if self.tzinfo is not None:
return self.utc_end.astimezone(self.tzinfo)
return self.utc_end.replace(tzinfo=None)


class Year(Period):
def __init__(self, events, date=None, parent_persisted_occurrences=None, tzinfo=pytz.utc):
self.tzinfo = self._get_tzinfo(tzinfo)
if date is None:
date = timezone.now()
start, end = self._get_year_range(date)
super(Year, self).__init__(events, start, end, parent_persisted_occurrences)
super(Year, self).__init__(events, start, end, parent_persisted_occurrences, tzinfo=tzinfo)

def get_months(self):
return self.get_periods(Month)

def next_year(self):
return Year(self.events, self.end)
return Year(self.events, self.end, tzinfo=self.tzinfo)
next = next_year

def prev_year(self):
start = datetime.datetime(self.start.year - 1, self.start.month, self.start.day, tzinfo=self.tzinfo)
return Year(self.events, start)
start = datetime.datetime(self.start.year - 1, self.start.month, self.start.day)
return Year(self.events, start, tzinfo=self.tzinfo)
prev = prev_year

def _get_year_range(self, year):
start = datetime.datetime(year.year, datetime.datetime.min.month,
datetime.datetime.min.day, tzinfo=self.tzinfo)
end = datetime.datetime(year.year + 1, datetime.datetime.min.month,
datetime.datetime.min.day, tzinfo=self.tzinfo)
#If tzinfo is not none get the local start of the year and convert it to utc.
naive_start = datetime.datetime(year.year, datetime.datetime.min.month, datetime.datetime.min.day)
naive_end = datetime.datetime(year.year + 1, datetime.datetime.min.month, datetime.datetime.min.day)

start = naive_start
end = naive_end
if self.tzinfo is not None:
local_start = self.tzinfo.localize(naive_start)
local_end = self.tzinfo.localize(naive_end)
start = local_start.astimezone(pytz.utc)
end = local_end.astimezone(pytz.utc)

return start, end

def __unicode__(self):
Expand All @@ -169,7 +204,7 @@ def __init__(self, events, date=None, parent_persisted_occurrences=None,
date = timezone.now()
start, end = self._get_month_range(date)
super(Month, self).__init__(events, start, end,
parent_persisted_occurrences, occurrence_pool)
parent_persisted_occurrences, occurrence_pool, tzinfo=tzinfo)

def get_weeks(self):
return self.get_periods(Week)
Expand All @@ -180,37 +215,47 @@ def get_days(self):
def get_day(self, daynumber):
date = self.start
if daynumber > 1:
date += datetime.timedelta(days=daynumber-1)
date += datetime.timedelta(days=daynumber - 1)
return self.create_sub_period(Day, date)

def next_month(self):
return Month(self.events, self.end)
return Month(self.events, self.end, tzinfo=self.tzinfo)
next = next_month

def prev_month(self):
start = (self.start - datetime.timedelta(days=1)).replace(day=1, tzinfo=self.tzinfo)
return Month(self.events, start)
return Month(self.events, start, tzinfo=self.tzinfo)
prev = prev_month

def current_year(self):
return Year(self.events, self.start)
return Year(self.events, self.start, tzinfo=self.tzinfo)

def prev_year(self):
start = datetime.datetime.min.replace(year=self.start.year - 1, tzinfo=self.tzinfo)
return Year(self.events, start)
return Year(self.events, start, tzinfo=self.tzinfo)

def next_year(self):
start = datetime.datetime.min.replace(year=self.start.year + 1, tzinfo=self.tzinfo)
return Year(self.events, start)
return Year(self.events, start, tzinfo=self.tzinfo)

def _get_month_range(self, month):
year = month.year
month = month.month
start = datetime.datetime.min.replace(year=year, month=month, tzinfo=self.tzinfo)
#If tzinfo is not none get the local start of the month and convert it to utc.
naive_start = datetime.datetime.min.replace(year=year, month=month)
if month == 12:
end = start.replace(month=1, year=year + 1, tzinfo=self.tzinfo)
naive_end = datetime.datetime.min.replace(month=1, year=year + 1, day=1)
else:
end = start.replace(month=month + 1, tzinfo=self.tzinfo)
naive_end = datetime.datetime.min.replace(month=month + 1, year=year, day=1)

start = naive_start
end = naive_end
if self.tzinfo is not None:
local_start = self.tzinfo.localize(naive_start)
local_end = self.tzinfo.localize(naive_end)
start = local_start.astimezone(pytz.utc)
end = local_end.astimezone(pytz.utc)

return start, end

def __unicode__(self):
Expand All @@ -234,21 +279,21 @@ def __init__(self, events, date=None, parent_persisted_occurrences=None,
date = timezone.now()
start, end = self._get_week_range(date)
super(Week, self).__init__(events, start, end,
parent_persisted_occurrences, occurrence_pool)
parent_persisted_occurrences, occurrence_pool, tzinfo=tzinfo)

def prev_week(self):
return Week(self.events, self.start - datetime.timedelta(days=7))
return Week(self.events, self.start - datetime.timedelta(days=7), tzinfo=self.tzinfo)
prev = prev_week

def next_week(self):
return Week(self.events, self.end)
return Week(self.events, self.end, tzinfo=self.tzinfo)
next = next_week

def current_month(self):
return Month(self.events, self.start)
return Month(self.events, self.start, tzinfo=self.tzinfo)

def current_year(self):
return Year(self.events, self.start)
return Year(self.events, self.start, tzinfo=self.tzinfo)

def get_days(self):
return self.get_periods(Day)
Expand All @@ -257,19 +302,29 @@ def _get_week_range(self, week):
if isinstance(week, datetime.datetime):
week = week.date()
# Adjust the start datetime to midnight of the week datetime
start = datetime.datetime.combine(week, datetime.time.min).replace(tzinfo=self.tzinfo)
naive_start = datetime.datetime.combine(week, datetime.time.min)
# Adjust the start datetime to Monday or Sunday of the current week
if FIRST_DAY_OF_WEEK == 1:
# The week begins on Monday
sub_days = start.isoweekday() - 1
sub_days = naive_start.isoweekday() - 1
else:
# The week begins on Sunday
sub_days = start.isoweekday()
sub_days = naive_start.isoweekday()
if sub_days == 7:
sub_days = 0
if sub_days > 0:
start = start - datetime.timedelta(days=sub_days)
end = start + datetime.timedelta(days=7)
naive_start = naive_start - datetime.timedelta(days=sub_days)
naive_end = naive_start + datetime.timedelta(days=7)

if self.tzinfo is not None:
local_start = self.tzinfo.localize(naive_start)
local_end = self.tzinfo.localize(naive_end)
start = local_start.astimezone(pytz.utc)
end = local_end.astimezone(pytz.utc)
else:
start = naive_start
end = naive_end

return start, end

def __unicode__(self):
Expand All @@ -288,13 +343,23 @@ def __init__(self, events, date=None, parent_persisted_occurrences=None,
date = timezone.now()
start, end = self._get_day_range(date)
super(Day, self).__init__(events, start, end,
parent_persisted_occurrences, occurrence_pool)
parent_persisted_occurrences, occurrence_pool, tzinfo=tzinfo)

def _get_day_range(self, date):
if isinstance(date, datetime.datetime):
date = date.date()
start = datetime.datetime.combine(date, datetime.time.min).replace(tzinfo=self.tzinfo)
end = start + datetime.timedelta(days=1)

naive_start = datetime.datetime.combine(date, datetime.time.min)
naive_end = datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time.min)
if self.tzinfo is not None:
local_start = self.tzinfo.localize(naive_start)
local_end = self.tzinfo.localize(naive_end)
start = local_start.astimezone(pytz.utc)
end = local_end.astimezone(pytz.utc)
else:
start = naive_start
end = naive_end

return start, end

def __unicode__(self):
Expand All @@ -305,18 +370,18 @@ def __unicode__(self):
}

def prev_day(self):
return Day(self.events, self.start - datetime.timedelta(days=1))
return Day(self.events, self.start - datetime.timedelta(days=1), tzinfo=self.tzinfo)
prev = prev_day

def next_day(self):
return Day(self.events, self.end)
return Day(self.events, self.end, tzinfo=self.tzinfo)
next = next_day

def current_year(self):
return Year(self.events, self.start)
return Year(self.events, self.start, tzinfo=self.tzinfo)

def current_month(self):
return Month(self.events, self.start)
return Month(self.events, self.start, tzinfo=self.tzinfo)

def current_week(self):
return Week(self.events, self.start)
return Week(self.events, self.start, tzinfo=self.tzinfo)
Loading

0 comments on commit 89c2a68

Please sign in to comment.