Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/views/generic/dates.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

333 statements  

1import datetime 

2 

3from django.conf import settings 

4from django.core.exceptions import ImproperlyConfigured 

5from django.db import models 

6from django.http import Http404 

7from django.utils import timezone 

8from django.utils.functional import cached_property 

9from django.utils.translation import gettext as _ 

10from django.views.generic.base import View 

11from django.views.generic.detail import ( 

12 BaseDetailView, 

13 SingleObjectTemplateResponseMixin, 

14) 

15from django.views.generic.list import ( 

16 MultipleObjectMixin, 

17 MultipleObjectTemplateResponseMixin, 

18) 

19 

20 

21class YearMixin: 

22 """Mixin for views manipulating year-based data.""" 

23 

24 year_format = "%Y" 

25 year = None 

26 

27 def get_year_format(self): 

28 """ 

29 Get a year format string in strptime syntax to be used to parse the 

30 year from url variables. 

31 """ 

32 return self.year_format 

33 

34 def get_year(self): 

35 """Return the year for which this view should display data.""" 

36 year = self.year 

37 if year is None: 

38 try: 

39 year = self.kwargs["year"] 

40 except KeyError: 

41 try: 

42 year = self.request.GET["year"] 

43 except KeyError: 

44 raise Http404(_("No year specified")) 

45 return year 

46 

47 def get_next_year(self, date): 

48 """Get the next valid year.""" 

49 return _get_next_prev(self, date, is_previous=False, period="year") 

50 

51 def get_previous_year(self, date): 

52 """Get the previous valid year.""" 

53 return _get_next_prev(self, date, is_previous=True, period="year") 

54 

55 def _get_next_year(self, date): 

56 """ 

57 Return the start date of the next interval. 

58 

59 The interval is defined by start date <= item date < next start date. 

60 """ 

61 try: 

62 return date.replace(year=date.year + 1, month=1, day=1) 

63 except ValueError: 

64 raise Http404(_("Date out of range")) 

65 

66 def _get_current_year(self, date): 

67 """Return the start date of the current interval.""" 

68 return date.replace(month=1, day=1) 

69 

70 

71class MonthMixin: 

72 """Mixin for views manipulating month-based data.""" 

73 

74 month_format = "%b" 

75 month = None 

76 

77 def get_month_format(self): 

78 """ 

79 Get a month format string in strptime syntax to be used to parse the 

80 month from url variables. 

81 """ 

82 return self.month_format 

83 

84 def get_month(self): 

85 """Return the month for which this view should display data.""" 

86 month = self.month 

87 if month is None: 

88 try: 

89 month = self.kwargs["month"] 

90 except KeyError: 

91 try: 

92 month = self.request.GET["month"] 

93 except KeyError: 

94 raise Http404(_("No month specified")) 

95 return month 

96 

97 def get_next_month(self, date): 

98 """Get the next valid month.""" 

99 return _get_next_prev(self, date, is_previous=False, period="month") 

100 

101 def get_previous_month(self, date): 

102 """Get the previous valid month.""" 

103 return _get_next_prev(self, date, is_previous=True, period="month") 

104 

105 def _get_next_month(self, date): 

106 """ 

107 Return the start date of the next interval. 

108 

109 The interval is defined by start date <= item date < next start date. 

110 """ 

111 if date.month == 12: 

112 try: 

113 return date.replace(year=date.year + 1, month=1, day=1) 

114 except ValueError: 

115 raise Http404(_("Date out of range")) 

116 else: 

117 return date.replace(month=date.month + 1, day=1) 

118 

119 def _get_current_month(self, date): 

120 """Return the start date of the previous interval.""" 

121 return date.replace(day=1) 

122 

123 

124class DayMixin: 

125 """Mixin for views manipulating day-based data.""" 

126 

127 day_format = "%d" 

128 day = None 

129 

130 def get_day_format(self): 

131 """ 

132 Get a day format string in strptime syntax to be used to parse the day 

133 from url variables. 

134 """ 

135 return self.day_format 

136 

137 def get_day(self): 

138 """Return the day for which this view should display data.""" 

139 day = self.day 

140 if day is None: 

141 try: 

142 day = self.kwargs["day"] 

143 except KeyError: 

144 try: 

145 day = self.request.GET["day"] 

146 except KeyError: 

147 raise Http404(_("No day specified")) 

148 return day 

149 

150 def get_next_day(self, date): 

