Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pendulum/datetime.py: 34%

494 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# -*- coding: utf-8 -*- 

2from __future__ import absolute_import 

3from __future__ import division 

4 

5import calendar 

6import datetime 

7 

8from typing import Optional 

9from typing import TypeVar 

10from typing import Union 

11 

12import pendulum 

13 

14from .constants import ATOM 

15from .constants import COOKIE 

16from .constants import MINUTES_PER_HOUR 

17from .constants import MONTHS_PER_YEAR 

18from .constants import RFC822 

19from .constants import RFC850 

20from .constants import RFC1036 

21from .constants import RFC1123 

22from .constants import RFC2822 

23from .constants import RSS 

24from .constants import SATURDAY 

25from .constants import SECONDS_PER_DAY 

26from .constants import SECONDS_PER_MINUTE 

27from .constants import SUNDAY 

28from .constants import W3C 

29from .constants import YEARS_PER_CENTURY 

30from .constants import YEARS_PER_DECADE 

31from .date import Date 

32from .exceptions import PendulumException 

33from .helpers import add_duration 

34from .helpers import timestamp 

35from .period import Period 

36from .time import Time 

37from .tz import UTC 

38from .tz.timezone import Timezone 

39from .utils._compat import _HAS_FOLD 

40 

41 

42_D = TypeVar("_D", bound="DateTime") 

43 

44 

45class DateTime(datetime.datetime, Date): 

46 

47 EPOCH = None # type: DateTime 

48 

49 # Formats 

50 

51 _FORMATS = { 

52 "atom": ATOM, 

53 "cookie": COOKIE, 

54 "iso8601": lambda dt: dt.isoformat(), 

55 "rfc822": RFC822, 

56 "rfc850": RFC850, 

57 "rfc1036": RFC1036, 

58 "rfc1123": RFC1123, 

59 "rfc2822": RFC2822, 

60 "rfc3339": lambda dt: dt.isoformat(), 

61 "rss": RSS, 

62 "w3c": W3C, 

63 } 

64 

65 _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC) 

66 

67 _MODIFIERS_VALID_UNITS = [ 

68 "second", 

69 "minute", 

70 "hour", 

71 "day", 

72 "week", 

73 "month", 

74 "year", 

75 "decade", 

76 "century", 

77 ] 

78 

79 if not _HAS_FOLD: 

80 

81 def __new__( 

82 cls, 

83 year, 

84 month, 

85 day, 

86 hour=0, 

87 minute=0, 

88 second=0, 

89 microsecond=0, 

90 tzinfo=None, 

91 fold=0, 

92 ): 

93 self = datetime.datetime.__new__( 

94 cls, year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo 

95 ) 

96 

97 self._fold = fold 

98 

99 return self 

100 

101 @classmethod 

102 def now(cls, tz=None): # type: (Optional[Union[str, Timezone]]) -> DateTime 

103 """ 

104 Get a DateTime instance for the current date and time. 

105 """ 

106 return pendulum.now(tz) 

107 

108 @classmethod 

109 def utcnow(cls): # type: () -> DateTime 

110 """ 

111 Get a DateTime instance for the current date and time in UTC. 

112 """ 

113 return pendulum.now(UTC) 

114 

115 @classmethod 

116 def today(cls): # type: () -> DateTime 

117 return pendulum.now() 

118 

119 @classmethod 

120 def strptime(cls, time, fmt): # type: (str, str) -> DateTime 

121 return pendulum.instance(datetime.datetime.strptime(time, fmt)) 

122 

123 # Getters/Setters 

124 

125 def set( 

126 self, 

127 year=None, 

128 month=None, 

129 day=None, 

130 hour=None, 

131 minute=None, 

132 second=None, 

133 microsecond=None, 

134 tz=None, 

135 ): 

136 if year is None: 

137 year = self.year 

138 if month is None: 

139 month = self.month 

140 if day is None: 

141 day = self.day 

142 if hour is None: 

143 hour = self.hour 

144 if minute is None: 

145 minute = self.minute 

146 if second is None: 

147 second = self.second 

148 if microsecond is None: 

149 microsecond = self.microsecond 

150 if tz is None: 

151 tz = self.tz 

152 

153 return pendulum.datetime( 

154 year, month, day, hour, minute, second, microsecond, tz=tz 

155 ) 

156 

157 if not _HAS_FOLD: 

158 

159 @property 

160 def fold(self): 

161 return self._fold 

162 

163 def timestamp(self): 

164 if self.tzinfo is None: 

165 s = timestamp(self) 

166 

167 return s + self.microsecond / 1e6 

168 else: 

169 kwargs = {"tzinfo": self.tzinfo} 

170 

171 if _HAS_FOLD: 

172 kwargs["fold"] = self.fold 

173 

174 dt = datetime.datetime( 

175 self.year, 

176 self.month, 

177 self.day, 

178 self.hour, 

179 self.minute, 

180 self.second, 

181 self.microsecond, 

182 **kwargs 

183 ) 

184 return (dt - self._EPOCH).total_seconds() 

185 

186 @property 

187 def float_timestamp(self): 

188 return self.timestamp() 

189 

190 @property 

191 def int_timestamp(self): 

192 # Workaround needed to avoid inaccuracy 

193 # for far into the future datetimes 

194 kwargs = {"tzinfo": self.tzinfo} 

195 

196 if _HAS_FOLD: 

197 kwargs["fold"] = self.fold 

198 

199 dt = datetime.datetime( 

200 self.year, 

201 self.month, 

202 self.day, 

203 self.hour, 

204 self.minute, 

205 self.second, 

206 self.microsecond, 

207 **kwargs 

208 ) 

209 

210 delta = dt - self._EPOCH 

211 

212 return delta.days * SECONDS_PER_DAY + delta.seconds 

