actually use index added/lastUpdated dates in UTC
The date/time written to index.xml and index-v1.json should always be in UTC format. These formats are often in the form of just a date, e.g. 2019-04-28. Those are then converted to UNIX seconds, which includes the time. In the date only case, the time is assumed to be 00:00, which will be different per time zone. index-v1.json is better since it mostly uses Java-style UNIX time in millis but the dates/times are parsed then stored in the local database in the old format yyyy-MM-dd_HH:mm:ss which will result in different UNIX times when the device is in different time zones. fdroid/fdroidclient#1757
This commit is contained in:
parent
c0c5721f6a
commit
1d359f82ce
@ -79,6 +79,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -96,6 +97,8 @@ public final class Utils {
|
||||
private static final SimpleDateFormat TIME_FORMAT =
|
||||
new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.ENGLISH);
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("Etc/GMT");
|
||||
|
||||
private static final String[] FRIENDLY_SIZE_FORMAT = {
|
||||
"%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB",
|
||||
};
|
||||
@ -583,6 +586,7 @@ public final class Utils {
|
||||
}
|
||||
Date result;
|
||||
try {
|
||||
format.setTimeZone(UTC);
|
||||
result = format.parse(str);
|
||||
} catch (ArrayIndexOutOfBoundsException | NumberFormatException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
@ -595,21 +599,34 @@ public final class Utils {
|
||||
if (date == null) {
|
||||
return fallback;
|
||||
}
|
||||
format.setTimeZone(UTC);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date string into UTC time
|
||||
*/
|
||||
public static Date parseDate(String str, Date fallback) {
|
||||
return parseDateFormat(DATE_FORMAT, str, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats UTC time into a date string
|
||||
*/
|
||||
public static String formatDate(Date date, String fallback) {
|
||||
return formatDateFormat(DATE_FORMAT, date, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date/time string into UTC time
|
||||
*/
|
||||
public static Date parseTime(String str, Date fallback) {
|
||||
return parseDateFormat(TIME_FORMAT, str, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats UTC time into a date/time string
|
||||
*/
|
||||
public static String formatTime(Date date, String fallback) {
|
||||
return formatDateFormat(TIME_FORMAT, date, fallback);
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -192,4 +194,25 @@ public class UtilsTest {
|
||||
}
|
||||
// TODO write tests that work with a Certificate
|
||||
|
||||
@Test
|
||||
public void testIndexDatesWithTimeZones() {
|
||||
for (int h = 0; h < 12; h++) {
|
||||
for (int m = 0; m < 60; m = m + 15) {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m)));
|
||||
|
||||
String timeString = "2017-11-27_20:13:24";
|
||||
Date time = Utils.parseTime(timeString, null);
|
||||
assertEquals("The String representation must match", timeString, Utils.formatTime(time, null));
|
||||
assertEquals(timeString + " failed to parse", 1511813604000L, time.getTime());
|
||||
assertEquals("time zones should match", -((h * 60) + m), time.getTimezoneOffset());
|
||||
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m)));
|
||||
String dateString = "2017-11-27";
|
||||
Date date = Utils.parseDate(dateString, null);
|
||||
assertEquals("The String representation must match", dateString, Utils.formatDate(date, null));
|
||||
assertEquals(dateString + " failed to parse", 1511740800000L, date.getTime());
|
||||
assertEquals("time zones should match", -((h * 60) + m), date.getTimezoneOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,12 @@ import android.net.Uri;
|
||||
import org.fdroid.fdroid.Assert;
|
||||
import org.fdroid.fdroid.BuildConfig;
|
||||
import org.fdroid.fdroid.TestUtils;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
|
||||
import org.fdroid.fdroid.data.Schema.RepoTable;
|
||||
import org.fdroid.fdroid.mock.MockApk;
|
||||
import org.fdroid.fdroid.mock.MockRepo;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@ -18,6 +20,7 @@ import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.fdroid.fdroid.Assert.assertCantDelete;
|
||||
import static org.fdroid.fdroid.Assert.assertResultCount;
|
||||
@ -34,6 +37,13 @@ public class ApkProviderTest extends FDroidProviderTest {
|
||||
|
||||
private static final String[] PROJ = Cols.ALL;
|
||||
|
||||
@BeforeClass
|
||||
public static void setRandomTimeZone() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
|
||||
System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
|
||||
System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppApks() {
|
||||
App fdroidApp = insertApp(context, "org.fdroid.fdroid", "F-Droid");
|
||||
@ -317,10 +327,11 @@ public class ApkProviderTest extends FDroidProviderTest {
|
||||
|
||||
apk.antiFeatures = new String[]{"KnownVuln", "Other anti feature"};
|
||||
apk.features = new String[]{"one", "two", "three"};
|
||||
long dateTimestamp = System.currentTimeMillis();
|
||||
apk.added = new Date(dateTimestamp);
|
||||
apk.hashType = "i'm a hash type";
|
||||
|
||||
Date testTime = Utils.parseDate(Utils.formatTime(new Date(System.currentTimeMillis()), null), null);
|
||||
apk.added = testTime;
|
||||
|
||||
ApkProvider.Helper.update(context, apk);
|
||||
|
||||
// Should not have inserted anything else, just updated the already existing apk.
|
||||
@ -340,9 +351,10 @@ public class ApkProviderTest extends FDroidProviderTest {
|
||||
|
||||
assertArrayEquals(new String[]{"KnownVuln", "Other anti feature"}, updatedApk.antiFeatures);
|
||||
assertArrayEquals(new String[]{"one", "two", "three"}, updatedApk.features);
|
||||
assertEquals(new Date(dateTimestamp).getYear(), updatedApk.added.getYear());
|
||||
assertEquals(new Date(dateTimestamp).getMonth(), updatedApk.added.getMonth());
|
||||
assertEquals(new Date(dateTimestamp).getDay(), updatedApk.added.getDay());
|
||||
assertEquals(testTime.getYear(), updatedApk.added.getYear());
|
||||
assertEquals(testTime.getYear(), updatedApk.added.getYear());
|
||||
assertEquals(testTime.getMonth(), updatedApk.added.getMonth());
|
||||
assertEquals(testTime.getDay(), updatedApk.added.getDay());
|
||||
assertEquals("i'm a hash type", updatedApk.hashType);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.TestUtils;
|
||||
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@ -19,6 +20,7 @@ import org.robolectric.shadows.ShadowContentResolver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.fdroid.fdroid.Assert.assertContainsOnly;
|
||||
import static org.fdroid.fdroid.Assert.assertResultCount;
|
||||
@ -36,6 +38,13 @@ public class AppProviderTest extends FDroidProviderTest {
|
||||
|
||||
private static final String[] PROJ = Cols.ALL;
|
||||
|
||||
@BeforeClass
|
||||
public static void setRandomTimeZone() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
|
||||
System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
|
||||
System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
|
||||
|
@ -30,6 +30,7 @@ import org.fdroid.fdroid.BuildConfig;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Schema.RepoTable;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@ -37,6 +38,7 @@ import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@ -48,6 +50,16 @@ public class RepoProviderTest extends FDroidProviderTest {
|
||||
|
||||
private static final String[] COLS = RepoTable.Cols.ALL;
|
||||
|
||||
/**
|
||||
* Set to random time zone to make sure that the dates are properly parsed.
|
||||
*/
|
||||
@BeforeClass
|
||||
public static void setRandomTimeZone() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
|
||||
System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
|
||||
System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countEnabledRepos() {
|
||||
|
||||
|
@ -29,6 +29,7 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.BuildConfig;
|
||||
import org.fdroid.fdroid.mock.MockRepo;
|
||||
import org.fdroid.fdroid.mock.RepoDetails;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@ -48,6 +49,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@ -63,6 +65,16 @@ public class RepoXMLHandlerTest {
|
||||
|
||||
private static final String FAKE_SIGNING_CERT = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"; // NOCHECKSTYLE LineLength
|
||||
|
||||
/**
|
||||
* Set to random time zone to make sure that the dates are properly parsed.
|
||||
*/
|
||||
@BeforeClass
|
||||
public static void setRandomTimeZone() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
|
||||
System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
|
||||
System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtendedPerms() throws IOException {
|
||||
Repo expectedRepo = new Repo();
|
||||
@ -129,6 +141,12 @@ public class RepoXMLHandlerTest {
|
||||
"org.gege.caldavsyncadapter",
|
||||
"info.guardianproject.checkey",
|
||||
});
|
||||
for (App app : actualDetails.apps) {
|
||||
if ("org.mozilla.firefox".equals(app.packageName)) {
|
||||
assertEquals(1411776000000L, app.added.getTime());
|
||||
assertEquals(1411862400000L, app.lastUpdated.getTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user