mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
_localOffsetForDate() was broken
It was including the elapsed time of day when calculating the offset, leading to incorrect results
This commit is contained in:
parent
fa56477205
commit
12c60f20fe
6 changed files with 43 additions and 27 deletions
|
@ -15,6 +15,7 @@ message BackendInput {
|
||||||
FindCardsIn find_cards = 19;
|
FindCardsIn find_cards = 19;
|
||||||
BrowserRowsIn browser_rows = 20;
|
BrowserRowsIn browser_rows = 20;
|
||||||
RenderCardIn render_card = 21;
|
RenderCardIn render_card = 21;
|
||||||
|
int64 local_minutes_west = 22;
|
||||||
|
|
||||||
PlusOneIn plus_one = 2046; // temporary, for testing
|
PlusOneIn plus_one = 2046; // temporary, for testing
|
||||||
}
|
}
|
||||||
|
@ -28,6 +29,7 @@ message BackendOutput {
|
||||||
FindCardsOut find_cards = 19;
|
FindCardsOut find_cards = 19;
|
||||||
BrowserRowsOut browser_rows = 20;
|
BrowserRowsOut browser_rows = 20;
|
||||||
RenderCardOut render_card = 21;
|
RenderCardOut render_card = 21;
|
||||||
|
sint32 local_minutes_west = 22;
|
||||||
|
|
||||||
PlusOneOut plus_one = 2046; // temporary, for testing
|
PlusOneOut plus_one = 2046; // temporary, for testing
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ class _Collection:
|
||||||
elif ver == 2:
|
elif ver == 2:
|
||||||
self.sched = V2Scheduler(self)
|
self.sched = V2Scheduler(self)
|
||||||
if not self.server:
|
if not self.server:
|
||||||
self.conf["localOffset"] = self.sched.currentTimezoneOffset()
|
self.conf["localOffset"] = self.sched._current_timezone_offset()
|
||||||
elif self.server.minutes_west is not None:
|
elif self.server.minutes_west is not None:
|
||||||
self.conf["localOffset"] = self.server.minutes_west
|
self.conf["localOffset"] = self.server.minutes_west
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ class _Collection:
|
||||||
if isinstance(self.sched, V1Scheduler):
|
if isinstance(self.sched, V1Scheduler):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.sched.currentTimezoneOffset()
|
return self.sched._current_timezone_offset()
|
||||||
|
|
||||||
# DB-related
|
# DB-related
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -143,3 +143,8 @@ class RustBackend:
|
||||||
anodes = proto_replacement_list_to_native(out.answer_nodes) # type: ignore
|
anodes = proto_replacement_list_to_native(out.answer_nodes) # type: ignore
|
||||||
|
|
||||||
return (qnodes, anodes)
|
return (qnodes, anodes)
|
||||||
|
|
||||||
|
def local_minutes_west(self, stamp: int) -> int:
|
||||||
|
return self._run_command(
|
||||||
|
pb.BackendInput(local_minutes_west=stamp)
|
||||||
|
).local_minutes_west
|
||||||
|
|
|
@ -1340,9 +1340,9 @@ where id = ?
|
||||||
|
|
||||||
def _updateCutoff(self) -> None:
|
def _updateCutoff(self) -> None:
|
||||||
oldToday = self.today
|
oldToday = self.today
|
||||||
timing = self._timingToday()
|
timing = self._timing_today()
|
||||||
|
|
||||||
if self._newTimezoneEnabled():
|
if self._new_timezone_enabled():
|
||||||
self.today = timing.days_elapsed
|
self.today = timing.days_elapsed
|
||||||
self.dayCutoff = timing.next_day_at
|
self.dayCutoff = timing.next_day_at
|
||||||
else:
|
else:
|
||||||
|
@ -1398,41 +1398,40 @@ where id = ?
|
||||||
# New timezone handling
|
# New timezone handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def _newTimezoneEnabled(self) -> bool:
|
def _new_timezone_enabled(self) -> bool:
|
||||||
return self.col.conf.get("creationOffset") is not None
|
return self.col.conf.get("creationOffset") is not None
|
||||||
|
|
||||||
def _timingToday(self) -> SchedTimingToday:
|
def _timing_today(self) -> SchedTimingToday:
|
||||||
return self.col.backend.sched_timing_today(
|
return self.col.backend.sched_timing_today(
|
||||||
self.col.crt,
|
self.col.crt,
|
||||||
self.creationTimezoneOffset(),
|
self._creation_timezone_offset(),
|
||||||
intTime(),
|
intTime(),
|
||||||
self.currentTimezoneOffset(),
|
self._current_timezone_offset(),
|
||||||
self._rolloverHour(),
|
self._rolloverHour(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def currentTimezoneOffset(self) -> int:
|
def _current_timezone_offset(self) -> int:
|
||||||
if self.col.server:
|
if self.col.server:
|
||||||
return self.col.conf.get("localOffset", 0)
|
return self.col.conf.get("localOffset", 0)
|
||||||
else:
|
else:
|
||||||
return self._localOffsetForDate(datetime.datetime.today())
|
return self.col.backend.local_minutes_west(intTime())
|
||||||
|
|
||||||
def creationTimezoneOffset(self) -> int:
|
def _creation_timezone_offset(self) -> int:
|
||||||
return self.col.conf.get("creationOffset", 0)
|
return self.col.conf.get("creationOffset", 0)
|
||||||
|
|
||||||
def setCreationOffset(self):
|
def set_creation_offset(self):
|
||||||
"""Save the UTC west offset at the time of creation into the DB.
|
"""Save the UTC west offset at the time of creation into the DB.
|
||||||
|
|
||||||
Once stored, this activates the new timezone handling code.
|
Once stored, this activates the new timezone handling code.
|
||||||
"""
|
"""
|
||||||
mins_west = self._localOffsetForDate(
|
mins_west = self.col.backend.local_minutes_west(self.col.crt)
|
||||||
datetime.datetime.fromtimestamp(self.col.crt)
|
|
||||||
)
|
|
||||||
self.col.conf["creationOffset"] = mins_west
|
self.col.conf["creationOffset"] = mins_west
|
||||||
self.col.setMod()
|
self.col.setMod()
|
||||||
|
|
||||||
def _localOffsetForDate(self, date: datetime.datetime) -> int:
|
def clear_creation_offset(self):
|
||||||
"Minutes west of UTC for a given datetime in the local timezone."
|
if "creationOffset" in self.col.conf:
|
||||||
return date.astimezone().utcoffset().seconds // -60
|
del self.col.conf["creationOffset"]
|
||||||
|
self.col.setMod()
|
||||||
|
|
||||||
# Deck finished state
|
# Deck finished state
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::backend_proto as pt;
|
||||||
use crate::backend_proto::backend_input::Value;
|
use crate::backend_proto::backend_input::Value;
|
||||||
use crate::backend_proto::RenderedTemplateReplacement;
|
use crate::backend_proto::RenderedTemplateReplacement;
|
||||||
use crate::err::{AnkiError, Result};
|
use crate::err::{AnkiError, Result};
|
||||||
use crate::sched::sched_timing_today;
|
use crate::sched::{local_minutes_west_for_stamp, sched_timing_today};
|
||||||
use crate::template::{
|
use crate::template::{
|
||||||
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
|
render_card, without_legacy_template_directives, FieldMap, FieldRequirements, ParsedTemplate,
|
||||||
RenderedNode,
|
RenderedNode,
|
||||||
|
@ -96,6 +96,9 @@ impl Backend {
|
||||||
Value::FindCards(_) => todo!(),
|
Value::FindCards(_) => todo!(),
|
||||||
Value::BrowserRows(_) => todo!(),
|
Value::BrowserRows(_) => todo!(),
|
||||||
Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?),
|
Value::RenderCard(input) => OValue::RenderCard(self.render_template(input)?),
|
||||||
|
Value::LocalMinutesWest(stamp) => {
|
||||||
|
OValue::LocalMinutesWest(local_minutes_west_for_stamp(stamp))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,21 +82,21 @@ fn fixed_offset_from_minutes(minutes_west: i32) -> FixedOffset {
|
||||||
FixedOffset::west(bounded_minutes * 60)
|
FixedOffset::west(bounded_minutes * 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relative to the local timezone, the number of minutes UTC differs by.
|
/// For the given timestamp, return minutes west of UTC in the
|
||||||
|
/// local timezone.
|
||||||
/// eg, Australia at +10 hours is -600.
|
/// eg, Australia at +10 hours is -600.
|
||||||
/// Includes the daylight savings offset if applicable.
|
/// Includes the daylight savings offset if applicable.
|
||||||
#[allow(dead_code)]
|
pub fn local_minutes_west_for_stamp(stamp: i64) -> i32 {
|
||||||
fn utc_minus_local_mins() -> i32 {
|
Local.timestamp(stamp, 0).offset().utc_minus_local() / 60
|
||||||
Local::now().offset().utc_minus_local() / 60
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::sched::{
|
use crate::sched::{
|
||||||
fixed_offset_from_minutes, normalized_rollover_hour, sched_timing_today,
|
fixed_offset_from_minutes, local_minutes_west_for_stamp, normalized_rollover_hour,
|
||||||
utc_minus_local_mins,
|
sched_timing_today,
|
||||||
};
|
};
|
||||||
use chrono::{FixedOffset, Local, TimeZone};
|
use chrono::{FixedOffset, Local, TimeZone, Utc};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rollover() {
|
fn test_rollover() {
|
||||||
|
@ -121,9 +121,16 @@ mod test {
|
||||||
today.days_elapsed
|
today.days_elapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_local_minutes_west() {
|
||||||
|
// -480 throughout the year
|
||||||
|
std::env::set_var("TZ", "Australia/Perth");
|
||||||
|
assert_eq!(local_minutes_west_for_stamp(Utc::now().timestamp()), -480);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_days_elapsed() {
|
fn test_days_elapsed() {
|
||||||
let local_offset = utc_minus_local_mins();
|
let local_offset = local_minutes_west_for_stamp(Utc::now().timestamp());
|
||||||
|
|
||||||
let created_dt = FixedOffset::west(local_offset * 60)
|
let created_dt = FixedOffset::west(local_offset * 60)
|
||||||
.ymd(2019, 12, 1)
|
.ymd(2019, 12, 1)
|
||||||
|
|
Loading…
Reference in a new issue