Skip to content

Commit ad92ae4

Browse files
committed
Merge Sherlock's 'non-aligned-periods' into stable
2 parents 2f3aa62 + b5b2221 commit ad92ae4

File tree

4 files changed

+308
-25
lines changed

4 files changed

+308
-25
lines changed

bindings/guile/gnc-optiondb.i

+4
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,10 @@ gnc_option_test_book_destroy(QofBook* book)
591591
$1 = rdp;
592592
}
593593

594+
%typecheck(SWIG_TYPECHECK_INTEGER) RelativeDatePeriod {
595+
$1 = scm_is_integer($input) || scm_is_symbol($input) ? 1 : 0;
596+
}
597+
594598
%typemap(in) RelativeDatePeriodVec& (RelativeDatePeriodVec period_set)
595599
{
596600
for (SCM node = $input; !scm_is_null (node); node = scm_cdr (node))

libgnucash/engine/gnc-option-date.cpp

+68-21
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,26 @@ days_in_month(int month, int year)
427427
return gnc_date_get_last_mday(month, year + 1900);
428428
}
429429

430+
static int
431+
get_last_day_of_month(struct tm& now)
432+
{
433+
/* Ensure that the month is between 0 and 11*/
434+
auto year_delta = (now.tm_mon / 12) + (now.tm_mon < 0 ? -1 : 0);
435+
return days_in_month(now.tm_mon - (12 * year_delta), now.tm_year + year_delta);
436+
}
437+
438+
static void
439+
set_last_day_in_month(struct tm& now)
440+
{
441+
now.tm_mday = get_last_day_of_month(now);
442+
}
443+
444+
static bool
445+
is_last_day_in_month(struct tm& now)
446+
{
447+
return now.tm_mday == get_last_day_of_month(now);
448+
}
449+
430450
/* Normalize the modified struct tm computed in gnc_relative_date_to_time64
431451
* before setting the time and perhaps beginning/end of the month. Using the
432452
* gnc_date API would involve multiple conversions to and from struct tm.
@@ -464,27 +484,39 @@ normalize_reldate_tm(struct tm& now)
464484
}
465485

466486
static void
467-
reldate_set_day_and_time(struct tm& now, RelativeDateType type)
487+
reldate_set_day_and_time(
488+
struct tm& now,
489+
RelativeDateType type,
490+
bool is_offset_quarter,
491+
struct tm& acct_per)
468492
{
469493
if (type == RelativeDateType::START)
470494
{
471-
gnc_tm_set_day_start(&now);
472495
now.tm_mday = 1;
496+
if (is_offset_quarter)
497+
{
498+
set_last_day_in_month(now);
499+
if (!is_last_day_in_month(acct_per) && now.tm_mday > acct_per.tm_mday)
500+
now.tm_mday = acct_per.tm_mday;
501+
}
502+
gnc_tm_set_day_start(&now);
473503
}
474504
else if (type == RelativeDateType::END)
475505
{
476-
/* Ensure that the month is between 0 and 11*/
477-
auto year_delta = (now.tm_mon / 12) + (now.tm_mon < 0 ? -1 : 0);
478-
auto month = now.tm_mon - (12 * year_delta);
479-
auto year = now.tm_year + year_delta;
480-
now.tm_mday = days_in_month(month, year);
506+
set_last_day_in_month(now);
507+
if (is_offset_quarter)
508+
{
509+
if (!is_last_day_in_month(acct_per) && now.tm_mday > acct_per.tm_mday)
510+
now.tm_mday = acct_per.tm_mday;
511+
--now.tm_mday;
512+
}
481513
gnc_tm_set_day_end(&now);
482514
}
483515
// Do nothing for LAST and NEXT.
484516
};
485517

