GeneralizedTime.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. /*
  3. * This file is part of the PHPASN1 library.
  4. *
  5. * Copyright © Friedrich Große <friedrich.grosse@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FG\ASN1\Universal;
  11. use FG\ASN1\AbstractTime;
  12. use FG\ASN1\Parsable;
  13. use FG\ASN1\Identifier;
  14. use FG\ASN1\Exception\ParserException;
  15. /**
  16. * This ASN.1 universal type contains date and time information according to ISO 8601.
  17. *
  18. * The type consists of values representing:
  19. * a) a calendar date, as defined in ISO 8601; and
  20. * b) a time of day, to any of the precisions defined in ISO 8601, except for the hours value 24 which shall not be used; and
  21. * c) the local time differential factor as defined in ISO 8601.
  22. *
  23. * Decoding of this type will accept the Basic Encoding Rules (BER)
  24. * The encoding will comply with the Distinguished Encoding Rules (DER).
  25. */
  26. class GeneralizedTime extends AbstractTime implements Parsable
  27. {
  28. private $microseconds;
  29. public function __construct($dateTime = null, $dateTimeZone = 'UTC')
  30. {
  31. parent::__construct($dateTime, $dateTimeZone);
  32. $this->microseconds = $this->value->format('u');
  33. if ($this->containsFractionalSecondsElement()) {
  34. // DER requires us to remove trailing zeros
  35. $this->microseconds = preg_replace('/([1-9]+)0+$/', '$1', $this->microseconds);
  36. }
  37. }
  38. public function getType()
  39. {
  40. return Identifier::GENERALIZED_TIME;
  41. }
  42. protected function calculateContentLength()
  43. {
  44. $contentSize = 15; // YYYYMMDDHHmmSSZ
  45. if ($this->containsFractionalSecondsElement()) {
  46. $contentSize += 1 + strlen($this->microseconds);
  47. }
  48. return $contentSize;
  49. }
  50. public function containsFractionalSecondsElement()
  51. {
  52. return intval($this->microseconds) > 0;
  53. }
  54. protected function getEncodedValue()
  55. {
  56. $encodedContent = $this->value->format('YmdHis');
  57. if ($this->containsFractionalSecondsElement()) {
  58. $encodedContent .= ".{$this->microseconds}";
  59. }
  60. return $encodedContent.'Z';
  61. }
  62. public function __toString()
  63. {
  64. if ($this->containsFractionalSecondsElement()) {
  65. return $this->value->format("Y-m-d\tH:i:s.uP");
  66. } else {
  67. return $this->value->format("Y-m-d\tH:i:sP");
  68. }
  69. }
  70. public static function fromBinary(&$binaryData, &$offsetIndex = 0)
  71. {
  72. self::parseIdentifier($binaryData[$offsetIndex], Identifier::GENERALIZED_TIME, $offsetIndex++);
  73. $lengthOfMinimumTimeString = 14; // YYYYMMDDHHmmSS
  74. $contentLength = self::parseContentLength($binaryData, $offsetIndex, $lengthOfMinimumTimeString);
  75. $maximumBytesToRead = $contentLength;
  76. $format = 'YmdGis';
  77. $content = substr($binaryData, $offsetIndex, $contentLength);
  78. $dateTimeString = substr($content, 0, $lengthOfMinimumTimeString);
  79. $offsetIndex += $lengthOfMinimumTimeString;
  80. $maximumBytesToRead -= $lengthOfMinimumTimeString;
  81. if ($contentLength == $lengthOfMinimumTimeString) {
  82. $localTimeZone = new \DateTimeZone(date_default_timezone_get());
  83. $dateTime = \DateTime::createFromFormat($format, $dateTimeString, $localTimeZone);
  84. } else {
  85. if ($binaryData[$offsetIndex] == '.') {
  86. $maximumBytesToRead--; // account for the '.'
  87. $nrOfFractionalSecondElements = 1; // account for the '.'
  88. while ($maximumBytesToRead > 0
  89. && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '+'
  90. && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != '-'
  91. && $binaryData[$offsetIndex + $nrOfFractionalSecondElements] != 'Z') {
  92. $nrOfFractionalSecondElements++;
  93. $maximumBytesToRead--;
  94. }
  95. $dateTimeString .= substr($binaryData, $offsetIndex, $nrOfFractionalSecondElements);
  96. $offsetIndex += $nrOfFractionalSecondElements;
  97. $format .= '.u';
  98. }
  99. $dateTime = \DateTime::createFromFormat($format, $dateTimeString, new \DateTimeZone('UTC'));
  100. if ($maximumBytesToRead > 0) {
  101. if ($binaryData[$offsetIndex] == '+'
  102. || $binaryData[$offsetIndex] == '-') {
  103. $dateTime = static::extractTimeZoneData($binaryData, $offsetIndex, $dateTime);
  104. } elseif ($binaryData[$offsetIndex++] != 'Z') {
  105. throw new ParserException('Invalid ISO 8601 Time String', $offsetIndex);
  106. }
  107. }
  108. }
  109. $parsedObject = new self($dateTime);
  110. $parsedObject->setContentLength($contentLength);
  111. return $parsedObject;
  112. }
  113. }