Replace GDateTime dependency with boost::date_time.

This commit is contained in:
John Ralls 2015-02-07 11:29:48 -08:00
parent 6673128b9f
commit 6c6153b5b6
3 changed files with 149 additions and 317 deletions

View File

@ -23,10 +23,15 @@
#ifndef __GNC_DATE_P_H__ #ifndef __GNC_DATE_P_H__
#define __GNC_DATE_P_H__ #define __GNC_DATE_P_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "gnc-date.h" #include "gnc-date.h"
#define NANOS_PER_SECOND 1000000000 #define NANOS_PER_SECOND INT32_C(1000000000)
/** Convert a given date/time format from UTF-8 to an encoding suitable for the /** Convert a given date/time format from UTF-8 to an encoding suitable for the
* strftime system call. * strftime system call.
@ -74,4 +79,7 @@ typedef struct
Testfuncs *gnc_date_load_funcs (void); Testfuncs *gnc_date_load_funcs (void);
#ifdef __cplusplus
}
#endif
#endif /* __GNC_DATE_P_H__ */ #endif /* __GNC_DATE_P_H__ */

View File

@ -25,46 +25,36 @@
\********************************************************************/ \********************************************************************/
#define __EXTENSIONS__ #define __EXTENSIONS__
#ifdef __cplusplus
extern "C" extern "C"
{ {
#endif
#include "config.h" #include "config.h"
#include <glib.h> #include <glib.h>
#include <glib/gprintf.h> #include <libintl.h>
/* to be renamed qofdate.c */ #include "platform.h"
#include <ctype.h>
#ifdef HAVE_LANGINFO_D_FMT
# include <langinfo.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <glib.h>
#include <glib/gi18n.h>
#include "gnc-date-p.h"
#include "qof.h" #include "qof.h"
#ifndef HAVE_STRPTIME #ifdef HAVE_LANGINFO_D_FMT
#include "strptime.h" # include <langinfo.h>
#endif #endif
#ifndef HAVE_LOCALTIME_R
#include "localtime_r.h"
#endif
#include "platform.h"
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef __cplusplus
} }
#endif
#include "gnc-date.h"
#include "gnc-date-p.h"
#include "gnc-timezone.hpp"
#define N_(string) string //So that xgettext will find it
using Date = boost::gregorian::date;
using Month = boost::gregorian::greg_month;
using PTime = boost::posix_time::ptime;
using LDT = boost::local_time::local_date_time;
using Duration = boost::posix_time::time_duration;
using LDTBase = boost::local_time::local_date_time_base<PTime, boost::date_time::time_zone_base<PTime, char>>;
#ifdef HAVE_LANGINFO_D_FMT #ifdef HAVE_LANGINFO_D_FMT
# define GNC_D_FMT (nl_langinfo (D_FMT)) # define GNC_D_FMT (nl_langinfo (D_FMT))
@ -101,151 +91,55 @@ static int dateCompletionBackMonths = 6;
/* This static indicates the debugging module that this .o belongs to. */ /* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = QOF_MOD_ENGINE; static QofLogModule log_module = QOF_MOD_ENGINE;
/* Getting a timezone is expensive, and we do it a lot. Cache the value. */ // Default constructor Initializes to the current locale.
static GTimeZone* static const TimeZoneProvider tzp;
gnc_g_time_zone_new_local (void) // For converting to/from POSIX time.
{ static const PTime unix_epoch (Date(1970, boost::gregorian::Jan, 1),
static GTimeZone* tz = NULL; boost::posix_time::seconds(0));
if (tz) /* To ensure things aren't overly screwed up by setting the nanosecond clock for boost::date_time. Don't do it, though, it doesn't get us anything and slows down the date/time library. */
return tz; #ifndef BOOST_DATE_TIME_HAS_NANOSECONDS
tz = g_time_zone_new_local(); static constexpr uint64_t ticks_per_second = UINT64_C(1000000);
return tz; #else
} static constexpr uint64_t ticks_per_second = UINT64_C(1000000000);
static GDateTime*
gnc_g_date_time_new_local (gint year, gint month, gint day, gint hour, gint minute, gdouble seconds)
{
GTimeZone *tz = gnc_g_time_zone_new_local();
GDateTime *gdt = g_date_time_new (tz, year, month, day,
hour, minute, seconds);
if (!gdt)
return gdt;
g_date_time_unref (gdt);
/* g_date_time_new truncates nanoseconds to microseconds. Sometimes in
* converting (particularly when parsing from a string) the
* nanoseconds will have lost 1/2 a femtosecond or so. Adding 1/2 a
* nano second ensures that the truncation doesn't lose a micorsecond
* in translation.
*/
seconds += 5e-10;
gdt = g_date_time_new (tz, year, month, day, hour, minute, seconds);
return gdt;
}
static GDateTime*
gnc_g_date_time_adjust_for_dst (GDateTime *gdt, GTimeZone *tz)
{
GDateTime *ngdt;
g_return_val_if_fail (gdt != NULL, NULL);
ngdt = g_date_time_to_timezone (gdt, tz);
g_date_time_unref (gdt);
gdt = g_date_time_to_timezone (ngdt, tz);
g_date_time_unref (ngdt);
return gdt;
}
GDateTime*
gnc_g_date_time_new_from_unix_local (time64 time)
{
GTimeZone *tz = gnc_g_time_zone_new_local ();
GDateTime *gdt = g_date_time_new_from_unix_utc (time);
if (gdt)
gdt = gnc_g_date_time_adjust_for_dst (gdt, tz);
return gdt;
}
static GDateTime*
gnc_g_date_time_new_from_timeval_local (const GTimeVal* tv)
{
GTimeZone *tz = gnc_g_time_zone_new_local ();
GDateTime *gdt = g_date_time_new_from_timeval_utc (tv);
if (gdt)
gdt = gnc_g_date_time_adjust_for_dst (gdt, tz);
return gdt;
}
static GDateTime*
gnc_g_date_time_new_now_local (void)
{
GTimeZone *tz = gnc_g_time_zone_new_local ();
GDateTime *gdt = g_date_time_new_now_utc ();
if (gdt)
gdt = gnc_g_date_time_adjust_for_dst (gdt, tz);
return gdt;
}
static GDateTime*
gnc_g_date_time_to_local (GDateTime* gdt)
{
GTimeZone *tz = NULL;
if (gdt)
{
tz = gnc_g_time_zone_new_local ();
gdt = gnc_g_date_time_adjust_for_dst (g_date_time_to_utc (gdt), tz);
}
return gdt;
}
typedef struct
{
GDateTime *(*new_local)(gint, gint, gint, gint, gint, gdouble);
GDateTime *(*adjust_for_dst)(GDateTime *, GTimeZone *);
GDateTime *(*new_from_unix_local)(time64);
GDateTime *(*new_from_timeval_local)(const GTimeVal *);
GDateTime *(*new_now_local)(void);
GDateTime *(*to_local)(GDateTime *);
} _GncDateTime;
#ifdef __cplusplus
extern "C"
{
#endif #endif
static LDT
void _gnc_date_time_init(_GncDateTime*); gnc_get_LDT(int year, int month, int day, int hour, int minute, int seconds)
void
_gnc_date_time_init (_GncDateTime *gncdt)
{ {
gncdt->new_local = gnc_g_date_time_new_local; Date date(year, static_cast<Month>(month), day);
gncdt->adjust_for_dst = gnc_g_date_time_adjust_for_dst; Duration time(hour, minute, seconds);
gncdt->new_from_unix_local = gnc_g_date_time_new_from_unix_local; auto tz = tzp.get(year);
gncdt->new_from_timeval_local = gnc_g_date_time_new_from_timeval_local; return LDT(date, time, tz, LDTBase::NOT_DATE_TIME_ON_ERROR);
gncdt->new_now_local = gnc_g_date_time_new_now_local;
gncdt->to_local = gnc_g_date_time_to_local;
} }
#ifdef __cplusplus static LDT
LDT_from_unix_local(const time64 time)
{
PTime temp(unix_epoch.date(),
boost::posix_time::hours(time / 3600) +
boost::posix_time::seconds(time % 3600));
auto tz = tzp.get(temp.date().year());
return LDT(temp, tz);
}
template<typename T>
static time64
time64_from_date_time(T time)
{
auto duration = time - unix_epoch;
return duration.ticks() / ticks_per_second;
} }
#endif
/****************** Posix Replacement Functions ***************************/ /****************** Posix Replacement Functions ***************************/
void void
gnc_tm_free (struct tm* time) gnc_tm_free (struct tm* time)
{ {
g_slice_free1 (sizeof (struct tm), time); free(time);
}
#define MAX_TZ_SIZE
static void
gnc_g_date_time_fill_struct_tm (GDateTime *gdt, struct tm* time)
{
memset (time, 0, sizeof (struct tm));
g_date_time_get_ymd (gdt, &(time->tm_year), &(time->tm_mon), &(time->tm_mday));
time->tm_sec = g_date_time_get_second (gdt);
time->tm_min = g_date_time_get_minute (gdt);
time->tm_hour = g_date_time_get_hour (gdt);
// Watch out: struct tm has wday=0..6 with Sunday=0, but GDateTime has wday=1..7 with Sunday=7.
time->tm_wday = g_date_time_get_day_of_week (gdt) % 7;
time->tm_yday = g_date_time_get_day_of_year (gdt);
time->tm_isdst = g_date_time_is_daylight_savings (gdt);
time->tm_year -= 1900;
--time->tm_mon;
} }
struct tm* struct tm*
gnc_localtime (const time64 *secs) gnc_localtime (const time64 *secs)
{ {
struct tm *time = static_cast<struct tm*>(g_slice_alloc0 (sizeof (struct tm))); auto time = static_cast<struct tm*>(calloc(1, sizeof(struct tm)));
if (gnc_localtime_r (secs, time) == NULL) if (gnc_localtime_r (secs, time) == NULL)
{ {
gnc_tm_free (time); gnc_tm_free (time);
@ -254,39 +148,41 @@ gnc_localtime (const time64 *secs)
return time; return time;
} }
/* Linux, Darwin, and MSWindows implementations of this function set the
* globals timezone and daylight; BSD doesn't have those globals, and
* Gnucash never uses them, so they're omitted from this
* implementation. Bug 704185.
*/
struct tm* struct tm*
gnc_localtime_r (const time64 *secs, struct tm* time) gnc_localtime_r (const time64 *secs, struct tm* time)
{ {
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (*secs); try
g_return_val_if_fail (gdt != NULL, NULL); {
auto ldt = LDT_from_unix_local(*secs);
gnc_g_date_time_fill_struct_tm (gdt, time); *time = boost::local_time::to_tm(ldt);
if (g_date_time_is_daylight_savings (gdt))
time->tm_isdst = 1;
#ifdef HAVE_STRUCT_TM_GMTOFF #ifdef HAVE_STRUCT_TM_GMTOFF
time->tm_gmtoff = g_date_time_get_utc_offset (gdt) / G_TIME_SPAN_SECOND; auto offset = ldt.zone()->base_utc_offset();
if (ldt.is_dst())
offset += ldt.zone()->dst_offset();
time->tm_gmtoff = offset.total_seconds();
#endif #endif
}
g_date_time_unref (gdt); catch(boost::gregorian::bad_year)
return time; {
return NULL; //Yeah, it should be nullptr, but this is a C-linkage func.
}
return time;
} }
struct tm* struct tm*
gnc_gmtime (const time64 *secs) gnc_gmtime (const time64 *secs)
{ {
struct tm *time; auto time = static_cast<struct tm*>(calloc(1, sizeof (struct tm)));
GDateTime *gdt = g_date_time_new_from_unix_utc (*secs); try {
g_return_val_if_fail (gdt != NULL, NULL); PTime pdt(unix_epoch.date(), boost::posix_time::hours(*secs / 3600) +
time = static_cast<struct tm*>(g_slice_alloc0 (sizeof (struct tm))); boost::posix_time::seconds(*secs % 3600));
gnc_g_date_time_fill_struct_tm (gdt, time); *time = boost::posix_time::to_tm(pdt);
g_date_time_unref (gdt); }
return time; catch(boost::gregorian::bad_year)
{
return NULL; //Yeah, it should be nullptr, but this is a C-linkage func.
}
return time;
} }
static void static void
@ -318,10 +214,10 @@ normalize_struct_tm (struct tm* time)
gint last_day; gint last_day;
++time->tm_mon; ++time->tm_mon;
/* GDateTime doesn't protect itself against out-of range years, /* Gregorian_date throws if it gets an out-of-range year
* so clamp year into GDateTime's range. * so clamp year into gregorian_date's range.
*/ */
if (year < 0) year = -year; if (year < 1400) year += 1400;
if (year > 9999) year %= 10000; if (year > 9999) year %= 10000;
normalize_time_component (&(time->tm_sec), &(time->tm_min), 60, 0); normalize_time_component (&(time->tm_sec), &(time->tm_min), 60, 0);
@ -348,68 +244,30 @@ normalize_struct_tm (struct tm* time)
time64 time64
gnc_mktime (struct tm* time) gnc_mktime (struct tm* time)
{ {
GDateTime *gdt; normalize_struct_tm (time);
time64 secs; return time64_from_date_time(boost::posix_time::ptime_from_tm(*time));
normalize_struct_tm (time);
gdt = gnc_g_date_time_new_local (time->tm_year + 1900, time->tm_mon,
time->tm_mday, time->tm_hour,
time->tm_min, (gdouble)(time->tm_sec));
if (gdt == NULL)
{
g_warning("Invalid time passed to gnc_mktime");
return -1;
}
time->tm_mon = time->tm_mon > 0 ? time->tm_mon - 1 : 11;
// Watch out: struct tm has wday=0..6 with Sunday=0, but GDateTime has wday=1..7 with Sunday=7.
time->tm_wday = g_date_time_get_day_of_week (gdt) % 7;
time->tm_yday = g_date_time_get_day_of_year (gdt);
time->tm_isdst = g_date_time_is_daylight_savings (gdt);
#ifdef HAVE_STRUCT_TM_GMTOFF
time->tm_gmtoff = g_date_time_get_utc_offset (gdt) / G_TIME_SPAN_SECOND;
#endif
secs = g_date_time_to_unix (gdt);
g_date_time_unref (gdt);
return secs;
} }
time64 time64
gnc_timegm (struct tm* time) gnc_timegm (struct tm* time)
{ {
GDateTime *gdt; auto newtime = *time;
time64 secs; newtime.tm_gmtoff = 0;
normalize_struct_tm (time); return gnc_mktime(&newtime);
gdt = g_date_time_new_utc (time->tm_year + 1900, time->tm_mon,
time->tm_mday, time->tm_hour, time->tm_min,
(gdouble)(time->tm_sec));
time->tm_mon = time->tm_mon > 0 ? time->tm_mon - 1 : 11;
// Watch out: struct tm has wday=0..6 with Sunday=0, but GDateTime has wday=1..7 with Sunday=7.
time->tm_wday = g_date_time_get_day_of_week (gdt) % 7;
time->tm_yday = g_date_time_get_day_of_year (gdt);
time->tm_isdst = g_date_time_is_daylight_savings (gdt);
secs = g_date_time_to_unix (gdt);
g_date_time_unref (gdt);
return secs;
} }
gchar* char*
gnc_ctime (const time64 *secs) gnc_ctime (const time64 *secs)
{ {
GDateTime *gdt = gnc_g_date_time_new_from_unix_local (*secs); return gnc_print_time64(*secs, "%a %b %e %H:%M:%S %Y");
gchar *string = g_date_time_format (gdt, "%a %b %e %H:%M:%S %Y");
g_date_time_unref (gdt);
return string;
} }
time64 time64
gnc_time (time64 *tbuf) gnc_time (time64 *tbuf)
{ {
GDateTime *gdt = gnc_g_date_time_new_now_local (); auto pdt = boost::posix_time::second_clock::local_time();
time64 secs = g_date_time_to_unix (gdt); auto secs = time64_from_date_time(pdt);
g_date_time_unref (gdt); if (tbuf != nullptr)
if (tbuf != NULL)
*tbuf = secs; *tbuf = secs;
return secs; return secs;
} }
@ -417,9 +275,8 @@ gnc_time (time64 *tbuf)
time64 time64
gnc_time_utc (time64 *tbuf) gnc_time_utc (time64 *tbuf)
{ {
GDateTime *gdt = g_date_time_new_now_utc (); auto pdt = boost::posix_time::second_clock::universal_time();
time64 secs = g_date_time_to_unix (gdt); auto secs = time64_from_date_time(pdt);
g_date_time_unref (gdt);
if (tbuf != NULL) if (tbuf != NULL)
*tbuf = secs; *tbuf = secs;
return secs; return secs;
@ -434,17 +291,6 @@ gnc_difftime (const time64 secs1, const time64 secs2)
/****************************************************************************/ /****************************************************************************/
GDateTime*
gnc_g_date_time_new_from_timespec_local (Timespec ts)
{
GDateTime *gdt1 = gnc_g_date_time_new_from_unix_local (ts.tv_sec);
double nsecs = ((double)ts.tv_nsec + 0.5)/ 1000000000.0L;
GDateTime *gdt2 = g_date_time_add_seconds (gdt1, nsecs);
g_date_time_unref (gdt1);
g_assert (g_date_time_to_unix (gdt2) == ts.tv_sec + (nsecs >= 1.0 ? (gint64)nsecs : 0));
return gdt2;
}
const char* const char*
gnc_date_dateformat_to_string(QofDateFormat format) gnc_date_dateformat_to_string(QofDateFormat format)
{ {
@ -530,6 +376,24 @@ gnc_date_string_to_monthformat(const char *fmt_str, GNCDateMonthFormat *format)
return FALSE; return FALSE;
} }
char*
gnc_print_time64(time64 time, const char* format)
{
using Facet = boost::local_time::local_time_facet;
auto date_time = LDT_from_unix_local(time);
std::stringstream ss;
//The stream destructor frees the facet, so it must be heap-allocated.
auto output_facet(new Facet(format));
ss.imbue(std::locale(std::locale(), output_facet));
ss << date_time;
auto sstr = ss.str();
//ugly C allocation so that the ptr can be freed at the other end
char* cstr = static_cast<char*>(malloc(sstr.length() + 1));
memset(cstr, 0, sstr.length() + 1);
strncpy(cstr, sstr.c_str(), sstr.length());
return cstr;
}
/********************************************************************\ /********************************************************************\
\********************************************************************/ \********************************************************************/
@ -1412,10 +1276,7 @@ qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
gchar * gchar *
gnc_date_timestamp (void) gnc_date_timestamp (void)
{ {
GDateTime *gdt = gnc_g_date_time_new_now_local (); return gnc_print_time64(gnc_time(nullptr), "%Y-%M-%d %H:%M%S");
gchar *timestr = g_date_time_format (gdt, "%Y%m%d%H%M%S");
g_date_time_unref (gdt);
return timestr;
} }
/********************************************************************\ /********************************************************************\
@ -1429,38 +1290,9 @@ gnc_date_timestamp (void)
Timespec Timespec
gnc_iso8601_to_timespec_gmt(const char *str) gnc_iso8601_to_timespec_gmt(const char *str)
{ {
Timespec time = { 0L, 0L }; auto pdt = boost::posix_time::time_from_string(str);
GDateTime *gdt; auto time = time64_from_date_time(pdt);
gint hour = 0, minute = 0, day = 0, month = 0, year = 0; return {time, 0};
gchar zone[12];
gdouble second = 0.0;
gint fields;
memset (zone, 0, sizeof (zone));
if (!str)
return time;
fields = sscanf (str, ISO_DATE_FORMAT, &year, &month,
&day, &hour, &minute, &second, zone);
if (fields < 1)
return time;
else if (fields > 6 && strlen (zone) > 0) /* Date string included a timezone */
{
GTimeZone *tz = g_time_zone_new (zone);
second += 5e-10;
gdt = g_date_time_new (tz, year, month, day, hour, minute, second);
}
else /* No zone info, assume UTC */
{
second += 5e-10;
gdt = g_date_time_new_utc (year, month, day, hour, minute, second);
}
time.tv_sec = g_date_time_to_unix (gdt);
time.tv_nsec = g_date_time_get_microsecond (gdt) * 1000;
g_date_time_unref (gdt);
return time;
} }
/********************************************************************\ /********************************************************************\
@ -1469,28 +1301,20 @@ gnc_iso8601_to_timespec_gmt(const char *str)
char * char *
gnc_timespec_to_iso8601_buff (Timespec ts, char * buff) gnc_timespec_to_iso8601_buff (Timespec ts, char * buff)
{ {
const gchar *fmt1 = "%Y-%m-%d %H:%M", *fmt2 = "%s:%02d.%06d %s"; constexpr size_t max_iso_date_length = 32;
GDateTime *gdt; std::string fmt1 = "%Y-%m-%d %H:%M";
gchar *time_base, *tz;
g_return_val_if_fail (buff != NULL, NULL); g_return_val_if_fail (buff != NULL, NULL);
gdt = gnc_g_date_time_new_from_timespec_local (ts);
g_return_val_if_fail (gdt != NULL, NULL);
time_base = g_date_time_format (gdt, fmt1);
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
tz = g_date_time_format (gdt, "%Z"); fmt1 += "%Z";
#else #else
tz = g_date_time_format (gdt, "%z"); fmt1 += "%z";
#endif #endif
snprintf (buff, MAX_DATE_LENGTH, fmt2, time_base, char* str = gnc_print_time64(ts.tv_sec, fmt1.c_str());
g_date_time_get_second (gdt), g_date_time_get_microsecond (gdt), strncpy (buff, str, max_iso_date_length);
tz); free(str);
g_free (time_base);
g_free (tz);
g_date_time_unref (gdt);
return buff + strlen (buff); return buff + strlen (buff);
} }
void void
@ -1614,16 +1438,13 @@ GDate timespec_to_gdate (Timespec ts)
GDate* gnc_g_date_new_today () GDate* gnc_g_date_new_today ()
{ {
GDateTime *gdt = gnc_g_date_time_new_now_local ();
gint day, month, year;
GDate *result;
g_date_time_get_ymd (gdt, &year, &month, &day); auto pdt = boost::posix_time::second_clock::local_time();
result = g_date_new_dmy (day, static_cast<GDateMonth>(month), year); auto ymd = pdt.date().year_month_day();
g_date_time_unref (gdt); auto month = static_cast<GDateMonth>(ymd.month.as_number());
g_assert(g_date_valid (result)); auto result = g_date_new_dmy (ymd.day, month, ymd.year);
g_assert(g_date_valid (result));
return result; return result;
} }
Timespec gdate_to_timespec (GDate d) Timespec gdate_to_timespec (GDate d)

View File

@ -238,12 +238,6 @@ time64 gnc_time_utc (time64 *tbuf);
*/ */
gdouble gnc_difftime (const time64 secs1, const time64 secs2); gdouble gnc_difftime (const time64 secs1, const time64 secs2);
/** Wrapper for g_date_time_new_from_unix_local() that takes special care on
* windows to take the local timezone into account. On unix, it just calles the
* g_date function. */
GDateTime*
gnc_g_date_time_new_from_unix_local (time64 time);
/** \brief free a struct tm* created with gnc_localtime() or gnc_gmtime() /** \brief free a struct tm* created with gnc_localtime() or gnc_gmtime()
* \param time: The struct tm* to be freed. * \param time: The struct tm* to be freed.
*/ */
@ -280,6 +274,19 @@ Note the reversed return values!
*/ */
gboolean gnc_date_string_to_monthformat(const gchar *format_string, gboolean gnc_date_string_to_monthformat(const gchar *format_string,
GNCDateMonthFormat *format); GNCDateMonthFormat *format);
/** \brief print a time64 as a date string per format
* \param time
* \param format A date format conforming to the strftime format rules.
* \return a raw heap-allocated char* which must be freed.
*/
char* gnc_print_time64(time64 time, const char* format);
/** Returns a newly allocated date of the current clock time, taken from
* time(2). The caller must g_date_free() the object afterwards. */
GDate* gnc_g_date_new_today (void);
// @} // @}
/* Datatypes *******************************************************/ /* Datatypes *******************************************************/
@ -338,10 +345,6 @@ void timespecFromTime64 (Timespec *ts, time64 t );
/** Turns a Timespec into a time64 */ /** Turns a Timespec into a time64 */
time64 timespecToTime64 (Timespec ts); time64 timespecToTime64 (Timespec ts);
/** Returns a newly allocated date of the current clock time, taken from
* time(2). The caller must g_date_free() the object afterwards. */
GDate* gnc_g_date_new_today (void);
/** Turns a Timespec into a GDate */ /** Turns a Timespec into a GDate */
GDate timespec_to_gdate (Timespec ts); GDate timespec_to_gdate (Timespec ts);