213 

214 @property 

215 def offset(self): 

216 return self.get_offset() 

217 

218 @property 

219 def offset_hours(self): 

220 return self.get_offset() / SECONDS_PER_MINUTE / MINUTES_PER_HOUR 

221 

222 @property 

223 def timezone(self): # type: () -> Optional[Timezone] 

224 if not isinstance(self.tzinfo, Timezone): 

225 return 

226 

227 return self.tzinfo 

228 

229 @property 

230 def tz(self): # type: () -> Optional[Timezone] 

231 return self.timezone 

232 

233 @property 

234 def timezone_name(self): # type: () -> Optional[str] 

235 tz = self.timezone 

236 

237 if tz is None: 

238 return None 

239 

240 return tz.name 

241 

242 @property 

243 def age(self): 

244 return self.date().diff(self.now(self.tz).date(), abs=False).in_years() 

245 

246 def is_local(self): 

247 return self.offset == self.in_timezone(pendulum.local_timezone()).offset 

248 

249 def is_utc(self): 

250 return self.offset == UTC.offset 

251 

252 def is_dst(self): 

253 return self.dst() != datetime.timedelta() 

254 

255 def get_offset(self): 

256 return int(self.utcoffset().total_seconds()) 

257 

258 def date(self): 

259 return Date(self.year, self.month, self.day) 

260 

261 def time(self): 

262 return Time(self.hour, self.minute, self.second, self.microsecond) 

263 

264 def naive(self): # type: (_D) -> _D 

265 """ 

266 Return the DateTime without timezone information. 

267 """ 

268 return self.__class__( 

269 self.year, 

270 self.month, 

271 self.day, 

272 self.hour, 

273 self.minute, 

274 self.second, 

275 self.microsecond, 

276 ) 

277 

278 def on(self, year, month, day): 

279 """ 

280 Returns a new instance with the current date set to a different date. 

281 

282 :param year: The year 

283 :type year: int 

284 

285 :param month: The month 

286 :type month: int 

287 

288 :param day: The day 

289 :type day: int 

290 

291 :rtype: DateTime 

292 """ 

293 return self.set(year=int(year), month=int(month), day=int(day)) 

294 

295 def at(self, hour, minute=0, second=0, microsecond=0): 

296 """ 

297 Returns a new instance with the current time to a different time. 

298 

299 :param hour: The hour 

300 :type hour: int 

301 

302 :param minute: The minute 

303 :type minute: int 

304 

305 :param second: The second 

306 :type second: int 

307 

308 :param microsecond: The microsecond 

309 :type microsecond: int 

310 

311 :rtype: DateTime 

312 """ 

313 return self.set( 

314 hour=hour, minute=minute, second=second, microsecond=microsecond 

315 ) 

316 

317 def in_timezone(self, tz): # type: (Union[str, Timezone]) -> DateTime 

318 """ 

319 Set the instance's timezone from a string or object. 

320 """ 

321 tz = pendulum._safe_timezone(tz) 

322 

323 return tz.convert(self, dst_rule=pendulum.POST_TRANSITION) 

324 

325 def in_tz(self, tz): # type: (Union[str, Timezone]) -> DateTime 

326 """ 

327 Set the instance's timezone from a string or object. 

328 """ 

329 return self.in_timezone(tz) 

330 

331 # STRING FORMATTING 

332 

333 def to_time_string(self): 

334 """ 

335 Format the instance as time. 

336 

337 :rtype: str 

338 """ 

339 return self.format("HH:mm:ss") 

340 

341 def to_datetime_string(self): 

342 """ 

343 Format the instance as date and time. 

344 

345 :rtype: str 

346 """ 

347 return self.format("YYYY-MM-DD HH:mm:ss") 

348 

349 def to_day_datetime_string(self): 

350 """ 

351 Format the instance as day, date and time (in english). 

352 

353 :rtype: str 

354 """ 

355 return self.format("ddd, MMM D, YYYY h:mm A", locale="en") 

356 

357 def to_atom_string(self): 

358 """ 

359 Format the instance as ATOM. 

360 

361 :rtype: str 

362 """ 

363 return self._to_string("atom") 

364 

365 def to_cookie_string(self): 

366 """ 

367 Format the instance as COOKIE. 

368 

369 :rtype: str 

370 """ 

371 return self._to_string("cookie", locale="en") 

372 

373 def to_iso8601_string(self): 

374 """ 

375 Format the instance as ISO 8601. 

376 

377 :rtype: str 

378 """ 

379 string = self._to_string("iso8601") 

380 

381 if self.tz and self.tz.name == "UTC": 

382 string = string.replace("+00:00", "Z") 

383 

384 return string 

385 

386 def to_rfc822_string(self): 

387 """ 

388 Format the instance as RFC 822. 

389 

390 :rtype: str 

391 """ 

392 return self._to_string("rfc822") 

393 

394 def to_rfc850_string(self): 

395 """ 

396 Format the instance as RFC 850. 

397 

398 :rtype: str 

399 """ 

400 return self._to_string("rfc850") 

401 

402 def to_rfc1036_string(self): 

403 """ 

404 Format the instance as RFC 1036. 

405 

406 :rtype: str 

407 """ 

408 return self._to_string("rfc1036") 

409 

410 def to_rfc1123_string(self): 

411 """ 

412 Format the instance as RFC 1123. 

413 

414 :rtype: str 

415 """ 

416 return self._to_string("rfc1123") 

417 

418 def to_rfc2822_string(self): 

419 """ 

420 Format the instance as RFC 2822. 

421 

422 :rtype: str 

423 """ 

424 return self._to_string("rfc2822") 

425 

426 def to_rfc3339_string(self): 

427 """ 

428 Format the instance as RFC 3339. 

429 

430 :rtype: str 

431 """ 