151 """Get the next valid day.""" 

152 return _get_next_prev(self, date, is_previous=False, period="day") 

153 

154 def get_previous_day(self, date): 

155 """Get the previous valid day.""" 

156 return _get_next_prev(self, date, is_previous=True, period="day") 

157 

158 def _get_next_day(self, date): 

159 """ 

160 Return the start date of the next interval. 

161 

162 The interval is defined by start date <= item date < next start date. 

163 """ 

164 return date + datetime.timedelta(days=1) 

165 

166 def _get_current_day(self, date): 

167 """Return the start date of the current interval.""" 

168 return date 

169 

170 

171class WeekMixin: 

172 """Mixin for views manipulating week-based data.""" 

173 

174 week_format = "%U" 

175 week = None 

176 

177 def get_week_format(self): 

178 """ 

179 Get a week format string in strptime syntax to be used to parse the 

180 week from url variables. 

181 """ 

182 return self.week_format 

183 

184 def get_week(self): 

185 """Return the week for which this view should display data.""" 

186 week = self.week 

187 if week is None: 

188 try: 

189 week = self.kwargs["week"] 

190 except KeyError: 

191 try: 

192 week = self.request.GET["week"] 

193 except KeyError: 

194 raise Http404(_("No week specified")) 

195 return week 

196 

197 def get_next_week(self, date): 

198 """Get the next valid week.""" 

199 return _get_next_prev(self, date, is_previous=False, period="week") 

200 

201 def get_previous_week(self, date): 

202 """Get the previous valid week.""" 

203 return _get_next_prev(self, date, is_previous=True, period="week") 

204 

205 def _get_next_week(self, date): 

206 """ 

207 Return the start date of the next interval. 

208 

209 The interval is defined by start date <= item date < next start date. 

210 """ 

211 try: 

212 return date + datetime.timedelta(days=7 - self._get_weekday(date)) 

213 except OverflowError: 

214 raise Http404(_("Date out of range")) 

215 

216 def _get_current_week(self, date): 

217 """Return the start date of the current interval.""" 

218 return date - datetime.timedelta(self._get_weekday(date)) 

219 

220 def _get_weekday(self, date): 

221 """ 

222 Return the weekday for a given date. 

223 

224 The first day according to the week format is 0 and the last day is 6. 

225 """ 

226 week_format = self.get_week_format() 

227 if week_format in {"%W", "%V"}: # week starts on Monday 

228 return date.weekday() 

229 elif week_format == "%U": # week starts on Sunday 

230 return (date.weekday() + 1) % 7 

231 else: 

232 raise ValueError("unknown week format: %s" % week_format) 

233 

234 

235class DateMixin: 

236 """Mixin class for views manipulating date-based data.""" 

237 

238 date_field = None 

239 allow_future = False 

240 

241 def get_date_field(self): 

242 """Get the name of the date field to be used to filter by.""" 

243 if self.date_field is None: 

244 raise ImproperlyConfigured( 

245 "%s.date_field is required." % self.__class__.__name__ 

246 ) 

247 return self.date_field 

248 

249 def get_allow_future(self): 

250 """ 

251 Return `True` if the view should be allowed to display objects from 

252 the future. 

253 """ 

254 return self.allow_future 

255 

256 # Note: the following three methods only work in subclasses that also 

257 # inherit SingleObjectMixin or MultipleObjectMixin. 

258 

259 @cached_property 

260 def uses_datetime_field(self): 

261 """ 

262 Return `True` if the date field is a `DateTimeField` and `False` 

263 if it's a `DateField`. 

264 """ 

265 model = self.get_queryset().model if self.model is None else self.model 

266 field = model._meta.get_field(self.get_date_field()) 

267 return isinstance(field, models.DateTimeField) 

268 

269 def _make_date_lookup_arg(self, value): 

270 """ 

271 Convert a date into a datetime when the date field is a DateTimeField. 

272 

273 When time zone support is enabled, `date` is assumed to be in the 

274 current time zone, so that displayed items are consistent with the URL. 

275 """ 

276 if self.uses_datetime_field: 

277 value = datetime.datetime.combine(value, datetime.time.min) 

278 if settings.USE_TZ: 

279 value = timezone.make_aware(value) 

280 return value 

281 

282 def _make_single_date_lookup(self, date): 

283 """ 

284 Get the lookup kwargs for filtering on a single date. 

285 

286 If the date field is a DateTimeField, we can't just filter on 

287 date_field=date because that doesn't take the time into account. 

288 """ 

