@@ -427,6 +427,26 @@ days_in_month(int month, int year)
427
427
return gnc_date_get_last_mday (month, year + 1900 );
428
428
}
429
429
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
+
430
450
/* Normalize the modified struct tm computed in gnc_relative_date_to_time64
431
451
* before setting the time and perhaps beginning/end of the month. Using the
432
452
* gnc_date API would involve multiple conversions to and from struct tm.
@@ -464,27 +484,39 @@ normalize_reldate_tm(struct tm& now)
464
484
}
465
485
466
486
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)
468
492
{
469
493
if (type == RelativeDateType::START)
470
494
{
471
- gnc_tm_set_day_start (&now);
472
495
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);
473
503
}
474
504
else if (type == RelativeDateType::END)
475
505
{
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
+ }
481
513
gnc_tm_set_day_end (&now);
482
514
}
483
515
// Do nothing for LAST and NEXT.
484
516
};
485
517
486
518
time64
487
- gnc_relative_date_to_time64 (RelativeDatePeriod period)
519
+ gnc_relative_date_to_time64 (RelativeDatePeriod period, time64 now_t )
488
520
{
489
521
if (period == RelativeDatePeriod::TODAY)
490
522
return static_cast <time64>(GncDateTime ());
@@ -493,19 +525,19 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
493
525
if (period == RelativeDatePeriod::END_ACCOUNTING_PERIOD)
494
526
return gnc_accounting_period_fiscal_end ();
495
527
496
- GncDateTime now_t ;
497
528
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 ;
504
536
505
- switch (reldate_offset (period) )
537
+ switch (offset )
506
538
{
507
539
case RelativeDateOffset::NONE:
508
- // Report on today so nothing to do
540
+ // Report on today so nothing to do
509
541
break ;
510
542
case RelativeDateOffset::YEAR:
511
543
if (reldate_is_prev (period))
@@ -517,14 +549,26 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
517
549
else if (gnc_relative_date_is_ending (period))
518
550
now.tm_mon = 11 ;
519
551
break ;
520
- case RelativeDateOffset::SIX:
552
+ case RelativeDateOffset::SIX:
521
553
if (reldate_is_prev (period))
522
554
now.tm_mon -= 6 ;
523
555
else if (reldate_is_next (period))
524
556
now.tm_mon += 6 ;
525
557
break ;
526
558
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
+ }
528
572
[[fallthrough]];
529
573
case RelativeDateOffset::THREE:
530
574
if (reldate_is_prev (period))
@@ -534,7 +578,7 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
534
578
if (gnc_relative_date_is_ending (period))
535
579
now.tm_mon += 2 ;
536
580
break ;
537
- case RelativeDateOffset::MONTH:
581
+ case RelativeDateOffset::MONTH:
538
582
if (reldate_is_prev (period))
539
583
--now.tm_mon ;
540
584
else if (reldate_is_next (period))
@@ -546,7 +590,10 @@ gnc_relative_date_to_time64(RelativeDatePeriod period)
546
590
else if (reldate_is_next (period))
547
591
now.tm_mday += 7 ;
548
592
}
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);
550
597
normalize_reldate_tm (now);
551
598
return static_cast <time64>(GncDateTime (now));
552
599
}
0 commit comments