432 return self._to_string("rfc3339") 

433 

434 def to_rss_string(self): 

435 """ 

436 Format the instance as RSS. 

437 

438 :rtype: str 

439 """ 

440 return self._to_string("rss") 

441 

442 def to_w3c_string(self): 

443 """ 

444 Format the instance as W3C. 

445 

446 :rtype: str 

447 """ 

448 return self._to_string("w3c") 

449 

450 def _to_string(self, fmt, locale=None): 

451 """ 

452 Format the instance to a common string format. 

453 

454 :param fmt: The name of the string format 

455 :type fmt: string 

456 

457 :param locale: The locale to use 

458 :type locale: str or None 

459 

460 :rtype: str 

461 """ 

462 if fmt not in self._FORMATS: 

463 raise ValueError("Format [{}] is not supported".format(fmt)) 

464 

465 fmt = self._FORMATS[fmt] 

466 if callable(fmt): 

467 return fmt(self) 

468 

469 return self.format(fmt, locale=locale) 

470 

471 def __str__(self): 

472 return self.isoformat("T") 

473 

474 def __repr__(self): 

475 us = "" 

476 if self.microsecond: 

477 us = ", {}".format(self.microsecond) 

478 

479 repr_ = "{klass}(" "{year}, {month}, {day}, " "{hour}, {minute}, {second}{us}" 

480 

481 if self.tzinfo is not None: 

482 repr_ += ", tzinfo={tzinfo}" 

483 

484 repr_ += ")" 

485 

486 return repr_.format( 

487 klass=self.__class__.__name__, 

488 year=self.year, 

489 month=self.month, 

490 day=self.day, 

491 hour=self.hour, 

492 minute=self.minute, 

493 second=self.second, 

494 us=us, 

495 tzinfo=self.tzinfo, 

496 ) 

497 

498 # Comparisons 

499 def closest(self, dt1, dt2, *dts): 

500 """ 

501 Get the farthest date from the instance. 

502 

503 :type dt1: datetime.datetime 

504 :type dt2: datetime.datetime 

505 :type dts: list[datetime.datetime,] 

506 

507 :rtype: DateTime 

508 """ 

509 dt1 = pendulum.instance(dt1) 

510 dt2 = pendulum.instance(dt2) 

511 dts = [dt1, dt2] + [pendulum.instance(x) for x in dts] 

512 dts = [(abs(self - dt), dt) for dt in dts] 

513 

514 return min(dts)[1] 

515 

516 def farthest(self, dt1, dt2, *dts): 

517 """ 

518 Get the farthest date from the instance. 

519 

520 :type dt1: datetime.datetime 

521 :type dt2: datetime.datetime 

522 :type dts: list[datetime.datetime,] 

523 

524 :rtype: DateTime 

525 """ 

526 dt1 = pendulum.instance(dt1) 

527 dt2 = pendulum.instance(dt2) 

528 

529 dts = [dt1, dt2] + [pendulum.instance(x) for x in dts] 

530 dts = [(abs(self - dt), dt) for dt in dts] 

531 

532 return max(dts)[1] 

533 

534 def is_future(self): 

535 """ 

536 Determines if the instance is in the future, ie. greater than now. 

537 

538 :rtype: bool 

539 """ 

540 return self > self.now(self.timezone) 

541 

542 def is_past(self): 

543 """ 

544 Determines if the instance is in the past, ie. less than now. 

545 

546 :rtype: bool 

547 """ 

548 return self < self.now(self.timezone) 

549 

550 def is_long_year(self): 

551 """ 

552 Determines if the instance is a long year 

553 

554 See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ 

555 

556 :rtype: bool 

557 """ 

558 return ( 

559 pendulum.datetime(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] 

560 == 53 

561 ) 

562 

563 def is_same_day(self, dt): 

564 """ 

565 Checks if the passed in date is the same day 

566 as the instance current day. 

567 

568 :type dt: DateTime or datetime or str or int 

569 

570 :rtype: bool 

571 """ 

572 dt = pendulum.instance(dt) 

573 

574 return self.to_date_string() == dt.to_date_string() 

575 

576 def is_anniversary(self, dt=None): 

577 """ 

578 Check if its the anniversary. 

579 Compares the date/month values of the two dates. 

580 

581 :rtype: bool 

582 """ 

583 if dt is None: 

584 dt = self.now(self.tz) 

585 

586 instance = pendulum.instance(dt) 

587 

588 return (self.month, self.day) == (instance.month, instance.day) 

589 

590 # the additional method for checking if today is the anniversary day 

591 # the alias is provided to start using a new name and keep the backward compatibility 

592 # the old name can be completely replaced with the new in one of the future versions 

593 is_birthday = is_anniversary 

594 

595 # ADDITIONS AND SUBSTRACTIONS 

596 

597 def add( 

598 self, 

599 years=0, 

600 months=0, 

601 weeks=0, 

602 days=0, 

603 hours=0, 

604 minutes=0, 

605 seconds=0, 

606 microseconds=0, 

607 ): # type: (_D, int, int, int, int, int, int, int, int) -> _D 

608 """ 

609 Add a duration to the instance. 

610 

611 If we're adding units of variable length (i.e., years, months), 

612 move forward from curren time, 

613 otherwise move forward from utc, for accuracy 

614 when moving across DST boundaries. 

615 """ 

616 units_of_variable_length = any([years, months, weeks, days]) 

617 

618 current_dt = datetime.datetime( 

619 self.year, 

620 self.month, 

621 self.day, 

622 self.hour, 

623 self.minute, 

624 self.second, 

625 self.microsecond, 

626 ) 

627 if not units_of_variable_length: 

628 offset = self.utcoffset() 

629 if offset: 

630 current_dt = current_dt - offset 

631 

