diff --git a/mz_zip.c b/mz_zip.c index 87c5f72..854f9bb 100644 --- a/mz_zip.c +++ b/mz_zip.c @@ -2669,24 +2669,35 @@ int32_t mz_zip_extrafield_write(void *stream, uint16_t type, uint16_t length) { /***************************************************************************/ +/** + This is mostly validating a given time struct is convertible to and from a valid dos date. + This does not however perform a calendar validation (like February 31). + */ static int32_t mz_zip_invalid_date(const struct tm *ptm) { #define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max)) return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || /* 1980-based year, allow 80 extra */ - !datevalue_in_range(0, 11, ptm->tm_mon) || !datevalue_in_range(1, 31, ptm->tm_mday) || - !datevalue_in_range(0, 23, ptm->tm_hour) || !datevalue_in_range(0, 59, ptm->tm_min) || - !datevalue_in_range(0, 59, ptm->tm_sec)); + !datevalue_in_range(0, 11, ptm->tm_mon) || /* allowing months 0-11 in a year */ + !datevalue_in_range(1, 31, ptm->tm_mday) || /* allowing days 1-31 in a month */ + !datevalue_in_range(0, 23, ptm->tm_hour) || /* allowing hours 0-23 in a day */ + !datevalue_in_range(0, 59, ptm->tm_min) || /* allowing minutes 0-59 in an hour */ + !datevalue_in_range(0, 59, ptm->tm_sec)); /* allowing seconds 0-59 in a minute */ #undef datevalue_in_range } +/** + Returns a year-1900 indexed time struct. + This may produce slightly out-of-range months, hours, minutes, seconds. + This may also produce invalid calendar days (like the 31st of February). + */ static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm) { uint64_t date = (uint64_t)(dos_date >> 16); - ptm->tm_mday = (int16_t)(date & 0x1f); - ptm->tm_mon = (int16_t)(((date & 0x1E0) / 0x20) - 1); - ptm->tm_year = (int16_t)(((date & 0x0FE00) / 0x0200) + 80); - ptm->tm_hour = (int16_t)((dos_date & 0xF800) / 0x800); - ptm->tm_min = (int16_t)((dos_date & 0x7E0) / 0x20); - ptm->tm_sec = (int16_t)(2 * (dos_date & 0x1f)); + ptm->tm_year = (int16_t)(((date & 0x0FE00) / 0x0200) + 80); /* result in 80..207 */ + ptm->tm_mon = (int16_t)(((date & 0x1E0) / 0x20) - 1); /* result in -1..14 */ + ptm->tm_mday = (int16_t)(date & 0x1f); /* result in 0..31 */ + ptm->tm_hour = (int16_t)((dos_date & 0xF800) / 0x800); /* result in 0..31 */ + ptm->tm_min = (int16_t)((dos_date & 0x7E0) / 0x20); /* result in 0..63 */ + ptm->tm_sec = (int16_t)(2 * (dos_date & 0x1f)); /* result in 0..62 */ ptm->tm_isdst = -1; } @@ -2711,6 +2722,7 @@ time_t mz_zip_dosdate_to_time_t(uint64_t dos_date) { } time_t mz_zip_tm_to_time_t(struct tm *ptm) { + // `ptm->tm_year` must be 1900-indexed. return mktime(ptm); } diff --git a/mz_zip.h b/mz_zip.h index 6f1ddec..dd1269c 100644 --- a/mz_zip.h +++ b/mz_zip.h @@ -221,7 +221,7 @@ time_t mz_zip_dosdate_to_time_t(uint64_t dos_date); /* Convert dos date/time format to time_t */ time_t mz_zip_tm_to_time_t(struct tm *ptm); -/* Convert time struct to time_t */ +/* Convert year-1900 indexed time struct to time_t */ int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm); /* Convert time_t to time struct */ diff --git a/test/test_compat.cc b/test/test_compat.cc index 10e777a..5f6896e 100644 --- a/test/test_compat.cc +++ b/test/test_compat.cc @@ -18,7 +18,7 @@ #include -#ifdef HAVE_ZLIB +#if defined(HAVE_ZLIB) || defined(HAVE_LIBCOMP) static void test_zip_compat(zipFile zip, const char *filename, int32_t level) { int32_t err = ZIP_OK; zip_fileinfo file_info;