Format dates with 310 backport

pull/996/head
Alex Baker 6 years ago
parent d045c75ff2
commit 447b6be7d2

@ -6,93 +6,31 @@
package com.todoroo.andlib.utility; package com.todoroo.andlib.utility;
import static androidx.test.InstrumentationRegistry.getTargetContext; import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.todoroo.andlib.utility.DateUtilities.getDateString; import static com.todoroo.andlib.utility.DateUtilities.getDateString;
import static com.todoroo.andlib.utility.DateUtilities.getStartOfDay;
import static com.todoroo.andlib.utility.DateUtilities.getTimeString; import static com.todoroo.andlib.utility.DateUtilities.getTimeString;
import static com.todoroo.andlib.utility.DateUtilities.getWeekday; import static com.todoroo.andlib.utility.DateUtilities.getWeekday;
import static com.todoroo.andlib.utility.DateUtilities.getWeekdayShort; import static com.todoroo.andlib.utility.DateUtilities.getWeekdayShort;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt; import static org.tasks.Freeze.freezeAt;
import static org.tasks.date.DateTimeUtils.newDate; import static org.tasks.date.DateTimeUtils.newDate;
import static org.tasks.date.DateTimeUtils.newDateTime;
import android.content.res.Configuration;
import android.util.DisplayMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.Locale; import java.util.Locale;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.tasks.Snippet; import org.tasks.Snippet;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.threeten.bp.format.FormatStyle;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class DateUtilitiesTest { public class DateUtilitiesTest {
private static Locale defaultLocale;
@Before
public void setUp() {
defaultLocale = Locale.getDefault();
setLocale(Locale.US);
}
@After @After
public void tearDown() { public void after() {
DateUtilities.is24HourOverride = null; DateUtilities.is24HourOverride = null;
setLocale(defaultLocale);
}
private void setLocale(Locale locale) {
org.tasks.locale.Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
DisplayMetrics metrics = getTargetContext().getResources().getDisplayMetrics();
getTargetContext().getResources().updateConfiguration(config, metrics);
}
public void forEachLocale(Runnable r) {
Locale[] locales = Locale.getAvailableLocales();
for (Locale locale : locales) {
setLocale(locale);
r.run();
}
}
@Test
public void testTimeString() {
forEachLocale(
() -> {
DateTime d = newDateTime();
DateUtilities.is24HourOverride = false;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
DateUtilities.is24HourOverride = true;
for (int i = 0; i < 24; i++) {
d = d.withHourOfDay(i);
getTimeString(getTargetContext(), d);
}
});
}
@Test
public void testDateString() {
forEachLocale(
() -> {
DateTime d = newDateTime();
for (int i = 0; i < 12; i++) {
d = d.withMonthOfYear(i);
getDateString(d);
}
});
} }
@Test @Test
@ -117,176 +55,246 @@ public class DateUtilitiesTest {
@Test @Test
public void testGetDateStringWithYear() { public void testGetDateStringWithYear() {
assertEquals("Jan 4 '14", getDateString(new DateTime(2014, 1, 4, 0, 0, 0))); assertEquals("Jan 4, 2014", getDateString(getApplicationContext(), new DateTime(2014, 1, 4, 0, 0, 0)));
} }
@Test @Test
public void testGetDateStringHidingYear() { public void testGetDateStringHidingYear() {
freezeAt(newDate(2014, 1, 1)) freezeAt(newDate(2014, 2, 1))
.thawAfter( .thawAfter(
new Snippet() { new Snippet() {
{ {
assertEquals("Jan 1", getDateString(newDateTime())); assertEquals("Jan 1", getDateString(getApplicationContext(), new DateTime(2014, 1, 1)));
} }
}); });
} }
@Test @Test
public void testGetDateStringWithDifferentYear() { public void testGetDateStringWithDifferentYear() {
freezeAt(newDate(2013, 12, 31)) freezeAt(newDate(2013, 12, 1))
.thawAfter( .thawAfter(
new Snippet() { new Snippet() {
{ {
assertEquals("Jan 1 '14", getDateString(new DateTime(2014, 1, 1, 0, 0, 0))); assertEquals("Jan 1, 2014", getDateString(getApplicationContext(),new DateTime(2014, 1, 1, 0, 0, 0)));
} }
}); });
} }
@Test @Test
public void testShouldGetStartOfDay() { public void testGetWeekdayLongString() {
DateTime now = new DateTime(2014, 1, 3, 10, 41, 41, 520); assertEquals("Sunday", getWeekday(newDate(2013, 12, 29), Locale.US));
assertEquals(now.startOfDay().getMillis(), getStartOfDay(now.getMillis())); assertEquals("Monday", getWeekday(newDate(2013, 12, 30), Locale.US));
assertEquals("Tuesday", getWeekday(newDate(2013, 12, 31), Locale.US));
assertEquals("Wednesday", getWeekday(newDate(2014, 1, 1), Locale.US));
assertEquals("Thursday", getWeekday(newDate(2014, 1, 2), Locale.US));
assertEquals("Friday", getWeekday(newDate(2014, 1, 3), Locale.US));
assertEquals("Saturday", getWeekday(newDate(2014, 1, 4), Locale.US));
} }
@Test @Test
public void testGetWeekdayLongString() { public void testGetWeekdayShortString() {
assertEquals("Sunday", getWeekday(newDate(2013, 12, 29))); assertEquals("Sun", getWeekdayShort(newDate(2013, 12, 29), Locale.US));
assertEquals("Monday", getWeekday(newDate(2013, 12, 30))); assertEquals("Mon", getWeekdayShort(newDate(2013, 12, 30), Locale.US));
assertEquals("Tuesday", getWeekday(newDate(2013, 12, 31))); assertEquals("Tue", getWeekdayShort(newDate(2013, 12, 31), Locale.US));
assertEquals("Wednesday", getWeekday(newDate(2014, 1, 1))); assertEquals("Wed", getWeekdayShort(newDate(2014, 1, 1), Locale.US));
assertEquals("Thursday", getWeekday(newDate(2014, 1, 2))); assertEquals("Thu", getWeekdayShort(newDate(2014, 1, 2), Locale.US));
assertEquals("Friday", getWeekday(newDate(2014, 1, 3))); assertEquals("Fri", getWeekdayShort(newDate(2014, 1, 3), Locale.US));
assertEquals("Saturday", getWeekday(newDate(2014, 1, 4))); assertEquals("Sat", getWeekdayShort(newDate(2014, 1, 4), Locale.US));
} }
@Test @Test
public void testGetWeekdayShortString() { public void getRelativeFullDate() {
assertEquals("Sun", getWeekdayShort(newDate(2013, 12, 29))); freezeAt(new DateTime(2018, 1, 1))
assertEquals("Mon", getWeekdayShort(newDate(2013, 12, 30))); .thawAfter(
assertEquals("Tue", getWeekdayShort(newDate(2013, 12, 31))); () ->
assertEquals("Wed", getWeekdayShort(newDate(2014, 1, 1))); assertEquals(
assertEquals("Thu", getWeekdayShort(newDate(2014, 1, 2))); "Sunday, January 14",
assertEquals("Fri", getWeekdayShort(newDate(2014, 1, 3))); DateUtilities.getRelativeDateTime(
assertEquals("Sat", getWeekdayShort(newDate(2014, 1, 4))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.US,
FormatStyle.FULL)));
}
@Test
public void getRelativeFullDateWithYear() {
freezeAt(new DateTime(2017, 12, 12))
.thawAfter(
() ->
assertEquals(
"Sunday, January 14, 2018",
DateUtilities.getRelativeDateTime(
getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.US,
FormatStyle.FULL)));
} }
@Test @Test
public void usDateNoYear() { public void getRelativeFullDateTime() {
setLocale(Locale.US);
freezeAt(new DateTime(2018, 1, 1)) freezeAt(new DateTime(2018, 1, 1))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( assertEquals(
"Jan 14", "Sunday, January 14 1:43 PM",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14, 13, 43, 1).getMillis(),
Locale.US,
FormatStyle.FULL)));
} }
@Test @Test
public void usDateWithYear() { public void getRelativeFullDateTimeWithYear() {
setLocale(Locale.US);
freezeAt(new DateTime(2017, 12, 12)) freezeAt(new DateTime(2017, 12, 12))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( assertEquals(
"Jan 14 '18", "Sunday, January 14, 2018 11:50 AM",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14, 11, 50, 1).getMillis(),
Locale.US,
FormatStyle.FULL)));
} }
@Test @Test
public void germanDateNoYear() { public void germanDateNoYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2018, 1, 1)) freezeAt(new DateTime(2018, 1, 1))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"14 Jan.", "Sonntag, 14. Januar",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.GERMAN,
FormatStyle.FULL)));
} }
@Test @Test
public void germanDateWithYear() { public void germanDateWithYear() {
setLocale(Locale.GERMAN);
freezeAt(new DateTime(2017, 12, 12)) freezeAt(new DateTime(2017, 12, 12))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"14 Jan. '18", "Sonntag, 14. Januar 2018",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.GERMAN,
FormatStyle.FULL)));
} }
@Test @Test
public void koreanDateNoYear() { public void koreanDateNoYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2018, 1, 1)) freezeAt(new DateTime(2018, 1, 1))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"1월 14일", "1월 14일 일요일",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.KOREAN,
FormatStyle.FULL)));
} }
@Test @Test
public void koreanDateWithYear() { public void koreanDateWithYear() {
setLocale(Locale.KOREAN);
freezeAt(new DateTime(2017, 12, 12)) freezeAt(new DateTime(2017, 12, 12))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"18년 1월 14일", "2018년 1월 14일 일요일",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.KOREAN,
FormatStyle.FULL)));
} }
@Test @Test
public void japaneseDateNoYear() { public void japaneseDateNoYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2018, 1, 1)) freezeAt(new DateTime(2018, 1, 1))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"1月 14日", "1月14日日曜日",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.JAPANESE,
FormatStyle.FULL)));
} }
@Test @Test
public void japaneseDateWithYear() { public void japaneseDateWithYear() {
setLocale(Locale.JAPANESE);
freezeAt(new DateTime(2017, 12, 12)) freezeAt(new DateTime(2017, 12, 12))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"18年 1月 14日", "2018年1月14日日曜日",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.JAPANESE,
FormatStyle.FULL)));
} }
@Test @Test
public void chineseDateNoYear() { public void chineseDateNoYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2018, 1, 1)) freezeAt(new DateTime(2018, 1, 1))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"1月 14日", "1月14日星期日",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.CHINESE,
FormatStyle.FULL)));
} }
@Test @Test
public void chineseDateWithYear() { public void chineseDateWithYear() {
setLocale(Locale.CHINESE);
freezeAt(new DateTime(2017, 12, 12)) freezeAt(new DateTime(2017, 12, 12))
.thawAfter( .thawAfter(
() -> () ->
assertEquals( Assert.assertEquals(
"18年 1月 14日", "2018年1月14日星期日",
DateUtilities.getRelativeDateStringWithTime( DateUtilities.getRelativeDateTime(
getTargetContext(), new DateTime(2018, 1, 14).getMillis()))); getApplicationContext(),
new DateTime(2018, 1, 14).getMillis(),
Locale.CHINESE,
FormatStyle.FULL)));
}
@Test
public void chineseDateTimeNoYear() {
freezeAt(new DateTime(2018, 1, 1))
.thawAfter(
() ->
Assert.assertEquals(
"1月14日星期日 上午11:53",
DateUtilities.getRelativeDateTime(
getApplicationContext(),
new DateTime(2018, 1, 14, 11, 53, 1).getMillis(),
Locale.CHINESE,
FormatStyle.FULL)));
}
@Test
public void chineseDateTimeWithYear() {
freezeAt(new DateTime(2017, 12, 12))
.thawAfter(
() ->
Assert.assertEquals(
"2018年1月14日星期日 下午1:45",
DateUtilities.getRelativeDateTime(
getApplicationContext(),
new DateTime(2018, 1, 14, 13, 45, 1).getMillis(),
Locale.CHINESE,
FormatStyle.FULL)));
} }
} }