632 dt = add_duration( 

633 current_dt, 

634 years=years, 

635 months=months, 

636 weeks=weeks, 

637 days=days, 

638 hours=hours, 

639 minutes=minutes, 

640 seconds=seconds, 

641 microseconds=microseconds, 

642 ) 

643 

644 if units_of_variable_length or self.tzinfo is None: 

645 return pendulum.datetime( 

646 dt.year, 

647 dt.month, 

648 dt.day, 

649 dt.hour, 

650 dt.minute, 

651 dt.second, 

652 dt.microsecond, 

653 tz=self.tz, 

654 ) 

655 

656 dt = self.__class__( 

657 dt.year, 

658 dt.month, 

659 dt.day, 

660 dt.hour, 

661 dt.minute, 

662 dt.second, 

663 dt.microsecond, 

664 tzinfo=UTC, 

665 ) 

666 

667 dt = self.tz.convert(dt) 

668 

669 return self.__class__( 

670 dt.year, 

671 dt.month, 

672 dt.day, 

673 dt.hour, 

674 dt.minute, 

675 dt.second, 

676 dt.microsecond, 

677 tzinfo=self.tz, 

678 fold=dt.fold, 

679 ) 

680 

681 def subtract( 

682 self, 

683 years=0, 

684 months=0, 

685 weeks=0, 

686 days=0, 

687 hours=0, 

688 minutes=0, 

689 seconds=0, 

690 microseconds=0, 

691 ): 

692 """ 

693 Remove duration from the instance. 

694 

695 :param years: The number of years 

696 :type years: int 

697 

698 :param months: The number of months 

699 :type months: int 

700 

701 :param weeks: The number of weeks 

702 :type weeks: int 

703 

704 :param days: The number of days 

705 :type days: int 

706 

707 :param hours: The number of hours 

708 :type hours: int 

709 

710 :param minutes: The number of minutes 

711 :type minutes: int 

712 

713 :param seconds: The number of seconds 

714 :type seconds: int 

715 

716 :param microseconds: The number of microseconds 

717 :type microseconds: int 

718 

719 :rtype: DateTime 

720 """ 

721 return self.add( 

722 years=-years, 

723 months=-months, 

724 weeks=-weeks, 

725 days=-days, 

726 hours=-hours, 

727 minutes=-minutes, 

728 seconds=-seconds, 

729 microseconds=-microseconds, 

730 ) 

731 

732 # Adding a final underscore to the method name 

733 # to avoid errors for PyPy which already defines 

734 # a _add_timedelta method 

735 def _add_timedelta_(self, delta): 

736 """ 

737 Add timedelta duration to the instance. 

738 

739 :param delta: The timedelta instance 

740 :type delta: pendulum.Duration or datetime.timedelta 

741 

742 :rtype: DateTime 

743 """ 

744 if isinstance(delta, pendulum.Period): 

745 return self.add( 

746 years=delta.years, 

747 months=delta.months, 

748 weeks=delta.weeks, 

749 days=delta.remaining_days, 

750 hours=delta.hours, 

751 minutes=delta.minutes, 

752 seconds=delta.remaining_seconds, 

753 microseconds=delta.microseconds, 

754 ) 

755 elif isinstance(delta, pendulum.Duration): 

756 return self.add( 

757 years=delta.years, months=delta.months, seconds=delta._total 

758 ) 

759 

760 return self.add(seconds=delta.total_seconds()) 

761 

762 def _subtract_timedelta(self, delta): 

763 """ 

764 Remove timedelta duration from the instance. 

765 

766 :param delta: The timedelta instance 

767 :type delta: pendulum.Duration or datetime.timedelta 

768 

769 :rtype: DateTime 

770 """ 

771 if isinstance(delta, pendulum.Duration): 

772 return self.subtract( 

773 years=delta.years, months=delta.months, seconds=delta._total 

774 ) 

775 

776 return self.subtract(seconds=delta.total_seconds()) 

777 

778 # DIFFERENCES 

779 

780 def diff(self, dt=None, abs=True): 

781 """ 

782 Returns the difference between two DateTime objects represented as a Duration. 

783 

784 :type dt: DateTime or None 

785 

786 :param abs: Whether to return an absolute interval or not 

787 :type abs: bool 

788 

789 :rtype: Period 

790 """ 

791 if dt is None: 

792 dt = self.now(self.tz) 

793 

794 return Period(self, dt, absolute=abs) 

795 

796 def diff_for_humans( 

797 self, 

798 other=None, # type: Optional[DateTime] 

799 absolute=False, # type: bool 

800 locale=None, # type: Optional[str] 

801 ): # type: (...) -> str 

802 """ 

803 Get the difference in a human readable format in the current locale. 

804 

805 When comparing a value in the past to default now: 

806 1 day ago 

807 5 months ago 

808 

809 When comparing a value in the future to default now: 

810 1 day from now 

811 5 months from now 

812 

813 When comparing a value in the past to another value: 

814 1 day before 

815 5 months before 

816 

817 When comparing a value in the future to another value: 

818 1 day after 

819 5 months after 

820 """ 

821 is_now = other is None 

822 

823 if is_now: 

824 other = self.now() 

825 

826 diff = self.diff(other) 

827 

828 return pendulum.format_diff(diff, is_now, absolute, locale) 

829 

830 # Modifiers 

831 def start_of(self, unit): 

832 """ 

833 Returns a copy of the instance with the time reset 

834 with the following rules: 

835 

836 * second: microsecond set to 0 

837 * minute: second and microsecond set to 0 

838 * hour: minute, second and microsecond set to 0 

839 * day: time to 00:00:00 

840 * week: date to first day of the week and time to 00:00:00 

841 * month: date to first day of the month and time to 00:00:00 

842 * year: date to first day of the year and time to 00:00:00 

843 * decade: date to first day of the decade and time to 00:00:00 

844 * century: date to first day of century and time to 00:00:00 

845 

846 :param unit: The unit to reset to 

847 :type unit: str 

848 

849 :rtype: DateTime 

850 """ 

