Fix/set due date on intraday learning card (#4101)

- Introduced `next_day_start` parameter to `set_due_date` for improved due date handling.
- Updated logic to account for Unix epoch timestamps when calculating due dates.
This commit is contained in:
Jarrett Ye 2025-06-21 20:15:30 +08:00 committed by GitHub
parent cc395f7c44
commit 88538d8bad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,6 +17,7 @@ use crate::collection::Collection;
use crate::config::StringKey;
use crate::error::Result;
use crate::prelude::*;
use crate::scheduler::timing::is_unix_epoch_timestamp;
impl Card {
/// Make card due in `days_from_today`.
@ -27,6 +28,7 @@ impl Card {
fn set_due_date(
&mut self,
today: u32,
next_day_start: i64,
days_from_today: u32,
ease_factor: f32,
force_reset: bool,
@ -34,8 +36,15 @@ impl Card {
let new_due = (today + days_from_today) as i32;
let fsrs_enabled = self.memory_state.is_some();
let new_interval = if fsrs_enabled {
self.interval
.saturating_add_signed(new_due - self.original_or_current_due())
let due = self.original_or_current_due();
let due_diff = if is_unix_epoch_timestamp(due) {
let offset = (due as i64 - next_day_start) / 86_400;
let due = (today as i64 + offset) as i32;
new_due - due
} else {
new_due - due
};
self.interval.saturating_add_signed(due_diff)
} else if force_reset || !matches!(self.ctype, CardType::Review | CardType::Relearn) {
days_from_today.max(1)
} else {
@ -114,6 +123,7 @@ impl Collection {
let spec = parse_due_date_str(days)?;
let usn = self.usn()?;
let today = self.timing_today()?.days_elapsed;
let next_day_start = self.timing_today()?.next_day_at.0;
let mut rng = rand::rng();
let distribution = Uniform::new_inclusive(spec.min, spec.max).unwrap();
let mut decks_initial_ease: HashMap<DeckId, f32> = HashMap::new();
@ -137,7 +147,13 @@ impl Collection {
};
let original = card.clone();
let days_from_today = distribution.sample(&mut rng);
card.set_due_date(today, days_from_today, ease_factor, spec.force_reset);
card.set_due_date(
today,
next_day_start,
days_from_today,
ease_factor,
spec.force_reset,
);
col.log_manually_scheduled_review(&card, original.interval, usn)?;
col.update_card_inner(&mut card, original, usn)?;
}
@ -228,26 +244,26 @@ mod test {
let mut c = Card::new(NoteId(0), 0, DeckId(0), 0);
// setting the due date of a new card will convert it
c.set_due_date(5, 2, 1.8, false);
c.set_due_date(5, 0, 2, 1.8, false);
assert_eq!(c.ctype, CardType::Review);
assert_eq!(c.due, 7);
assert_eq!(c.interval, 2);
assert_eq!(c.ease_factor, 1800);
// reschedule it again the next day, shifting it from day 7 to day 9
c.set_due_date(6, 3, 2.5, false);
c.set_due_date(6, 0, 3, 2.5, false);
assert_eq!(c.due, 9);
assert_eq!(c.interval, 2);
assert_eq!(c.ease_factor, 1800); // interval doesn't change
// we can bring cards forward too - return it to its original due date
c.set_due_date(6, 1, 2.4, false);
c.set_due_date(6, 0, 1, 2.4, false);
assert_eq!(c.due, 7);
assert_eq!(c.interval, 2);
assert_eq!(c.ease_factor, 1800); // interval doesn't change
// we can force the interval to be reset instead of shifted
c.set_due_date(6, 3, 2.3, true);
c.set_due_date(6, 0, 3, 2.3, true);
assert_eq!(c.due, 9);
assert_eq!(c.interval, 3);
assert_eq!(c.ease_factor, 1800); // interval doesn't change
@ -259,7 +275,7 @@ mod test {
c.original_deck_id = DeckId(1);
c.due = -10000;
c.queue = CardQueue::New;
c.set_due_date(6, 1, 2.2, false);
c.set_due_date(6, 0, 1, 2.2, false);
assert_eq!(c.due, 7);
assert_eq!(c.interval, 2);
assert_eq!(c.ease_factor, 2200);
@ -271,7 +287,7 @@ mod test {
c.ctype = CardType::Relearn;
c.original_due = c.due;
c.due = 12345678;
c.set_due_date(6, 10, 2.1, false);
c.set_due_date(6, 0, 10, 2.1, false);
assert_eq!(c.due, 16);
assert_eq!(c.interval, 2);
assert_eq!(c.ease_factor, 2200); // interval doesn't change