# -*- coding: utf-8 -*- from __future__ import unicode_literals # Erik Stein , 2016 """ Extends django.utils.dateformat Adds date and time range functions # TODO Describe custom formats # TODO Use Django's names 'MONTH_DAY_FORMAT' and 'YEAR_MONTH_FORMAT', with "_" """ import datetime import re from django.conf import settings from django.utils.dateformat import DateFormat, re_escaped from django.utils.formats import get_format from django.utils.encoding import force_text from django.utils.translation import get_language, ugettext_lazy as _ DEFAULT_VARIANT = getattr(settings, 'DEFAULT_DATE_VARIANT', 'SHORT') # Adding "q" re_formatchars = re.compile(r'(?>> import datetime >>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,1,20)) '15. - 20.01.2009.' >>> format_date_range(datetime.date(2009,1,15), datetime.date(2009,2,20)) '15.01. - 20.02.2009.' >>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,2,20)) '15.01.2009. - 20.02.2010.' >>> format_date_range(datetime.date(2009,1,15), datetime.date(2010,1,20)) '15.01.2009. - 20.01.2010.' """ if not (from_date or to_date): return "" variant = _normalize_variant(variant) # Only deal with dates, ignoring time def datetime_to_date(dt): try: return dt.date() except AttributeError: return dt from_date = datetime_to_date(from_date) to_date = datetime_to_date(to_date) from_format = to_format = get_format(variant + 'DATE_FORMAT') if from_date == to_date or not to_date: return date_format(from_date, from_format) else: if (from_date.year == to_date.year): from_format = get_format(variant + 'DAYMONTH_FORMAT') or 'd/m/' if (from_date.month == to_date.month): from_format = get_format(variant + 'DAYONLY_FORMAT') or 'd' f = t = "" if from_date: f = date_format(from_date, get_format(from_format)) if to_date: t = date_format(to_date, get_format(to_format)) separator = get_format('DATE_RANGE_SEPARATOR') or " - " return separator.join((f, t)) def format_year_range(start_date, end_date, variant=DEFAULT_VARIANT): """ Returns a range with only the years, i.e. either a single year or e.g. "2015-2017". """ start_year = start_date.year start_year_formatted = format_partial_date(year=start_year, variant=variant) end_year = end_date.year if end_year != start_year: end_year_formatted = format_partial_date(year=end_year, variant=variant) separator = get_format('DATE_RANGE_SEPARATOR') or " - " return separator.join((start_year_formatted, end_year_formatted)) else: return start_year_formatted def format_time_range(from_time, to_time, variant=DEFAULT_VARIANT): """ Knows how to deal with left out from_time/to_time values. """ if not (from_time or to_time): return "" variant = _normalize_variant(variant) from_format = to_format = "q" # get_format(variant + 'TIME_FORMAT') if from_time == to_time or not to_time: return time_format(from_time, get_format(from_format)) else: f = t = "" if from_time: f = time_format(from_time, get_format(from_format)) if to_time: t = time_format(to_time, get_format(to_format)) separator = get_format('DATE_RANGE_SEPARATOR') or "–" return separator.join((f, t)) def format_timespan_range(timespan_object, force_wholeday=False, variant=DEFAULT_VARIANT): """ For Timespan-objects, i.e. object with start_date, end_date, start_time and end_time properties. Multiday or force_wholeday: "10.07.2016-11.07.2016" Single days: "10.07.2016 11 Uhr" "10.07.2016 11-14 Uhr" >>> import datetime >>> sd, ed = datetime.date(2009,1,15), datetime.date(2009,1,20) >>> st, et = datetime.date(2009,1,15), datetime.date(2009,1,20) >>> class TestObject(object): >>> start_date = None >>> end_date = None >>> start_time = None >>> end_time = None >>> obj = TestObject() >>> obj.start_date = obj.end_date = sd >>> format_timespan_range(obj) '15.01.–20.01.2009' """ variant = _normalize_variant(variant) rv = format_date_range(timespan_object.start_date, timespan_object.end_date, variant) if (timespan_object.is_multiday() or not timespan_object.start_time or force_wholeday): # Don't show times return rv else: rv = _("%(daterange)s %(timespan)s Uhr") % { 'daterange': rv, 'timespan': format_time_range(timespan_object.start_time, timespan_object.end_time, variant) } return rv def format_partial_date(year=None, month=None, day=None, variant=DEFAULT_VARIANT, use_l10n=None): if year and month and day: format_name = 'DATE_FORMAT' elif year and month: format_name = 'YEAR_MONTH_FORMAT' elif month and day: format_name = 'DAY_MONTH_FORMAT' elif year: format_name = 'YEAR_FORMAT' elif month: format_name = 'MONTH_FORMAT' elif day: format_name = 'DAY_FORMAT' else: return "" name = _normalize_variant(variant) + format_name # TODO Django bug or what? Sometimes get_language returns None, therefore force a language here partial_date_format = get_format(name, use_l10n=use_l10n) # , lang=get_language() or settings.LANGUAGE_CODE) return date_format(datetime.date(year or 2000, month or 1, day or 1), partial_date_format) # TODO Add format_partial_date_range function # def format_partial_date_range( # start_year=None, start_month=None, start_day=None, # end_year=None, end_month=None, end_day=None, # variant=DEFAULT_VARIANT): # if start_year and start_month and start_day: # format_name = 'DATE_FORMAT' # elif start_year and start_month: # format_name = 'YEARMONTH_FORMAT' # elif start_month and start_day: # format_name = 'DAYMONTH_FORMAT' # elif start_year: # format_name = 'YEAR_FORMAT' # elif start_month: # format_name = 'MONTH_FORMAT' # elif start_day: # format_name = 'DAYONLY_FORMAT' # name = _normalize_variant(variant) + format_name # # TODO Django bug or what? Sometimes get_language returns None, therefore force a language here # partial_date_format = get_format(name, lang=get_language() or settings.LANGUAGE_CODE) # return date_format(datetime.date(start_year or 2000, start_month or 1, start_day or 1), partial_date_format) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()