mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Bug 137017 - date of transaction change with time zone change
First step: Change the timestamp to 11:00 UTC instead of midnight local, adjusting by an hour or two if the local timezone is one near the International Date Line to keep the date from flipping around. Scrub all old entries to make current files correct. Note: This effectively disposes of the distinction of close-book transactions having a noon instead of midnight timestamp as a way to distinguish them from regular transactions, but that distinction doesn't seem to be used; xaccTransIsCloseBook() is used instead.
This commit is contained in:
parent
6a81738e96
commit
51e29e7836
@ -36,6 +36,7 @@
|
||||
|
||||
#include "Account.h"
|
||||
#include "Transaction.h"
|
||||
#include <Scrub.h>
|
||||
#include "gnc-lot.h"
|
||||
#include "engine-helpers.h"
|
||||
|
||||
@ -376,6 +377,7 @@ query_transactions( GncSqlBackend* be, GncSqlStatement* stmt )
|
||||
if ( tx != NULL )
|
||||
{
|
||||
tx_list = g_list_prepend( tx_list, tx );
|
||||
xaccTransScrubPostedDate (tx);
|
||||
}
|
||||
row = gnc_sql_result_get_next_row( result );
|
||||
}
|
||||
|
@ -278,6 +278,7 @@ add_transaction_local(sixtp_gdv2 *data, Transaction *trn)
|
||||
xaccTransSetCurrency);
|
||||
|
||||
xaccTransScrubCurrency (trn);
|
||||
xaccTransScrubPostedDate (trn);
|
||||
xaccTransCommitEdit (trn);
|
||||
|
||||
data->counter.transactions_loaded++;
|
||||
|
@ -1373,4 +1373,17 @@ xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency,
|
||||
return acc;
|
||||
}
|
||||
|
||||
void
|
||||
xaccTransScrubPostedDate (Transaction *trans)
|
||||
{
|
||||
time64 orig = xaccTransGetDate(trans);
|
||||
GDate date = xaccTransGetDatePostedGDate(trans);
|
||||
Timespec ts = gdate_to_timespec(date);
|
||||
if (orig && orig != ts.tv_sec)
|
||||
{
|
||||
/* xaccTransSetDatePostedTS handles committing the change. */
|
||||
xaccTransSetDatePostedTS(trans, &ts);
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== END OF FILE ==================== */
|
||||
|
@ -142,8 +142,18 @@ void xaccAccountTreeScrubCommodities (Account *acc);
|
||||
*/
|
||||
void xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table);
|
||||
|
||||
/** Removes empty "notes", "placeholder", and "hbci" KVP slots from Accounts. */
|
||||
void xaccAccountScrubKvp (Account *account);
|
||||
|
||||
/** Changes Transaction date_posted timestamps from 00:00 local to 11:00 UTC.
|
||||
* 11:00 UTC is the same day local time in almost all timezones, the exceptions
|
||||
* being the -12, +13, and +14 timezones along the International Date Line. If
|
||||
* Local time is set to one of these timezones then the new date_posted time
|
||||
* will be adjusted as needed to ensure that the date doesn't change there. This
|
||||
* change was made for v2.6.14 to partially resolve bug 137017.
|
||||
*/
|
||||
void xaccTransScrubPostedDate (Transaction *trans);
|
||||
|
||||
#endif /* XACC_SCRUB_H */
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
@ -183,6 +183,7 @@ gnc_g_date_time_to_local (GDateTime* gdt)
|
||||
typedef struct
|
||||
{
|
||||
GDateTime *(*new_local)(gint, gint, gint, gint, gint, gdouble);
|
||||
GDateTime *(*new_utc)(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 *);
|
||||
@ -195,6 +196,7 @@ void
|
||||
_gnc_date_time_init (_GncDateTime *gncdt)
|
||||
{
|
||||
gncdt->new_local = gnc_g_date_time_new_local;
|
||||
gncdt->new_utc = g_date_time_new_utc;
|
||||
gncdt->adjust_for_dst = gnc_g_date_time_adjust_for_dst;
|
||||
gncdt->new_from_unix_local = gnc_g_date_time_new_from_unix_local;
|
||||
gncdt->new_from_timeval_local = gnc_g_date_time_new_from_timeval_local;
|
||||
@ -372,12 +374,13 @@ gnc_timegm (struct tm* time)
|
||||
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);
|
||||
|
||||
if (gdt == NULL)
|
||||
{
|
||||
PERR("Failed to get valid GDateTime with struct tm: %d-%d-%d %d:%d:%d",
|
||||
time->tm_year + 1900, time->tm_mon, time->tm_mday, time->tm_hour,
|
||||
time->tm_min, time->tm_sec);
|
||||
return 0;
|
||||
}
|
||||
secs = g_date_time_to_unix (gdt);
|
||||
g_date_time_unref (gdt);
|
||||
return secs;
|
||||
@ -1551,6 +1554,7 @@ gnc_dmy2timespec_internal (int day, int month, int year, gboolean start_of_day)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Timespec
|
||||
gnc_dmy2timespec (int day, int month, int year)
|
||||
{
|
||||
@ -1563,6 +1567,29 @@ gnc_dmy2timespec_end (int day, int month, int year)
|
||||
return gnc_dmy2timespec_internal (day, month, year, FALSE);
|
||||
}
|
||||
|
||||
Timespec
|
||||
gnc_dmy2timespec_neutral (int day, int month, int year)
|
||||
{
|
||||
struct tm date;
|
||||
Timespec ts = {0, 0};
|
||||
GTimeZone *zone = gnc_g_time_zone_new_local();
|
||||
GDateTime *gdt = gnc_g_date_time_new_local (year, month, day, 12, 0, 0.0);
|
||||
int interval = g_time_zone_find_interval (zone, G_TIME_TYPE_STANDARD,
|
||||
g_date_time_to_unix(gdt));
|
||||
int offset = g_time_zone_get_offset(gnc_g_time_zone_new_local(),
|
||||
interval) / 3600;
|
||||
g_date_time_unref (gdt);
|
||||
memset (&date, 0, sizeof(struct tm));
|
||||
date.tm_year = year - 1900;
|
||||
date.tm_mon = month - 1;
|
||||
date.tm_mday = day;
|
||||
date.tm_hour = offset < -11 ? -offset : offset > 13 ? 24 - offset : 11;
|
||||
date.tm_min = 0;
|
||||
date.tm_sec = 0;
|
||||
|
||||
ts.tv_sec = gnc_timegm(&date);
|
||||
return ts;
|
||||
}
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
|
||||
@ -1648,9 +1675,9 @@ GDate* gnc_g_date_new_today ()
|
||||
Timespec gdate_to_timespec (GDate d)
|
||||
{
|
||||
gnc_gdate_range_check (&d);
|
||||
return gnc_dmy2timespec(g_date_get_day(&d),
|
||||
g_date_get_month(&d),
|
||||
g_date_get_year(&d));
|
||||
return gnc_dmy2timespec_neutral (g_date_get_day(&d),
|
||||
g_date_get_month(&d),
|
||||
g_date_get_year(&d));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -351,6 +351,15 @@ Timespec gnc_dmy2timespec (gint day, gint month, gint year);
|
||||
/** Same as gnc_dmy2timespec, but last second of the day */
|
||||
Timespec gnc_dmy2timespec_end (gint day, gint month, gint year);
|
||||
|
||||
/** Converts a day, month, and year to a Timespec representing 11:00:00 UTC
|
||||
* 11:00:00 UTC falls on the same time in almost all timezones, the exceptions
|
||||
* being the +13, +14, and -12 timezones used by countries along the
|
||||
* International Date Line. Since users in those timezones would see dates
|
||||
* immediately change by one day, the function checks the current timezone for
|
||||
* those changes and adjusts the UTC time so that the date will be consistent.
|
||||
*/
|
||||
Timespec gnc_dmy2timespec_neutral (gint day, gint month, gint year);
|
||||
|
||||
/** The gnc_iso8601_to_timespec_gmt() routine converts an ISO-8601 style
|
||||
* date/time string to Timespec. Please note that ISO-8601 strings
|
||||
* are a representation of Universal Time (UTC), and as such, they
|
||||
|
@ -47,6 +47,7 @@ void test_suite_gnc_date ( void );
|
||||
typedef struct
|
||||
{
|
||||
GDateTime *(*new_local)(gint, gint, gint, gint, gint, gdouble);
|
||||
GDateTime *(*new_utc)(gint, gint, gint, gint, gint, gdouble);
|
||||
GDateTime *(*adjust_for_dst)(GDateTime *, GTimeZone *);
|
||||
GDateTime *(*new_from_unix_local)(time64);
|
||||
GDateTime *(*new_from_timeval_local)(GTimeVal *);
|
||||
@ -1967,6 +1968,48 @@ test_gnc_dmy2timespec_end (void)
|
||||
g_date_time_unref (gdt3);
|
||||
g_date_time_unref (gdt4);
|
||||
}
|
||||
|
||||
/*gnc_dmy2timespec_neutral*/
|
||||
static void
|
||||
test_gnc_dmy2timespec_neutral (void)
|
||||
{
|
||||
GDateTime *gdt1 = gncdt.new_utc (1999, 7, 21, 11, 0, 0);
|
||||
GDateTime *gdt2 = gncdt.new_utc (1918, 3, 31, 11, 0, 0);
|
||||
GDateTime *gdt3 = gncdt.new_utc (1918, 4, 1, 11, 0, 0);
|
||||
GDateTime *gdt4 = gncdt.new_utc (2057, 11, 20, 11, 0, 0);
|
||||
|
||||
gint day, mon, yr;
|
||||
Timespec t, r_t;
|
||||
|
||||
t = g_date_time_to_timespec (gdt1);
|
||||
g_date_time_get_ymd (gdt1, &yr, &mon, &day);
|
||||
r_t = gnc_dmy2timespec_neutral (day, mon, yr);
|
||||
g_assert_cmpint (r_t.tv_sec, ==, t.tv_sec);
|
||||
g_assert_cmpint (r_t.tv_nsec, ==, t.tv_nsec);
|
||||
|
||||
t = g_date_time_to_timespec (gdt2);
|
||||
g_date_time_get_ymd (gdt2, &yr, &mon, &day);
|
||||
r_t = gnc_dmy2timespec_neutral (day, mon, yr);
|
||||
g_assert_cmpint (r_t.tv_sec, ==, t.tv_sec);
|
||||
g_assert_cmpint (r_t.tv_nsec, ==, t.tv_nsec);
|
||||
|
||||
t = g_date_time_to_timespec (gdt3);
|
||||
g_date_time_get_ymd (gdt3, &yr, &mon, &day);
|
||||
r_t = gnc_dmy2timespec_neutral (day, mon, yr);
|
||||
g_assert_cmpint (r_t.tv_sec, ==, t.tv_sec);
|
||||
g_assert_cmpint (r_t.tv_nsec, ==, t.tv_nsec);
|
||||
|
||||
t = g_date_time_to_timespec (gdt4);
|
||||
g_date_time_get_ymd (gdt4, &yr, &mon, &day);
|
||||
r_t = gnc_dmy2timespec_neutral (day, mon, yr);
|
||||
g_assert_cmpint (r_t.tv_sec, ==, t.tv_sec);
|
||||
g_assert_cmpint (r_t.tv_nsec, ==, t.tv_nsec);
|
||||
|
||||
g_date_time_unref (gdt1);
|
||||
g_date_time_unref (gdt2);
|
||||
g_date_time_unref (gdt3);
|
||||
g_date_time_unref (gdt4);
|
||||
}
|
||||
/* gnc_timezone
|
||||
long int
|
||||
gnc_timezone (const struct tm *tm)// C: 5 in 2 Local: 2:0:0
|
||||
@ -2086,10 +2129,10 @@ Timespec gdate_to_timespec (GDate d)// C: 7 in 6 Local: 0:0:0
|
||||
static void
|
||||
test_gdate_to_timespec (void)
|
||||
{
|
||||
GDateTime *gdt1 = gncdt.new_local (1999, 7, 21, 0, 0, 0);
|
||||
GDateTime *gdt2 = gncdt.new_local (1918, 3, 31, 0, 0, 0);
|
||||
GDateTime *gdt3 = gncdt.new_local (1918, 4, 1, 0, 0, 0);
|
||||
GDateTime *gdt4 = gncdt.new_local (2057, 11, 20, 0, 0, 0);
|
||||
GDateTime *gdt1 = gncdt.new_utc (1999, 7, 21, 11, 0, 0);
|
||||
GDateTime *gdt2 = gncdt.new_utc (1918, 3, 31, 11, 0, 0);
|
||||
GDateTime *gdt3 = gncdt.new_utc (1918, 4, 1, 11, 0, 0);
|
||||
GDateTime *gdt4 = gncdt.new_utc (2057, 11, 20, 11, 0, 0);
|
||||
|
||||
gint day, mon, yr;
|
||||
Timespec t, r_t;
|
||||
@ -2411,6 +2454,7 @@ test_suite_gnc_date (void)
|
||||
// GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec internal", test_gnc_dmy2timespec_internal);
|
||||
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec", test_gnc_dmy2timespec);
|
||||
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec end", test_gnc_dmy2timespec_end);
|
||||
GNC_TEST_ADD_FUNC (suitename, "gnc dmy2timespec Neutral", test_gnc_dmy2timespec_neutral);
|
||||
// GNC_TEST_ADD_FUNC (suitename, "gnc timezone", test_gnc_timezone);
|
||||
// GNC_TEST_ADD_FUNC (suitename, "timespecFromTime t", test_timespecFromtime64);
|
||||
// GNC_TEST_ADD_FUNC (suitename, "timespec now", test_timespec_now);
|
||||
|
Loading…
Reference in New Issue
Block a user