289 date_field = self.get_date_field() 

290 if self.uses_datetime_field: 

291 since = self._make_date_lookup_arg(date) 

292 until = self._make_date_lookup_arg(date + datetime.timedelta(days=1)) 

293 return { 

294 "%s__gte" % date_field: since, 

295 "%s__lt" % date_field: until, 

296 } 

297 else: 

298 # Skip self._make_date_lookup_arg, it's a no-op in this branch. 

299 return {date_field: date} 

300 

301 

302class BaseDateListView(MultipleObjectMixin, DateMixin, View): 

303 """ 

304 Base class for date-based views displaying a list of objects. 

305 

306 This requires subclassing to provide a response mixin. 

307 """ 

308 

309 allow_empty = False 

310 date_list_period = "year" 

311 

312 def get(self, request, *args, **kwargs): 

313 self.date_list, self.object_list, extra_context = self.get_dated_items() 

314 context = self.get_context_data( 

315 object_list=self.object_list, date_list=self.date_list, **extra_context 

316 ) 

317 return self.render_to_response(context) 

318 

319 def get_dated_items(self): 

320 """Obtain the list of dates and items.""" 

321 raise NotImplementedError( 

322 "A DateView must provide an implementation of get_dated_items()" 

323 ) 

324 

325 def get_ordering(self): 

326 """ 

327 Return the field or fields to use for ordering the queryset; use the 

328 date field by default. 

329 """ 

330 return "-%s" % self.get_date_field() if self.ordering is None else self.ordering 

331 

332 def get_dated_queryset(self, **lookup): 

333 """ 

334 Get a queryset properly filtered according to `allow_future` and any 

335 extra lookup kwargs. 

336 """ 

337 qs = self.get_queryset().filter(**lookup) 

338 date_field = self.get_date_field() 

339 allow_future = self.get_allow_future() 

340 allow_empty = self.get_allow_empty() 

341 paginate_by = self.get_paginate_by(qs) 

342 

343 if not allow_future: 

344 now = timezone.now() if self.uses_datetime_field else timezone_today() 

345 qs = qs.filter(**{"%s__lte" % date_field: now}) 

346 

347 if not allow_empty: 

348 # When pagination is enabled, it's better to do a cheap query 

349 # than to load the unpaginated queryset in memory. 

350 is_empty = not qs if paginate_by is None else not qs.exists() 

351 if is_empty: 

352 raise Http404( 

353 _("No %(verbose_name_plural)s available") 

354 % { 

355 "verbose_name_plural": qs.model._meta.verbose_name_plural, 

356 } 

357 ) 

358 

359 return qs 

360 

361 def get_date_list_period(self): 

362 """ 

363 Get the aggregation period for the list of dates: 'year', 'month', or 

364 'day'. 

365 """ 

366 return self.date_list_period 

367 

368 def get_date_list(self, queryset, date_type=None, ordering="ASC"): 

369 """ 

370 Get a date list by calling `queryset.dates/datetimes()`, checking 

371 along the way for empty lists that aren't allowed. 

372 """ 

373 date_field = self.get_date_field() 

374 allow_empty = self.get_allow_empty() 

375 if date_type is None: 

376 date_type = self.get_date_list_period() 

377 

378 if self.uses_datetime_field: 

379 date_list = queryset.datetimes(date_field, date_type, ordering) 

380 else: 

381 date_list = queryset.dates(date_field, date_type, ordering) 

382 if date_list is not None and not date_list and not allow_empty: 

383 raise Http404( 

384 _("No %(verbose_name_plural)s available") 

385 % { 

386 "verbose_name_plural": queryset.model._meta.verbose_name_plural, 

387 } 

388 ) 

389 

390 return date_list 

391 

392 

393class BaseArchiveIndexView(BaseDateListView): 

394 """ 

395 Base view for archives of date-based items. 

396 

397 This requires subclassing to provide a response mixin. 

398 """ 

399 

400 context_object_name = "latest" 

401 

402 def get_dated_items(self): 

403 """Return (date_list, items, extra_context) for this request.""" 

404 qs = self.get_dated_queryset() 

405 date_list = self.get_date_list(qs, ordering="DESC") 

406 

407 if not date_list: 

408 qs = qs.none() 

409 

410 return (date_list, qs, {}) 

411 

412 

