DateTimeConverter.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of the nelexa/zip package.
  5. * (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. namespace PhpZip\Util;
  10. /**
  11. * Convert unix timestamp values to DOS date/time values and vice versa.
  12. *
  13. * The DOS date/time format is a bitmask:
  14. *
  15. * 24 16 8 0
  16. * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
  17. * |Y|Y|Y|Y|Y|Y|Y|M| |M|M|M|D|D|D|D|D| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s|
  18. * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
  19. * \___________/\________/\_________/ \________/\____________/\_________/
  20. * year month day hour minute second
  21. *
  22. * The year is stored as an offset from 1980.
  23. * Seconds are stored in two-second increments.
  24. * (So if the "second" value is 15, it actually represents 30 seconds.)
  25. *
  26. * @see https://docs.microsoft.com/ru-ru/windows/win32/api/winbase/nf-winbase-filetimetodosdatetime?redirectedfrom=MSDN
  27. *
  28. * @internal
  29. */
  30. class DateTimeConverter
  31. {
  32. /**
  33. * Smallest supported DOS date/time value in a ZIP file,
  34. * which is January 1st, 1980 AD 00:00:00 local time.
  35. *
  36. * @var int
  37. */
  38. public const MIN_DOS_TIME = (1 << 21) | (1 << 16);
  39. /**
  40. * Largest supported DOS date/time value in a ZIP file,
  41. * which is December 31st, 2107 AD 23:59:58 local time.
  42. *
  43. * @var int
  44. */
  45. public const MAX_DOS_TIME = ((2107 - 1980) << 25) | (12 << 21) | (31 << 16) | (23 << 11) | (59 << 5) | (58 >> 1);
  46. /**
  47. * Convert a 32 bit integer DOS date/time value to a UNIX timestamp value.
  48. *
  49. * @param int $dosTime Dos date/time
  50. *
  51. * @return int Unix timestamp
  52. */
  53. public static function msDosToUnix(int $dosTime): int
  54. {
  55. if ($dosTime <= self::MIN_DOS_TIME) {
  56. $dosTime = 0;
  57. } elseif ($dosTime > self::MAX_DOS_TIME) {
  58. $dosTime = self::MAX_DOS_TIME;
  59. }
  60. // date_default_timezone_set('UTC');
  61. return mktime(
  62. (($dosTime >> 11) & 0x1f), // hours
  63. (($dosTime >> 5) & 0x3f), // minutes
  64. (($dosTime << 1) & 0x3e), // seconds
  65. (($dosTime >> 21) & 0x0f), // month
  66. (($dosTime >> 16) & 0x1f), // day
  67. ((($dosTime >> 25) & 0x7f) + 1980) // year
  68. );
  69. }
  70. /**
  71. * Converts a UNIX timestamp value to a DOS date/time value.
  72. *
  73. * @param int $unixTimestamp the number of seconds since midnight, January 1st,
  74. * 1970 AD UTC
  75. *
  76. * @return int a DOS date/time value reflecting the local time zone and
  77. * rounded down to even seconds
  78. * and is in between DateTimeConverter::MIN_DOS_TIME and DateTimeConverter::MAX_DOS_TIME
  79. */
  80. public static function unixToMsDos(int $unixTimestamp): int
  81. {
  82. if ($unixTimestamp < 0) {
  83. throw new \InvalidArgumentException('Negative unix timestamp: ' . $unixTimestamp);
  84. }
  85. $date = getdate($unixTimestamp);
  86. $dosTime = (
  87. (($date['year'] - 1980) << 25)
  88. | ($date['mon'] << 21)
  89. | ($date['mday'] << 16)
  90. | ($date['hours'] << 11)
  91. | ($date['minutes'] << 5)
  92. | ($date['seconds'] >> 1)
  93. );
  94. if ($dosTime <= self::MIN_DOS_TIME) {
  95. $dosTime = 0;
  96. }
  97. return $dosTime;
  98. }
  99. }