851 if unit not in self._MODIFIERS_VALID_UNITS: 

852 raise ValueError('Invalid unit "{}" for start_of()'.format(unit)) 

853 

854 return getattr(self, "_start_of_{}".format(unit))() 

855 

856 def end_of(self, unit): 

857 """ 

858 Returns a copy of the instance with the time reset 

859 with the following rules: 

860 

861 * second: microsecond set to 999999 

862 * minute: second set to 59 and microsecond set to 999999 

863 * hour: minute and second set to 59 and microsecond set to 999999 

864 * day: time to 23:59:59.999999 

865 * week: date to last day of the week and time to 23:59:59.999999 

866 * month: date to last day of the month and time to 23:59:59.999999 

867 * year: date to last day of the year and time to 23:59:59.999999 

868 * decade: date to last day of the decade and time to 23:59:59.999999 

869 * century: date to last day of century and time to 23:59:59.999999 

870 

871 :param unit: The unit to reset to 

872 :type unit: str 

873 

874 :rtype: DateTime 

875 """ 

876 if unit not in self._MODIFIERS_VALID_UNITS: 

877 raise ValueError('Invalid unit "%s" for end_of()' % unit) 

878 

879 return getattr(self, "_end_of_%s" % unit)() 

880 

881 def _start_of_second(self): 

882 """ 

883 Reset microseconds to 0. 

884 

885 :rtype: DateTime 

886 """ 

887 return self.set(microsecond=0) 

888 

889 def _end_of_second(self): 

890 """ 

891 Set microseconds to 999999. 

892 

893 :rtype: DateTime 

894 """ 

895 return self.set(microsecond=999999) 

896 

897 def _start_of_minute(self): 

898 """ 

899 Reset seconds and microseconds to 0. 

900 

901 :rtype: DateTime 

902 """ 

903 return self.set(second=0, microsecond=0) 

904 

905 def _end_of_minute(self): 

906 """ 

907 Set seconds to 59 and microseconds to 999999. 

908 

909 :rtype: DateTime 

910 """ 

911 return self.set(second=59, microsecond=999999) 

912 

913 def _start_of_hour(self): 

914 """ 

915 Reset minutes, seconds and microseconds to 0. 

916 

917 :rtype: DateTime 

918 """ 

919 return self.set(minute=0, second=0, microsecond=0) 

920 

921 def _end_of_hour(self): 

922 """ 

923 Set minutes and seconds to 59 and microseconds to 999999. 

924 

925 :rtype: DateTime 

926 """ 

927 return self.set(minute=59, second=59, microsecond=999999) 

928 

929 def _start_of_day(self): 

930 """ 

931 Reset the time to 00:00:00 

932 

933 :rtype: DateTime 

934 """ 

935 return self.at(0, 0, 0, 0) 

936 

937 def _end_of_day(self): 

938 """ 

939 Reset the time to 23:59:59.999999 

940 

941 :rtype: DateTime 

942 """ 

943 return self.at(23, 59, 59, 999999) 

944 

945 def _start_of_month(self): 

946 """ 

947 Reset the date to the first day of the month and the time to 00:00:00. 

948 

949 :rtype: DateTime 

950 """ 

951 return self.set(self.year, self.month, 1, 0, 0, 0, 0) 

952 

953 def _end_of_month(self): 

954 """ 

955 Reset the date to the last day of the month 

956 and the time to 23:59:59.999999. 

957 

958 :rtype: DateTime 

959 """ 

960 return self.set(self.year, self.month, self.days_in_month, 23, 59, 59, 999999) 

961 

962 def _start_of_year(self): 

963 """ 

964 Reset the date to the first day of the year and the time to 00:00:00. 

965 

966 :rtype: DateTime 

967 """ 

968 return self.set(self.year, 1, 1, 0, 0, 0, 0) 

969 

970 def _end_of_year(self): 

971 """ 

972 Reset the date to the last day of the year 

973 and the time to 23:59:59.999999 

974 

975 :rtype: DateTime 

976 """ 

977 return self.set(self.year, 12, 31, 23, 59, 59, 999999) 

978 

979 def _start_of_decade(self): 

980 """ 

981 Reset the date to the first day of the decade 

982 and the time to 00:00:00. 

983 

984 :rtype: DateTime 

985 """ 

986 year = self.year - self.year % YEARS_PER_DECADE 

987 return self.set(year, 1, 1, 0, 0, 0, 0) 

988 

989 def _end_of_decade(self): 

990 """ 

991 Reset the date to the last day of the decade 

992 and the time to 23:59:59.999999. 

993 

994 :rtype: DateTime 

995 """ 

996 year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1 

997 

998 return self.set(year, 12, 31, 23, 59, 59, 999999) 

999 

1000 def _start_of_century(self): 

1001 """ 

1002 Reset the date to the first day of the century 

1003 and the time to 00:00:00. 

1004 

1005 :rtype: DateTime 

1006 """ 

1007 year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1 

1008 

1009 return self.set(year, 1, 1, 0, 0, 0, 0) 

1010 

1011 def _end_of_century(self): 

1012 """ 

1013 Reset the date to the last day of the century 

1014 and the time to 23:59:59.999999. 

1015 

1016 :rtype: DateTime 

1017 """ 

1018 year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY 

1019 

1020 return self.set(year, 12, 31, 23, 59, 59, 999999) 

1021 

1022 def _start_of_week(self): 

1023 """ 

1024 Reset the date to the first day of the week 

1025 and the time to 00:00:00. 

1026 

1027 :rtype: DateTime 

1028 """ 