@ -1,6 +1,6 @@
package com.todoroo.andlib.utility; package com.todoroo.andlib.utility;
import static androidx.test.InstrumentationRegistry.getTargetContext; import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.todoroo.andlib.utility.DateUtilities.getRelativeDay; import static com.todoroo.andlib.utility.DateUtilities.getRelativeDay;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static org.tasks.Freeze.freezeAt; import static org.tasks.Freeze.freezeAt;
@ -13,6 +13,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.threeten.bp.format.FormatStyle;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class RelativeDayTest { public class RelativeDayTest {
@ -62,16 +63,20 @@ public class RelativeDayTest {
@Test @Test
public void testRelativeDayOneWeek() { public void testRelativeDayOneWeek() {
checkRelativeDay(new DateTime().minusDays(7), "Dec 24", "Dec 24"); checkRelativeDay(new DateTime().minusDays(7), "December 24", "Dec 24");
} }
@Test @Test
public void testRelativeDayOneWeekNextYear() { public void testRelativeDayOneWeekNextYear() {
checkRelativeDay(new DateTime().plusDays(7), "Jan 7 '14", "Jan 7 '14"); checkRelativeDay(new DateTime().plusDays(7), "January 7, 2014", "Jan 7, 2014");
} }
private void checkRelativeDay(DateTime now, String full, String abbreviated) { private void checkRelativeDay(DateTime now, String full, String abbreviated) {
assertEquals(full, getRelativeDay(getTargetContext(), now.getMillis(), false)); assertEquals(
assertEquals(abbreviated, getRelativeDay(getTargetContext(), now.getMillis(), true)); full,
getRelativeDay(getApplicationContext(), now.getMillis(), Locale.US, FormatStyle.LONG));
assertEquals(
abbreviated,
getRelativeDay(getApplicationContext(), now.getMillis(), Locale.US, FormatStyle.MEDIUM));
} }
} }

@ -11,10 +11,15 @@ import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.content.Context; import android.content.Context;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import androidx.annotation.Nullable;
import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import org.tasks.BuildConfig;
import org.tasks.R; import org.tasks.R;
import org.tasks.locale.Locale;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.FormatStyle;
import org.threeten.bp.format.TextStyle;
public class DateUtilities { public class DateUtilities {
@ -27,13 +32,6 @@ public class DateUtilities {
/** Represents a single minute */ /** Represents a single minute */
public static final long ONE_MINUTE = 60000L; public static final long ONE_MINUTE = 60000L;
private static final long abbreviationLimit = DateUtilities.ONE_DAY * 6;
private static final String JA = "MMM d\u65E5";
private static final String JA_YEAR = "yy\u5E74 " + JA;
private static final String KO = "MMM d\uC77C";
private static final String KO_YEAR = "yy\uB144 " + KO;
private static final String ZH = "MMM d\u65E5";
private static final String ZH_YEAR = "yy\u5E74 " + ZH;
static Boolean is24HourOverride = null; static Boolean is24HourOverride = null;
/** Returns unixtime for current time */ /** Returns unixtime for current time */
@ -45,16 +43,10 @@ public class DateUtilities {
* =========================================================== formatters * =========================================================== formatters
* ====================================================================== */ * ====================================================================== */
public static boolean is24HourFormat(Context context) { private static boolean is24HourFormat(Context context) {
if (is24HourOverride != null) { return BuildConfig.DEBUG && is24HourOverride != null
return is24HourOverride; ? is24HourOverride
} : DateFormat.is24HourFormat(context);
return DateFormat.is24HourFormat(context);
}
public static String getTimeString(Context context, long timestamp) {
return getTimeString(context, newDateTime(timestamp));
} }
public static String getTimeString(Context context, DateTime date) { public static String getTimeString(Context context, DateTime date) {
@ -69,120 +61,106 @@ public class DateUtilities {
return date.toString(value); return date.toString(value);
} }
public static String getLongDateString(DateTime date) { public static String getLongDateString(DateTime date, java.util.Locale locale) {
return getDateString("MMMM", date); return getFullDate(date, locale, FormatStyle.LONG);
} }
/** /**
* @param date date to format * @param date date to format
* @return date, with month, day, and year * @return date, with month, day, and year
*/ */
public static String getDateString(DateTime date) { public static String getDateString(Context context, DateTime date) {
return getDateString("MMM", date); return getRelativeDay(
context, date.getMillis(), java.util.Locale.getDefault(), FormatStyle.MEDIUM);
} }
private static String getDateString(String simpleDateFormat, DateTime date) { static String getWeekday(DateTime date, java.util.Locale locale) {
boolean includeYear = date.getYear() != newDateTime().getYear(); return date.toLocalDate().getDayOfWeek().getDisplayName(TextStyle.FULL, locale);
String format = getFormat(Locale.getInstance(), simpleDateFormat, includeYear);
return date.toString(format);
}
private static String getFormat(Locale locale, String monthFormat, boolean includeYear) {
switch (locale.getLanguage()) {
case "ja":
return includeYear ? JA_YEAR : JA;
case "ko":
return includeYear ? KO_YEAR : KO;
case "zh":
return includeYear ? ZH_YEAR : ZH;
}
switch (locale.getCountry()) {
case "BZ":
case "CA":
case "KE":
case "MN":
case "US":
return includeYear ? monthFormat + " d ''yy" : monthFormat + " d";
default:
return includeYear ? "d " + monthFormat + " ''yy" : "d " + monthFormat;
}
} }
/** @return weekday */ /** @return weekday */
public static String getWeekday(DateTime date) { static String getWeekdayShort(DateTime date, java.util.Locale locale) {
return date.toString("EEEE"); return date.toLocalDate().getDayOfWeek().getDisplayName(TextStyle.SHORT, locale);
} }
/** @return weekday */ public static String getLongDateStringWithTime(long timestamp, java.util.Locale locale) {
public static String getWeekdayShort(DateTime date) { return getFullDateTime(newDateTime(timestamp), locale, FormatStyle.LONG);
return date.toString("EEE");
} }
public static String getLongDateStringWithTime(Context context, long timestamp) { public static String getRelativeDateTime(
DateTime date = newDateTime(timestamp); Context context, long date, java.util.Locale locale, FormatStyle style) {
return getLongDateString(date) + ", " + getTimeString(context, date); String day = getRelativeDay(context, date, locale, isAbbreviated(style));
if (!Strings.isNullOrEmpty(day)) {
if (Task.hasDueTime(date)) {
String time = getTimeString(context, newDateTime(date));
return newDateTime().startOfDay().equals(newDateTime(date).startOfDay()) ? time : String.format("%s %s", day, time);
} else {
return day;
}
}
return Task.hasDueTime(date)
? getFullDateTime(newDateTime(date), locale, style)
: getFullDate(newDateTime(date), locale, style);
} }
public static String getDateStringWithTime(Context context, long timestamp) { private static boolean isAbbreviated(FormatStyle style) {
DateTime date = newDateTime(timestamp); return style == FormatStyle.SHORT || style == FormatStyle.MEDIUM;
return getDateString(date) + ", " + getTimeString(context, date);
} }
public static String getRelativeDateStringWithTime(Context context, long timestamp) { static String getRelativeDay(
String string = DateUtilities.getRelativeDay(context, timestamp, false); Context context,
if (Task.hasDueTime(timestamp)) { long date,
string = java.util.Locale locale,
String.format( FormatStyle style) {
"%s %s", String relativeDay = getRelativeDay(context, date, locale, isAbbreviated(style));
string, // $NON-NLS-1$ return Strings.isNullOrEmpty(relativeDay)
DateUtilities.getTimeString(context, timestamp)); ? getFullDate(newDateTime(date), locale, style)
} : relativeDay;
return string;
} }
public static String getAbbreviatedRelativeDateWithTime(Context context, long date) { private static String getFullDate(DateTime date, java.util.Locale locale, FormatStyle style) {
long startOfToday = getStartOfDay(currentTimeMillis()); return stripYear(
long startOfDate = getStartOfDay(date); DateTimeFormatter.ofLocalizedDate(style)
String day = getRelativeDay(context, date, startOfDate, startOfToday, true); .withLocale(locale)
if (Task.hasDueTime(date)) { .format(date.toLocalDate()),
String time = getTimeString(context, date); newDateTime().getYear());
return startOfToday == startOfDate ? time : String.format("%s %s", day, time);
}
return day;
} }
/** @return yesterday, today, tomorrow, or null */ private static String getFullDateTime(DateTime date, java.util.Locale locale, FormatStyle style) {
public static String getRelativeDay(Context context, long date, boolean abbreviated) { return stripYear(
long today = getStartOfDay(currentTimeMillis()); DateTimeFormatter.ofLocalizedDateTime(style, FormatStyle.SHORT)
long input = getStartOfDay(date); .withLocale(locale)
.format(date.toLocalDateTime()),
newDateTime().getYear());
}
return getRelativeDay(context, date, input, today, abbreviated); private static String stripYear(String date, int year) {
return date.replaceFirst("(,? )?" + year + "(年|년 )?", "");
} }
private static String getRelativeDay( private static @Nullable String getRelativeDay(Context context, long date, java.util.Locale locale, boolean abbreviated) {
Context context, long date, long input, long today, boolean abbreviated) { DateTime startOfToday = newDateTime().startOfDay();
if (today == input) { DateTime startOfDate = newDateTime(date).startOfDay();
if (startOfToday.equals(startOfDate)) {
return context.getString(R.string.today); return context.getString(R.string.today);
} }
if (today + ONE_DAY == input) { if (startOfToday.plusDays(1).equals(startOfDate)) {
return context.getString(abbreviated ? R.string.tmrw : R.string.tomorrow); return context.getString(abbreviated ? R.string.tmrw : R.string.tomorrow);
} }
if (today == input + ONE_DAY) { if (startOfDate.plusDays(1).equals(startOfToday)) {
return context.getString(abbreviated ? R.string.yest : R.string.yesterday); return context.getString(abbreviated ? R.string.yest : R.string.yesterday);
} }
if (today + abbreviationLimit >= input && today - abbreviationLimit <= input) { if (Math.abs(startOfToday.getMillis() - startOfDate.getMillis()) <= DateUtilities.ONE_DAY * 6) {
DateTime dateTime = newDateTime(date);
return abbreviated return abbreviated
? DateUtilities.getWeekdayShort(newDateTime(date)) ? DateUtilities.getWeekdayShort(dateTime, locale)
: DateUtilities.getWeekday(newDateTime(date)); : DateUtilities.getWeekday(dateTime, locale);
}
return getDateString(newDateTime(date));
}
public static long getStartOfDay(long time) { }
return newDateTime(time).startOfDay().getMillis(); return null;
} }
} }

@ -276,7 +276,7 @@ public final class TaskEditFragment extends InjectingFragment
String.format( String.format(
"%s %s\n%s %s", // $NON-NLS-1$ "%s %s\n%s %s", // $NON-NLS-1$
getString(R.string.TEA_timer_comment_stopped), getString(R.string.TEA_timer_comment_stopped),
DateUtilities.getTimeString(getActivity(), newDateTime()), DateUtilities.getTimeString(context, newDateTime()),
getString(R.string.TEA_timer_comment_spent), getString(R.string.TEA_timer_comment_spent),
elapsedTime), elapsedTime),
null); null);
@ -289,7 +289,7 @@ public final class TaskEditFragment extends InjectingFragment
String.format( String.format(
"%s %s", "%s %s",
getString(R.string.TEA_timer_comment_started), getString(R.string.TEA_timer_comment_started),
DateUtilities.getTimeString(getActivity(), newDateTime())), DateUtilities.getTimeString(context, newDateTime())),
null); null);
return model; return model;
} }

