datehelper.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python
  3. """
  4. A set of utils for datetime/date/ objects
  5. Majorly borrowed from maya.
  6. We don't need to import maya for this since we don't need to parse timezones and other non-trivial datetime operations for now.
  7. """
  8. import datetime
  9. from .utils import validate_arguments_type_of_function
  10. def _seconds_or_timedelta(duration):
  11. """Returns `datetime.timedelta` object for the passed duration.
  12. Keyword Arguments:
  13. duration -- `datetime.timedelta` object or seconds in `int` format.
  14. """
  15. if isinstance(duration, int):
  16. dt_timedelta = datetime.timedelta(seconds=duration)
  17. elif isinstance(duration, datetime.timedelta):
  18. dt_timedelta = duration
  19. else:
  20. raise TypeError(
  21. 'Expects argument as `datetime.timedelta` object '
  22. 'or seconds in `int` format'
  23. )
  24. return dt_timedelta
  25. def intervals(start, end, interval):
  26. """
  27. Yields MayaDT objects between the start and end MayaDTs given,
  28. at a given interval (seconds or timedelta).
  29. """
  30. interval = _seconds_or_timedelta(interval)
  31. current_timestamp = start
  32. while current_timestamp.epoch < end.epoch:
  33. yield current_timestamp
  34. current_timestamp = current_timestamp.add(seconds=interval.seconds)
  35. class Interval(object):
  36. def __init__(self, start=None, end=None, duration=None):
  37. try:
  38. # Ensure that proper arguments were passed.
  39. assert any(
  40. (
  41. (start and end),
  42. (start and duration is not None),
  43. (end and duration is not None),
  44. )
  45. )
  46. assert not all((start, end, duration is not None))
  47. except AssertionError:
  48. raise ValueError(
  49. 'Exactly 2 of start, end, and duration must be specified'
  50. )
  51. # Convert duration to timedelta if seconds were provided.
  52. if duration:
  53. duration = _seconds_or_timedelta(duration)
  54. if not start:
  55. start = end - duration
  56. if not end:
  57. end = start + duration
  58. if start > end:
  59. raise ValueError('MayaInterval cannot end before it starts')
  60. self.start = start
  61. self.end = end
  62. @property
  63. def duration(self):
  64. return self.timedelta.total_seconds()
  65. @property
  66. def timedelta(self):
  67. return datetime.timedelta(seconds=(self.end.epoch - self.start.epoch))
  68. @property
  69. def is_instant(self):
  70. return self.timedelta == datetime.timedelta(seconds=0)
  71. @validate_arguments_type_of_function()
  72. def __cmp__(self, maya_interval):
  73. return (
  74. cmp(self.start, maya_interval.start)
  75. or cmp(self.end, maya_interval.end)
  76. )
  77. @validate_arguments_type_of_function()
  78. def intersection(self, maya_interval):
  79. """Returns the intersection between two intervals."""
  80. start = max(self.start, maya_interval.start)
  81. end = min(self.end, maya_interval.end)
  82. either_instant = self.is_instant or maya_interval.is_instant
  83. instant_overlap = (self.start == maya_interval.start or start <= end)
  84. if (either_instant and instant_overlap) or (start < end):
  85. return Interval(start, end)
  86. @validate_arguments_type_of_function()
  87. def contains(self, maya_interval):
  88. return (
  89. self.start <= maya_interval.start and self.end >= maya_interval.end
  90. )
  91. def __contains__(self, maya_dt):
  92. if isinstance(maya_dt, datetime.datetime):
  93. return self.contains_dt(maya_dt)
  94. return self.contains(maya_dt)
  95. def contains_dt(self, dt):
  96. return self.start <= dt < self.end
  97. @validate_arguments_type_of_function()
  98. def is_adjacent(self, maya_interval):
  99. return (
  100. self.start == maya_interval.end or self.end == maya_interval.start
  101. )