Improve printing of dates and times with non-UTF-8 locales and on Windows.

* qof_time_format_from_utf8 converts an strftime format specification
  from UTF-8 to the locale encoding
* qof_format_time calls strftime repeatedly with growing allocated
  buffers until the result fits into one, or return NULL
* qof_formatted_time_to_utf8 converts the result back to UTF-8
* qof_strftime takes similar arguments to strftime, but in UTF-8
* the conversion functions on Windows are implemented in qof-win32.c to
  allow them to use windows.h, as they need wcstombs and mbstowcs


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15795 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Andreas Köhler 2007-04-04 18:48:42 +00:00
parent 91eb3d4262
commit 095c71a5f1
5 changed files with 290 additions and 7 deletions

View File

@ -39,6 +39,7 @@ qofincludedir = ${pkgincludedir}
qofinclude_HEADERS = \ qofinclude_HEADERS = \
deprecated.h \ deprecated.h \
gnc-date.h \ gnc-date.h \
gnc-date-p.h \
gnc-numeric.h \ gnc-numeric.h \
guid.h \ guid.h \
kvp_frame.h \ kvp_frame.h \
@ -86,6 +87,12 @@ EXTRA_DIST = \
qofla-dir.h.in \ qofla-dir.h.in \
qofmath128.c qofmath128.c
if OS_WIN32
libgnc_qof_la_SOURCES += qof-win32.c
else
EXTRA_DIST += qof-win32.c
endif
qofla-dir.h: $(srcdir)/qofla-dir.h.in Makefile qofla-dir.h: $(srcdir)/qofla-dir.h.in Makefile
rm -f $@.tmp rm -f $@.tmp
sed < $< > $@.tmp \ sed < $< > $@.tmp \

View File

@ -0,0 +1,45 @@
/*
* gnc-date-p.h
*
* Copyright (C) 2007 Andreas Koehler <andi5.py@gmx.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#ifndef __GNC_DATE_P_H__
#define __GNC_DATE_P_H__
#include "gnc-date.h"
/** Convert a given date/time format from UTF-8 to an encoding suitable for the
* strftime system call.
*
* @param utf8_format Date/time format specification in UTF-8.
*
* @return A newly allocated string on success, or NULL otherwise.
*/
gchar *qof_time_format_from_utf8(const gchar *utf8_format);
/** Convert a result of a call to strftime back to UTF-8.
*
* @param locale_string The result of a call to strftime.
*
* @return A newly allocated string on success, or NULL otherwise.
*/
gchar *qof_formatted_time_to_utf8(const gchar *locale_string);
#endif /* __GNC_DATE_P_H__ */

View File

