mirror of
https://github.com/jmcnamara/libxlsxwriter.git
synced 2026-05-15 06:06:09 -06:00
datetime: add validation for date/time parameters
Added validation of lxw_datetime fields to ensure that they are within Excel's allowable ranges. Closes #491
This commit is contained in:
parent
9a48ca8996
commit
f0647157ba
6 changed files with 213 additions and 0 deletions
|
|
@ -110,6 +110,9 @@ typedef enum lxw_error {
|
|||
/** Function string parameter is empty. */
|
||||
LXW_ERROR_PARAMETER_IS_EMPTY,
|
||||
|
||||
/** A #lxw_datetime parameter has a validation error. */
|
||||
LXW_ERROR_DATETIME_VALIDATION,
|
||||
|
||||
/** Worksheet name exceeds Excel's limit of 31 characters. */
|
||||
LXW_ERROR_SHEETNAME_LENGTH_EXCEEDED,
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,30 @@ double lxw_datetime_to_excel_datetime(lxw_datetime *datetime);
|
|||
double lxw_datetime_to_excel_date_with_epoch(lxw_datetime *datetime,
|
||||
uint8_t use_1904_epoch);
|
||||
|
||||
/**
|
||||
* @brief Validate a #lxw_datetime struct.
|
||||
*
|
||||
* Validates a #lxw_datetime struct to ensure its fields are within acceptable
|
||||
* ranges for Excel dates and times.
|
||||
*
|
||||
* The members of the #lxw_datetime struct and the range of their values are:
|
||||
*
|
||||
* Member | Value
|
||||
* -------- | -----------
|
||||
* year | 1900 - 9999
|
||||
* month | 1 - 12
|
||||
* day | 1 - 31
|
||||
* hour | 0 - 23
|
||||
* min | 0 - 59
|
||||
* sec | 0 - 59.999
|
||||
*
|
||||
* @param datetime A pointer to a #lxw_datetime struct.
|
||||
*
|
||||
* @return A #lxw_error code. Either #LXW_NO_ERROR or
|
||||
* #LXW_ERROR_DATETIME_VALIDATION if a field is out of range.
|
||||
*/
|
||||
lxw_error lxw_datetime_validate(lxw_datetime *datetime);
|
||||
|
||||
/**
|
||||
* @brief Converts a unix datetime to an Excel datetime number.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ char *error_strings[LXW_MAX_ERRNO + 1] = {
|
|||
"NULL function parameter ignored.",
|
||||
"Function parameter validation error.",
|
||||
"Function string parameter is empty.",
|
||||
"Datetime struct parameter has an invalid field value.",
|
||||
"Worksheet name exceeds Excel's limit of 31 characters.",
|
||||
"Worksheet name cannot contain invalid characters: '[ ] : * ? / \\'",
|
||||
"Worksheet name cannot start or end with an apostrophe.",
|
||||
|
|
@ -332,6 +333,69 @@ lxw_name_to_col_2(const char *col_str)
|
|||
return 0;
|
||||
}
|
||||
|
||||
lxw_error
|
||||
lxw_datetime_validate(lxw_datetime *datetime)
|
||||
{
|
||||
if (!datetime)
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
|
||||
/*
|
||||
* Excel uses the year 1900 as the default epoch but it uses 1899-12-31 as
|
||||
* the 0 date and internally we use the 0-0-0 date for time only values.
|
||||
*/
|
||||
if (datetime->year < 1900 &&
|
||||
!(datetime->year == 0 &&
|
||||
datetime->month == 0 && datetime->day == 0) &&
|
||||
!(datetime->year == 1899 &&
|
||||
datetime->month == 12 && datetime->day == 31)) {
|
||||
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid year: %d. "
|
||||
"Valid range is 1900-9999.", datetime->year);
|
||||
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
if (datetime->year > 9999) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid year: %d. "
|
||||
"Valid range is 1900-9999.", datetime->year);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
if (datetime->year != 0) {
|
||||
if (datetime->month < 1 || datetime->month > 12) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid month: %d. "
|
||||
"Valid range is 1-12.", datetime->month);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
if (datetime->day < 1 || datetime->day > 31) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid day: %d. "
|
||||
"Valid range is 1-31.", datetime->day);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (datetime->hour < 0 || datetime->hour > 23) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid hour: %d. "
|
||||
"Valid range is 0-23.", datetime->hour);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
if (datetime->min < 0 || datetime->min > 59) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid minute: %d. "
|
||||
"Valid range is 0-59.", datetime->min);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
if (datetime->sec < 0.0 || datetime->sec >= 60.0) {
|
||||
LXW_WARN_FORMAT1("lxw_datetime_validate(): invalid seconds: %.3f. "
|
||||
"Valid range is 0.0-59.999.", datetime->sec);
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
return LXW_NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a lxw_datetime struct to an Excel serial date, with a 1900
|
||||
* or 1904 epoch.
|
||||
|
|
@ -357,6 +421,9 @@ lxw_datetime_to_excel_date_with_epoch(lxw_datetime *datetime,
|
|||
int days = 0;
|
||||
int i;
|
||||
|
||||
if (lxw_datetime_validate(datetime) != LXW_NO_ERROR)
|
||||
return 0.0;
|
||||
|
||||
/* For times without dates set the default date for the epoch. */
|
||||
if (!year) {
|
||||
if (!use_1904_epoch) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "xlsxwriter/utility.h"
|
||||
#include "xlsxwriter/packager.h"
|
||||
#include "xlsxwriter/hash_table.h"
|
||||
#include "xlsxwriter/hash_table.h"
|
||||
|
||||
STATIC int _worksheet_name_cmp(lxw_worksheet_name *name1,
|
||||
lxw_worksheet_name *name2);
|
||||
|
|
@ -2659,6 +2660,10 @@ workbook_set_custom_property_datetime(lxw_workbook *self, const char *name,
|
|||
return LXW_ERROR_NULL_PARAMETER_IGNORED;
|
||||
}
|
||||
|
||||
if (lxw_datetime_validate(datetime) != LXW_NO_ERROR) {
|
||||
return LXW_ERROR_DATETIME_VALIDATION;
|
||||
}
|
||||
|
||||
/* Create a struct to hold the custom property. */
|
||||
custom_property = calloc(1, sizeof(struct lxw_custom_property));
|
||||
RETURN_ON_MEM_ERROR(custom_property, LXW_ERROR_MEMORY_MALLOC_FAILED);
|
||||
|
|
|
|||
|
|
@ -8334,6 +8334,10 @@ worksheet_write_datetime(lxw_worksheet *self,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = lxw_datetime_validate(datetime);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
excel_date =
|
||||
lxw_datetime_to_excel_date_with_epoch(datetime, self->use_1904_epoch);
|
||||
|
||||
|
|
|
|||
110
test/unit/utility/test_datetime_validate.c
Normal file
110
test/unit/utility/test_datetime_validate.c
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Tests for the libxlsxwriter library.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
* Copyright 2014-2025, John McNamara, jmcnamara@cpan.org.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../ctest.h"
|
||||
#include "../helper.h"
|
||||
|
||||
#include "../../../include/xlsxwriter/utility.h"
|
||||
|
||||
// Test valid datetime.
|
||||
CTEST(utility, test_datetime_validate01) {
|
||||
|
||||
lxw_datetime datetime = {2025, 10, 30, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_NO_ERROR;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test valid datetime (time only).
|
||||
CTEST(utility, test_datetime_validate02) {
|
||||
|
||||
lxw_datetime datetime = {0, 0, 0, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_NO_ERROR;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test valid datetime (1900 epoch).
|
||||
CTEST(utility, test_datetime_validate03) {
|
||||
|
||||
lxw_datetime datetime = {1899, 12, 31, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_NO_ERROR;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test invalid year.
|
||||
CTEST(utility, test_datetime_validate04) {
|
||||
|
||||
lxw_datetime datetime = {1800, 10, 30, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test invalid month.
|
||||
CTEST(utility, test_datetime_validate05) {
|
||||
|
||||
lxw_datetime datetime = {1900, 13, 30, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
|
||||
// Test invalid day.
|
||||
CTEST(utility, test_datetime_validate06) {
|
||||
|
||||
lxw_datetime datetime = {1900, 10, 32, 21, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test invalid hour.
|
||||
CTEST(utility, test_datetime_validate07) {
|
||||
|
||||
lxw_datetime datetime = {1900, 1, 1, 24, 07, 0.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test invalid minute.
|
||||
CTEST(utility, test_datetime_validate08) {
|
||||
lxw_datetime datetime = {1900, 1, 1, 21, 60, 0.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
|
||||
// Test invalid second.
|
||||
CTEST(utility, test_datetime_validate09) {
|
||||
lxw_datetime datetime = {1900, 1, 1, 21, 07, 60.0};
|
||||
|
||||
lxw_error exp = LXW_ERROR_DATETIME_VALIDATION;
|
||||
lxw_error got = lxw_datetime_validate(&datetime);
|
||||
|
||||
ASSERT_EQUAL(exp, got);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue