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;
 | 
			
		||||
@ -77,9 +79,9 @@ public class UtilsTest {
 | 
			
		||||
        assertEquals("three", tripleValue[2]);
 | 
			
		||||
 | 
			
		||||
        assertNull(Utils.serializeCommaSeparatedString(null));
 | 
			
		||||
        assertNull(Utils.serializeCommaSeparatedString(new String[] {}));
 | 
			
		||||
        assertEquals("Single", Utils.serializeCommaSeparatedString(new String[] {"Single"}));
 | 
			
		||||
        assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[] {"One", "TWO", "three"}));
 | 
			
		||||
        assertNull(Utils.serializeCommaSeparatedString(new String[]{}));
 | 
			
		||||
        assertEquals("Single", Utils.serializeCommaSeparatedString(new String[]{"Single"}));
 | 
			
		||||
        assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[]{"One", "TWO", "three"}));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@ -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");
 | 
			
		||||
@ -153,7 +163,7 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCount() {
 | 
			
		||||
        String[] projectionCount = new String[] {Cols._COUNT};
 | 
			
		||||
        String[] projectionCount = new String[]{Cols._COUNT};
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < 13; i++) {
 | 
			
		||||
            Assert.insertApk(context, "com.example", i);
 | 
			
		||||
@ -315,12 +325,13 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
			
		||||
        assertNull(apk.added);
 | 
			
		||||
        assertNull(apk.hashType);
 | 
			
		||||
 | 
			
		||||
        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.antiFeatures = new String[]{"KnownVuln", "Other anti feature"};
 | 
			
		||||
        apk.features = new String[]{"one", "two", "three"};
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -381,8 +393,8 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
			
		||||
        assertEquals("a hash type", apk.hashType);
 | 
			
		||||
 | 
			
		||||
        String[] projection = {
 | 
			
		||||
            Cols.Package.PACKAGE_NAME,
 | 
			
		||||
            Cols.HASH,
 | 
			
		||||
                Cols.Package.PACKAGE_NAME,
 | 
			
		||||
                Cols.HASH,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection);
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -897,7 +915,7 @@ public class RepoXMLHandlerTest {
 | 
			
		||||
        List<App> apps = actualDetails.apps;
 | 
			
		||||
        assertNotNull(apps);
 | 
			
		||||
        assertEquals(apps.size(), appCount);
 | 
			
		||||
        for (App app: apps) {
 | 
			
		||||
        for (App app : apps) {
 | 
			
		||||
            assertTrue("Added should have been set", app.added.getTime() > 0);
 | 
			
		||||
            assertTrue("Last Updated should have been set", app.lastUpdated.getTime() > 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user