413class ArchiveIndexView(MultipleObjectTemplateResponseMixin, BaseArchiveIndexView): 

414 """Top-level archive of date-based items.""" 

415 

416 template_name_suffix = "_archive" 

417 

418 

419class BaseYearArchiveView(YearMixin, BaseDateListView): 

420 """ 

421 Base view for a list of objects published in a given year. 

422 

423 This requires subclassing to provide a response mixin. 

424 """ 

425 

426 date_list_period = "month" 

427 make_object_list = False 

428 

429 def get_dated_items(self): 

430 """Return (date_list, items, extra_context) for this request.""" 

431 year = self.get_year() 

432 

433 date_field = self.get_date_field() 

434 date = _date_from_string(year, self.get_year_format()) 

435 

436 since = self._make_date_lookup_arg(date) 

437 until = self._make_date_lookup_arg(self._get_next_year(date)) 

438 lookup_kwargs = { 

439 "%s__gte" % date_field: since, 

440 "%s__lt" % date_field: until, 

441 } 

442 

443 qs = self.get_dated_queryset(**lookup_kwargs) 

444 date_list = self.get_date_list(qs) 

445 

446 if not self.get_make_object_list(): 

447 # We need this to be a queryset since parent classes introspect it 

448 # to find information about the model. 

449 qs = qs.none() 

450 

451 return ( 

452 date_list, 

453 qs, 

454 { 

455 "year": date, 

456 "next_year": self.get_next_year(date), 

457 "previous_year": self.get_previous_year(date), 

458 }, 

459 ) 

460 

461 def get_make_object_list(self): 

462 """ 

463 Return `True` if this view should contain the full list of objects in 

464 the given year. 

465 """ 

466 return self.make_object_list 

467 

468 

469class YearArchiveView(MultipleObjectTemplateResponseMixin, BaseYearArchiveView): 

470 """List of objects published in a given year.""" 

471 

472 template_name_suffix = "_archive_year" 

473 

474 

475class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): 

476 """ 

477 Base view for a list of objects published in a given month. 

478 

479 This requires subclassing to provide a response mixin. 

480 """ 

481 

482 date_list_period = "day" 

483 

484 def get_dated_items(self): 

485 """Return (date_list, items, extra_context) for this request.""" 

486 year = self.get_year() 

487 month = self.get_month() 

488 

489 date_field = self.get_date_field() 

490 date = _date_from_string( 

491 year, self.get_year_format(), month, self.get_month_format() 

492 ) 

493 

494 since = self._make_date_lookup_arg(date) 

495 until = self._make_date_lookup_arg(self._get_next_month(date)) 

496 lookup_kwargs = { 

497 "%s__gte" % date_field: since, 

498 "%s__lt" % date_field: until, 

499 } 

500 

501 qs = self.get_dated_queryset(**lookup_kwargs) 

502 date_list = self.get_date_list(qs) 

503 

504 return ( 

505 date_list, 

506 qs, 

507 { 

508 "month": date, 

509 "next_month": self.get_next_month(date), 

510 "previous_month": self.get_previous_month(date), 

511 }, 

512 ) 

513 

514 

515class MonthArchiveView(MultipleObjectTemplateResponseMixin, BaseMonthArchiveView): 

516 """List of objects published in a given month.""" 

517 

518 template_name_suffix = "_archive_month" 

519 

520 

521class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): 

522 """ 

523 Base view for a list of objects published in a given week. 

524 

525 This requires subclassing to provide a response mixin. 

526 """ 

527 

528 def get_dated_items(self): 

529 """Return (date_list, items, extra_context) for this request.""" 

530 year = self.get_year() 

531 week = self.get_week() 

532 

533 date_field = self.get_date_field() 

534 week_format = self.get_week_format() 

535 week_choices = {"%W": "1", "%U": "0", "%V": "1"} 

536 try: 

537 week_start = week_choices[week_format] 

538 except KeyError: 

539 raise ValueError( 

540 "Unknown week format %r. Choices are: %s" 

541 % ( 

542 week_format, 

543 ", ".join(sorted(week_choices)), 

544 ) 

545 ) 

546 year_format = self.get_year_format() 

547 if week_format == "%V" and year_format != "%G": 

548 raise ValueError( 

549 "ISO week directive '%s' is incompatible with the year " 

550 "directive '%s'. Use the ISO year '%%G' instead." 

551 % ( 

552 week_format, 

553 year_format, 

554 ) 

555 ) 

