mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
@@ -412,7 +412,7 @@ public abstract class CheckmarkList
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Checkmark> groupBy(DateUtils.TruncateField field)
|
||||
public List<Checkmark> groupBy(DateUtils.TruncateField field, int firstWeekday)
|
||||
{
|
||||
List<Checkmark> checks = getAll();
|
||||
|
||||
@@ -422,7 +422,7 @@ public abstract class CheckmarkList
|
||||
|
||||
for (Checkmark rep : checks)
|
||||
{
|
||||
Timestamp tt = rep.getTimestamp().truncate(field);
|
||||
Timestamp tt = rep.getTimestamp().truncate(field, firstWeekday);
|
||||
if (count == 0 || !truncatedTimestamps[count - 1].equals(tt))
|
||||
truncatedTimestamps[count++] = tt;
|
||||
|
||||
|
||||
@@ -129,10 +129,10 @@ public abstract class ScoreList implements Iterable<Score>
|
||||
return values;
|
||||
}
|
||||
|
||||
public List<Score> groupBy(DateUtils.TruncateField field)
|
||||
public List<Score> groupBy(DateUtils.TruncateField field, int firstWeekday)
|
||||
{
|
||||
computeAll();
|
||||
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field);
|
||||
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field, firstWeekday);
|
||||
List<Score> scores = groupsToAvgScores(groups);
|
||||
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
||||
return scores;
|
||||
@@ -293,14 +293,18 @@ public abstract class ScoreList implements Iterable<Score>
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field)
|
||||
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field,
|
||||
int firstWeekday)
|
||||
{
|
||||
HashMap<Timestamp, ArrayList<Double>> groups = new HashMap<>();
|
||||
|
||||
for (Score s : this)
|
||||
{
|
||||
Timestamp groupTimestamp = new Timestamp(
|
||||
DateUtils.truncate(field, s.getTimestamp().getUnixTime()));
|
||||
DateUtils.truncate(
|
||||
field,
|
||||
s.getTimestamp().getUnixTime(),
|
||||
firstWeekday));
|
||||
|
||||
if (!groups.containsKey(groupTimestamp))
|
||||
groups.put(groupTimestamp, new ArrayList<>());
|
||||
|
||||
@@ -143,13 +143,17 @@ public final class Timestamp
|
||||
return DateFormats.getCSVDateFormat().format(new Date(unixTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an integer corresponding to the day of the week. Saturday maps
|
||||
* to 0, Sunday maps to 1, and so on.
|
||||
*/
|
||||
public int getWeekday()
|
||||
{
|
||||
return toCalendar().get(DAY_OF_WEEK) % 7;
|
||||
}
|
||||
|
||||
public Timestamp truncate(DateUtils.TruncateField field)
|
||||
Timestamp truncate(DateUtils.TruncateField field, int firstWeekday)
|
||||
{
|
||||
return new Timestamp(DateUtils.truncate(field, unixTime));
|
||||
return new Timestamp(DateUtils.truncate(field, unixTime, firstWeekday));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +333,19 @@ public class Preferences
|
||||
return Integer.parseInt(storage.getString("pref_widget_opacity", "102"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An integer representing the first day of the week. Sunday
|
||||
* corresponds to 1, Monday to 2, and so on, until Saturday, which is
|
||||
* represented by 7. By default, this is based on the current system locale,
|
||||
* unless the user changed this in the settings.
|
||||
*/
|
||||
public int getFirstWeekday()
|
||||
{
|
||||
String weekday = storage.getString("pref_first_weekday", "");
|
||||
if (weekday.isEmpty()) return DateUtils.getFirstWeekdayNumberAccordingToLocale();
|
||||
return Integer.parseInt(weekday);
|
||||
}
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
default void onCheckmarkSequenceChanged()
|
||||
|
||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.utils;
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.core.models.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -73,23 +74,6 @@ public abstract class DateUtils
|
||||
return day;
|
||||
}
|
||||
|
||||
private static String[] getDayNames(int format)
|
||||
{
|
||||
String[] wdays = new String[7];
|
||||
|
||||
Calendar day = new GregorianCalendar();
|
||||
day.set(DAY_OF_WEEK, Calendar.SATURDAY);
|
||||
|
||||
for (int i = 0; i < wdays.length; i++)
|
||||
{
|
||||
wdays[i] =
|
||||
day.getDisplayName(DAY_OF_WEEK, format, getLocale());
|
||||
day.add(DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
return wdays;
|
||||
}
|
||||
|
||||
public static long getLocalTime()
|
||||
{
|
||||
if (fixedLocalTime != null) return fixedLocalTime;
|
||||
@@ -100,19 +84,25 @@ public abstract class DateUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array with weekday names starting according to locale settings,
|
||||
* e.g. [Mo,Di,Mi,Do,Fr,Sa,So] in Germany
|
||||
* Returns an array of strings with the names for each day of the week,
|
||||
* in either SHORT or LONG format. The first entry corresponds to the
|
||||
* first day of the week, according to the provided argument.
|
||||
*
|
||||
* @param format Either GregorianCalendar.SHORT or LONG
|
||||
* @param firstWeekday An integer representing the first day of the week,
|
||||
* following java.util.Calendar conventions. That is,
|
||||
* Saturday corresponds to 7, and Sunday corresponds
|
||||
* to 1.
|
||||
*/
|
||||
public static String[] getLocaleDayNames(int format)
|
||||
@NotNull
|
||||
private static String[] getWeekdayNames(int format, int firstWeekday)
|
||||
{
|
||||
String[] days = new String[7];
|
||||
|
||||
Calendar calendar = new GregorianCalendar();
|
||||
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
|
||||
for (int i = 0; i < days.length; i++)
|
||||
{
|
||||
calendar.set(DAY_OF_WEEK, firstWeekday);
|
||||
for (int i = 0; i < days.length; i++) {
|
||||
days[i] = calendar.getDisplayName(DAY_OF_WEEK, format,
|
||||
getLocale());
|
||||
getLocale());
|
||||
calendar.add(DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
@@ -120,30 +110,63 @@ public abstract class DateUtils
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array with week days numbers starting according to locale
|
||||
* settings, e.g. [2,3,4,5,6,7,1] in Europe
|
||||
* Returns a vector of exactly seven integers, where the first integer is
|
||||
* the provided firstWeekday number, and each subsequent number is the
|
||||
* previous number plus 1, wrapping back to 1 after 7. For example,
|
||||
* providing 3 as firstWeekday returns {3,4,5,6,7,1,2}
|
||||
*
|
||||
* This function is supposed to be used to construct a sequence of weekday
|
||||
* number following java.util.Calendar conventions.
|
||||
*/
|
||||
public static Integer[] getLocaleWeekdayList()
|
||||
public static int[] getWeekdaySequence(int firstWeekday)
|
||||
{
|
||||
Integer[] dayNumbers = new Integer[7];
|
||||
Calendar calendar = new GregorianCalendar();
|
||||
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
|
||||
for (int i = 0; i < dayNumbers.length; i++)
|
||||
return new int[]
|
||||
{
|
||||
dayNumbers[i] = calendar.get(DAY_OF_WEEK);
|
||||
calendar.add(DAY_OF_MONTH, 1);
|
||||
}
|
||||
return dayNumbers;
|
||||
(firstWeekday - 1) % 7 + 1,
|
||||
(firstWeekday) % 7 + 1,
|
||||
(firstWeekday + 1) % 7 + 1,
|
||||
(firstWeekday + 2) % 7 + 1,
|
||||
(firstWeekday + 3) % 7 + 1,
|
||||
(firstWeekday + 4) % 7 + 1,
|
||||
(firstWeekday + 5) % 7 + 1,
|
||||
};
|
||||
}
|
||||
|
||||
public static String[] getLongDayNames()
|
||||
/**
|
||||
* @return An integer representing the first day of the week, according to
|
||||
* the current locale. Sunday corresponds to 1, Monday to 2, and so on,
|
||||
* until Saturday, which is represented by 7. This is consistent
|
||||
* with java.util.Calendar constants.
|
||||
*/
|
||||
public static int getFirstWeekdayNumberAccordingToLocale()
|
||||
{
|
||||
return getDayNames(GregorianCalendar.LONG);
|
||||
return new GregorianCalendar().getFirstDayOfWeek();
|
||||
}
|
||||
|
||||
public static String[] getShortDayNames()
|
||||
/**
|
||||
* @return A vector of strings with the long names for the week days,
|
||||
* according to the current locale. The first entry corresponds to Saturday,
|
||||
* the second entry corresponds to Monday, and so on.
|
||||
*
|
||||
* @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other
|
||||
* weekdays defined in this class.
|
||||
*/
|
||||
public static String[] getLongWeekdayNames(int firstWeekday)
|
||||
{
|
||||
return getDayNames(SHORT);
|
||||
return getWeekdayNames(GregorianCalendar.LONG, firstWeekday);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a vector of strings with the short names for the week days,
|
||||
* according to the current locale. The first entry corresponds to Saturday,
|
||||
* the second entry corresponds to Monday, and so on.
|
||||
*
|
||||
* @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other
|
||||
* weekdays defined in this class.
|
||||
*/
|
||||
public static String[] getShortWeekdayNames(int firstWeekday)
|
||||
{
|
||||
return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -206,10 +229,13 @@ public abstract class DateUtils
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
public static Long truncate(TruncateField field, long timestamp)
|
||||
public static Long truncate(TruncateField field,
|
||||
long timestamp,
|
||||
int firstWeekday)
|
||||
{
|
||||
GregorianCalendar cal = DateUtils.getCalendar(timestamp);
|
||||
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case MONTH:
|
||||
@@ -217,7 +243,6 @@ public abstract class DateUtils
|
||||
return cal.getTimeInMillis();
|
||||
|
||||
case WEEK_NUMBER:
|
||||
int firstWeekday = cal.getFirstDayOfWeek();
|
||||
int weekday = cal.get(DAY_OF_WEEK);
|
||||
int delta = weekday - firstWeekday;
|
||||
if (delta < 0) delta += 7;
|
||||
|
||||
@@ -388,20 +388,20 @@ public class CheckmarkListTest extends BaseUnitTest
|
||||
Habit habit = fixtures.createLongNumericalHabit(timestamp(2014, JUNE, 1));
|
||||
CheckmarkList checkmarks = habit.getCheckmarks();
|
||||
|
||||
List<Checkmark> byMonth = checkmarks.groupBy(MONTH);
|
||||
List<Checkmark> byMonth = checkmarks.groupBy(MONTH, Calendar.SATURDAY);
|
||||
assertThat(byMonth.size(), equalTo(25)); // from 2013-01-01 to 2015-01-01
|
||||
assertThat(byMonth.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||
assertThat(byMonth.get(6), equalTo(new Checkmark(timestamp(2014, JULY, 1), 0)));
|
||||
assertThat(byMonth.get(12), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 1706)));
|
||||
assertThat(byMonth.get(18), equalTo(new Checkmark(timestamp(2013, JULY, 1), 1379)));
|
||||
|
||||
List<Checkmark> byQuarter = checkmarks.groupBy(QUARTER);
|
||||
List<Checkmark> byQuarter = checkmarks.groupBy(QUARTER, Calendar.SATURDAY);
|
||||
assertThat(byQuarter.size(), equalTo(9)); // from 2013-Q1 to 2015-Q1
|
||||
assertThat(byQuarter.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||
assertThat(byQuarter.get(4), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 4964)));
|
||||
assertThat(byQuarter.get(8), equalTo(new Checkmark(timestamp(2013, JANUARY, 1), 4975)));
|
||||
|
||||
List<Checkmark> byYear = checkmarks.groupBy(YEAR);
|
||||
List<Checkmark> byYear = checkmarks.groupBy(YEAR, Calendar.SATURDAY);
|
||||
assertThat(byYear.size(), equalTo(3)); // from 2013 to 2015
|
||||
assertThat(byYear.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||
assertThat(byYear.get(1), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 8227)));
|
||||
|
||||
@@ -150,7 +150,7 @@ public class ScoreListTest extends BaseUnitTest
|
||||
{
|
||||
Habit habit = fixtures.createLongHabit();
|
||||
List<Score> list =
|
||||
habit.getScores().groupBy(DateUtils.TruncateField.MONTH);
|
||||
habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SATURDAY);
|
||||
|
||||
assertThat(list.size(), equalTo(5));
|
||||
assertThat(list.get(0).getValue(), closeTo(0.653659, E));
|
||||
|
||||
@@ -34,6 +34,9 @@ import static org.isoron.uhabits.core.utils.DateUtils.removeTimezone;
|
||||
|
||||
public class DateUtilsTest extends BaseUnitTest
|
||||
{
|
||||
|
||||
int firstWeekday = SUNDAY;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception
|
||||
@@ -61,18 +64,29 @@ public class DateUtilsTest extends BaseUnitTest
|
||||
long t1 = unixTime(2015, Calendar.JANUARY, 16);
|
||||
long t2 = unixTime(2015, Calendar.JANUARY, 17);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
|
||||
expected = unixTime(2015, Calendar.JANUARY, 18);
|
||||
t0 = unixTime(2015, Calendar.JANUARY, 18);
|
||||
t1 = unixTime(2015, Calendar.JANUARY, 19);
|
||||
t2 = unixTime(2015, Calendar.JANUARY, 24);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
|
||||
|
||||
firstWeekday = WEDNESDAY;
|
||||
expected = unixTime(2015, Calendar.JANUARY, 7);
|
||||
t0 = unixTime(2015, Calendar.JANUARY, 7);
|
||||
t1 = unixTime(2015, Calendar.JANUARY, 9);
|
||||
t2 = unixTime(2015, Calendar.JANUARY, 13);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,18 +99,18 @@ public class DateUtilsTest extends BaseUnitTest
|
||||
|
||||
DateUtils.TruncateField field = DateUtils.TruncateField.MONTH;
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
|
||||
expected = unixTime(2016, DECEMBER, 1);
|
||||
t0 = unixTime(2016, DECEMBER, 1);
|
||||
t1 = unixTime(2016, DECEMBER, 15);
|
||||
t2 = unixTime(2016, DECEMBER, 31);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,18 +123,18 @@ public class DateUtilsTest extends BaseUnitTest
|
||||
long t1 = unixTime(2016, FEBRUARY, 15);
|
||||
long t2 = unixTime(2016, MARCH, 30);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
|
||||
expected = unixTime(2016, APRIL, 1);
|
||||
t0 = unixTime(2016, APRIL, 1);
|
||||
t1 = unixTime(2016, MAY, 30);
|
||||
t2 = unixTime(2016, JUNE, 20);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -133,18 +147,18 @@ public class DateUtilsTest extends BaseUnitTest
|
||||
long t1 = unixTime(2016, FEBRUARY, 25);
|
||||
long t2 = unixTime(2016, DECEMBER, 31);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
|
||||
expected = unixTime(2017, JANUARY, 1);
|
||||
t0 = unixTime(2017, JANUARY, 1);
|
||||
t1 = unixTime(2017, MAY, 30);
|
||||
t2 = unixTime(2017, DECEMBER, 31);
|
||||
|
||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user