@ -46,7 +46,7 @@
#include <glib.h> #include <glib.h>
#include "gnc-date.h" #include "gnc-date-p.h"
#include "qof.h" #include "qof.h"
#ifndef HAVE_STRPTIME #ifndef HAVE_STRPTIME
@ -439,7 +439,7 @@ qof_print_date_dmy_buff (char * buff, size_t len, int day, int month, int year)
gnc_tm_set_day_start (&tm_str); gnc_tm_set_day_start (&tm_str);
t = mktime (&tm_str); t = mktime (&tm_str);
localtime_r (&t, &tm_str); localtime_r (&t, &tm_str);
flen = strftime (buff, len, GNC_D_FMT, &tm_str); flen = qof_strftime (buff, len, GNC_D_FMT, &tm_str);
if (flen != 0) if (flen != 0)
break; break;
} }
@ -618,12 +618,12 @@ qof_print_date_time_buff (char * buff, size_t len, time_t secs)
case QOF_DATE_FORMAT_UTC: case QOF_DATE_FORMAT_UTC:
{ {
gtm = *gmtime (&secs); gtm = *gmtime (&secs);
flen = strftime (buff, len, QOF_UTC_DATE_FORMAT, &gtm); flen = qof_strftime (buff, len, QOF_UTC_DATE_FORMAT, &gtm);
break; break;
} }
case QOF_DATE_FORMAT_LOCALE: case QOF_DATE_FORMAT_LOCALE:
{ {
flen = strftime (buff, len, GNC_D_T_FMT, &ltm); flen = qof_strftime (buff, len, GNC_D_T_FMT, &ltm);
} }
break; break;
@ -645,11 +645,11 @@ qof_print_time_buff (char * buff, size_t len, time_t secs)
if(dateFormat == QOF_DATE_FORMAT_UTC) if(dateFormat == QOF_DATE_FORMAT_UTC)
{ {
gtm = *gmtime (&secs); gtm = *gmtime (&secs);
flen = strftime(buff, len, QOF_UTC_DATE_FORMAT, &gtm); flen = qof_strftime(buff, len, QOF_UTC_DATE_FORMAT, &gtm);
return flen; return flen;
} }
ltm = *localtime (&secs); ltm = *localtime (&secs);
flen = strftime (buff, len, GNC_T_FMT, &ltm); flen = qof_strftime (buff, len, GNC_T_FMT, &ltm);
return flen; return flen;
} }
@ -909,7 +909,7 @@ char dateSeparator (void)
secs = time(NULL); secs = time(NULL);
localtime_r(&secs, &tm); localtime_r(&secs, &tm);
strftime(string, sizeof(string), GNC_D_FMT, &tm); qof_strftime(string, sizeof(string), GNC_D_FMT, &tm);
for (s = string; s != '\0'; s++) for (s = string; s != '\0'; s++)
if (!isdigit(*s)) if (!isdigit(*s))
@ -920,6 +920,127 @@ char dateSeparator (void)
return '\0'; return '\0';
} }
#ifndef G_OS_WIN32
gchar *
qof_time_format_from_utf8(const gchar *utf8_format)
{
gchar *retval;
GError *error = NULL;
retval = g_locale_from_utf8(utf8_format, -1, NULL, NULL, &error);
if (!retval) {
g_warning("Could not convert format '%s' from UTF-8: %s", utf8_format,
error->message);
g_error_free(error);
}
return retval;
}
gchar *
qof_formatted_time_to_utf8(const gchar *locale_string)
{
gchar *retval;
GError *error = NULL;
retval = g_locale_to_utf8(locale_string, -1, NULL, NULL, &error);
if (!retval) {
g_warning("Could not convert '%s' to UTF-8: %s", locale_string,
error->message);
g_error_free(error);
}
return retval;
}
#endif /* G_OS_WIN32 */
gchar *
qof_format_time(const gchar *format, const struct tm *tm)
{
gchar *locale_format, *tmpbuf, *retval;
gsize tmplen, tmpbufsize;
g_return_val_if_fail(format, 0);
g_return_val_if_fail(tm, 0);
locale_format = qof_time_format_from_utf8(format);
if (!locale_format)
return NULL;
tmpbufsize = MAX(128, strlen(locale_format) * 2);
while (TRUE) {
tmpbuf = g_malloc(tmpbufsize);
/* Set the first byte to something other than '\0', to be able to
* recognize whether strftime actually failed or just returned "".
*/
tmpbuf[0] = '\1';
tmplen = strftime(tmpbuf, tmpbufsize, locale_format, tm);
if (tmplen == 0 && tmpbuf[0] != '\0') {
g_free(tmpbuf);
tmpbufsize *= 2;
if (tmpbufsize > 65536) {
g_warning("Maximum buffer size for qof_format_time "
"exceeded: giving up");
g_free(locale_format);
return NULL;
}
} else {
break;
}
}
g_free(locale_format);
retval = qof_formatted_time_to_utf8(tmpbuf);
g_free(tmpbuf);
return retval;
}
gsize
qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
{
gsize convlen, retval;
gchar *convbuf;
GError *error = NULL;
g_return_val_if_fail(buf, 0);
g_return_val_if_fail(max > 0, 0);
g_return_val_if_fail(format, 0);
g_return_val_if_fail(tm, 0);
convbuf = qof_format_time(format, tm);
if (!convbuf) {
buf[0] = '\0';
return 0;
}
convlen = strlen(convbuf);
if (max <= convlen) {
/* Ensure only whole characters are copied into the buffer. */
gchar *end = g_utf8_find_prev_char(convbuf, convbuf + max);
g_assert(end != NULL);
convlen = end - convbuf;
/* Return 0 because the buffer isn't large enough. */
retval = 0;
} else {
retval = convlen;
}
memcpy(buf, convbuf, convlen);
buf[convlen] = '\0';
g_free(convbuf);
return retval;
}
/********************************************************************\ /********************************************************************\
\********************************************************************/ \********************************************************************/

