【问题标题】:How to compare two dates using Android Room?如何使用 Android Room 比较两个日期?
【发布时间】:2022-02-04 02:51:57
【问题描述】:

总结

我正在制作一个 Android 应用程序,并且我有一个名为 Objective 的 Room 实体,其中存储了它的 startDateendDate
我想进行一个查询,在其中可以检索“活动”目标,或者换句话说,已经开始但尚未结束的目标。

这是我目前得到的:

查询

@Dao
public interface ObjectiveDao {
    /* some code goes here... */

    /*
       For an objective to be active, its start date should be before the current day,
       and its end date should be after the current day.
    */
    @Query("SELECT * FROM objectives " +
            "WHERE end_date >= strftime('%s', 'now') + 0 " +
            "AND start_date <= strftime('%s', 'now') + 0")
    List<Objective> getActive();

    /* rest of the code goes here... */
}

问题

我的问题在于,当我调用此方法时,会返回一个空列表,而不是预期的活动目标列表 (至少在我尝试运行的各种测试中都发生了这种情况)。

(请参阅底部的编辑以了解此问题出了什么问题。)


额外信息

我还将包括我的EntityTypeConverterRoomDatabase 和测试代码,以防万一它可以帮助您解决任何问题。

实体

@Entity(tableName = "objectives")
public class Objective {
    @PrimaryKey(autoGenerate = true)
    private int id;
    
    /* 
        I feel like I don't need to 
        include the whole thing, just the part that matters.
    */
    
    @ColumnInfo(name = "start_date")
    private LocalDate startDate;

    @ColumnInfo(name = "end_date")
    private LocalDate endDate;

    public Objective() {
        this("", ObjectiveType.OBJECTIVE_DO, 0, LocalDate.now(), LocalDate.now().plusDays(1));
    }

    public Objective(String title, ObjectiveType type, int quantity, LocalDate startDate, LocalDate endDate) {
        this.title = title;
        this.type = type;
        this.quantity = quantity;
        this.startDate = startDate;
        this.endDate = endDate;
        id = 0;
    }

    /* rest of the code goes here...*/
}

类型转换器

public class Converters {
    @TypeConverter
    public static LocalDate fromTimestamp(Long value) {
        return value == null ? null : LocalDate.ofEpochDay(value);
    }

    @TypeConverter
    public static Long localDateToTimestamp(LocalDate date) {
        return date == null ? null : date.toEpochDay();
    }
    /* rest of the code goes here...*/
}

房间数据库

@Database(entities = {Objective.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    public abstract ObjectiveDao getObjectiveDao();
}

仪器测试

class TestUtil {
    static Objective createObjective() {
        Objective objective = new Objective();
        objective.setTitle("Test Objective");
        objective.setType(ObjectiveType.OBJECTIVE_DO);
        objective.setQuantity(10);
        objective.setStartDate(LocalDate.now());
        objective.setEndDate(LocalDate.now().plusDays(1));

        return objective;
    }
}

public class ObjectivesInstrumentedTest {
    private ObjectiveDao objectiveDao;
    private AppDatabase db;

    @Before
    public void createDb() {
        Context context = ApplicationProvider.getApplicationContext();
        db = Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build();
        objectiveDao = db.getObjectiveDao();
    }

    @After
    public void closeDb() throws IOException {
        db.close();
    }

    @Test
    public void objectiveIsInsertedIntoDatabase() {
        Objective objective = TestUtil.createObjective();
        /*
            id is set to 1 because, otherwise, Room automatically sets the ID to 1,
            and when the objects are compared they do not match. (a value of 0 counts as null).
        */
        objective.setId(1);
        objectiveDao.insert(objective);
        List<Objective> objectives = objectiveDao.getAll();
        assertThat(objectives.get(0), equalTo(objective));
    }

    @Test
    public void objectiveIsActive() {
        Objective objective = TestUtil.createObjective();
        objective.setId(1);
        objectiveDao.insert(objective);
        List<Objective> objectives = objectiveDao.getActive();
        assertThat(objectives.get(0), equalTo(objective));
    }
}

如果您想知道,第一个测试运行得很好,但是在运行第二个测试时我得到一个IndexOutOfBoundsException

希望您能帮我解决这个问题,并提前感谢您抽出时间阅读本文并考虑任何可能的解决方案。

编辑

正如@MikeT 指出的那样,我不是将这些日期存储为秒,也不是作为 unix 时间戳,而是作为自 1970-01-01 以来经过的天数。

【问题讨论】:

  • 表格中日期的格式是什么?
  • @forpas 日期存储为 Longs,(如果我没记错的话,技术上是 8 字节长整数),因为 Room 不允许存储“复杂数据”(换句话说,它 doesn't support object references .

标签: java android sqlite android-room


【解决方案1】:

Strftime 的%s 以秒为单位返回日期时间,ofEpochDay 返回自 1970-01-01 以来的天数。所以你需要同样比较。

我相信你也许可以使用:-

@Query("SELECT * FROM objectives " +
            "WHERE end_date >= CAST(strftime('%J','now') - strftime('%J','1970-01-01') AS INTEGER) " +
            "AND start_date <= CAST(strftime('%J','now') - strftime('%J','1970-01-01') AS INTEGER);")

基于strftimeLocalDate,但我不确定是否存在差异。我建议不要以这种尴尬的格式存储数据,并将其存储为 SQLite 识别为日期时间的格式(根据下面的链接)

【讨论】:

  • 非常感谢!解决方案现在看起来很明显,感谢您解释我做错了什么。正如您所说,我尝试以更简单的格式存储数据,但除非我错过了一些我相信Room doesn't support object references 的东西,所以我唯一的选择是存储原始数据类型。
  • 通过更简单的格式,我的意思是长或字符串,但不是天数,而是日期时间(unix 时间戳(可选除以 1000 以减少毫秒))或字符串,例如 YYYY-MM- DD(有或没有时间 HH:MM:SS)。原因是您不必在日期时间基准之间进行转换/计算(尴尬),这可能会由于调整方式而导致差异,例如实施闰年/秒。然而,你显然是最重要的。
猜你喜欢
  • 1970-01-01
  • 2016-06-25
  • 1970-01-01
  • 2011-01-13
  • 2020-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多