Beispiel #1
0
void
gnc_tree_util_split_reg_parse_date (GDate *parsed, const char *datestr)
{
    int day, month, year;
    gboolean use_autoreadonly = qof_book_uses_autoreadonly (gnc_get_current_book ());

    if (!parsed) return;
    if (!datestr) return;

    if (!qof_scan_date (datestr, &day, &month, &year))
    {
        // Couldn't parse date, use today
        struct tm tm_today;
        gnc_tm_get_today_start (&tm_today);
        day = tm_today.tm_mday;
        month = tm_today.tm_mon + 1;
        year = tm_today.tm_year + 1900;
    }

    // If we have an auto-read-only threshold, do not accept a date that is
    // older than the threshold.
    if (use_autoreadonly)
    {
        GDate *d = g_date_new_dmy (day, month, year);
        GDate *readonly_threshold = qof_book_get_autoreadonly_gdate (gnc_get_current_book());
        if (g_date_compare (d, readonly_threshold) < 0)
        {
            g_warning("Entered date %s is before the \"auto-read-only threshold\"; resetting to the threshold.", datestr);
#if 0
            GtkWidget *dialog = gtk_message_dialog_new (NULL,
                                                       0,
                                                       GTK_MESSAGE_ERROR,
                                                       GTK_BUTTONS_OK,
                                                       "%s", _("Cannot store a transaction at this date"));
            gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                     "%s", _("The entered date of the new transaction is older than the \"Read-Only Threshold\" set for this book. "
                                                             "This setting can be changed in File -> Properties -> Accounts."));
            gtk_dialog_run (GTK_DIALOG (dialog));
            gtk_widget_destroy (dialog);
#endif

            // Reset the date to the threshold date
            day = g_date_get_day (readonly_threshold);
            month = g_date_get_month (readonly_threshold);
            year = g_date_get_year (readonly_threshold);
        }
        g_date_free (d);
        g_date_free (readonly_threshold);
    }
    g_date_set_dmy (parsed, day, month, year);
}
Beispiel #2
0
static void
gnc_parse_date (struct tm *parsed, const char * datestr)
{
    int day, month, year;
    gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book());

    if (!parsed) return;
    if (!datestr) return;

    if (!qof_scan_date (datestr, &day, &month, &year))
    {
        // Couldn't parse date, use today
        struct tm tm_today;

	memset (&tm_today, 0, sizeof (struct tm));
        gnc_tm_get_today_start (&tm_today);
        day = tm_today.tm_mday;
        month = tm_today.tm_mon + 1;
        year = tm_today.tm_year + 1900;
    }

    // If we have an auto-read-only threshold, do not accept a date that is
    // older than the threshold.
    if (use_autoreadonly)
    {
        GDate *d = g_date_new_dmy(day, month, year);
	if (check_readonly_threshold (datestr, d))
	{
	    day = g_date_get_day (d);
	    month = g_date_get_month (d);
	    year = g_date_get_year (d);
	}
	g_date_free (d);
    }

    parsed->tm_mday = day;
    parsed->tm_mon  = month - 1;
    parsed->tm_year = year - 1900;

    gnc_tm_set_day_start(parsed);
    /* Using gnc_mktime purely for its side effect of filling in the
     * rest of parsed and to check that it's valid.
     */
    if (gnc_mktime (parsed) == -1)
        gnc_tm_get_today_start (parsed);
    gnc_mktime (parsed);
}
/* This function converts a string date to a time64 value */
static time64
gcrd_string_dmy2time (const gchar *date_string)
{
    gint year = 0, month = 0, day = 0;

    if(qof_scan_date (date_string, &day, &month, &year))
    {
	struct tm when;
	memset (&when, 0, sizeof (when));
        when.tm_year = year - 1900;
        when.tm_mon = month - 1 ;
        when.tm_mday = day;

        return gnc_mktime (&when);
    }
    else
    {
	return gnc_time (NULL);
    }
}
static gboolean
gtk_cell_editable_key_press_event (GtkEntry      *entry,
                                   GdkEventKey   *key_event,
                                   GncPopupEntry *widget)
{
    const char *date_string;
    gint year = 0, month = 0, day = 0;
    struct tm when;

    if (key_event->keyval == GDK_KEY_Escape)
    {
        widget->editing_canceled = TRUE;

        gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget));
        gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget));

        return TRUE;
    }

    date_string = gtk_entry_get_text (entry);

    memset (&when, 0, sizeof (when));

    if (qof_scan_date (date_string, &day, &month, &year))
    {
        when.tm_year = year - 1900;
        when.tm_mon = month - 1 ;
        when.tm_mday = day;

        if (!gnc_handle_date_accelerator (key_event, &when, date_string))
            return FALSE;

        gtk_entry_set_text (entry, qof_print_date (gnc_mktime (&when)));
        gtk_widget_grab_focus (GTK_WIDGET (entry));
        return TRUE;
    }
    return FALSE;
}
Beispiel #5
0
/***********************************************************************
 * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
 * rather than in here?  But that is more concerned with ensuring the csv is consistent.
 * @param GtkListStore *store
 * @param guint *n_invoices_created
 * @param guint *n_invoices_updated
 * @return void
 ***********************************************************************/