@ -417,8 +417,7 @@ public class Task implements Parcelable {
public boolean isOverdue() { public boolean isOverdue() {
long dueDate = getDueDate(); long dueDate = getDueDate();
long compareTo = long compareTo = hasDueTime() ? DateUtilities.now() : newDateTime().startOfDay().getMillis();
hasDueTime() ? DateUtilities.now() : DateUtilities.getStartOfDay(DateUtilities.now());
return dueDate < compareTo && !isCompleted(); return dueDate < compareTo && !isCompleted();
} }

@ -26,6 +26,7 @@ import org.tasks.R;
import org.tasks.data.UserActivity; import org.tasks.data.UserActivity;
import org.tasks.data.UserActivityDao; import org.tasks.data.UserActivityDao;
import org.tasks.files.FileHelper; import org.tasks.files.FileHelper;
import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
public class CommentsController { public class CommentsController {
@ -34,6 +35,7 @@ public class CommentsController {
private final ArrayList<UserActivity> items = new ArrayList<>(); private final ArrayList<UserActivity> items = new ArrayList<>();
private final Activity activity; private final Activity activity;
private final Preferences preferences; private final Preferences preferences;
private final Locale locale;
private int commentItems = 10; private int commentItems = 10;
private Task task; private Task task;
@ -41,10 +43,11 @@ public class CommentsController {
@Inject @Inject
public CommentsController( public CommentsController(
UserActivityDao userActivityDao, Activity activity, Preferences preferences) { UserActivityDao userActivityDao, Activity activity, Preferences preferences, Locale locale) {
this.userActivityDao = userActivityDao; this.userActivityDao = userActivityDao;
this.activity = activity; this.activity = activity;
this.preferences = preferences; this.preferences = preferences;
this.locale = locale;
} }
private static void setupImagePopupForCommentView( private static void setupImagePopupForCommentView(
@ -114,7 +117,7 @@ public class CommentsController {
// date // date
final TextView date = view.findViewById(R.id.date); final TextView date = view.findViewById(R.id.date);
date.setText(DateUtilities.getLongDateStringWithTime(activity, item.getCreated())); date.setText(DateUtilities.getLongDateStringWithTime(item.getCreated(), locale.getLocale()));
// picture // picture
final ImageView commentPictureView = view.findViewById(R.id.comment_picture); final ImageView commentPictureView = view.findViewById(R.id.comment_picture);

@ -41,6 +41,7 @@ import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.dialogs.MyTimePickerDialog; import org.tasks.dialogs.MyTimePickerDialog;
import org.tasks.injection.ForActivity; import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.themes.ThemeBase; import org.tasks.themes.ThemeBase;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@ -66,6 +67,7 @@ public class HideUntilControlSet extends TaskEditControlFragment implements OnIt
@Inject @ForActivity Context context; @Inject @ForActivity Context context;
@Inject ThemeBase themeBase; @Inject ThemeBase themeBase;
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject Locale locale;
// private final CheckBox enabled; // private final CheckBox enabled;
@BindView(R.id.hideUntil) @BindView(R.id.hideUntil)
Spinner spinner; Spinner spinner;
@ -266,12 +268,12 @@ public class HideUntilControlSet extends TaskEditControlFragment implements OnIt
&& hideUntilAsDate.getMinuteOfHour() == 0 && hideUntilAsDate.getMinuteOfHour() == 0
&& hideUntilAsDate.getSecondOfMinute() == 0) { && hideUntilAsDate.getSecondOfMinute() == 0) {
return new HideUntilValue( return new HideUntilValue(
DateUtilities.getDateString(newDateTime(timestamp)), DateUtilities.getDateString(context, newDateTime(timestamp)),
Task.HIDE_UNTIL_SPECIFIC_DAY, Task.HIDE_UNTIL_SPECIFIC_DAY,
timestamp); timestamp);
} else { } else {
return new HideUntilValue( return new HideUntilValue(
DateUtilities.getDateStringWithTime(context, timestamp), DateUtilities.getLongDateStringWithTime(timestamp, locale.getLocale()),
Task.HIDE_UNTIL_SPECIFIC_DAY_TIME, Task.HIDE_UNTIL_SPECIFIC_DAY_TIME,
timestamp); timestamp);
} }

@ -45,6 +45,7 @@ import org.tasks.data.Alarm;
import org.tasks.dialogs.MyTimePickerDialog; import org.tasks.dialogs.MyTimePickerDialog;
import org.tasks.injection.ForActivity; import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.locale.Locale;
import org.tasks.ui.HiddenTopArrayAdapter; import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment; import org.tasks.ui.TaskEditControlFragment;
@ -66,6 +67,7 @@ public class ReminderControlSet extends TaskEditControlFragment {
private final Set<Long> alarms = new LinkedHashSet<>(); private final Set<Long> alarms = new LinkedHashSet<>();
@Inject AlarmService alarmService; @Inject AlarmService alarmService;
@Inject @ForActivity Context context; @Inject @ForActivity Context context;
@Inject Locale locale;
@BindView(R.id.alert_container) @BindView(R.id.alert_container)
LinearLayout alertContainer; LinearLayout alertContainer;
@ -231,7 +233,7 @@ public class ReminderControlSet extends TaskEditControlFragment {
private void addAlarmRow(final Long timestamp) { private void addAlarmRow(final Long timestamp) {
if (alarms.add(timestamp)) { if (alarms.add(timestamp)) {
addAlarmRow(getLongDateStringWithTime(context, timestamp), v -> alarms.remove(timestamp)); addAlarmRow(getLongDateStringWithTime(timestamp, locale.getLocale()), v -> alarms.remove(timestamp));
} }
} }

@ -15,6 +15,8 @@ import java.text.ParseException;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.R; import org.tasks.R;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
import org.tasks.locale.Locale;
import org.threeten.bp.format.FormatStyle;
import timber.log.Timber; import timber.log.Timber;
public class RepeatConfirmationReceiver extends BroadcastReceiver { public class RepeatConfirmationReceiver extends BroadcastReceiver {
@ -22,12 +24,15 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
private final Activity activity; private final Activity activity;
private final Tracker tracker; private final Tracker tracker;
private final TaskDao taskDao; private final TaskDao taskDao;
private final Locale locale;
@Inject @Inject
public RepeatConfirmationReceiver(Activity activity, Tracker tracker, TaskDao taskDao) { public RepeatConfirmationReceiver(
Activity activity, Tracker tracker, TaskDao taskDao, Locale locale) {
this.activity = activity; this.activity = activity;
this.tracker = tracker; this.tracker = tracker;
this.taskDao = taskDao; this.taskDao = taskDao;
this.locale = locale;
} }
@Override @Override
@ -60,7 +65,9 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
final Task task, final Task task,
final long oldDueDate, final long oldDueDate,
final long newDueDate) { final long newDueDate) {
String dueDateString = getRelativeDateAndTimeString(activity, newDueDate); String dueDateString =
DateUtilities.getRelativeDateTime(
activity, newDueDate, locale.getLocale(), FormatStyle.LONG);
String snackbarText = String snackbarText =
activity.getString(R.string.repeat_snackbar, task.getTitle(), dueDateString); activity.getString(R.string.repeat_snackbar, task.getTitle(), dueDateString);
taskListFragment taskListFragment
@ -84,14 +91,4 @@ public class RepeatConfirmationReceiver extends BroadcastReceiver {
}) })
.show(); .show();
} }
private String getRelativeDateAndTimeString(Context context, long date) {
String dueString = date > 0 ? DateUtilities.getRelativeDay(context, date, false) : "";
if (Task.hasDueTime(date)) {
dueString =
context.getString(
R.string.repeat_snackbar_time, dueString, DateUtilities.getTimeString(context, date));
}
return dueString;
}
} }

@ -9,7 +9,6 @@ import static com.google.ical.values.Frequency.MONTHLY;
import static com.google.ical.values.Frequency.WEEKLY; import static com.google.ical.values.Frequency.WEEKLY;
import static com.google.ical.values.Frequency.YEARLY; import static com.google.ical.values.Frequency.YEARLY;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.dialogs.MyDatePickerDialog.newDatePicker; import static org.tasks.dialogs.MyDatePickerDialog.newDatePicker;
import static org.tasks.time.DateTimeUtils.currentTimeMillis; import static org.tasks.time.DateTimeUtils.currentTimeMillis;
@ -51,7 +50,6 @@ import com.google.ical.values.RRule;
import com.google.ical.values.Weekday; import com.google.ical.values.Weekday;
import com.google.ical.values.WeekdayNum; import com.google.ical.values.WeekdayNum;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.repeats.RepeatControlSet; import com.todoroo.astrid.repeats.RepeatControlSet;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.ArrayList; import java.util.ArrayList;
@ -68,6 +66,7 @@ import org.tasks.injection.InjectingDialogFragment;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.tasks.preferences.ResourceResolver; import org.tasks.preferences.ResourceResolver;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
import org.threeten.bp.format.FormatStyle;
import timber.log.Timber; import timber.log.Timber;
public class CustomRecurrenceDialog extends InjectingDialogFragment { public class CustomRecurrenceDialog extends InjectingDialogFragment {
@ -158,19 +157,6 @@ public class CustomRecurrenceDialog extends InjectingDialogFragment {
return dialog; return dialog;
} }
private static String getDisplayString(Context context, long repeatUntilValue) {
StringBuilder displayString = new StringBuilder();
DateTime d = newDateTime(repeatUntilValue);
if (d.getMillis() > 0) {
displayString.append(DateUtilities.getDateString(d));
if (Task.hasDueTime(repeatUntilValue)) {
displayString.append(", "); // $NON-NLS-1$ //$NON-NLS-2$
displayString.append(DateUtilities.getTimeString(context, repeatUntilValue));
}
}
return displayString.toString();
}
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
@ -532,7 +518,10 @@ public class CustomRecurrenceDialog extends InjectingDialogFragment {
int count = rrule.getCount(); int count = rrule.getCount();
if (repeatUntil > 0) { if (repeatUntil > 0) {
repeatUntilOptions.add( repeatUntilOptions.add(
getString(R.string.repeat_until, getDisplayString(context, repeatUntil))); getString(
R.string.repeat_until,
DateUtilities.getRelativeDateTime(
context, repeatUntil, locale.getLocale(), FormatStyle.MEDIUM)));
repeatTimes.setVisibility(View.GONE); repeatTimes.setVisibility(View.GONE);
repeatTimesText.setVisibility(View.GONE); repeatTimesText.setVisibility(View.GONE);
} else if (count > 0) { } else if (count > 0) {

@ -58,7 +58,7 @@ public class RepeatRuleToString {
R.string.repeats_single_on_until, R.string.repeats_single_on_until,
frequencyString, frequencyString,
dayString, dayString,
DateUtilities.getLongDateString(repeatUntil)); DateUtilities.getLongDateString(repeatUntil, locale.getLocale()));
} }
} else if (count > 0) { } else if (count > 0) {
return context.getString( return context.getString(
@ -69,7 +69,7 @@ public class RepeatRuleToString {
return context.getString( return context.getString(
R.string.repeats_single_until, R.string.repeats_single_until,
frequencyString, frequencyString,
DateUtilities.getLongDateString(repeatUntil)); DateUtilities.getLongDateString(repeatUntil, locale.getLocale()));
} }
} else { } else {
int plural = getFrequencyPlural(frequency); int plural = getFrequencyPlural(frequency);
@ -90,7 +90,7 @@ public class RepeatRuleToString {
R.string.repeats_plural_on_until, R.string.repeats_plural_on_until,
frequencyPlural, frequencyPlural,
dayString, dayString,
DateUtilities.getLongDateString(repeatUntil)); DateUtilities.getLongDateString(repeatUntil, locale.getLocale()));
} }
} else if (count > 0) { } else if (count > 0) {
return context.getString( return context.getString(
@ -101,7 +101,7 @@ public class RepeatRuleToString {
return context.getString( return context.getString(
R.string.repeats_plural_until, R.string.repeats_plural_until,
frequencyPlural, frequencyPlural,
DateUtilities.getLongDateString(repeatUntil)); DateUtilities.getLongDateString(repeatUntil, locale.getLocale()));
} }
} }
} }

