Improved support for "Short Date" fields

* Separate -D (date only) and -T (date/time) format options in mdb-export and mdb-json

* New public mdb_set_shortdate_fmt() function in libmdb

* New private(ish) mdb_col_is_shortdate() function

I'm calling it "shortdate" in order to preserve the existing API.

See https://github.com/mdbtools/mdbtools/issues/12
This commit is contained in:
Evan Miller 2020-09-02 22:14:57 -04:00
parent 0023e4efe4
commit 7f7761e884
8 changed files with 37 additions and 12 deletions

View File

@ -273,6 +273,7 @@ typedef struct {
MdbFormatConstants *fmt;
size_t bind_size;
char date_fmt[64];
char shortdate_fmt[64];
const char *boolean_false_value;
const char *boolean_true_value;
unsigned int num_catalog;
@ -498,6 +499,7 @@ int mdb_is_user_table(MdbCatalogEntry *entry);
int mdb_is_system_table(MdbCatalogEntry *entry);
const char *mdb_table_get_prop(const MdbTableDef *table, const gchar *key);
const char *mdb_col_get_prop(const MdbColumn *col, const gchar *key);
int mdb_col_is_shortdate(const MdbColumn *col);
/* data.c */
int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr);
@ -520,6 +522,7 @@ size_t mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, size_t chunk_
void* mdb_ole_read_full(MdbHandle *mdb, MdbColumn *col, size_t *size);
void mdb_set_bind_size(MdbHandle *mdb, size_t bind_size);
void mdb_set_date_fmt(MdbHandle *mdb, const char *);
void mdb_set_shortdate_fmt(MdbHandle *mdb, const char *);
void mdb_set_boolean_fmt_words(MdbHandle *mdb);
void mdb_set_boolean_fmt_numbers(MdbHandle *mdb);
int mdb_read_row(MdbTableDef *table, unsigned int row);

View File

@ -253,11 +253,6 @@ quote_with_squotes(const gchar* value)
return quote_generic(value, '\'', '\'');
}
static int mdb_col_is_shortdate(const MdbColumn *col) {
const char *format = mdb_col_get_prop(col, "Format");
return format && !strcmp(format, "Short Date");
}
const MdbBackendType*
mdb_get_colbacktype(const MdbColumn *col) {
MdbBackend *backend = col->table->entry->mdb->default_backend;

View File

@ -29,7 +29,7 @@ char *mdb_numeric_to_string(MdbHandle *mdb, int start, int prec, int scale);
static int _mdb_attempt_bind(MdbHandle *mdb,
MdbColumn *col, unsigned char isnull, int offset, int len);
static char *mdb_date_to_string(MdbHandle *mdb, void *buf, int start);
static char *mdb_date_to_string(MdbHandle *mdb, const char *fmt, void *buf, int start);
#ifdef MDB_COPY_OLE
static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size);
#endif
@ -56,6 +56,11 @@ void mdb_set_date_fmt(MdbHandle *mdb, const char *fmt)
snprintf(mdb->date_fmt, sizeof(mdb->date_fmt), "%s", fmt);
}
void mdb_set_shortdate_fmt(MdbHandle *mdb, const char *fmt)
{
snprintf(mdb->shortdate_fmt, sizeof(mdb->shortdate_fmt), "%s", fmt);
}
void mdb_set_boolean_fmt_numbers(MdbHandle *mdb)
{
mdb->boolean_false_value = boolean_false_number;
@ -257,6 +262,12 @@ int ret;
char *str;
if (col->col_type == MDB_NUMERIC) {
str = mdb_numeric_to_string(mdb, start, col->col_scale, col->col_prec);
} else if (col->col_type == MDB_DATETIME) {
if (mdb_col_is_shortdate(col)) {
str = mdb_date_to_string(mdb, mdb->shortdate_fmt, mdb->pg_buf, start);
} else {
str = mdb_date_to_string(mdb, mdb->date_fmt, mdb->pg_buf, start);
}
} else {
str = mdb_col_to_string(mdb, mdb->pg_buf, start, col->col_type, len);
}
@ -877,7 +888,7 @@ mdb_date_to_tm(double td, struct tm *t)
}
static char *
mdb_date_to_string(MdbHandle *mdb, void *buf, int start)
mdb_date_to_string(MdbHandle *mdb, const char *fmt, void *buf, int start)
{
struct tm t;
char *text = (char *) g_malloc(mdb->bind_size);
@ -985,7 +996,7 @@ char *mdb_col_to_string(MdbHandle *mdb, void *buf, int start, int datatype, int
}
break;
case MDB_DATETIME:
text = mdb_date_to_string(mdb, buf, start);
text = mdb_date_to_string(mdb, mdb->date_fmt, buf, start);
break;
case MDB_MEMO:
text = mdb_memo_to_string(mdb, start, size);

View File

@ -174,6 +174,7 @@ static MdbHandle *mdb_handle_from_stream(FILE *stream, MdbFileFlags flags) {
MdbHandle *mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle));
mdb_set_default_backend(mdb, "access");
mdb_set_date_fmt(mdb, "%x %X");
mdb_set_shortdate_fmt(mdb, "%x");
mdb_set_bind_size(mdb, MDB_BIND_SIZE);
mdb_set_boolean_fmt_numbers(mdb);
#ifdef HAVE_ICONV