void
gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                          guint * n_invoices_created,
                          guint * n_invoices_updated,
                          gchar * type, gchar * open_mode, GString * info)
{
    gboolean valid;
    GtkTreeIter iter;
    gchar *id, *date_opened, *owner_id, *billing_id, *notes;
    gchar *date, *desc, *action, *account, *quantity, *price, *disc_type,
          *disc_how, *discount, *taxable, *taxincluded, *tax_table;
    gchar *date_posted, *due_date, *account_posted, *memo_posted,
          *accumulatesplits;
    guint dummy;
    GncInvoice *invoice;
    GncEntry *entry;
    gint day, month, year;
    gnc_numeric value;
    GncOwner *owner;
    Account *acc;
    enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
    GtkWidget *dialog;
    Timespec today;
    InvoiceWindow *iw;
    gchar *new_id = NULL;
    gint64 denom = 0;
    gnc_commodity *currency;
    
    // these arguments are needed
    g_return_if_fail (store && book);
    // logic of this function only works for bills or invoices
    g_return_if_fail ((g_ascii_strcasecmp (type, "INVOICE") == 0) ||
            (g_ascii_strcasecmp (type, "BILL") == 0));

    // allow to call this function without statistics
    if (!n_invoices_created)
        n_invoices_created = &dummy;
    if (!n_invoices_updated)
        n_invoices_updated = &dummy;
    *n_invoices_created = 0;
    *n_invoices_updated = 0;

    invoice = NULL;
    update = NO;

    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
    while (valid)
    {
        // Walk through the list, reading each row
        gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
                            ID, &id,
                            DATE_OPENED, &date_opened,
                            DATE_POSTED, &date_posted,       // if autoposting requested
                            DUE_DATE, &due_date,             // if autoposting requested
                            ACCOUNT_POSTED, &account_posted, // if autoposting requested
                            MEMO_POSTED, &memo_posted,       // if autoposting requested
                            ACCU_SPLITS, &accumulatesplits,  // if autoposting requested
                            OWNER_ID, &owner_id,
                            BILLING_ID, &billing_id,
                            NOTES, &notes,
                            DATE, &date,
                            DESC, &desc,
                            ACTION, &action,
                            ACCOUNT, &account,
                            QUANTITY, &quantity,
                            PRICE, &price,
                            DISC_TYPE, &disc_type,
                            DISC_HOW, &disc_how,
                            DISCOUNT, &discount,
                            TAXABLE, &taxable,
                            TAXINCLUDED, &taxincluded,
                            TAX_TABLE, &tax_table, -1);

        // TODO:  Assign a new invoice number if one is absent.  BUT we don't want to assign a new invoice for every line!!
        // so we'd have to flag this up somehow or add an option in the import GUI.  The former implies that we make
        // an assumption about what the importer (person) wants to do.  It seems reasonable that a CSV file full of items with
        // If an invoice exists then we add to it in this current schema.
        // no predefined invoice number is a new invoice that's in need of a new number.
        // This was  not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
        // It should be easier to copy an invoice with a new ID than to go through all this malarky.
        if (g_ascii_strcasecmp (type, "BILL") == 0)
            invoice = gnc_search_bill_on_id (book, id);
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
            invoice = gnc_search_invoice_on_id (book, id);
        DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));

        // If the search is empty then there is no existing invoice so make a new one
        if (invoice == NULL)
        {
             DEBUG( "Creating a new : %s\n", type );
            // new invoice
            invoice = gncInvoiceCreate (book);
            /* Protect against thrashing the DB and trying to write the invoice
             * record prematurely */
            gncInvoiceBeginEdit (invoice);
            gncInvoiceSetID (invoice, id);
            owner = gncOwnerNew ();
            if (g_ascii_strcasecmp (type, "BILL") == 0)
                gncOwnerInitVendor (owner,
                                    gnc_search_vendor_on_id (book, owner_id));
            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                gncOwnerInitCustomer (owner,
                                      gnc_search_customer_on_id (book, owner_id));
            gncInvoiceSetOwner (invoice, owner);
            gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));	// Set the invoice currency based on the owner
            if (strlen (date_opened) != 0)	// If a date is specified in CSV
            {
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (date_opened, &day, &month, &year);
                gncInvoiceSetDateOpened (invoice,
                                         gnc_dmy2timespec (day, month, year));
            }
            else			// If no date in CSV
            {
                time64 now = gnc_time (NULL);
                Timespec now_timespec;
                timespecFromTime64 (&now_timespec, now);
                gncInvoiceSetDateOpened (invoice, now_timespec);
            }
            gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
            gncInvoiceSetNotes (invoice, notes ? notes : "");
            gncInvoiceSetActive (invoice, TRUE);
            //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
            (*n_invoices_created)++;
            update = YES;

            // open new bill / invoice in a tab, if requested
            if (g_ascii_strcasecmp(open_mode, "ALL") == 0
                    || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
                        && strlen(date_posted) == 0))
            {
                iw =  gnc_ui_invoice_edit (invoice);
                gnc_plugin_page_invoice_new (iw);
            }
            gncInvoiceCommitEdit (invoice);
        }