@ -2,7 +2,7 @@ package org.tasks.tasklist;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastKitKat; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastKitKat;
import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop; import static com.todoroo.andlib.utility.AndroidUtilities.atLeastLollipop;
import static com.todoroo.andlib.utility.DateUtilities.getAbbreviatedRelativeDateWithTime; import static com.todoroo.andlib.utility.DateUtilities.getRelativeDateTime;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
@ -30,6 +30,7 @@ import org.tasks.dialogs.Linkify;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxProvider; import org.tasks.ui.CheckBoxProvider;
import org.tasks.ui.ChipProvider; import org.tasks.ui.ChipProvider;
import org.threeten.bp.format.FormatStyle;
public class ViewHolder extends RecyclerView.ViewHolder { public class ViewHolder extends RecyclerView.ViewHolder {
@ -253,7 +254,9 @@ public class ViewHolder extends RecyclerView.ViewHolder {
} else { } else {
dueDate.setTextColor(textColorSecondary); dueDate.setTextColor(textColorSecondary);
} }
String dateValue = getAbbreviatedRelativeDateWithTime(context, task.getDueDate()); String dateValue =
getRelativeDateTime(
context, task.getDueDate(), java.util.Locale.getDefault(), FormatStyle.MEDIUM);
dueDate.setText(dateValue); dueDate.setText(dateValue);
dueDate.setVisibility(View.VISIBLE); dueDate.setVisibility(View.VISIBLE);
} else { } else {

@ -18,6 +18,8 @@ import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalDateTime;
public class DateTime { public class DateTime {
@ -320,6 +322,14 @@ public class DateTime {
return timestamp == 0 ? null : new DateValueImpl(getYear(), getMonthOfYear(), getDayOfMonth()); return timestamp == 0 ? null : new DateValueImpl(getYear(), getMonthOfYear(), getDayOfMonth());
} }
public LocalDate toLocalDate() {
return timestamp == 0 ? null : LocalDate.of(getYear(), getMonthOfYear(), getDayOfMonth());
}
public LocalDateTime toLocalDateTime() {
return timestamp == 0 ? null : LocalDateTime.of(getYear(), getMonthOfYear(), getDayOfMonth(), getHourOfDay(), getMinuteOfHour());
}
public int getDayOfWeekInMonth() { public int getDayOfWeekInMonth() {
return getCalendar().get(Calendar.DAY_OF_WEEK_IN_MONTH); return getCalendar().get(Calendar.DAY_OF_WEEK_IN_MONTH);
} }

@ -38,6 +38,7 @@ import org.tasks.dialogs.MyDatePickerDialog;
import org.tasks.dialogs.MyTimePickerDialog; import org.tasks.dialogs.MyTimePickerDialog;
import org.tasks.injection.ForActivity; import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent; import org.tasks.injection.FragmentComponent;
import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime; import org.tasks.time.DateTime;
@ -54,6 +55,7 @@ public class DeadlineControlSet extends TaskEditControlFragment {
@Inject Preferences preferences; @Inject Preferences preferences;
@Inject @ForActivity Context context; @Inject @ForActivity Context context;
@Inject Locale locale;
@BindView(R.id.due_date) @BindView(R.id.due_date)
Spinner dueDateSpinner; Spinner dueDateSpinner;
@ -364,7 +366,7 @@ public class DeadlineControlSet extends TaskEditControlFragment {
} else if (date == today.plusWeeks(1).getMillis()) { } else if (date == today.plusWeeks(1).getMillis()) {
dueDateOptions.set(0, nextWeekString); dueDateOptions.set(0, nextWeekString);
} else { } else {
dueDateOptions.set(0, DateUtilities.getLongDateString(newDateTime(date))); dueDateOptions.set(0, DateUtilities.getLongDateString(newDateTime(date), locale.getLocale()));
} }
} }
dueDateOptions.set(3, nextWeekString); dueDateOptions.set(3, nextWeekString);

@ -28,6 +28,7 @@ import org.tasks.locale.Locale;
import org.tasks.preferences.DefaultFilterProvider; import org.tasks.preferences.DefaultFilterProvider;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxProvider; import org.tasks.ui.CheckBoxProvider;
import org.threeten.bp.format.FormatStyle;
import timber.log.Timber; import timber.log.Timber;
class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory { class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
@ -36,6 +37,7 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private final TaskDao taskDao; private final TaskDao taskDao;
private final DefaultFilterProvider defaultFilterProvider; private final DefaultFilterProvider defaultFilterProvider;
private final CheckBoxProvider checkBoxProvider; private final CheckBoxProvider checkBoxProvider;
private final Locale locale;
private final SubtasksHelper subtasksHelper; private final SubtasksHelper subtasksHelper;
private final Preferences preferences; private final Preferences preferences;
private final WidgetPreferences widgetPreferences; private final WidgetPreferences widgetPreferences;
@ -60,7 +62,8 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
int widgetId, int widgetId,
TaskDao taskDao, TaskDao taskDao,
DefaultFilterProvider defaultFilterProvider, DefaultFilterProvider defaultFilterProvider,
CheckBoxProvider checkBoxProvider) { CheckBoxProvider checkBoxProvider,
Locale locale) {
this.subtasksHelper = subtasksHelper; this.subtasksHelper = subtasksHelper;
this.preferences = preferences; this.preferences = preferences;
this.context = context; this.context = context;
@ -68,6 +71,7 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
this.taskDao = taskDao; this.taskDao = taskDao;
this.defaultFilterProvider = defaultFilterProvider; this.defaultFilterProvider = defaultFilterProvider;
this.checkBoxProvider = checkBoxProvider; this.checkBoxProvider = checkBoxProvider;
this.locale = locale;
widgetPreferences = new WidgetPreferences(context, preferences, widgetId); widgetPreferences = new WidgetPreferences(context, preferences, widgetId);
DisplayMetrics metrics = context.getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();
widgetPadding = (int)(10 * metrics.density); widgetPadding = (int)(10 * metrics.density);
@ -220,7 +224,8 @@ class ScrollableViewsFactory implements RemoteViewsService.RemoteViewsFactory {
row.setViewVisibility(R.id.widget_due_date, View.VISIBLE); row.setViewVisibility(R.id.widget_due_date, View.VISIBLE);
row.setTextViewText( row.setTextViewText(
R.id.widget_due_date, R.id.widget_due_date,
DateUtilities.getRelativeDateStringWithTime(context, task.getDueDate())); DateUtilities.getRelativeDateTime(
context, task.getDueDate(), locale.getLocale(), FormatStyle.MEDIUM));
//noinspection ResourceAsColor //noinspection ResourceAsColor
row.setTextColor( row.setTextColor(
R.id.widget_due_date, R.id.widget_due_date,

@ -57,6 +57,7 @@ public class ScrollableWidgetUpdateService extends RemoteViewsService {
widgetId, widgetId,
taskDao, taskDao,
defaultFilterProvider, defaultFilterProvider,
new CheckBoxProvider(context, new ColorProvider(context, preferences))); new CheckBoxProvider(context, new ColorProvider(context, preferences)),
locale);
} }
} }

@ -261,7 +261,6 @@
<string name="repeat_number_of_times">Повтаряй брой пъти</string> <string name="repeat_number_of_times">Повтаряй брой пъти</string>
<string name="repeat_occurs">Възниква</string> <string name="repeat_occurs">Възниква</string>
<string name="repeat_snackbar">%1$s е насрочено за %2$s</string> <string name="repeat_snackbar">%1$s е насрочено за %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s в %2$s</string>
<string name="new_tag">Нов таг</string> <string name="new_tag">Нов таг</string>
<string name="new_list">Създаване на нов списък</string> <string name="new_list">Създаване на нов списък</string>
<string name="tag_FEx_untagged">Без категория</string> <string name="tag_FEx_untagged">Без категория</string>

@ -368,7 +368,6 @@
<string name="repeat_type_completion">okamžiku dokočení</string> <string name="repeat_type_completion">okamžiku dokočení</string>
<string name="repeat_occurs">Vyskytne se</string> <string name="repeat_occurs">Vyskytne se</string>
<string name="repeat_snackbar">Úkol %1$s byl znovu naplánován na %2$s</string> <string name="repeat_snackbar">Úkol %1$s byl znovu naplánován na %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. 'Tomorrow at 8 AM' or '21 May at 8 AM'">%1$s na %2$s</string>
<string name="new_list">Vytvořit nový seznam</string> <string name="new_list">Vytvořit nový seznam</string>
<string name="EPr_voiceRemindersEnabled_desc_enabled">Aplikace bude nahlas číst název úkolu při upozornění</string> <string name="EPr_voiceRemindersEnabled_desc_enabled">Aplikace bude nahlas číst název úkolu při upozornění</string>
<string name="attachment_directory">Adresář příloh</string> <string name="attachment_directory">Adresář příloh</string>

@ -257,7 +257,6 @@
<string name="repeat_number_of_times">Wiederhole n-mal</string> <string name="repeat_number_of_times">Wiederhole n-mal</string>
<string name="repeat_occurs">Tritt ein</string> <string name="repeat_occurs">Tritt ein</string>
<string name="repeat_snackbar">%1$s neu geplant für %2$s</string> <string name="repeat_snackbar">%1$s neu geplant für %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s um %2$s</string>
<string name="new_tag">Neues Schlagwort erstellen</string> <string name="new_tag">Neues Schlagwort erstellen</string>
<string name="new_list">Neue Liste erstellen</string> <string name="new_list">Neue Liste erstellen</string>
<string name="tag_FEx_untagged">Nicht kategorisiert</string> <string name="tag_FEx_untagged">Nicht kategorisiert</string>

@ -262,7 +262,6 @@
<string name="repeat_number_of_times">Repetir un número de veces</string> <string name="repeat_number_of_times">Repetir un número de veces</string>
<string name="repeat_occurs">Número de repeticiones</string> <string name="repeat_occurs">Número de repeticiones</string>
<string name="repeat_snackbar">%1$s He reprogramado esta tarea recurrente para %2$s</string> <string name="repeat_snackbar">%1$s He reprogramado esta tarea recurrente para %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s a %2$s</string>
<string name="new_tag">Crear nueva etiqueta</string> <string name="new_tag">Crear nueva etiqueta</string>
<string name="new_list">Crea una nueva lista</string> <string name="new_list">Crea una nueva lista</string>
<string name="tag_FEx_untagged">Sin Categoría</string> <string name="tag_FEx_untagged">Sin Categoría</string>