1029 dt = self 

1030 

1031 if self.day_of_week != pendulum._WEEK_STARTS_AT: 

1032 dt = self.previous(pendulum._WEEK_STARTS_AT) 

1033 

1034 return dt.start_of("day") 

1035 

1036 def _end_of_week(self): 

1037 """ 

1038 Reset the date to the last day of the week 

1039 and the time to 23:59:59. 

1040 

1041 :rtype: DateTime 

1042 """ 

1043 dt = self 

1044 

1045 if self.day_of_week != pendulum._WEEK_ENDS_AT: 

1046 dt = self.next(pendulum._WEEK_ENDS_AT) 

1047 

1048 return dt.end_of("day") 

1049 

1050 def next(self, day_of_week=None, keep_time=False): 

1051 """ 

1052 Modify to the next occurrence of a given day of the week. 

1053 If no day_of_week is provided, modify to the next occurrence 

1054 of the current day of the week. Use the supplied consts 

1055 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1056 

1057 :param day_of_week: The next day of week to reset to. 

1058 :type day_of_week: int or None 

1059 

1060 :param keep_time: Whether to keep the time information or not. 

1061 :type keep_time: bool 

1062 

1063 :rtype: DateTime 

1064 """ 

1065 if day_of_week is None: 

1066 day_of_week = self.day_of_week 

1067 

1068 if day_of_week < SUNDAY or day_of_week > SATURDAY: 

1069 raise ValueError("Invalid day of week") 

1070 

1071 if keep_time: 

1072 dt = self 

1073 else: 

1074 dt = self.start_of("day") 

1075 

1076 dt = dt.add(days=1) 

1077 while dt.day_of_week != day_of_week: 

1078 dt = dt.add(days=1) 

1079 

1080 return dt 

1081 

1082 def previous(self, day_of_week=None, keep_time=False): 

1083 """ 

1084 Modify to the previous occurrence of a given day of the week. 

1085 If no day_of_week is provided, modify to the previous occurrence 

1086 of the current day of the week. Use the supplied consts 

1087 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1088 

1089 :param day_of_week: The previous day of week to reset to. 

1090 :type day_of_week: int or None 

1091 

1092 :param keep_time: Whether to keep the time information or not. 

1093 :type keep_time: bool 

1094 

1095 :rtype: DateTime 

1096 """ 

1097 if day_of_week is None: 

1098 day_of_week = self.day_of_week 

1099 

1100 if day_of_week < SUNDAY or day_of_week > SATURDAY: 

1101 raise ValueError("Invalid day of week") 

1102 

1103 if keep_time: 

1104 dt = self 

1105 else: 

1106 dt = self.start_of("day") 

1107 

1108 dt = dt.subtract(days=1) 

1109 while dt.day_of_week != day_of_week: 

1110 dt = dt.subtract(days=1) 

1111 

1112 return dt 

1113 

1114 def first_of(self, unit, day_of_week=None): 

1115 """ 

1116 Returns an instance set to the first occurrence 

1117 of a given day of the week in the current unit. 

1118 If no day_of_week is provided, modify to the first day of the unit. 

1119 Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1120 

1121 Supported units are month, quarter and year. 

1122 

1123 :param unit: The unit to use 

1124 :type unit: str 

1125 

1126 :type day_of_week: int or None 

1127 

1128 :rtype: DateTime 

1129 """ 

1130 if unit not in ["month", "quarter", "year"]: 

1131 raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) 

1132 

1133 return getattr(self, "_first_of_{}".format(unit))(day_of_week) 

1134 

1135 def last_of(self, unit, day_of_week=None): 

1136 """ 

1137 Returns an instance set to the last occurrence 

1138 of a given day of the week in the current unit. 

1139 If no day_of_week is provided, modify to the last day of the unit. 

1140 Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1141 

1142 Supported units are month, quarter and year. 

1143 

1144 :param unit: The unit to use 

1145 :type unit: str 

1146 

1147 :type day_of_week: int or None 

1148 

1149 :rtype: DateTime 

1150 """ 

1151 if unit not in ["month", "quarter", "year"]: 

1152 raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) 

1153 

1154 return getattr(self, "_last_of_{}".format(unit))(day_of_week) 

1155 

1156 def nth_of(self, unit, nth, day_of_week): 

1157 """ 

1158 Returns a new instance set to the given occurrence 

1159 of a given day of the week in the current unit. 

1160 If the calculated occurrence is outside the scope of the current unit, 

1161 then raise an error. Use the supplied consts 

1162 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1163 

1164 Supported units are month, quarter and year. 

1165 

1166 :param unit: The unit to use 

1167 :type unit: str 

1168 

1169 :type nth: int 

1170 

1171 :type day_of_week: int or None 

1172 

1173 :rtype: DateTime 

1174 """ 

1175 if unit not in ["month", "quarter", "year"]: 

1176 raise ValueError('Invalid unit "{}" for first_of()'.format(unit)) 

1177 

1178 dt = getattr(self, "_nth_of_{}".format(unit))(nth, day_of_week) 

1179 if dt is False: 

1180 raise PendulumException( 

1181 "Unable to find occurence {} of {} in {}".format( 

1182 nth, self._days[day_of_week], unit 

1183 ) 

1184 ) 

1185 

1186 return dt 

1187 

1188 def _first_of_month(self, day_of_week): 

1189 """ 

1190 Modify to the first occurrence of a given day of the week 

1191 in the current month. If no day_of_week is provided, 

1192 modify to the first day of the month. Use the supplied consts 

1193 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1194 

1195 :type day_of_week: int 

1196 

1197 :rtype: DateTime 

1198 """ 

1199 dt = self.start_of("day") 

1200 

1201 if day_of_week is None: 

1202 return dt.set(day=1) 

1203 