556 date = _date_from_string(year, year_format, week_start, "%w", week, week_format) 

557 since = self._make_date_lookup_arg(date) 

558 until = self._make_date_lookup_arg(self._get_next_week(date)) 

559 lookup_kwargs = { 

560 "%s__gte" % date_field: since, 

561 "%s__lt" % date_field: until, 

562 } 

563 

564 qs = self.get_dated_queryset(**lookup_kwargs) 

565 

566 return ( 

567 None, 

568 qs, 

569 { 

570 "week": date, 

571 "next_week": self.get_next_week(date), 

572 "previous_week": self.get_previous_week(date), 

573 }, 

574 ) 

575 

576 

577class WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView): 

578 """List of objects published in a given week.""" 

579 

580 template_name_suffix = "_archive_week" 

581 

582 

583class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): 

584 """ 

585 Base view for a list of objects published on a given day. 

586 

587 This requires subclassing to provide a response mixin. 

588 """ 

589 

590 def get_dated_items(self): 

591 """Return (date_list, items, extra_context) for this request.""" 

592 year = self.get_year() 

593 month = self.get_month() 

594 day = self.get_day() 

595 

596 date = _date_from_string( 

597 year, 

598 self.get_year_format(), 

599 month, 

600 self.get_month_format(), 

601 day, 

602 self.get_day_format(), 

603 ) 

604 

605 return self._get_dated_items(date) 

606 

607 def _get_dated_items(self, date): 

608 """ 

609 Do the actual heavy lifting of getting the dated items; this accepts a 

610 date object so that TodayArchiveView can be trivial. 

611 """ 

612 lookup_kwargs = self._make_single_date_lookup(date) 

613 qs = self.get_dated_queryset(**lookup_kwargs) 

614 

615 return ( 

616 None, 

617 qs, 

618 { 

619 "day": date, 

620 "previous_day": self.get_previous_day(date), 

621 "next_day": self.get_next_day(date), 

622 "previous_month": self.get_previous_month(date), 

623 "next_month": self.get_next_month(date), 

624 }, 

625 ) 

626 

627 

628class DayArchiveView(MultipleObjectTemplateResponseMixin, BaseDayArchiveView): 

629 """List of objects published on a given day.""" 

630 

631 template_name_suffix = "_archive_day" 

632 

633 

634class BaseTodayArchiveView(BaseDayArchiveView): 

635 """ 

636 Base view for a list of objects published today. 

637 

638 This requires subclassing to provide a response mixin. 

639 """ 

640 

641 def get_dated_items(self): 

642 """Return (date_list, items, extra_context) for this request.""" 

643 return self._get_dated_items(datetime.date.today()) 

644 

645 

646class TodayArchiveView(MultipleObjectTemplateResponseMixin, BaseTodayArchiveView): 

647 """List of objects published today.""" 

648 

649 template_name_suffix = "_archive_day" 

650 

651 

652class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailView): 

653 """ 

654 Base detail view for a single object on a single date; this differs from the 

655 standard DetailView by accepting a year/month/day in the URL. 

656 

657 This requires subclassing to provide a response mixin. 

658 """ 

659 

660 def get_object(self, queryset=None): 

661 """Get the object this request displays.""" 

662 year = self.get_year() 

663 month = self.get_month() 

664 day = self.get_day() 

665 date = _date_from_string( 

666 year, 

667 self.get_year_format(), 

668 month, 

669 self.get_month_format(), 

670 day, 

671 self.get_day_format(), 

672 ) 

673 

674 # Use a custom queryset if provided 

675 qs = self.get_queryset() if queryset is None else queryset 

676 

677 if not self.get_allow_future() and date > datetime.date.today(): 

678 raise Http404( 

679 _( 

680 "Future %(verbose_name_plural)s not available because " 

681 "%(class_name)s.allow_future is False." 

682 ) 

683 % { 

684 "verbose_name_plural": qs.model._meta.verbose_name_plural, 

685 "class_name": self.__class__.__name__, 

686 } 

687 ) 

688 

689 # Filter down a queryset from self.queryset using the date from the 

690 # URL. This'll get passed as the queryset to DetailView.get_object, 

691 # which'll handle the 404 

692 lookup_kwargs = self._make_single_date_lookup(date) 

693 qs = qs.filter(**lookup_kwargs) 

694 

695 return super().get_object(queryset=qs) 

696 

697 

698class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView): 

