From d997b1378d6874c59bd2bf6d73db89957edfa213 Mon Sep 17 00:00:00 2001 From: Kristian Tashkov Date: Sun, 20 Sep 2020 03:23:00 +0300 Subject: [PATCH] Setting custom start of the day (#621) --- .../org/isoron/uhabits/BaseAndroidTest.java | 1 + .../org/isoron/uhabits/HabitsApplication.kt | 2 + .../activities/common/views/BarChart.java | 4 +- .../common/views/FrequencyChart.java | 4 +- .../activities/common/views/HistoryChart.java | 10 ++-- .../activities/common/views/ScoreChart.java | 4 +- .../habits/list/views/CheckmarkPanelView.kt | 2 +- .../habits/list/views/HabitCardView.kt | 2 +- .../habits/list/views/HeaderView.kt | 2 +- .../habits/list/views/NumberPanelView.kt | 2 +- .../habits/show/views/OverviewCard.java | 2 +- .../habits/show/views/TargetCard.java | 2 +- .../uhabits/automation/FireSettingReceiver.kt | 2 +- .../isoron/uhabits/intents/IntentParser.kt | 4 +- .../uhabits/receivers/ReminderReceiver.java | 2 +- .../isoron/uhabits/widgets/WidgetUpdater.kt | 2 +- .../isoron/uhabits/BaseAndroidJVMTest.java | 3 +- .../uhabits/core/models/CheckmarkList.java | 14 ++--- .../uhabits/core/models/RepetitionList.java | 2 +- .../isoron/uhabits/core/models/ScoreList.java | 4 +- .../uhabits/core/models/StreakList.java | 2 +- .../core/reminders/ReminderScheduler.java | 2 +- .../habits/list/HabitCardListCache.java | 2 +- .../isoron/uhabits/core/utils/DateUtils.java | 53 +++++++++++++++++-- .../uhabits/core/utils/MidnightTimer.java | 2 +- .../org/isoron/uhabits/core/BaseUnitTest.java | 2 + .../uhabits/core/utils/DateUtilsTest.java | 37 +++++++++++-- 27 files changed, 125 insertions(+), 45 deletions(-) diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java index d5a37dbaa..c778789b6 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java @@ -92,6 +92,7 @@ public class BaseAndroidTest extends TestCase testContext = InstrumentationRegistry.getInstrumentation().getContext(); DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME); + DateUtils.setStartDayOffset(0, 0); setResolution(2.0f); setTheme(R.style.AppBaseTheme); setLocale("en", "US"); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt index a2c2b0e99..a33a22e3b 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt @@ -25,6 +25,7 @@ import org.isoron.androidbase.* import org.isoron.uhabits.core.database.* import org.isoron.uhabits.core.reminders.* import org.isoron.uhabits.core.ui.* +import org.isoron.uhabits.core.utils.* import org.isoron.uhabits.utils.* import org.isoron.uhabits.widgets.* import java.io.* @@ -59,6 +60,7 @@ class HabitsApplication : Application() { db.renameTo(File(db.absolutePath + ".invalid")) DatabaseUtils.initializeDatabase(context) } + DateUtils.setStartDayOffset(3, 0) widgetUpdater = component.widgetUpdater widgetUpdater.startListening() diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java index 3a420c811..8999d0a93 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java @@ -396,7 +396,7 @@ public class BarChart extends ScrollableChart private float getMaxDayWidth() { float maxDayWidth = 0; - GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar day = DateUtils.getStartOfTodayCalendarWithOffset(); for (int i = 0; i < 28; i++) { @@ -411,7 +411,7 @@ public class BarChart extends ScrollableChart private float getMaxMonthWidth() { float maxMonthWidth = 0; - GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar day = DateUtils.getStartOfTodayCalendarWithOffset(); for (int i = 0; i < 12; i++) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java index 4d365a663..1ca67df4c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java @@ -153,7 +153,7 @@ public class FrequencyChart extends ScrollableChart pGraph.setColor(primaryColor); prevRect.setEmpty(); - GregorianCalendar currentDate = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar currentDate = DateUtils.getStartOfTodayCalendarWithOffset(); currentDate.set(Calendar.DAY_OF_MONTH, 1); currentDate.add(Calendar.MONTH, -nColumns + 2 - getDataOffset()); @@ -274,7 +274,7 @@ public class FrequencyChart extends ScrollableChart private float getMaxMonthWidth() { float maxMonthWidth = 0; - GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar day = DateUtils.getStartOfTodayCalendarWithOffset(); for (int i = 0; i < 12; i++) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java index 611271cbd..da488d048 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java @@ -148,7 +148,7 @@ public class HistoryChart extends ScrollableChart final Timestamp timestamp = positionToTimestamp(x, y); if (timestamp == null) return false; - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); int newValue = YES_MANUAL; int offset = timestamp.daysUntil(today); if (offset < checkmarks.length) @@ -522,20 +522,20 @@ public class HistoryChart extends ScrollableChart Calendar date = (Calendar) baseDate.clone(); date.add(Calendar.DAY_OF_YEAR, offset); - if (DateUtils.getStartOfDay(date.getTimeInMillis()) > - DateUtils.getStartOfToday()) return null; + if (DateUtils.getStartOfDayWithOffset(date.getTimeInMillis()) > + DateUtils.getStartOfTodayWithOffset()) return null; return new Timestamp(date.getTimeInMillis()); } private void updateDate() { - baseDate = DateUtils.getStartOfTodayCalendar(); + baseDate = DateUtils.getStartOfTodayCalendarWithOffset(); baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7); nDays = (nColumns - 1) * 7; int realWeekday = - DateUtils.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK); + DateUtils.getStartOfTodayCalendarWithOffset().get(Calendar.DAY_OF_WEEK); todayPositionInColumn = (7 + realWeekday - firstWeekday) % 7; diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java index 74b8ad4b3..3eb1a641b 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java @@ -357,7 +357,7 @@ public class ScoreChart extends ScrollableChart private float getMaxDayWidth() { float maxDayWidth = 0; - GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar day = DateUtils.getStartOfTodayCalendarWithOffset(); for (int i = 0; i < 28; i++) { @@ -372,7 +372,7 @@ public class ScoreChart extends ScrollableChart private float getMaxMonthWidth() { float maxMonthWidth = 0; - GregorianCalendar day = DateUtils.getStartOfTodayCalendar(); + GregorianCalendar day = DateUtils.getStartOfTodayCalendarWithOffset(); for (int i = 0; i < 12; i++) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt index 53194abae..4696f0bb5 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt @@ -56,7 +56,7 @@ class CheckmarkPanelView( @Synchronized override fun setupButtons() { - val today = DateUtils.getToday() + val today = DateUtils.getTodayWithOffset() buttons.forEachIndexed { index, button -> val timestamp = today.minus(index + dataOffset) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt index e7891253d..3b92e2092 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt @@ -172,7 +172,7 @@ class HabitCardView( } fun triggerRipple(timestamp: Timestamp) { - val today = DateUtils.getToday() + val today = DateUtils.getTodayWithOffset() val offset = timestamp.daysUntil(today) - dataOffset val button = checkmarkPanel.buttons[offset] val y = button.height / 2.0f diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt index e5c057f55..23620da03 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt @@ -106,7 +106,7 @@ class HeaderView( } fun draw(canvas: Canvas) { - val day = getStartOfTodayCalendar() + val day = getStartOfTodayCalendarWithOffset() val width = dim(R.dimen.checkmarkWidth) val height = dim(R.dimen.checkmarkHeight) val isReversed = prefs.isCheckmarkSequenceReversed diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt index 81b082209..9597736c5 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelView.kt @@ -67,7 +67,7 @@ class NumberPanelView( @Synchronized override fun setupButtons() { - val today = DateUtils.getToday() + val today = DateUtils.getTodayWithOffset() buttons.forEachIndexed { index, button -> val timestamp = today.minus(index + dataOffset) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java index 14327643c..6c5334a3a 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java @@ -153,7 +153,7 @@ public class OverviewCard extends HabitCard ScoreList scores = habit.getScores(); - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); Timestamp lastMonth = today.minus(30); Timestamp lastYear = today.minus(365); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java index 464ca1955..aef2570c2 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java @@ -134,7 +134,7 @@ public class TargetCard extends HabitCard public void onPostExecute() { if (isCanceled()) return; - Calendar cal = DateUtils.getStartOfTodayCalendar(); + Calendar cal = DateUtils.getStartOfTodayCalendarWithOffset(); int daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH); int daysInQuarter = 91; int daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt index eb33a4515..6aff0ead6 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt @@ -48,7 +48,7 @@ class FireSettingReceiver : BroadcastReceiver() { .build() allHabits = app.component.habitList val args = SettingUtils.parseIntent(intent, allHabits) ?: return - val timestamp = DateUtils.getToday() + val timestamp = DateUtils.getTodayWithOffset() val controller = component.widgetController when (args.action) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentParser.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentParser.kt index ca2204c06..792f43c08 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentParser.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentParser.kt @@ -38,7 +38,7 @@ class IntentParser fun copyIntentData(source: Intent, destination: Intent) { destination.data = source.data; - destination.putExtra("timestamp", source.getLongExtra("timestamp", DateUtils.getToday().unixTime)) + destination.putExtra("timestamp", source.getLongExtra("timestamp", DateUtils.getTodayWithOffset().unixTime)) } private fun parseHabit(uri: Uri): Habit { @@ -48,7 +48,7 @@ class IntentParser } private fun parseTimestamp(intent: Intent): Timestamp { - val today = DateUtils.getToday().unixTime + val today = DateUtils.getTodayWithOffset().unixTime var timestamp = intent.getLongExtra("timestamp", today) timestamp = DateUtils.getStartOfDay(timestamp) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index 9d39ce433..2d387c74f 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -65,7 +65,7 @@ public class ReminderReceiver extends BroadcastReceiver Log.i(TAG, String.format("Received intent: %s", intent.toString())); Habit habit = null; - long today = DateUtils.getStartOfToday(); + long today = DateUtils.getStartOfTodayWithOffset(); if (intent.getData() != null) habit = habits.getById(parseId(intent.getData())); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt index e05bb48db..3427bce26 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt @@ -64,7 +64,7 @@ class WidgetUpdater } fun scheduleStartDayWidgetUpdate() { - val timestamp = DateUtils.getStartOfTomorrow() + val timestamp = DateUtils.getStartOfTomorrowWithOffset() intentScheduler.scheduleWidgetUpdate(timestamp); } diff --git a/android/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.java b/android/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.java index a2f2c780d..440d422ea 100644 --- a/android/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.java +++ b/android/uhabits-android/src/test/java/org/isoron/uhabits/BaseAndroidJVMTest.java @@ -47,7 +47,7 @@ public class BaseAndroidJVMTest { long fixed_local_time = 1422172800000L; DateUtils.setFixedLocalTime(fixed_local_time); - + DateUtils.setStartDayOffset(0, 0); modelFactory = new MemoryModelFactory(); habitList = spy(modelFactory.buildHabitList()); fixtures = new HabitFixtures(modelFactory, habitList); @@ -59,6 +59,7 @@ public class BaseAndroidJVMTest public void tearDown() { DateUtils.setFixedLocalTime(null); + DateUtils.setStartDayOffset(0, 0); } public long timestamp(int year, int month, int day) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java index 5b3f3aed7..9e838d6c3 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/CheckmarkList.java @@ -55,7 +55,7 @@ public abstract class CheckmarkList { if (reps.length == 0) throw new IllegalArgumentException(); - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); Timestamp begin = reps[0].getTimestamp(); if (intervals.size() > 0) begin = Timestamp.oldest(begin, intervals.get(0).begin); @@ -181,7 +181,7 @@ public abstract class CheckmarkList if (oldestRep == null) return new int[0]; Timestamp fromTimestamp = oldestRep.getTimestamp(); - Timestamp toTimestamp = DateUtils.getToday(); + Timestamp toTimestamp = DateUtils.getTodayWithOffset(); return getValues(fromTimestamp, toTimestamp); } @@ -211,7 +211,7 @@ public abstract class CheckmarkList public synchronized final Checkmark getToday() { compute(); - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); return getByInterval(today, today).get(0); } @@ -325,7 +325,7 @@ public abstract class CheckmarkList */ protected final synchronized void compute() { - final Timestamp today = DateUtils.getToday(); + final Timestamp today = DateUtils.getTodayWithOffset(); Checkmark newest = getNewestComputed(); if (newest != null && newest.getTimestamp().equals(today)) return; @@ -335,6 +335,8 @@ public abstract class CheckmarkList if (oldestRep == null) return; final Timestamp from = oldestRep.getTimestamp(); + if (from.isNewerThan(today)) return; + Repetition reps[] = habit .getRepetitions() .getByInterval(from, today) @@ -364,7 +366,7 @@ public abstract class CheckmarkList { if (reps.length == 0) return; - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); Timestamp begin = reps[0].getTimestamp(); int nDays = begin.daysUntil(today) + 1; @@ -392,7 +394,7 @@ public abstract class CheckmarkList public List getAll() { Repetition oldest = habit.getRepetitions().getOldest(); if(oldest == null) return new ArrayList<>(); - return getByInterval(oldest.getTimestamp(), DateUtils.getToday()); + return getByInterval(oldest.getTimestamp(), DateUtils.getTodayWithOffset()); } static final class Interval diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java index 2196c7304..f5c9a268c 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/RepetitionList.java @@ -135,7 +135,7 @@ public abstract class RepetitionList public HashMap getWeekdayFrequency() { List reps = - getByInterval(Timestamp.ZERO, DateUtils.getToday()); + getByInterval(Timestamp.ZERO, DateUtils.getTodayWithOffset()); HashMap map = new HashMap<>(); for (Repetition r : reps) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java index edc945c5e..1d2c5794d 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/ScoreList.java @@ -71,7 +71,7 @@ public abstract class ScoreList implements Iterable */ public double getTodayValue() { - return getValue(DateUtils.getToday()); + return getValue(DateUtils.getTodayWithOffset()); } /** @@ -224,7 +224,7 @@ public abstract class ScoreList implements Iterable Repetition oldestRep = habit.getRepetitions().getOldest(); if (oldestRep == null) return; - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); compute(oldestRep.getTimestamp(), today); } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java index 542ed9962..036ca2119 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/StreakList.java @@ -67,7 +67,7 @@ public abstract class StreakList public synchronized void rebuild() { - Timestamp today = DateUtils.getToday(); + Timestamp today = DateUtils.getTodayWithOffset(); Timestamp beginning = findBeginning(); if (beginning == null || beginning.isNewerThan(today)) return; diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java index fcc562771..35fa3140b 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/reminders/ReminderScheduler.java @@ -121,7 +121,7 @@ public class ReminderScheduler implements CommandRunner.Listener return; } - long timestamp = getStartOfDay(removeTimezone(reminderTime)); + long timestamp = getStartOfDayWithOffset(removeTimezone(reminderTime)); sys.log("ReminderScheduler", String.format( Locale.US, diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java index 27c8ecdda..86f67c29d 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.java @@ -332,7 +332,7 @@ public class HabitCardListCache implements CommandRunner.Listener newData.copyScoresFrom(data); newData.copyCheckmarksFrom(data); - Timestamp dateTo = DateUtils.getToday(); + Timestamp dateTo = DateUtils.getTodayWithOffset(); Timestamp dateFrom = dateTo.minus(checkmarkCount - 1); if (runner != null) runner.publishProgress(this, -1); diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java index e10625d05..7d4abb242 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java @@ -37,10 +37,25 @@ public abstract class DateUtils private static Locale fixedLocale = null; + private static int startDayHourOffset = 0; + + private static int startDayMinuteOffset = 0; + + /** + * Number of milliseconds in one minute. + */ + + public static final long MINUTE_LENGTH = 60 * 1000; + + /** + * Number of milliseconds in one hour. + */ + public static final long HOUR_LENGTH = 60 * MINUTE_LENGTH; + /** * Number of milliseconds in one day. */ - public static final long DAY_LENGTH = 24 * 60 * 60 * 1000; + public static final long DAY_LENGTH = 24 * HOUR_LENGTH; public static long applyTimezone(long localTimestamp) { @@ -165,24 +180,41 @@ public abstract class DateUtils return new Timestamp(getStartOfToday()); } + @NonNull + public static Timestamp getTodayWithOffset() + { + return new Timestamp(getStartOfTodayWithOffset()); + } + public static long getStartOfDay(long timestamp) { return (timestamp / DAY_LENGTH) * DAY_LENGTH; } + public static long getStartOfDayWithOffset(long timestamp) + { + long offset = startDayHourOffset * HOUR_LENGTH + startDayMinuteOffset * MINUTE_LENGTH; + return getStartOfDay(timestamp - offset); + } + public static long getStartOfToday() { return getStartOfDay(getLocalTime()); } - public static long getStartOfTomorrow() + public static long getStartOfTomorrowWithOffset() + { + return getUpcomingTimeInMillis(startDayHourOffset, startDayMinuteOffset); + } + + public static long getStartOfTodayWithOffset() { - return getUpcomingTimeInMillis(0, 0); + return getStartOfDayWithOffset(getLocalTime()); } - public static long millisecondsUntilTomorrow() + public static long millisecondsUntilTomorrowWithOffset() { - return getStartOfTomorrow() - getLocalTime(); + return getStartOfTomorrowWithOffset() - getLocalTime(); } public static GregorianCalendar getStartOfTodayCalendar() @@ -190,6 +222,11 @@ public abstract class DateUtils return getCalendar(getStartOfToday()); } + public static GregorianCalendar getStartOfTodayCalendarWithOffset() + { + return getCalendar(getStartOfTodayWithOffset()); + } + private static TimeZone getTimezone() { if(fixedTimeZone != null) return fixedTimeZone; @@ -217,6 +254,12 @@ public abstract class DateUtils fixedLocale = locale; } + public static void setStartDayOffset(int hourOffset, int minuteOffset) + { + startDayHourOffset = hourOffset; + startDayMinuteOffset = minuteOffset; + } + private static Locale getLocale() { if(fixedLocale != null) return fixedLocale; diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java index 8d3a89480..0beee5cd8 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java @@ -56,7 +56,7 @@ public class MidnightTimer { executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(() -> notifyListeners(), - DateUtils.millisecondsUntilTomorrow() + 1000, + DateUtils.millisecondsUntilTomorrowWithOffset() + 1000, DateUtils.DAY_LENGTH, TimeUnit.MILLISECONDS); } diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java index d16902678..c377d3187 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/BaseUnitTest.java @@ -78,6 +78,7 @@ public class BaseUnitTest public void setUp() throws Exception { DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME); + DateUtils.setStartDayOffset(0, 0); modelFactory = new MemoryModelFactory(); habitList = spy(modelFactory.buildHabitList()); @@ -91,6 +92,7 @@ public class BaseUnitTest { validateMockitoUsage(); DateUtils.setFixedLocalTime(null); + DateUtils.setStartDayOffset(0, 0); } public long unixTime(int year, int month, int day) diff --git a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java index 84c942cb4..ad7898ffb 100644 --- a/android/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java +++ b/android/uhabits-core/src/test/java/org/isoron/uhabits/core/utils/DateUtilsTest.java @@ -29,8 +29,7 @@ import static java.util.Calendar.*; import static junit.framework.Assert.assertEquals; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; -import static org.isoron.uhabits.core.utils.DateUtils.applyTimezone; -import static org.isoron.uhabits.core.utils.DateUtils.removeTimezone; +import static org.isoron.uhabits.core.utils.DateUtils.*; public class DateUtilsTest extends BaseUnitTest { @@ -165,12 +164,42 @@ public class DateUtilsTest extends BaseUnitTest public void testMillisecondsUntilTomorrow() throws Exception { DateUtils.setFixedTimeZone(TimeZone.getTimeZone("GMT")); + DateUtils.setFixedLocalTime(unixTime(2017, JANUARY, 1, 23, 59)); - assertThat(DateUtils.millisecondsUntilTomorrow(), equalTo(60000L)); + assertThat(DateUtils.millisecondsUntilTomorrowWithOffset(), equalTo(MINUTE_LENGTH)); DateUtils.setFixedLocalTime(unixTime(2017, JANUARY, 1, 20, 0)); - assertThat(DateUtils.millisecondsUntilTomorrow(), equalTo(14400000L)); + assertThat(DateUtils.millisecondsUntilTomorrowWithOffset(), equalTo(4 * HOUR_LENGTH)); + + DateUtils.setStartDayOffset(3, 30); + DateUtils.setFixedLocalTime(unixTime(2017, JANUARY, 1, 23, 59)); + assertThat(DateUtils.millisecondsUntilTomorrowWithOffset(), equalTo(3 * HOUR_LENGTH + 31 * MINUTE_LENGTH)); + DateUtils.setFixedLocalTime(unixTime(2017, JANUARY, 2, 1, 0)); + assertThat(DateUtils.millisecondsUntilTomorrowWithOffset(), equalTo(2 * HOUR_LENGTH + 30 * MINUTE_LENGTH)); + } + + @Test + public void testGetTodayWithOffset() throws Exception + { + assertThat(DateUtils.getTodayWithOffset(), equalTo(new Timestamp(FIXED_LOCAL_TIME))); + DateUtils.setStartDayOffset(9, 0); + assertThat( + DateUtils.getTodayWithOffset(), + equalTo(new Timestamp(FIXED_LOCAL_TIME - DAY_LENGTH))); + } + + @Test + public void testGetStartOfDayWithOffset() throws Exception + { + long timestamp = unixTime(2020, SEPTEMBER, 3); + assertThat( + DateUtils.getStartOfDayWithOffset(timestamp + HOUR_LENGTH), + equalTo(timestamp)); + DateUtils.setStartDayOffset(3, 30); + assertThat( + DateUtils.getStartOfDayWithOffset(timestamp + 3 * HOUR_LENGTH + 29 * MINUTE_LENGTH), + equalTo(timestamp - DAY_LENGTH)); } @Test