View File

@ -328,6 +328,35 @@ gchar dateSeparator(void);
* itself, instead of depending on the routines here. * itself, instead of depending on the routines here.
*/ */
/** qof_format_time takes a format specification in UTF-8 and a broken-down time,
* tries to call strftime with a sufficiently large buffer and, if successful,
* return a newly allocated string in UTF-8 for the printing result.
*
* @param format A format specification in UTF-8.
*
* @param tm A broken-down time.
*
* @return A newly allocated string on success, or NULL otherwise.
*/
gchar *qof_format_time(const gchar *format, const struct tm *tm);
/** qof_strftime calls qof_format_time to print a given time and afterwards tries
* to put the result into a buffer of fixed size.
*
* @param buf A buffer.
*
* @param max The size of buf in bytes.
*
* @param format A format specification in UTF-8.
*
* @param tm A broken-down time.
*
* @return The number of characters written, not include the null byte, if the
* complete string, including the null byte, fits into the buffer. Otherwise 0.
*/
gsize qof_strftime(gchar *buf, gsize max, const gchar *format,
const struct tm *tm);
/** qof_print_date_dmy_buff /** qof_print_date_dmy_buff
* Convert a date as day / month / year integers into a localized string * Convert a date as day / month / year integers into a localized string
* representation * representation

View File

@ -0,0 +1,81 @@
/*
* qof-win32.c
*
* Copyright (C) 2007 Andreas Koehler <andi5.py@gmx.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact:
*
* Free Software Foundation Voice: +1-617-542-5942
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
* Boston, MA 02110-1301, USA gnu@gnu.org
*/
#include "config.h"
#include <glib.h>
#include "gnc-date-p.h"
#include <windows.h>
gchar *
qof_time_format_from_utf8(const gchar *utf8_format)
{
gunichar2 *utf16_format;
gchar *retval;
gsize count;
utf16_format = g_utf8_to_utf16(utf8_format, -1, NULL, NULL, NULL);
if (!utf16_format)
return NULL;
/* get number of resulting wide characters */
count = wcstombs(NULL, utf16_format, 0);
if (count <= 0)
return NULL;
/* malloc and convert */
retval = g_malloc((count+1) * sizeof(gchar));
count = wcstombs(retval, utf16_format, count+1);
g_free(utf16_format);
if (count <= 0) {
g_free(retval);
return NULL;
}
return retval;
}
gchar *
qof_formatted_time_to_utf8(const gchar *locale_string)
{
gunichar2 *utf16_string;
gchar *retval;
gsize count;
/* get number of resulting wide characters */
count = mbstowcs(NULL, locale_string, 0);
if (count <= 0)
return NULL;
/* malloc and convert */
utf16_string = g_malloc((count+1) * sizeof(gunichar2));
count = mbstowcs(utf16_string, locale_string, count+1);
if (count <= 0) {
g_free(utf16_string);
return NULL;
}
retval = g_utf16_to_utf8(utf16_string, -1, NULL, NULL, NULL);
g_free(utf16_string);
return retval;
}