[libgda] Added some gda_parse_formatted_*() functions to helps parsing date and time strings
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Added some gda_parse_formatted_*() functions to helps parsing date and time strings
- Date: Thu, 26 Sep 2013 19:33:36 +0000 (UTC)
commit 5415687db99337b42adcc14d96379fdafe1f98a4
Author: Vivien Malerba <malerba gnome-db org>
Date: Thu Sep 26 21:16:04 2013 +0200
Added some gda_parse_formatted_*() functions to helps parsing date and time strings
doc/C/libgda-sections.txt | 3 +
libgda/gda-util.c | 362 +++++++++++++++++++++++++++++++++++----------
libgda/gda-util.h | 7 +-
libgda/libgda.symbols | 3 +
4 files changed, 293 insertions(+), 82 deletions(-)
---
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index f08cfc4..4d90534 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -825,6 +825,9 @@ gda_connection_string_split
gda_parse_iso8601_date
gda_parse_iso8601_time
gda_parse_iso8601_timestamp
+gda_parse_formatted_date
+gda_parse_formatted_time
+gda_parse_formatted_timestamp
</SECTION>
<SECTION>
diff --git a/libgda/gda-util.c b/libgda/gda-util.c
index 113a8d9..92118b5 100644
--- a/libgda/gda-util.c
+++ b/libgda/gda-util.c
@@ -6,7 +6,7 @@
* Copyright (C) 2004 Caolan McNamara <caolanm redhat com>
* Copyright (C) 2004 J�rg Billeter <j bitron ch>
* Copyright (C) 2004 - 2011 Murray Cumming <murrayc murrayc com>
- * Copyright (C) 2005 - 2012 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2005 - 2013 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2007 - 2009 Armin Burgmeier <armin openismus com>
* Copyright (C) 2008 - 2009 Bas Driessen <bas driessen xobas com>
* Copyright (C) 2008 Phil Longstaff <plongstaff rogers com>
@@ -625,7 +625,7 @@ gda_utility_data_model_find_column_description (GdaDataSelect *model, const gcha
*
* Note: this method may set the "source" custom string property
*
- * Returns: TRUE if no error occurred
+ * Returns: %TRUE if no error occurred
*/
gboolean
gda_utility_holder_load_attributes (GdaHolder *holder, xmlNodePtr node, GSList *sources, GError **error)
@@ -1993,7 +1993,7 @@ gda_identifier_hash (const gchar *id)
* Does the same as strcmp(@id1, @id2), but handles the case where id1 and/or id2 are enclosed in double
quotes.
* can also be used in hash tables as a #GEqualFunc.
*
- * Returns: TRUE if @id1 and @id2 are equal.
+ * Returns: %TRUE if @id1 and @id2 are equal.
*/
gboolean
gda_identifier_equal (const gchar *id1, const gchar *id2)
@@ -2800,7 +2800,7 @@ gda_rfc1738_encode (const gchar *string)
*
* @string is decoded in place, no new string gets created.
*
- * Returns: TRUE if no error occurred.
+ * Returns: %TRUE if no error occurred.
*/
gboolean
gda_rfc1738_decode (gchar *string)
@@ -3043,54 +3043,126 @@ gda_connection_string_split (const gchar *string, gchar **out_cnc_params, gchar
gda_rfc1738_decode (*out_password);
}
+/*
+ * NB: @sep must not be zero
+ */
static gboolean
-_parse_iso8601_date (GDate *gdate, const gchar *value, char **out_endptr)
+_parse_formatted_date (GDate *gdate, const gchar *value, GDateDMY first, GDateDMY second, GDateDMY third,
gchar sep,
+ const char **out_endptr)
{
GDateYear year;
GDateMonth month;
GDateDay day;
unsigned long int tmp;
- char *endptr;
+ const char *endptr;
+ guint8 iter;
+ guint16 parsed_values [3] = {0, 0, 0};
g_date_clear (gdate, 1);
- if (! isdigit (*value))
+ /* checks */
+ if ((first == second) || (first == third) || (second == third)) {
+ g_warning (_("The 'first', 'second' and 'third' arguments must be different"));
return FALSE;
- tmp = strtoul (value, &endptr, 10);
- if (tmp <= G_MAXUINT16)
- year = tmp;
- else
+ }
+ if ((sep >= '0') && (sep <= '9')) {
+ if (sep)
+ g_warning (_("Invalid separator '%c'"), sep);
+ else
+ g_warning (_("Invalid null separator"));
return FALSE;
- if (*endptr != '-')
+ }
+
+ /* 1st number */
+ for (endptr = value, tmp = 0, iter = 0; (*endptr >= '0') && (*endptr <= '9') && (iter < 4); endptr++)
+ tmp = tmp * 10 + *endptr - '0';
+ parsed_values[0] = tmp;
+ if (*endptr != sep)
return FALSE;
- value = endptr + 1;
- if (! isdigit (*value))
+ /* 2nd number */
+ endptr++;
+ for (tmp = 0, iter = 0; (*endptr >= '0') && (*endptr <= '9') && (iter < 4); endptr++)
+ tmp = tmp * 10 + *endptr - '0';
+ parsed_values[1] = tmp;
+ if (*endptr != sep)
return FALSE;
- tmp = strtoul (value, &endptr, 10);
- month = tmp > 0 ? (tmp <= G_DATE_DECEMBER ? tmp : G_DATE_BAD_MONTH) : G_DATE_BAD_MONTH;
- if (month == G_DATE_BAD_MONTH)
+
+ /* 3rd number */
+ endptr++;
+ for (tmp = 0, iter = 0; (*endptr >= '0') && (*endptr <= '9') && (iter < 4); endptr++)
+ tmp = tmp * 10 + *endptr - '0';
+ parsed_values[2] = tmp;
+
+ /* reordering */
+ guint16 rmonth = 0, rday = 0;
+ switch (first) {
+ case G_DATE_YEAR:
+ year = parsed_values[0];
+ break;
+ case G_DATE_MONTH:
+ rmonth = parsed_values[0];
+ break;
+ case G_DATE_DAY:
+ rday = parsed_values[0];
+ break;
+ default:
+ g_warning (_("Unknown GDateDMY value %u"), first);
+ return FALSE;
+ }
+
+ switch (second) {
+ case G_DATE_YEAR:
+ year = parsed_values[1];
+ break;
+ case G_DATE_MONTH:
+ rmonth = parsed_values[1];
+ break;
+ case G_DATE_DAY:
+ rday = parsed_values[1];
+ break;
+ default:
+ g_warning (_("Unknown GDateDMY value %u"), second);
return FALSE;
- if (*endptr != '-')
+ }
+
+ switch (third) {
+ case G_DATE_YEAR:
+ year = parsed_values[2];
+ break;
+ case G_DATE_MONTH:
+ rmonth = parsed_values[2];
+ break;
+ case G_DATE_DAY:
+ rday = parsed_values[2];
+ break;
+ default:
+ g_warning (_("Unknown GDateDMY value %u"), third);
return FALSE;
+ }
- value = endptr + 1;
- if (! isdigit (*value))
+ /* checks */
+ month = rmonth > 0 ? (rmonth <= G_DATE_DECEMBER ? rmonth : G_DATE_BAD_MONTH) : G_DATE_BAD_MONTH;
+ if (month == G_DATE_BAD_MONTH)
return FALSE;
- tmp = strtoul (value, &endptr, 10);
- day = tmp > 0 ? (tmp <= G_MAXUINT8 ? tmp : G_DATE_BAD_DAY) : G_DATE_BAD_DAY;
+ day = rday > 0 ? (rday <= G_MAXUINT8 ? rday : G_DATE_BAD_DAY) : G_DATE_BAD_DAY;
if (day == G_DATE_BAD_DAY)
return FALSE;
if (g_date_valid_dmy (day, month, year)) {
g_date_set_dmy (gdate, day, month, year);
- *out_endptr = endptr;
+ if (out_endptr)
+ *out_endptr = endptr;
return TRUE;
}
- else {
- memset (gdate, 0, sizeof (GDate));
+ else
return FALSE;
- }
+}
+
+static gboolean
+_parse_iso8601_date (GDate *gdate, const gchar *value, const char **out_endptr)
+{
+ return _parse_formatted_date (gdate, value, G_DATE_YEAR, G_DATE_MONTH, G_DATE_DAY, '-', out_endptr);
}
/**
@@ -3104,86 +3176,168 @@ _parse_iso8601_date (GDate *gdate, const gchar *value, char **out_endptr)
* less than 2 digits for month and day are accepted). Years must be in the 1-65535 range,
* a limitation imposed by #GDate.
*
- * Returns: %TRUE if no error occurred
+ * Returns: %TRUE if @value has been sucessfuly parsed as a valid date (see g_date_valid()).
*/
gboolean
gda_parse_iso8601_date (GDate *gdate, const gchar *value)
{
g_return_val_if_fail (gdate, FALSE);
- char *endptr;
+ const char *endptr;
+ if (!value)
+ return FALSE;
+
+ if (! _parse_iso8601_date (gdate, value, &endptr) || *endptr)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/**
+ * gda_parse_formatted_date:
+ * @gdate: a pointer to a #GDate structure which will be filled
+ * @value: a string to be parsed
+ * @first: a #GDateDMY specifying which of year, month or day appears first (in the first bytes) in @value
+ * @second: a #GDateDMY specifying which of year, month or day appears second (in the first bytes) in @value
+ * @third: a #GDateDMY specifying which of year, month or day appears third (in the first bytes) in @value
+ * @sep: spcifies the expected separator character bewteen year, month and day (for example '-')
+ *
+ * This function is similar to gda_parse_iso8601_date() (with @first being @G_DATE_YEAR, @second being
@G_DATE_MONTH,
+ * @third being @G_DATE_DAY and @sep being '-') but allows one to specify the expected date format.
+ *
+ * Returns: %TRUE if @value has been sucessfuly parsed as a valid date (see g_date_valid()).
+ *
+ * Since: 5.2
+ */
+gboolean
+gda_parse_formatted_date (GDate *gdate, const gchar *value, GDateDMY first, GDateDMY second, GDateDMY third,
gchar sep)
+{
+ g_return_val_if_fail (gdate, FALSE);
+
+ const char *endptr;
if (!value)
return FALSE;
- if (! _parse_iso8601_date (gdate, value, &endptr))
+ if (! _parse_formatted_date (gdate, value, first, second, third, sep, &endptr))
return FALSE;
if (*endptr)
return FALSE;
return TRUE;
}
+
static gboolean
-_parse_iso8601_time (GdaTime *timegda, const gchar *value, char **out_endptr)
+_parse_iso8601_time (GdaTime *timegda, const gchar *value, gchar sep, glong timezone, const char
**out_endptr)
{
unsigned long int tmp;
- char *endptr;
+ const char *endptr;
memset (timegda, 0, sizeof (GdaTime));
- timegda->timezone = GDA_TIMEZONE_INVALID;
+ timegda->timezone = timezone;
- if (! isdigit (*value))
- return FALSE;
- tmp = strtoul (value, &endptr, 10);
- if (tmp <= 23)
- timegda->hour = tmp;
- else
- return FALSE;
- if (*endptr != ':')
- return FALSE;
-
- value = endptr + 1;
- if (! isdigit (*value))
- return FALSE;
- tmp = strtoul (value, &endptr, 10);
- if (tmp < 60)
- timegda->minute = tmp;
- else
- return FALSE;
- if (*endptr != ':')
+ if ((*value < '0') || (*value > '9'))
return FALSE;
- value = endptr + 1;
- if (! isdigit (*value))
+ /* hour */
+ guint8 iter;
+ for (iter = 0, tmp = 0, endptr = value; (*endptr >= '0') && (*endptr <= '9') && (iter < 2); iter++,
endptr++) {
+ tmp = tmp * 10 + *endptr - '0';
+ if (tmp > 23)
+ return FALSE;
+ }
+ timegda->hour = tmp;
+ if ((sep && *endptr != sep) || !*endptr)
return FALSE;
- tmp = strtoul (value, &endptr, 10);
- if (tmp < 60)
- timegda->second = tmp;
- else
+
+ /* minutes */
+ if (sep)
+ endptr++;
+ for (tmp = 0, iter = 0 ; (*endptr >= '0') && (*endptr <= '9') && (iter < 2); iter ++, endptr++) {
+ tmp = tmp * 10 + *endptr - '0';
+ if (tmp > 59)
+ return FALSE;
+ }
+ timegda->minute = tmp;
+ if ((sep && *endptr != sep) || !*endptr)
return FALSE;
+ /* seconds */
+ if (sep)
+ endptr++;
+ for (tmp = 0, iter = 0 ; (*endptr >= '0') && (*endptr <= '9') && (iter < 2); iter++, endptr++) {
+ tmp = tmp * 10 + *endptr - '0';
+ if (tmp > 59)
+ return FALSE;
+ }
+ timegda->second = tmp;
if (*endptr && (*endptr != '.') && (*endptr != '+') && (*endptr != '-')) {
*out_endptr = endptr;
return TRUE; /* end of the parsing */
}
if (*endptr == '.') {
- value = endptr + 1;
- if (! isdigit (*value))
- return FALSE;
- tmp = strtoul (value, &endptr, 10);
- if (tmp < G_MAXULONG)
- timegda->fraction = tmp;
- else
+ endptr++;
+ if (!*endptr)
return FALSE;
+ for (tmp = 0 ; (*endptr >= '0') && (*endptr <= '9'); endptr++) {
+ if (tmp > G_MAXULONG / 10)
+ return FALSE;
+ tmp = tmp * 10 + *endptr - '0';
+ }
+ timegda->fraction = tmp;
}
if ((*endptr == '+') || (*endptr == '-')) {
- long int stmp;
- value = endptr;
- stmp = strtol (value, &endptr, 10);
- if ((stmp >= -24) && (stmp <= 24))
- timegda->timezone = stmp * 60 * 60;
- else
- return FALSE;
+ gint8 mult = 1;
+ if (*endptr == '-')
+ mult = -1;
+ for (tmp = 0,endptr++ ; (*endptr >= '0') && (*endptr <= '9'); endptr++) {
+ tmp = tmp * 10 + *endptr - '0';
+ if (tmp >= 24)
+ return FALSE;
+ }
+ timegda->timezone = tmp * 60 * 60 * mult;
+ }
+ else if (*endptr) {
+ for (; g_ascii_isspace (*endptr); endptr++);
+ if (((*endptr == 'G') || (*endptr == 'g')) &&
+ ((endptr[1] == 'M') || (endptr[1] == 'm')) &&
+ ((endptr[2] == 'T') || (endptr[2] == 't')) && !endptr[3]) {
+ timegda->timezone = 0;
+ endptr += 3;
+ }
+ else if (((*endptr == 'U') || (*endptr == 'u')) &&
+ ((endptr[1] == 'T') || (endptr[1] == 't')) &&
+ ((endptr[2] == 'C') || (endptr[2] == 'c')) && !endptr[3]) {
+ timegda->timezone = 0;
+ endptr += 3;
+ }
+ else if (((*endptr == 'T') || (*endptr == 'u')) &&
+ ((endptr[1] == 'U') || (endptr[1] == 'u')) && !endptr[2]) {
+ timegda->timezone = 0;
+ endptr += 2;
+ }
+ else if (((*endptr == 'Z') || (*endptr == 'z')) && !endptr[1]) {
+ timegda->timezone = 0;
+ endptr += 1;
+ }
+ else {
+ /* http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations */
+ GTimeZone *tz;
+ tz = g_time_zone_new (endptr);
+ if (tz) {
+ if (g_time_zone_get_offset (tz, 0) == 0) {
+ g_time_zone_unref (tz);
+ return FALSE;
+ }
+ else {
+ timegda->timezone = g_time_zone_get_offset (tz, 0);
+ g_time_zone_unref (tz);
+ for (; *endptr; endptr++);
+ }
+ }
+ else
+ return FALSE;
+ }
}
*out_endptr = endptr;
@@ -3199,22 +3353,45 @@ _parse_iso8601_time (GdaTime *timegda, const gchar *value, char **out_endptr)
*
* Accepted date format is "HH:MM:SS[.ms][TZ]" where TZ is +hour or -hour
*
- * Returns: TRUE if no error occurred
+ * Returns: %TRUE if no error occurred
*/
gboolean
gda_parse_iso8601_time (GdaTime *timegda, const gchar *value)
{
g_return_val_if_fail (timegda, FALSE);
- char *endptr;
if (!value)
return FALSE;
- if (! _parse_iso8601_time (timegda, value, &endptr))
+ const char *endptr;
+ if (! _parse_iso8601_time (timegda, value, ':', GDA_TIMEZONE_INVALID, &endptr) || *endptr)
return FALSE;
- if (*endptr)
+ else
+ return TRUE;
+}
+
+/**
+ * gda_parse_formatted_time:
+ * @timegda: a pointer to a #GdaTime structure which will be filled
+ * @value: a string
+ * @sep: the time separator, usually ':'. If equal to @0, then the expexted format will be HHMMSS...
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 5.2
+ */
+gboolean
+gda_parse_formatted_time (GdaTime *timegda, const gchar *value, gchar sep)
+{
+ g_return_val_if_fail (timegda, FALSE);
+
+ if (!value)
return FALSE;
- return TRUE;
+ const char *endptr;
+ if (! _parse_iso8601_time (timegda, value, sep, GDA_TIMEZONE_INVALID, &endptr) || *endptr)
+ return FALSE;
+ else
+ return TRUE;
}
/**
@@ -3226,15 +3403,38 @@ gda_parse_iso8601_time (GdaTime *timegda, const gchar *value)
*
* Accepted date format is "YYYY-MM-DD HH:MM:SS[.ms][TZ]" where TZ is +hour or -hour
*
- * Returns: TRUE if no error occurred
+ * Returns: %TRUE if @value has been sucessfuly parsed as a valid timestamp (see g_date_valid())
*/
gboolean
gda_parse_iso8601_timestamp (GdaTimestamp *timestamp, const gchar *value)
{
+ return gda_parse_formatted_timestamp (timestamp, value, G_DATE_YEAR, G_DATE_MONTH, G_DATE_DAY, '-');
+}
+
+/**
+ * gda_parse_formatted_timestamp:
+ * @timestamp: a pointer to a #GdaTimeStamp structure which will be filled
+ * @value: a string to be parsed
+ * @first: a #GDateDMY specifying which of year, month or day appears first (in the first bytes) in @value
+ * @second: a #GDateDMY specifying which of year, month or day appears second (in the first bytes) in @value
+ * @third: a #GDateDMY specifying which of year, month or day appears third (in the first bytes) in @value
+ * @sep: spcifies the expected separator character bewteen year, month and day (for example '-')
+ *
+ * This function is similar to gda_parse_iso8601_timestamp() (with @first being @G_DATE_YEAR, @second being
@G_DATE_MONTH,
+ * @third being @G_DATE_DAY and @sep being '-') but allows one to specify the expected date format.
+ *
+ * Returns: %TRUE if @value has been sucessfuly parsed as a valid date (see g_date_valid()).
+ *
+ * Since: 5.2
+ */
+gboolean
+gda_parse_formatted_timestamp (GdaTimestamp *timestamp, const gchar *value,
+ GDateDMY first, GDateDMY second, GDateDMY third, gchar sep)
+{
g_return_val_if_fail (timestamp, FALSE);
gboolean retval = TRUE;
- char *endptr;
+ const char *endptr;
GDate gdate;
GdaTime timegda;
@@ -3246,7 +3446,7 @@ gda_parse_iso8601_timestamp (GdaTimestamp *timestamp, const gchar *value)
return FALSE;
/* date part */
- if (! _parse_iso8601_date (&gdate, value, &endptr)) {
+ if (! _parse_formatted_date (&gdate, value, first, second, third, sep, &endptr)) {
retval = FALSE;
goto out;
}
@@ -3267,7 +3467,7 @@ gda_parse_iso8601_timestamp (GdaTimestamp *timestamp, const gchar *value)
goto out;
/* time part */
- if (! _parse_iso8601_time (&timegda, value, &endptr) ||
+ if (! _parse_iso8601_time (&timegda, value, ':', GDA_TIMEZONE_INVALID, &endptr) ||
*endptr)
retval = FALSE;
out:
@@ -3277,5 +3477,5 @@ gda_parse_iso8601_timestamp (GdaTimestamp *timestamp, const gchar *value)
timestamp->fraction = timegda.fraction;
timestamp->timezone = timegda.timezone;
- return retval;
+ return retval;
}
diff --git a/libgda/gda-util.h b/libgda/gda-util.h
index b0f71c2..a1a8bfe 100644
--- a/libgda/gda-util.h
+++ b/libgda/gda-util.h
@@ -2,7 +2,7 @@
* Copyright (C) 2000 Reinhard Müller <reinhard src gnome org>
* Copyright (C) 2000 - 2002 Rodrigo Moya <rodrigo gnome-db org>
* Copyright (C) 2001 Carlos Perell� Mar�n <carlos gnome-db org>
- * Copyright (C) 2001 - 2012 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2001 - 2013 Vivien Malerba <malerba gnome-db org>
* Copyright (C) 2002 Gonzalo Paniagua Javier <gonzalo src gnome org>
* Copyright (C) 2006 - 2007 Murray Cumming <murrayc murrayc com>
* Copyright (C) 2007 Armin Burgmeier <armin openismus com>
@@ -120,6 +120,11 @@ void gda_connection_string_split (const gchar *string, gchar **out_cnc_p
gboolean gda_parse_iso8601_date (GDate *gdate, const gchar *value);
gboolean gda_parse_iso8601_time (GdaTime *timegda, const gchar *value);
gboolean gda_parse_iso8601_timestamp (GdaTimestamp *timestamp, const gchar *value);
+gboolean gda_parse_formatted_date (GDate *gdate, const gchar *value,
+ GDateDMY first, GDateDMY second, GDateDMY third, gchar sep);
+gboolean gda_parse_formatted_time (GdaTime *timegda, const gchar *value, gchar sep);
+gboolean gda_parse_formatted_timestamp (GdaTimestamp *timestamp, const gchar *value,
+ GDateDMY first, GDateDMY second, GDateDMY third, gchar sep);
G_END_DECLS
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index e6bd926..3b3a7f2 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -526,6 +526,9 @@
gda_numeric_set_precision
gda_numeric_set_width
gda_paramlist_dtd
+ gda_parse_formatted_date
+ gda_parse_formatted_time
+ gda_parse_formatted_timestamp
gda_parse_iso8601_date
gda_parse_iso8601_time
gda_parse_iso8601_timestamp
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]