@ -266,7 +266,6 @@
<string name="repeat_number_of_times">Errepikatu aldi kopuru bat</string> <string name="repeat_number_of_times">Errepikatu aldi kopuru bat</string>
<string name="repeat_occurs">Errepikapenak</string> <string name="repeat_occurs">Errepikapenak</string>
<string name="repeat_snackbar">"%1$s %2$s-rako programatuta"</string> <string name="repeat_snackbar">"%1$s %2$s-rako programatuta"</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. 'Tomorrow at 8 AM' or '21 May at 8 AM'">%1$s %2$s(e)tan</string>
<string name="new_tag">Sortu etiketa berria</string> <string name="new_tag">Sortu etiketa berria</string>
<string name="new_list">Sortu zerrenda berria</string> <string name="new_list">Sortu zerrenda berria</string>
<string name="tag_FEx_untagged">Kategoria gabe</string> <string name="tag_FEx_untagged">Kategoria gabe</string>

@ -247,7 +247,6 @@
<string name="repeat_number_of_times">Nombre de répétitions</string> <string name="repeat_number_of_times">Nombre de répétitions</string>
<string name="repeat_occurs">Se répète</string> <string name="repeat_occurs">Se répète</string>
<string name="repeat_snackbar">%1$s replanifiée à %2$s</string> <string name="repeat_snackbar">%1$s replanifiée à %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s à %2$s</string>
<string name="new_tag">Créer un nouveau tag</string> <string name="new_tag">Créer un nouveau tag</string>
<string name="new_list">Créer une nouvelle liste</string> <string name="new_list">Créer une nouvelle liste</string>
<string name="tag_FEx_untagged">Non classé</string> <string name="tag_FEx_untagged">Non classé</string>

@ -261,7 +261,6 @@
<string name="repeat_number_of_times">Ismétlés meghatározott alkalommal</string> <string name="repeat_number_of_times">Ismétlés meghatározott alkalommal</string>
<string name="repeat_occurs">Előfordul</string> <string name="repeat_occurs">Előfordul</string>
<string name="repeat_snackbar">%1$s újraütemezve ekkorra: %2$s</string> <string name="repeat_snackbar">%1$s újraütemezve ekkorra: %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s ekkor: %2$s</string>
<string name="new_tag">Új címke létrehozása</string> <string name="new_tag">Új címke létrehozása</string>
<string name="new_list">Új lista létrehozása</string> <string name="new_list">Új lista létrehozása</string>
<string name="tag_FEx_untagged">Kategória nélküli</string> <string name="tag_FEx_untagged">Kategória nélküli</string>

@ -259,7 +259,6 @@
<string name="repeat_number_of_times">Ripeti un dato numero di volte</string> <string name="repeat_number_of_times">Ripeti un dato numero di volte</string>
<string name="repeat_occurs">Si verifica</string> <string name="repeat_occurs">Si verifica</string>
<string name="repeat_snackbar">%1$s ripianificata per %2$s</string> <string name="repeat_snackbar">%1$s ripianificata per %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s alle %2$s</string>
<string name="new_tag">Crea nuova etichetta</string> <string name="new_tag">Crea nuova etichetta</string>
<string name="new_list">Crea nuova lista</string> <string name="new_list">Crea nuova lista</string>
<string name="tag_FEx_untagged">Non classificate</string> <string name="tag_FEx_untagged">Non classificate</string>

@ -294,7 +294,6 @@
<string name="repeat_number_of_times">חוזר מספר פעמים</string> <string name="repeat_number_of_times">חוזר מספר פעמים</string>
<string name="repeat_occurs">קורה</string> <string name="repeat_occurs">קורה</string>
<string name="repeat_snackbar">%1$s תזמן מחדש משימה חוזרת זו ל־%2$s</string> <string name="repeat_snackbar">%1$s תזמן מחדש משימה חוזרת זו ל־%2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s בשעה %2$s</string>
<string name="new_tag">יצירת תגית חדשה</string> <string name="new_tag">יצירת תגית חדשה</string>
<string name="new_list">יצירת רשימה חדשה</string> <string name="new_list">יצירת רשימה חדשה</string>
<string name="tag_FEx_untagged">ללא קיטלוג</string> <string name="tag_FEx_untagged">ללא קיטלוג</string>

@ -259,7 +259,6 @@
<string name="repeat_number_of_times">繰り返し回数</string> <string name="repeat_number_of_times">繰り返し回数</string>
<string name="repeat_occurs">発生</string> <string name="repeat_occurs">発生</string>
<string name="repeat_snackbar">%1$s を %2$s にスケジュール変更しました</string> <string name="repeat_snackbar">%1$s を %2$s にスケジュール変更しました</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s %2$s</string>
<string name="new_tag">新しいタグを作成</string> <string name="new_tag">新しいタグを作成</string>
<string name="new_list">新しいリストを作成</string> <string name="new_list">新しいリストを作成</string>
<string name="tag_FEx_untagged">未分類</string> <string name="tag_FEx_untagged">未分類</string>

@ -263,7 +263,6 @@
<string name="repeat_number_of_times">여러회 반복</string> <string name="repeat_number_of_times">여러회 반복</string>
<string name="repeat_occurs">반복횟수</string> <string name="repeat_occurs">반복횟수</string>
<string name="repeat_snackbar">%1$s 이 %2$s 로 변경되었습니다</string> <string name="repeat_snackbar">%1$s 이 %2$s 로 변경되었습니다</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s %2$s</string>
<string name="new_tag">새 태그 만들기</string> <string name="new_tag">새 태그 만들기</string>
<string name="new_list">새 목록 만들기</string> <string name="new_list">새 목록 만들기</string>
<string name="tag_FEx_untagged">미분류 할일</string> <string name="tag_FEx_untagged">미분류 할일</string>

@ -258,7 +258,6 @@
<string name="repeat_number_of_times">Kartoti nustatytą kiekį kartų</string> <string name="repeat_number_of_times">Kartoti nustatytą kiekį kartų</string>
<string name="repeat_occurs">Įvyksta</string> <string name="repeat_occurs">Įvyksta</string>
<string name="repeat_snackbar">%1$s perplanuotas šiai datai: %2$s</string> <string name="repeat_snackbar">%1$s perplanuotas šiai datai: %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s %2$s metu</string>
<string name="new_tag">Sukurti naują etiketę</string> <string name="new_tag">Sukurti naują etiketę</string>
<string name="new_list">Sukurti naują sąrašą</string> <string name="new_list">Sukurti naują sąrašą</string>
<string name="tag_FEx_untagged">Nekategorizuotos</string> <string name="tag_FEx_untagged">Nekategorizuotos</string>

@ -325,7 +325,6 @@
<string name="repeat_number_of_times">Gjenta et antall ganger</string> <string name="repeat_number_of_times">Gjenta et antall ganger</string>
<string name="repeat_occurs">Inntreffer</string> <string name="repeat_occurs">Inntreffer</string>
<string name="repeat_snackbar">%1$s flyttet til %2$s</string> <string name="repeat_snackbar">%1$s flyttet til %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. 'Tomorrow at 8 AM' or '21 May at 8 AM'">%1$s %2$s</string>
<string name="new_tag">Opprett en ny etikett</string> <string name="new_tag">Opprett en ny etikett</string>
<string name="new_list">Opprett en ny liste</string> <string name="new_list">Opprett en ny liste</string>
<string name="delete_tag_confirmation">Slett %s\?</string> <string name="delete_tag_confirmation">Slett %s\?</string>

