1
0

DateTimeHelper.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <?php
  2. namespace fphammerle\helpers;
  3. class DateTimeHelper
  4. {
  5. const _timezone_iso_pattern = '(Z|[\+-]\d{2}.\d{2})';
  6. /**
  7. * @param integer|null $timestamp unix timestamp
  8. * @return \DateTime|null
  9. */
  10. public static function timestampToDateTime($timestamp)
  11. {
  12. if($timestamp === null) {
  13. return null;
  14. } elseif(is_int($timestamp)) {
  15. $dt = new \DateTime();
  16. $dt->setTimestamp($timestamp);
  17. return $dt;
  18. } else {
  19. throw new \InvalidArgumentException('expected integer or null');
  20. }
  21. }
  22. /**
  23. * @param string|null $text
  24. * @return \DatePeriod|null
  25. */
  26. public static function parse($text)
  27. {
  28. if($text === null) {
  29. return null;
  30. } else {
  31. if(preg_match('/^\d{4}-(?P<m>\d{2})( ?' . self::_timezone_iso_pattern . ')?$/', $text, $attr)) {
  32. $start = new \DateTime($text);
  33. $interval = new \DateInterval('P1M');
  34. return new \DatePeriod($start, $interval, 0);
  35. } elseif(preg_match(
  36. '/^(?P<y>\d{4})-(?P<m>\d{2})(-(?P<d>\d{2})'
  37. .'([ T](?P<h>\d{2}):(?P<i>\d{2})(:(?P<s>\d{2}))?)?)?'
  38. . '(' . self::_timezone_iso_pattern . ')?$/',
  39. $text,
  40. $attr
  41. )) {
  42. $start = new \DateTime($text);
  43. if(!empty($attr['s'])) {
  44. $interval = new \DateInterval('PT1S');
  45. } elseif(!empty($attr['i'])) {
  46. $interval = new \DateInterval('PT1M');
  47. } else {
  48. $interval = new \DateInterval('P1D');
  49. }
  50. return new \DatePeriod($start, $interval, 0);
  51. } else {
  52. throw new \InvalidArgumentException(
  53. sprintf("could not parse string '%s'", $text)
  54. );
  55. }
  56. }
  57. }
  58. /**
  59. * @param string|null $text
  60. * @return \DateTime|null
  61. */
  62. public static function parseGetStart($text)
  63. {
  64. $period = self::parse($text);
  65. if($period) {
  66. return $period->start;
  67. } else {
  68. return null;
  69. }
  70. }
  71. public static function deinvertInterval(\DateInterval $source = null)
  72. {
  73. // \DateInterval does not implement clone.
  74. // @see https://bugs.php.net/bug.php?id=50559
  75. $result = unserialize(serialize($source));
  76. if($result->invert) {
  77. $result->y *= -1;
  78. $result->m *= -1;
  79. $result->d *= -1;
  80. $result->h *= -1;
  81. $result->i *= -1;
  82. $result->s *= -1;
  83. $result->invert = 0;
  84. }
  85. return $result;
  86. }
  87. /**
  88. * @param \DateInterval|null $i
  89. * @return string|null
  90. */
  91. public static function intervalToIso(\DateInterval $i = null)
  92. {
  93. if(is_null($i)) {
  94. return null;
  95. } elseif(sizeof(get_object_vars($i)) == 0) {
  96. throw new \InvalidArgumentException(
  97. sprintf("given interval is invalid\n%s", print_r($i, true))
  98. );
  99. } else {
  100. $i = self::deinvertInterval($i);
  101. if($i->y < 0 || $i->m < 0 || $i->d < 0 || $i->h < 0 || $i->i < 0 || $i->s < 0) {
  102. throw new \Exception('negative intervals are not supported');
  103. } else {
  104. return $i->format('P%yY%mM%dDT%hH%iM%sS');
  105. }
  106. }
  107. }
  108. /**
  109. * @param \DatePeriod|null $p
  110. * @return string|null
  111. */
  112. public static function periodToIso(\DatePeriod $p = null)
  113. {
  114. if(is_null($p)) {
  115. return null;
  116. } else {
  117. // Cave:
  118. // (new \DatePeriod(
  119. // new \DateTime('2016-08-05T14:50:14Z'),
  120. // new \DateInterval('P1D'),
  121. // -1
  122. // )->recurrences == 1
  123. if($p->recurrences <= 0) {
  124. throw new \Exception(
  125. 'conversion of periods with number of occurances'
  126. . ' being negative is not supported'
  127. );
  128. }
  129. $repetitions = -1;
  130. foreach($p as $dt) {
  131. $repetitions++;
  132. // printf("%d. %s\n", $repetitions, $dt->format(\DateTime::ATOM));
  133. }
  134. // \DatePeriod::getStartDate() is available from php 5.6.5.
  135. $start_iso = $p->start->format(\DateTime::ATOM);
  136. // \DatePeriod::getDateInterval() is available from php 5.6.5.
  137. // \DatePeriod::$interval returned an invalid \DatePeriod instance
  138. // in php 7.0.8
  139. $interval_iso = self::intervalToIso(get_object_vars($p)['interval']);
  140. switch($repetitions) {
  141. case -1:
  142. // no valid date within period
  143. // e.g. new \DatePeriod(
  144. // new \DateTime('2016-08-05T14:50:14+08:00'),
  145. // new \DateInterval('PT1S'),
  146. // new \DateTime('2016-08-05T14:50:14+08:00')
  147. // )
  148. throw new \InvalidArgumentException(
  149. 'given period does not contain any valid date'
  150. );
  151. case 0:
  152. return sprintf('%s/%s', $start_iso, $interval_iso);
  153. default:
  154. return sprintf('R%d/%s/%s', $repetitions, $start_iso, $interval_iso);
  155. }
  156. }
  157. }
  158. }