检查时间戳(milli)是否属于特定日期

时间:2014-07-06 05:37:36

标签: java android sql sqlite datetime

在我的SQLite数据库的表中,有一个时间戳(millisec)列。这个表非常庞大,我想要一种高效的方式来查询there timestamp列引用特定日期的所有行(在这种情况下,我想获得属于TODAY的所有enteries)。

我想出了这个,但它不起作用而且速度很慢。任何想法都会得到满足。 (不,我不能在数据库中进行任何更改,例如添加DATETIME列......我必须使用时间戳进行更改)。

public boolean isTimestampInToday(long timestamp){

    //get a timestamp from today
    long todayStamp = System.currentTimeMillis();

    Calendar c = Calendar.getInstance();
    c.setTimeInMillis(todayStamp);
    c.setTimeZone(TimeZone.getDefault());

    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);

    Date startDate = c.getTime(); //START OF DAY

    c.set(Calendar.HOUR_OF_DAY, 23);
    c.set(Calendar.MINUTE, 59);
    c.set(Calendar.SECOND, 59);
    c.set(Calendar.MILLISECOND, 999);

    Date endDate = c.getTime(); //END OF DAY

    Long startStamp = startDate.getTime();
    Long endStamp = endDate.getTime();

    if(timestamp >= startStamp && timestamp < endStamp) {
        return true;            
    } else {
        return false;
    }      
}

我从expirienced家伙那里得到了所有答案,但说实话,我能理解的是来自@iaune的答案。现在我在查询本身内完成了这个:

Calendar gc = new GregorianCalendar();
gc.setTimeZone(TimeZone.getDefault());
long toBeg = gc.getTimeInMillis();
toBeg -= toBeg % (24*60*60*1000);
long toEnd = toBeg + 24*60*60*1000;


Cursor cursor = mDb.rawQuery(
        "SELECT * FROM "
                + DbHelper.TABLE_PA_DATA
                + " WHERE "
                + DbHelper.COLUMN_TIMESTAMP
                + " BETWEEN "
                + toBeg
                + " AND "
                + toEnd
        , null);

这种方法有什么问题,还是可以进一步改进?

更新

我意识到使用INTEGER作为时间戳列类型是错误的。我添加了一个DATETIME列...我现在应该使用直接的SQLite查询函数。任何想法?

这是我转储桌子时得到的结果:

ID  DATETIME                    TIMESTAMP      STEPS    CALORIES
1   2014-07-06 07:18:55         169629539           3   0
2   2014-07-06 07:19:10         169644509           4   0
3   2014-07-06 08:15:36         173030229           1   0
4   2014-07-06 08:16:04         173058397           4   0
5   2014-07-06 08:31:39         173993598           2   0
6   2014-07-06 08:33:20         174094714           5   0
7   2014-07-06 09:31:54         177609142           1   0
8   2014-07-06 09:42:24         178238517           1   0
9   2014-07-06 10:37:37         181551849           1   0
10  2014-07-06 11:00:31         182925950           1   0
11  2014-07-06 12:17:42         187557035           1   0
12  2014-07-06 14:14:39         194573993           2   0

好的,我设法在@laune解决方案中添加了一些内容:

Calendar cal = Calendar.getInstance();
// offset to add since we're not UTC
long offset = cal.get(Calendar.ZONE_OFFSET) +
        cal.get(Calendar.DST_OFFSET);
long toBeg = cal.getTimeInMillis();
toBeg -= (toBeg + offset) % (24*60*60*1000);
long toEnd = (toBeg) + 24*60*60*1000;

使用此查询:

Cursor cursor = mDb.rawQuery(
        "SELECT * FROM "
                + Database.TABLE_PA_DATA
                + " WHERE "
                + Database.COLUMN_TIMESTAMP
                + " BETWEEN "
                + toBeg
                + " AND "
                + toEnd, null
);

6 个答案:

答案 0 :(得分:3)

使用PreparedStatement选择您的数据,并使用包含

where子句
date_column between ? and ?

一旦您计算了startDateendDate - 您上面的方式完全没问题 - 将它们设置为PreparedStatement中的参数并运行它。

答案 1 :(得分:2)

我会像这样使用SimpleDateFormatnew Date(long)

// four digit year, two digit month, two digit day. For example, 20140706
private final DateFormat sdf = new SimpleDateFormat("yyyyMMdd");
public boolean isTimestampInToday(final long timestamp){
  return sdf.format(new Date(timestamp)).equals(sdf.format(new Date()));
}

答案 2 :(得分:2)

半开

使用“半开”方法可以更好地完成日期时间比较,其中一段时间​​的开始是包容性的,结尾是独占的。对于一天来说,这意味着您希望查询日期时间大于或等于当天的第一时刻,而不是一天中的第一时刻。也就是说,最后一点不包括第二天。这种方法避免了在一天的最后时刻无限分裂的问题。

约达时间

java,util.Date和.Calendar类非常麻烦。避免他们。请改用Joda-Time库。它适用于Android。

时区

时区至关重要。一天的定义取决于时区。

指定所需的时区,而不是依赖隐式默认值。使用proper time zone names,而不是3或4个字母代码。

如果你真的想使用JVM的默认时区,我建议显式调用getDefault而不是依赖隐式默认值。这种依赖往往会导致日期工作混乱。

示例代码

DateTimeZone timeZone = DateTimeZone.getDefault();  // Or DateTimeZone.forID( "Europe/Amsterdam" )
DateTime now = DateTime.now( timeZone );
DateTime todayStartOfDay = now.withTimeAtStartOfDay():
DateTime tomorrowStartOfDay = today.plusDays( 1 ).withTimeAtStartOfDay();

从那里你可以调用getMillis得到一个long,你可以用它为你的SQL查询构造一个java.sql.Timestamp对象。

SQL逻辑

对于Half-Open方法,我们不能使用SQL命令BETWEEN。两个比较器都包含该命令。

相反,我们需要编写两个比较器,就像这个伪代码一样。注意>=<(没有等号)。

SELECT * 
FROM some_table_ 
WHERE target >= todayStartOfDay.getMillis() 
AND target < tomorrowStartOfDay.getMillis();

答案 3 :(得分:1)

简单的算术运算可以避免昂贵的字符串和格式化操作。不确定大卫是否意味着我的建议,但也许代码表达得更清楚:

Calendar gc = new GregorianCalendar();
long toBeg = gc.getTimeInMillis();
toBeg -= toBeg % (24*60*60*1000);
long toEnd = toBeg + 24*60*60*1000;

long now = gc.getTimeInMillis();
if( toBeg <= now && now < toEnd ){
    System.out.println( "today" );
}

根据if,可以使用预先计算的toBeg和toEnd值来运行查询。 (确切地说,请注意使用<=<。)

答案 4 :(得分:1)

我会这样做

boolean isTimestampInToday(long timestamp) {
    Calendar c1 =  Calendar.getInstance();
    Calendar c2 =  Calendar.getInstance();
    c2.setTimeInMillis(timestamp);
    return c1.get(Calendar.DATE) == c2.get(Calendar.DATE);
}

答案 5 :(得分:0)

DateUtils.isToday(timeStampInMilli)

您可以使用此方法检查时间戳记是否为今天。