699 """ 

700 Detail view of a single object on a single date; this differs from the 

701 standard DetailView by accepting a year/month/day in the URL. 

702 """ 

703 

704 template_name_suffix = "_detail" 

705 

706 

707def _date_from_string( 

708 year, year_format, month="", month_format="", day="", day_format="", delim="__" 

709): 

710 """ 

711 Get a datetime.date object given a format string and a year, month, and day 

712 (only year is mandatory). Raise a 404 for an invalid date. 

713 """ 

714 format = year_format + delim + month_format + delim + day_format 

715 datestr = str(year) + delim + str(month) + delim + str(day) 

716 try: 

717 return datetime.datetime.strptime(datestr, format).date() 

718 except ValueError: 

719 raise Http404( 

720 _("Invalid date string “%(datestr)s” given format “%(format)s”") 

721 % { 

722 "datestr": datestr, 

723 "format": format, 

724 } 

725 ) 

726 

727 

728def _get_next_prev(generic_view, date, is_previous, period): 

729 """ 

730 Get the next or the previous valid date. The idea is to allow links on 

731 month/day views to never be 404s by never providing a date that'll be 

732 invalid for the given view. 

733 

734 This is a bit complicated since it handles different intervals of time, 

735 hence the coupling to generic_view. 

736 

737 However in essence the logic comes down to: 

738 

739 * If allow_empty and allow_future are both true, this is easy: just 

740 return the naive result (just the next/previous day/week/month, 

741 regardless of object existence.) 

742 

743 * If allow_empty is true, allow_future is false, and the naive result 

744 isn't in the future, then return it; otherwise return None. 

745 

746 * If allow_empty is false and allow_future is true, return the next 

747 date *that contains a valid object*, even if it's in the future. If 

748 there are no next objects, return None. 

749 

750 * If allow_empty is false and allow_future is false, return the next 

751 date that contains a valid object. If that date is in the future, or 

752 if there are no next objects, return None. 

753 """ 

754 date_field = generic_view.get_date_field() 

755 allow_empty = generic_view.get_allow_empty() 

756 allow_future = generic_view.get_allow_future() 

757 

758 get_current = getattr(generic_view, "_get_current_%s" % period) 

759 get_next = getattr(generic_view, "_get_next_%s" % period) 

760 

761 # Bounds of the current interval 

762 start, end = get_current(date), get_next(date) 

763 

764 # If allow_empty is True, the naive result will be valid 

765 if allow_empty: 

766 if is_previous: 

767 result = get_current(start - datetime.timedelta(days=1)) 

768 else: 

769 result = end 

770 

771 if allow_future or result <= timezone_today(): 

772 return result 

773 else: 

774 return None 

775 

776 # Otherwise, we'll need to go to the database to look for an object 

777 # whose date_field is at least (greater than/less than) the given 

778 # naive result 

779 else: 

780 # Construct a lookup and an ordering depending on whether we're doing 

781 # a previous date or a next date lookup. 

782 if is_previous: 

783 lookup = {"%s__lt" % date_field: generic_view._make_date_lookup_arg(start)} 

784 ordering = "-%s" % date_field 

785 else: 

786 lookup = {"%s__gte" % date_field: generic_view._make_date_lookup_arg(end)} 

787 ordering = date_field 

788 

789 # Filter out objects in the future if appropriate. 

790 if not allow_future: 

791 # Fortunately, to match the implementation of allow_future, 

792 # we need __lte, which doesn't conflict with __lt above. 

793 if generic_view.uses_datetime_field: 

794 now = timezone.now() 

795 else: 

796 now = timezone_today() 

797 lookup["%s__lte" % date_field] = now 

798 

799 qs = generic_view.get_queryset().filter(**lookup).order_by(ordering) 

800 

801 # Snag the first object from the queryset; if it doesn't exist that 

802 # means there's no next/previous link available. 

803 try: 

804 result = getattr(qs[0], date_field) 

805 except IndexError: 

806 return None 

807 

808 # Convert datetimes to dates in the current time zone. 

809 if generic_view.uses_datetime_field: 

810 if settings.USE_TZ: 

811 result = timezone.localtime(result) 

812 result = result.date() 

813 

814 # Return the first day of the period. 

815 return get_current(result) 

816 

817 

818def timezone_today(): 

819 """Return the current date in the current time zone.""" 

820 if settings.USE_TZ: 

821 return timezone.localdate() 

822 else: 

823 return datetime.date.today()