1204 month = calendar.monthcalendar(dt.year, dt.month) 

1205 

1206 calendar_day = (day_of_week - 1) % 7 

1207 

1208 if month[0][calendar_day] > 0: 

1209 day_of_month = month[0][calendar_day] 

1210 else: 

1211 day_of_month = month[1][calendar_day] 

1212 

1213 return dt.set(day=day_of_month) 

1214 

1215 def _last_of_month(self, day_of_week=None): 

1216 """ 

1217 Modify to the last occurrence of a given day of the week 

1218 in the current month. If no day_of_week is provided, 

1219 modify to the last day of the month. Use the supplied consts 

1220 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1221 

1222 :type day_of_week: int or None 

1223 

1224 :rtype: DateTime 

1225 """ 

1226 dt = self.start_of("day") 

1227 

1228 if day_of_week is None: 

1229 return dt.set(day=self.days_in_month) 

1230 

1231 month = calendar.monthcalendar(dt.year, dt.month) 

1232 

1233 calendar_day = (day_of_week - 1) % 7 

1234 

1235 if month[-1][calendar_day] > 0: 

1236 day_of_month = month[-1][calendar_day] 

1237 else: 

1238 day_of_month = month[-2][calendar_day] 

1239 

1240 return dt.set(day=day_of_month) 

1241 

1242 def _nth_of_month(self, nth, day_of_week): 

1243 """ 

1244 Modify to the given occurrence of a given day of the week 

1245 in the current month. If the calculated occurrence is outside, 

1246 the scope of the current month, then return False and no 

1247 modifications are made. Use the supplied consts 

1248 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1249 

1250 :type nth: int 

1251 

1252 :type day_of_week: int or None 

1253 

1254 :rtype: DateTime 

1255 """ 

1256 if nth == 1: 

1257 return self.first_of("month", day_of_week) 

1258 

1259 dt = self.first_of("month") 

1260 check = dt.format("%Y-%M") 

1261 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)): 

1262 dt = dt.next(day_of_week) 

1263 

1264 if dt.format("%Y-%M") == check: 

1265 return self.set(day=dt.day).start_of("day") 

1266 

1267 return False 

1268 

1269 def _first_of_quarter(self, day_of_week=None): 

1270 """ 

1271 Modify to the first occurrence of a given day of the week 

1272 in the current quarter. If no day_of_week is provided, 

1273 modify to the first day of the quarter. Use the supplied consts 

1274 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1275 

1276 :type day_of_week: int or None 

1277 

1278 :rtype: DateTime 

1279 """ 

1280 return self.on(self.year, self.quarter * 3 - 2, 1).first_of( 

1281 "month", day_of_week 

1282 ) 

1283 

1284 def _last_of_quarter(self, day_of_week=None): 

1285 """ 

1286 Modify to the last occurrence of a given day of the week 

1287 in the current quarter. If no day_of_week is provided, 

1288 modify to the last day of the quarter. Use the supplied consts 

1289 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1290 

1291 :type day_of_week: int or None 

1292 

1293 :rtype: DateTime 

1294 """ 

1295 return self.on(self.year, self.quarter * 3, 1).last_of("month", day_of_week) 

1296 

1297 def _nth_of_quarter(self, nth, day_of_week): 

1298 """ 

1299 Modify to the given occurrence of a given day of the week 

1300 in the current quarter. If the calculated occurrence is outside, 

1301 the scope of the current quarter, then return False and no 

1302 modifications are made. Use the supplied consts 

1303 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1304 

1305 :type nth: int 

1306 

1307 :type day_of_week: int or None 

1308 

1309 :rtype: DateTime 

1310 """ 

1311 if nth == 1: 

1312 return self.first_of("quarter", day_of_week) 

1313 

1314 dt = self.set(day=1, month=self.quarter * 3) 

1315 last_month = dt.month 

1316 year = dt.year 

1317 dt = dt.first_of("quarter") 

1318 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)): 

1319 dt = dt.next(day_of_week) 

1320 

1321 if last_month < dt.month or year != dt.year: 

1322 return False 

1323 

1324 return self.on(self.year, dt.month, dt.day).start_of("day") 

1325 

1326 def _first_of_year(self, day_of_week=None): 

1327 """ 

1328 Modify to the first occurrence of a given day of the week 

1329 in the current year. If no day_of_week is provided, 

1330 modify to the first day of the year. Use the supplied consts 

1331 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1332 

1333 :type day_of_week: int or None 

1334 

1335 :rtype: DateTime 

1336 """ 

1337 return self.set(month=1).first_of("month", day_of_week) 

1338 

1339 def _last_of_year(self, day_of_week=None): 

1340 """ 

1341 Modify to the last occurrence of a given day of the week 

1342 in the current year. If no day_of_week is provided, 

1343 modify to the last day of the year. Use the supplied consts 

1344 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1345 

1346 :type day_of_week: int or None 

1347 

1348 :rtype: DateTime 

1349 """ 

1350 return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week) 

1351 

1352 def _nth_of_year(self, nth, day_of_week): 

1353 """ 

1354 Modify to the given occurrence of a given day of the week 

1355 in the current year. If the calculated occurrence is outside, 

1356 the scope of the current year, then return False and no 

1357 modifications are made. Use the supplied consts 

1358 to indicate the desired day_of_week, ex. DateTime.MONDAY. 

1359 

1360 :type nth: int 

1361 

1362 :type day_of_week: int or None 

1363 

1364 :rtype: DateTime 

1365 """ 

1366 if nth == 1: 

1367 return self.first_of("year", day_of_week) 

1368 

1369 dt = self.first_of("year") 

1370 year = dt.year 

1371 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)): 

1372 dt = dt.next(day_of_week) 

1373 

1374 if year != dt.year: 

1375 return False 