View File

@ -417,3 +417,8 @@ mdb_col_get_prop(const MdbColumn *col, const gchar *key) {
return NULL;
return g_hash_table_lookup(col->props->hash, key);
}
int mdb_col_is_shortdate(const MdbColumn *col) {
const char *format = mdb_col_get_prop(col, "Format");
return format && !strcmp(format, "Short Date");
}

View File

@ -167,6 +167,7 @@ static SQLRETURN do_connect (
// ODBC requires ISO format dates, see
// https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-date-time/datetime-data-type-conversions-odbc?view=sql-server-ver15
mdb_set_date_fmt( dbc->sqlconn->mdb, "%F %H:%M:%S" );
mdb_set_shortdate_fmt( dbc->sqlconn->mdb, "%F" );
return SQL_SUCCESS;
}
else
@ -1592,8 +1593,7 @@ SQLRETURN SQL_API SQLGetData(
struct tm tmp_t;
mdb_date_to_tm(mdb_get_double(mdb->pg_buf, col->cur_value_start), &tmp_t);
const char *format = mdb_col_get_prop(col, "Format");
if (format && !strcmp(format, "Short Date")) {
if (mdb_col_is_shortdate(col)) {
DATE_STRUCT sql_dt;
sql_dt.year = tmp_t.tm_year + 1900;
sql_dt.month = tmp_t.tm_mon + 1;

View File

@ -43,6 +43,7 @@ main(int argc, char **argv)
int boolean_words = 0;
int batch_size = 1000;
char *insert_dialect = NULL;
char *shortdate_fmt = NULL;
char *date_fmt = NULL;
char *namespace = NULL;
char *str_bin_mode = NULL;
@ -58,7 +59,8 @@ main(int argc, char **argv)
{"row-delimiter", 'R', 0, G_OPTION_ARG_STRING, &row_delimiter, "Specify a row delimiter", "char"},
{"quote", 'q', 0, G_OPTION_ARG_STRING, &quote_char, "Use <char> to wrap text-like fields. Default is double quote.", "char"},
{"backend", 'I', 0, G_OPTION_ARG_STRING, &insert_dialect, "INSERT statements (instead of CSV)", "backend"},
{"date-format", 'D', 0, G_OPTION_ARG_STRING, &date_fmt, "Set the date format (see strftime(3) for details)", "format"},
{"date-format", 'D', 0, G_OPTION_ARG_STRING, &shortdate_fmt, "Set the date format (see strftime(3) for details)", "format"},
{"datetime-format", 'T', 0, G_OPTION_ARG_STRING, &date_fmt, "Set the date/time format (see strftime(3) for details)", "format"},
{"escape", 'X', 0, G_OPTION_ARG_STRING, &escape_char, "Use <char> to escape quoted characters within a field. Default is doubling.", "format"},
{"namespace", 'N', 0, G_OPTION_ARG_STRING, &namespace, "Prefix identifiers with namespace", "namespace"},
{"null", '0', 0, G_OPTION_ARG_STRING, &null_text, "Use <char> to represent a NULL value", "char"},
@ -139,6 +141,9 @@ main(int argc, char **argv)
if (date_fmt)
mdb_set_date_fmt(mdb, date_fmt);
if (shortdate_fmt)
mdb_set_shortdate_fmt(mdb, shortdate_fmt);
if (boolean_words)
mdb_set_boolean_fmt_words(mdb);

View File

@ -109,11 +109,13 @@ main(int argc, char **argv)
int *bound_lens;
FILE *outfile = stdout;
char *date_fmt = NULL;
char *shortdate_fmt = NULL;
char *value;
size_t length;
GOptionEntry entries[] = {
{"date-format", 'D', 0, G_OPTION_ARG_STRING, &date_fmt, "Set the date format (see strftime(3) for details)", "format"},
{"date-format", 'D', 0, G_OPTION_ARG_STRING, &shortdate_fmt, "Set the date format (see strftime(3) for details)", "format"},
{"datetime-format", 'T', 0, G_OPTION_ARG_STRING, &date_fmt, "Set the date/time format (see strftime(3) for details)", "format"},
{"no-unprintable", 'U', 0, G_OPTION_ARG_NONE, &drop_nonascii, "Change unprintable characters to spaces (otherwise escaped as \\u00XX)", NULL},
{NULL}
};
@ -143,6 +145,9 @@ main(int argc, char **argv)
if (date_fmt)
mdb_set_date_fmt(mdb, date_fmt);
if (shortdate_fmt)
mdb_set_shortdate_fmt(mdb, shortdate_fmt);
mdb_set_bind_size(mdb, EXPORT_BIND_SIZE);
table = mdb_read_table_by_name(mdb, argv[2], MDB_TABLE);