@ -258,7 +258,6 @@
<string name="repeat_number_of_times">Herhaal een aantal keer</string> <string name="repeat_number_of_times">Herhaal een aantal keer</string>
<string name="repeat_occurs">Keren</string> <string name="repeat_occurs">Keren</string>
<string name="repeat_snackbar">%1$s opnieuw ingepland op %2$s</string> <string name="repeat_snackbar">%1$s opnieuw ingepland op %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s om %2$s</string>
<string name="new_tag">Nieuw label aanmaken</string> <string name="new_tag">Nieuw label aanmaken</string>
<string name="new_list">Nieuwe lijst aanmaken</string> <string name="new_list">Nieuwe lijst aanmaken</string>
<string name="tag_FEx_untagged">Niet gecategoriseerd</string> <string name="tag_FEx_untagged">Niet gecategoriseerd</string>

@ -272,7 +272,6 @@
<string name="repeat_number_of_times">Powtarzaj podaną ilość razy</string> <string name="repeat_number_of_times">Powtarzaj podaną ilość razy</string>
<string name="repeat_occurs">Występuje</string> <string name="repeat_occurs">Występuje</string>
<string name="repeat_snackbar">%1$s przełożone na %2$s</string> <string name="repeat_snackbar">%1$s przełożone na %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s o %2$s</string>
<string name="new_tag">Stwórz nowy tag</string> <string name="new_tag">Stwórz nowy tag</string>
<string name="new_list">Utwórz nową listę</string> <string name="new_list">Utwórz nową listę</string>
<string name="tag_FEx_untagged">Na żadnej liście</string> <string name="tag_FEx_untagged">Na żadnej liście</string>

@ -259,7 +259,6 @@
<string name="repeat_number_of_times">Repetir um número de vezes</string> <string name="repeat_number_of_times">Repetir um número de vezes</string>
<string name="repeat_occurs">Ocorre</string> <string name="repeat_occurs">Ocorre</string>
<string name="repeat_snackbar">%1$s remarcada para %2$s</string> <string name="repeat_snackbar">%1$s remarcada para %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s as %2$s</string>
<string name="new_tag">Criar nova etiqueta</string> <string name="new_tag">Criar nova etiqueta</string>
<string name="new_list">Criar nova lista</string> <string name="new_list">Criar nova lista</string>
<string name="tag_FEx_untagged">Sem categoria</string> <string name="tag_FEx_untagged">Sem categoria</string>

@ -278,7 +278,6 @@
<string name="repeat_number_of_times">Повторять несколько раз</string> <string name="repeat_number_of_times">Повторять несколько раз</string>
<string name="repeat_occurs">Повторять</string> <string name="repeat_occurs">Повторять</string>
<string name="repeat_snackbar">«%1$s» перенесено на %2$s</string> <string name="repeat_snackbar">«%1$s» перенесено на %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s в %2$s</string>
<string name="new_tag">Создать новый тег</string> <string name="new_tag">Создать новый тег</string>
<string name="new_list">Создать новый список</string> <string name="new_list">Создать новый список</string>
<string name="tag_FEx_untagged">Без тега</string> <string name="tag_FEx_untagged">Без тега</string>

@ -259,7 +259,6 @@
<string name="repeat_number_of_times">Opakovať určitý počet krát</string> <string name="repeat_number_of_times">Opakovať určitý počet krát</string>
<string name="repeat_occurs">Vyskytuje sa</string> <string name="repeat_occurs">Vyskytuje sa</string>
<string name="repeat_snackbar">%1$s preložené na %2$s</string> <string name="repeat_snackbar">%1$s preložené na %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s, %2$s</string>
<string name="new_tag">Vytvoriť nový štítok</string> <string name="new_tag">Vytvoriť nový štítok</string>
<string name="new_list">Vytvoriť nový zoznam</string> <string name="new_list">Vytvoriť nový zoznam</string>
<string name="tag_FEx_untagged">Nezaradené</string> <string name="tag_FEx_untagged">Nezaradené</string>

@ -367,7 +367,6 @@
<string name="repeat_type_completion">datum färdigställt</string> <string name="repeat_type_completion">datum färdigställt</string>
<string name="repeat_number_of_times">Repetera ett visst antal gånger</string> <string name="repeat_number_of_times">Repetera ett visst antal gånger</string>
<string name="repeat_occurs">Inträffar</string> <string name="repeat_occurs">Inträffar</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. 'Tomorrow at 8 AM' or '21 May at 8 AM'">%1$s vid %2$s</string>
<string name="TEA_timer_controls">Timer</string> <string name="TEA_timer_controls">Timer</string>
<string name="google_drive_backup">Kopiera till Google Drive</string> <string name="google_drive_backup">Kopiera till Google Drive</string>
<string name="row_spacing">Radavstånd</string> <string name="row_spacing">Radavstånd</string>

@ -264,7 +264,6 @@
<string name="repeat_number_of_times">Birkaç kez yinele</string> <string name="repeat_number_of_times">Birkaç kez yinele</string>
<string name="repeat_occurs">Şu kadar gerçekleşir</string> <string name="repeat_occurs">Şu kadar gerçekleşir</string>
<string name="repeat_snackbar">%1$s, %2$s için yeniden zamanlandı</string> <string name="repeat_snackbar">%1$s, %2$s için yeniden zamanlandı</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s saat %2$s</string>
<string name="new_tag">Yeni etiket oluştur</string> <string name="new_tag">Yeni etiket oluştur</string>
<string name="new_list">Yeni liste oluştur</string> <string name="new_list">Yeni liste oluştur</string>
<string name="tag_FEx_untagged">Kategorilendirilmemiş</string> <string name="tag_FEx_untagged">Kategorilendirilmemiş</string>

@ -261,7 +261,6 @@
<string name="repeat_number_of_times">Повторити кількість разів</string> <string name="repeat_number_of_times">Повторити кількість разів</string>
<string name="repeat_occurs">Станеться</string> <string name="repeat_occurs">Станеться</string>
<string name="repeat_snackbar">%1$s перенесено на %2$s</string> <string name="repeat_snackbar">%1$s перенесено на %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s о %2$s</string>
<string name="new_tag">Створити новий ярлик</string> <string name="new_tag">Створити новий ярлик</string>
<string name="new_list">Новий список</string> <string name="new_list">Новий список</string>
<string name="tag_FEx_untagged">Без категорії</string> <string name="tag_FEx_untagged">Без категорії</string>

@ -248,7 +248,6 @@
<string name="repeat_number_of_times">重复多次</string> <string name="repeat_number_of_times">重复多次</string>
<string name="repeat_occurs">发生</string> <string name="repeat_occurs">发生</string>
<string name="repeat_snackbar">%1$s 重新安排在 %2$s</string> <string name="repeat_snackbar">%1$s 重新安排在 %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. \'Tomorrow at 8 AM\' or \'21 May at 8 AM\'">%1$s 的 %2$s</string>
<string name="new_tag">新建标签</string> <string name="new_tag">新建标签</string>
<string name="new_list">新建列表</string> <string name="new_list">新建列表</string>
<string name="tag_FEx_untagged">未分类</string> <string name="tag_FEx_untagged">未分类</string>

@ -275,7 +275,6 @@ File %1$s contained %2$s.\n\n
<string name="repeat_number_of_times">Repeat a number of times</string> <string name="repeat_number_of_times">Repeat a number of times</string>
<string name="repeat_occurs">Occurs</string> <string name="repeat_occurs">Occurs</string>
<string name="repeat_snackbar">%1$s rescheduled for %2$s</string> <string name="repeat_snackbar">%1$s rescheduled for %2$s</string>
<string name="repeat_snackbar_time" comment="display new due date and time in snackbar after completing repeating task, e.g. 'Tomorrow at 8 AM' or '21 May at 8 AM'">%1$s at %2$s</string>
<string name="new_tag">Create new tag</string> <string name="new_tag">Create new tag</string>
<string name="new_list">Create new list</string> <string name="new_list">Create new list</string>
<string name="tag_FEx_untagged">Uncategorized</string> <string name="tag_FEx_untagged">Uncategorized</string>

Loading…
Cancel
Save