1376 

1377 return self.on(self.year, dt.month, dt.day).start_of("day") 

1378 

1379 def average(self, dt=None): 

1380 """ 

1381 Modify the current instance to the average 

1382 of a given instance (default now) and the current instance. 

1383 

1384 :type dt: DateTime or datetime 

1385 

1386 :rtype: DateTime 

1387 """ 

1388 if dt is None: 

1389 dt = self.now(self.tz) 

1390 

1391 diff = self.diff(dt, False) 

1392 return self.add( 

1393 microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2 

1394 ) 

1395 

1396 def __sub__(self, other): 

1397 if isinstance(other, datetime.timedelta): 

1398 return self._subtract_timedelta(other) 

1399 

1400 if not isinstance(other, datetime.datetime): 

1401 return NotImplemented 

1402 

1403 if not isinstance(other, self.__class__): 

1404 if other.tzinfo is None: 

1405 other = pendulum.naive( 

1406 other.year, 

1407 other.month, 

1408 other.day, 

1409 other.hour, 

1410 other.minute, 

1411 other.second, 

1412 other.microsecond, 

1413 ) 

1414 else: 

1415 other = pendulum.instance(other) 

1416 

1417 return other.diff(self, False) 

1418 

1419 def __rsub__(self, other): 

1420 if not isinstance(other, datetime.datetime): 

1421 return NotImplemented 

1422 

1423 if not isinstance(other, self.__class__): 

1424 if other.tzinfo is None: 

1425 other = pendulum.naive( 

1426 other.year, 

1427 other.month, 

1428 other.day, 

1429 other.hour, 

1430 other.minute, 

1431 other.second, 

1432 other.microsecond, 

1433 ) 

1434 else: 

1435 other = pendulum.instance(other) 

1436 

1437 return self.diff(other, False) 

1438 

1439 def __add__(self, other): 

1440 if not isinstance(other, datetime.timedelta): 

1441 return NotImplemented 

1442 

1443 return self._add_timedelta_(other) 

1444 

1445 def __radd__(self, other): 

1446 return self.__add__(other) 

1447 

1448 # Native methods override 

1449 

1450 @classmethod 

1451 def fromtimestamp(cls, t, tz=None): 

1452 return pendulum.instance(datetime.datetime.fromtimestamp(t, tz=tz), tz=tz) 

1453 

1454 @classmethod 

1455 def utcfromtimestamp(cls, t): 

1456 return pendulum.instance(datetime.datetime.utcfromtimestamp(t), tz=None) 

1457 

1458 @classmethod 

1459 def fromordinal(cls, n): 

1460 return pendulum.instance(datetime.datetime.fromordinal(n), tz=None) 

1461 

1462 @classmethod 

1463 def combine(cls, date, time): 

1464 return pendulum.instance(datetime.datetime.combine(date, time), tz=None) 

1465 

1466 def astimezone(self, tz=None): 

1467 return pendulum.instance(super(DateTime, self).astimezone(tz)) 

1468 

1469 def replace( 

1470 self, 

1471 year=None, 

1472 month=None, 

1473 day=None, 

1474 hour=None, 

1475 minute=None, 

1476 second=None, 

1477 microsecond=None, 

1478 tzinfo=True, 

1479 fold=None, 

1480 ): 

1481 if year is None: 

1482 year = self.year 

1483 if month is None: 

1484 month = self.month 

1485 if day is None: 

1486 day = self.day 

1487 if hour is None: 

1488 hour = self.hour 

1489 if minute is None: 

1490 minute = self.minute 

1491 if second is None: 

1492 second = self.second 

1493 if microsecond is None: 

1494 microsecond = self.microsecond 

1495 if tzinfo is True: 

1496 tzinfo = self.tzinfo 

1497 if fold is None: 

1498 fold = self.fold 

1499 

1500 transition_rule = pendulum.POST_TRANSITION 

1501 if fold is not None: 

1502 transition_rule = pendulum.PRE_TRANSITION 

1503 if fold: 

1504 transition_rule = pendulum.POST_TRANSITION 

1505 

1506 return pendulum.datetime( 

1507 year, 

1508 month, 

1509 day, 

1510 hour, 

1511 minute, 

1512 second, 

1513 microsecond, 

1514 tz=tzinfo, 

1515 dst_rule=transition_rule, 

1516 ) 

1517 

1518 def __getnewargs__(self): 

1519 return (self,) 

1520 

1521 def _getstate(self, protocol=3): 

1522 return ( 

1523 self.year, 

1524 self.month, 

1525 self.day, 

1526 self.hour, 

1527 self.minute, 

1528 self.second, 

1529 self.microsecond, 

1530 self.tzinfo, 

1531 ) 

1532 

1533 def __reduce__(self): 

1534 return self.__reduce_ex__(2) 

1535 

1536 def __reduce_ex__(self, protocol): 

1537 return self.__class__, self._getstate(protocol) 

1538 

1539 def _cmp(self, other, **kwargs): 

1540 # Fix for pypy which compares using this method 

1541 # which would lead to infinite recursion if we didn't override 

1542 kwargs = {"tzinfo": self.tz} 

1543 

1544 if _HAS_FOLD: 

1545 kwargs["fold"] = self.fold 

1546 

1547 dt = datetime.datetime( 

1548 self.year, 

1549 self.month, 

1550 self.day, 

1551 self.hour, 

1552 self.minute, 

1553 self.second, 

1554 self.microsecond, 

1555 **kwargs 

1556 ) 

1557 

1558 return 0 if dt == other else 1 if dt > other else -1 

1559 

1560 

1561DateTime.min = DateTime(1, 1, 1, 0, 0, tzinfo=UTC) 

1562DateTime.max = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC) 

1563DateTime.EPOCH = DateTime(1970, 1, 1)