// I want to warn the user that an existing billvoice exists, but not every
// time.
// An import can contain many lines usually referring to the same invoice.
// NB: Posted invoices are NEVER updated.
        else			// if invoice exists
        {
            if (gncInvoiceIsPosted (invoice))	// Is it already posted?
            {
                valid =
                    gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
                continue;		// If already posted then never import
            }
            if (update != YES)	// Pop up a dialog to ask if updates are the expected action
            {
                dialog = gtk_message_dialog_new (NULL,
                                                 GTK_DIALOG_MODAL,
                                                 GTK_MESSAGE_ERROR,
                                                 GTK_BUTTONS_YES_NO,
                                                 "%s",
                                                 _("Are you sure you have bills/invoices to update?"));
                update = gtk_dialog_run (GTK_DIALOG (dialog));
                gtk_widget_destroy (dialog);
                if (update == NO)
                {
                    // Cleanup and leave
                    g_free (id);
                    g_free (date_opened);
                    g_free (owner_id);
                    g_free (billing_id);
                    g_free (notes);
                    g_free (date);
                    g_free (desc);
                    g_free (action);
                    g_free (account);
                    g_free (quantity);
                    g_free (price);
                    g_free (disc_type);
                    g_free (disc_how);
                    g_free (discount);
                    g_free (taxable);
                    g_free (taxincluded);
                    g_free (tax_table);
                    g_free (date_posted);
                    g_free (due_date);
                    g_free (account_posted);
                    g_free (memo_posted);
                    g_free (accumulatesplits);
                    return;
                }
            }
            (*n_invoices_updated)++;
        }


        // add entry to invoice/bill
        entry = gncEntryCreate (book);
        gncEntryBeginEdit(entry);
        currency = gncInvoiceGetCurrency(invoice);
        if (currency) denom = gnc_commodity_get_fraction(currency);
        // FIXME: Must check for the return value of qof_scan_date!
        qof_scan_date (date, &day, &month, &year);
        {
            GDate *date = g_date_new_dmy(day, month, year);
            gncEntrySetDateGDate (entry, date);
            g_date_free (date);
        }
        timespecFromTime64 (&today, gnc_time (NULL));	// set today to the current date
        gncEntrySetDateEntered (entry, today);
        gncEntrySetDescription (entry, desc);
        gncEntrySetAction (entry, action);
        value = gnc_numeric_zero();
        gnc_exp_parser_parse (quantity, &value, NULL);
        gncEntrySetQuantity (entry, value);
        acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
                                               account);
        
        if (g_ascii_strcasecmp (type, "BILL") == 0)
        {
            gncEntrySetBillAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            gncEntrySetBillPrice (entry, value);
            gncEntrySetBillTaxable (entry, text2bool (taxable));
            gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetBillTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            gncEntryCommitEdit(entry);
            gncBillAddEntry (invoice, entry);
        }
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
        {
            gncEntrySetNotes (entry, notes);
            gncEntrySetInvAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            gncEntrySetInvPrice (entry, value);
            gncEntrySetInvTaxable (entry, text2bool (taxable));
            gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetInvTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (discount, &value, NULL);
            gncEntrySetInvDiscount (entry, value);
            gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
            gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
            gncEntryCommitEdit(entry);
            gncInvoiceAddEntry (invoice, entry);
        }
        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);

        // handle auto posting of invoices
    
        
        if (valid)
            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
        if (g_strcmp0 (id, new_id) != 0)
        {
            // the next invoice id is different => try to autopost this invoice
            if (qof_scan_date (date_posted, &day, &month, &year))
            {
                // autopost this invoice
                gboolean auto_pay;
                Timespec d1, d2;

                if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
                else
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);

                d1 = gnc_dmy2timespec (day, month, year);
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (due_date, &day, &month, &year);	// obtains the due date, or leaves it at date_posted
                d2 = gnc_dmy2timespec (day, month, year);
                acc = gnc_account_lookup_for_register
                      (gnc_get_current_root_account (), account_posted);
                gncInvoicePostToAccount (invoice, acc, &d1, &d2,
                                         memo_posted,
                                         text2bool (accumulatesplits),
                                         auto_pay);
            }

        }
        
        
    }
    // cleanup
    g_free (new_id);
    g_free (id);
    g_free (date_opened);
    g_free (owner_id);
    g_free (billing_id);
    g_free (notes);
    g_free (date);
    g_free (desc);
    g_free (action);
    g_free (account);
    g_free (quantity);
    g_free (price);
    g_free (disc_type);
    g_free (disc_how);
    g_free (discount);
    g_free (taxable);
    g_free (taxincluded);
    g_free (tax_table);
    g_free (date_posted);
    g_free (due_date);
    g_free (account_posted);
    g_free (memo_posted);
    g_free (accumulatesplits);
    
}
Beispiel #6
0
/*   gnc_parse_time_date parsing tm (+0000) (from %+05d %d:%d:%d then the date) in datestr

 I hope that github will show differences between my new function gnc_parse_time_date and function qof_scan_date_internal found below, existing in HEAD (as of 25/1/2016) of the branch master of gnucash. Thanks to spaces added to qof_scan_date_internal.

**********************************************************
KNOWN BUG: gnucash crashes if a date string starts by ":" and longer thant ":".
**********************************************************
*/
void
gnc_parse_time_date (struct tm *parsed, const char * buff)
/* gnc_parse_time_date should call GncDateTime gncdt(std::string) when gnc_print_date_time_buff will be using GncDateTime and TZenv will disappear from everywhere*/
{
    char *dupe, *tmp, *TZhourtxt, *TZtxt, *hourtxt, *minutetxt, *secondtxt; /* tmp may be the unparsed part of the string. , */
    int iday, imonth, iyear;
    time64 secs;
    int tz; /* signed timezone in seconds */
    hourtxt = NULL;/* will stay NULL if TZ is not in buff */
    if (!parsed) return;
    parsed->tm_year=-1; /* means failure. */

    if (!buff) return;
    dupe = g_strdup (buff);

    tmp = dupe;
    TZhourtxt = strtok (tmp, ":");
    minutetxt = strtok (NULL, ":");
    secs = time(NULL); /* gnc_time(NULL) or time(NULL) ?? */
    gnc_localtime_r(&secs, parsed);
    tz = parsed->tm_gmtoff;
    if (TZhourtxt || minutetxt) {
        if (dupe[0] != ':' && minutetxt) {/* "hh:mm date" and "hh:mm:ss date" are parsed. "hh date" is not recognized. "hh:mm" and "hh:mm:ss" imply today. Default value for the timezone is TZenv */
            secondtxt = strtok (NULL, " ");
            tmp = strtok (NULL, "\a");/* hack: \a is the audible bell, hope this one is never used in a date format ; tmp = minutetxt + strlen (strtok (minutetxt, " "))+1 might be better */
            TZtxt = strtok (TZhourtxt, " ");
            hourtxt = strtok (NULL, " ");
            if (hourtxt) {
                tz = atoi(TZtxt);
                parsed->tm_hour = atoi(hourtxt) ;
                tz = 60 * ( (tz % 100) + 60 * (tz / 100) ) ;
                if (atoi(TZtxt) < 0) tz = - tz;
            } else {
                if (TZtxt) {
                    parsed->tm_hour = atoi(TZtxt) ;
                } else {
                    parsed->tm_hour = 0 ; /* the case "::" falls here ? */
                }
            }
            if (tmp) {
                /* +0200 02:01:03 2016-06-03
                * <+0200 02> TZhourtxt
                *          <01> minutetxt
                *             <03> secondtxt
                *                <2016-06-03> tmp
                * <+0200> TZtxt
                *       <02> hourtxt  */
                parsed->tm_min = atoi(minutetxt) ;
                parsed->tm_sec = atoi(secondtxt) ;
            } else {
                /* +0200 02:01 2016-06-03
                * <+0200 02> TZhourtxt
                *          <01 2016-06-03> minutetxt */
                parsed->tm_min = atoi ( strtok (minutetxt, " ") );
                parsed->tm_sec = 0 ;
                tmp = secondtxt ;
                /* +0200 02:01 2016-06-03
                * <+0200 02> TZhourtxt
                *             <2016-06-03> minutetxt
                *             <2016-06-03> tmp */
            }
            parsed->tm_sec = (parsed->tm_sec) - tz + (parsed->tm_gmtoff) ;
        }
    } else {
       parsed->tm_hour = k_reference_time_TZ_hour;
       /* parsed->tm_gmtoff = 0; does not work */
       parsed->tm_min = k_reference_time_TZ_min;
       parsed->tm_sec = k_reference_time_TZ_sec_of_min + parsed->tm_gmtoff;
       tmp = TZhourtxt;
    }
    if (tmp && qof_scan_date (tmp, &iday, &imonth, &iyear))
    {
        parsed->tm_mday = iday;
        parsed->tm_mon  = imonth - 1;
        parsed->tm_year = iyear - 1900;
    } /* else { Couldn't parse date, use today ; } */
    
    g_free (dupe);

}
Beispiel #7
0
static void
gnc_date_edit_popup (GNCDateEdit *gde)
{
    GtkWidget *toplevel;
    struct tm mtm;
    gboolean date_was_valid;
    GdkDevice *device, *keyboard, *pointer;

    g_return_if_fail (GNC_IS_DATE_EDIT (gde));

    ENTER("gde %p", gde);

    device = gtk_get_current_event_device ();

    /* This code is pretty much just copied from gtk_date_edit_get_date */
    date_was_valid = qof_scan_date (gtk_entry_get_text (GTK_ENTRY (gde->date_entry)),
                                    &mtm.tm_mday, &mtm.tm_mon, &mtm.tm_year);
    if (!date_was_valid)
    {
        /* No valid date. Hacky workaround: Instead of crashing we randomly choose today's date. */
        gnc_tm_get_today_start(&mtm);
    }

    mtm.tm_mon--;

    /* Hope the user does not actually mean years early in the A.D. days...
     * This date widget will obviously not work for a history program :-)
     */
    if (mtm.tm_year >= 1900)
        mtm.tm_year -= 1900;

    gnc_tm_set_day_start(&mtm);

    /* Set the calendar.  */
    gtk_calendar_select_day (GTK_CALENDAR (gde->calendar), 1);
    gtk_calendar_select_month (GTK_CALENDAR (gde->calendar), mtm.tm_mon,
                               1900 + mtm.tm_year);
    gtk_calendar_select_day (GTK_CALENDAR (gde->calendar), mtm.tm_mday);

    /* Make sure we'll get notified of clicks outside the popup
     * window so we can properly pop down if that happens. */
    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gde));
    if (GTK_IS_WINDOW (toplevel))
    {
        gtk_window_group_add_window (
            gtk_window_get_group (GTK_WINDOW (toplevel)),
            GTK_WINDOW (gde->cal_popup));
        gtk_window_set_transient_for (GTK_WINDOW (gde->cal_popup),
                                      GTK_WINDOW (toplevel));
    }

    position_popup (gde);

    gtk_widget_show (gde->cal_popup);

    gtk_widget_grab_focus (gde->cal_popup);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gde->date_button),
                                  TRUE);

    if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
    {
        keyboard = device;
        pointer = gdk_device_get_associated_device (device);
    }
    else
    {
        pointer = device;
        keyboard = gdk_device_get_associated_device (device);
    }

    if (!gtk_widget_has_focus (gde->calendar))
        gtk_widget_grab_focus (gde->calendar);

    if (!popup_grab_on_window (gtk_widget_get_window ((GTK_WIDGET(gde->cal_popup))),
                               keyboard, pointer, GDK_CURRENT_TIME))
    {
        gtk_widget_hide (gde->cal_popup);
        LEAVE("Failed to grab window");
        return;
    }

    gtk_grab_add (gde->cal_popup);

    LEAVE(" ");
}
Beispiel #8
0
static struct tm
gnc_date_edit_get_date_internal (GNCDateEdit *gde)
{
    struct tm tm = {0};
    char *str;
    gchar *flags = NULL;
    gboolean date_was_valid;

    /* Assert, because we're just hosed if it's NULL */
    g_assert(gde != NULL);
    g_assert(GNC_IS_DATE_EDIT(gde));

    date_was_valid = qof_scan_date (gtk_entry_get_text (GTK_ENTRY (gde->date_entry)),
                                    &tm.tm_mday, &tm.tm_mon, &tm.tm_year);

    if (!date_was_valid)
    {
        /* Hm... no valid date. What should we do not? As a hacky workaround we
        revert to today's date. Alternatively we can return some value that
        signals that we don't get a valid date, but all callers of this
        function will have to check this. Alas, I'm too lazy to do this here. */
        gnc_tm_get_today_start(&tm);
    }

    tm.tm_mon--;

    tm.tm_year -= 1900;

    if (gde->flags & GNC_DATE_EDIT_SHOW_TIME)
    {
        char *tokp = NULL;
        gchar *temp;

        str = g_strdup (gtk_entry_get_text
                        (GTK_ENTRY (gde->time_entry)));
        temp = gnc_strtok_r (str, ": ", &tokp);
        if (temp)
        {
            tm.tm_hour = atoi (temp);
            temp = gnc_strtok_r (NULL, ": ", &tokp);
            if (temp)
            {
                if (isdigit (*temp))
                {
                    tm.tm_min = atoi (temp);
                    flags = gnc_strtok_r (NULL, ": ",
                                          &tokp);
                    if (flags && isdigit (*flags))
                    {
                        tm.tm_sec = atoi (flags);
                        flags = gnc_strtok_r (NULL,
                                              ": ",
                                              &tokp);
                    }
                }
                else
                    flags = temp;
            }
        }

        if (flags && (strcasecmp (flags, "PM") == 0))
        {
            if (tm.tm_hour < 12)
                tm.tm_hour += 12;
        }
        g_free (str);
    }
    else
    {
        gnc_tm_set_day_start(&tm);
    }

    tm.tm_isdst = -1;

    return tm;
}