486518
time64
487-
gnc_relative_date_to_time64(RelativeDatePeriod period)
519+
gnc_relative_date_to_time64(RelativeDatePeriod period, time64 now_t)
488520
{
489521
if (period == RelativeDatePeriod::TODAY)
490522
return static_cast<time64>(GncDateTime());
@@ -493,19 +525,19 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
493525
if (period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
494526
return gnc_accounting_period_fiscal_end();
495527

496-
GncDateTime now_t;
497528
if (period == RelativeDatePeriod::TODAY)
498-
return static_cast<time64>(now_t);
499-
auto now{static_cast<tm>(now_t)};
500-
struct tm acct_per{};
501-
if (gnc_prefs_get_bool (GNC_PREFS_GROUP_ACCT_SUMMARY,
502-
GNC_PREF_START_CHOICE_ABS))
503-
acct_per = static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()));
529+
return now_t;
530+
auto now{static_cast<tm>(GncDateTime(now_t))};
531+
struct tm acct_per =
532+
static_cast<tm>(GncDateTime(gnc_accounting_period_fiscal_start()));
533+
auto offset = reldate_offset(period);
534+
bool is_offset_quarter =
535+
offset == RelativeDateOffset::QUARTER && acct_per.tm_mday > 1;
504536

505-
switch(reldate_offset(period))
537+
switch(offset)
506538
{
507539
case RelativeDateOffset::NONE:
508-
// Report on today so nothing to do
540+
// Report on today so nothing to do
509541
break;
510542
case RelativeDateOffset::YEAR:
511543
if (reldate_is_prev(period))
@@ -517,14 +549,26 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
517549
else if (gnc_relative_date_is_ending(period))
518550
now.tm_mon = 11;
519551
break;
520-
case RelativeDateOffset::SIX:
552+
case RelativeDateOffset::SIX:
521553
if (reldate_is_prev(period))
522554
now.tm_mon -= 6;
523555
else if (reldate_is_next(period))
524556
now.tm_mon += 6;
525557
break;
526558
case RelativeDateOffset::QUARTER:
527-
now.tm_mon -= (12 + now.tm_mon - acct_per.tm_mon) % 3;
559+
{
560+
auto delta = (12 + now.tm_mon - acct_per.tm_mon) % 3;
561+
if (is_offset_quarter)
562+
{
563+
if (delta == 0 && !is_last_day_in_month(now) &&
564+
(is_last_day_in_month(acct_per) ||
565+
now.tm_mday < acct_per.tm_mday))
566+
delta = 3;
567+
if (gnc_relative_date_is_ending(period))
568+
--delta;
569+
}
570+
now.tm_mon -= delta;
571+
}
528572
[[fallthrough]];
529573
case RelativeDateOffset::THREE:
530574
if (reldate_is_prev(period))
@@ -534,7 +578,7 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
534578
if (gnc_relative_date_is_ending(period))
535579
now.tm_mon += 2;
536580
break;
537-
case RelativeDateOffset::MONTH:
581+
case RelativeDateOffset::MONTH:
538582
if (reldate_is_prev(period))
539583
--now.tm_mon;
540584
else if (reldate_is_next(period))
@@ -546,7 +590,10 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
546590
else if (reldate_is_next(period))
547591
now.tm_mday += 7;
548592
}
549-
reldate_set_day_and_time(now, checked_reldate(period).m_type);
593+
reldate_set_day_and_time( now,
594+
checked_reldate(period).m_type,
595+
is_offset_quarter,
596+
acct_per);
550597
normalize_reldate_tm(now);
551598
return static_cast<time64>(GncDateTime(now));
552599
}

libgnucash/engine/gnc-option-date.hpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
#define GNC_OPTION_DATE_HPP_
3333

3434
#include "gnc-date.h"
35-
35+
#include "gnc-datetime.hpp"
3636
#include <vector>
3737
#include <iostream>
3838
/**
@@ -167,9 +167,11 @@ RelativeDatePeriod gnc_relative_date_from_storage_string(const char*);
167167
* both in the current time zone.
168168
*
169169
* @param period The relative date period to use to calculate the concrete date.
170+
* @param now_t Optional "now" date. Primarily for testing.
170171
* @return a time64.
171172
*/
172-
time64 gnc_relative_date_to_time64(RelativeDatePeriod);
173+
time64 gnc_relative_date_to_time64(RelativeDatePeriod,
174+
time64 now_t = static_cast<time64>(GncDateTime()));
173175

174176
/**
175177
* Add the display string to the provided std::ostream.

0 commit comments

Comments
 (0)