如何检查日期数组是否从今天开始连续?
How to check array of dates are consecutive from todays date?
我有一组用户每次完成任务时的唯一日期。我想检查数组中的日期是否与今天的日期连续并包括今天的日期。
如果数组包含日期:"2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5"
那么根据今天的日期 2017/6/5
函数将 return 4
因为有 4 个连续的日期从今天开始(包括今天) .
如果数组包含日期 "2017/6/2, 2017/6/3, 2017/6/4"
那么它将 return 0
因为数组不包含今天的日期。否则计数将在非连续日期中断。
List<Date> dateList = new ArrayList<Date>();
int count = 0;
Date todayDate = new Date();
for (int i=0; i<dateList.size(); i++){
// Check if dates within the array are consecutive from todayDate, if so then increment count by 1.
}
有很多方法可以写的更清楚:
- 使用新日期API;
- 使用图书馆;
但是,在这种情况下,使用旧日期 类,我会这样做:
public static void main(String[] args) {
long millisInDay = TimeUnit.DAYS.toMillis(1);
List<Date> dates = Arrays.asList(new Date("2017/6/2"), new Date("2017/6/3"), new Date("2017/6/4"), new Date("2017/6/5"));
System.out.println(getSequentialNumber(millisInDay, dates));
}
private static int getSequentialNumber(long millisInDay, List<Date> dates) {
int count = 0;
Date now = setMidnight(Calendar.getInstance().getTime());
for (int i = dates.size() - 1; i >= 0; i--) {
Date date = setMidnight(dates.get(i));
if (date.getTime() == now.getTime()) {
count++;
}
now.setTime(now.getTime() - millisInDay);
}
return count;
}
private static Date setMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
return calendar.getTime();
}
如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.
如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).
虽然您也可以使用 JodaTime,但它已经停产并被新的 API 取代,我不建议您使用 joda 开始一个新项目。即使在 joda's website 它说:"Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
因为你只想比较日期 (day/month/year),而不是时间 (hour/minute/second),最好的选择是使用 LocalDate
class .对于 java 8,这个 class 在 java.time
包中,而在 ThreeTen Backport 中,包在 org.threeten.bp
中。但是 classes 和方法名称是相同的。
代码应该是这样的:
public int count(List<LocalDate> dateList, LocalDate today) {
if (!dateList.contains(today)) { // today is not in the list, return 0
return 0;
}
int count = 0;
LocalDate prev = dateList.get(0); // get first date from list
for (int i = 1; i < dateList.size(); i++) {
LocalDate next = dateList.get(i);
if (prev.plusDays(1).equals(next)) {
// difference between dates is one day
count++;
} else {
// difference between dates is not 1
// Do what? return 0? throw exception?
}
prev = next;
}
return count + 1; // didn't count the first element, adding 1
}
测试此方法:
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
dateList.add(LocalDate.of(2017, 6, 5));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 4
另一个测试(当今天不在列表中时)
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 0
备注:
- 由于没有指定当日子不连续时要做什么(return
0
或抛出异常),我将这部分留在评论中。但是将其添加到代码中应该很简单
如果你想把java.util.Date
转换成LocalDate
,你可以按照下面的方式做(使用代码of this answer,完整的解释在这个link 如果您有任何问题):
public LocalDate convert(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
// if your Date has no toInstant method, try this:
public LocalDate convert(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
我了解到您想检查 连续 天(因此,日期之间相差 1 天)。但是如果你想检查前一个日期是否在之前下一个(无论多少天),你可以将if (prev.plusDays(1).equals(next))
更改为if (prev.isBefore(next))
我不确定是不是这样,但如果你愿意,你也可以将 String
直接解析为 LocalDate
(所以你不需要创建大量 Date
个对象),使用 DateTimeFormatter
:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d");
LocalDate d = LocalDate.parse("2017/6/2", formatter); // 2017-06-02
如果我对要求的理解正确,你有一个 Date
对象数组,按日期排序,并保证同一天不会有两个 Date
对象,但可能有间隔那些日子。您的目标是 return 仅包含连续天数且还包括当前日期的最大子数组的长度,或者如果没有这样的子数组,则 return 0 。当前日期可能位于该子数组内的任何位置,不一定在开头或结尾。
不清楚您是否需要支持跨越年份界限,但我假设是这样。我还假设列表中的所有 Date
对象都针对同一时区,这也是您所在设备的时区 运行。如果不是这种情况,您应该参考 this answer 以获取有关测试两个 Date
对象是否指同一天的更多信息。
如果您使用 Calendar
个对象而不是 Date
个对象,则执行此操作相当简单。您不需要任何第三方库,因为 Date
和 Calendar
都是标准 Android API 的一部分。我建议分两个阶段执行此操作:首先在数组中搜索当前日期,然后在两个方向上扫描日期中的间隙或数组边界。然后数一数你在每个方向上能走多远。
public int getDateSpanCount(List<Date> dateList) {
final int n = dateList.size();
final Calendar today = Calendar.getInstance();
final Calendar other = Calendar.getInstance();
int count = 0;
// First search for today in the date array
int posToday = -1;
for (int i=0; i<n; i++) {
other.setTime(dateList.get(i));
if (areSameDay(today, other)) {
posToday = i;
break;
}
}
// If today is in the list, count the size of the sub-array containing today
if (posToday >= 0) {
count++; // count today, at least
final Calendar probe = Calendar.getInstance();
// scan backwards from position of today's date
for (int prevPos = posToday - 1; prevPos >= 0; prevPos--) {
final Date prev = dateList.get(prevPos);
probe.setTime(prev);
other.add(Calendar.DAY_OF_YEAR, -1);
if (areSameDay(probe, other)) {
count++;
other.setTime(prev);
} else {
break;
}
}
// reset the other time
other.setTime(today.getTime());
// scan forward from position of today's date
for (int nextPos = posToday + 1; nextPos < n; nextPos++) {
final Date next = dateList.get(nextPos);
probe.setTime(next);
other.add(Calendar.DAY_OF_YEAR, 1);
if (areSameDay(probe, other)) {
count++;
other.setTime(next);
} else {
break;
}
}
}
return count;
}
/** Test whether two Calendar objects are set to the same day */
private static boolean areSameDay(Calendar c1, Calendar c2) {
// see discussion above if dates may not all be for the local time zone
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}
我有一组用户每次完成任务时的唯一日期。我想检查数组中的日期是否与今天的日期连续并包括今天的日期。
如果数组包含日期:"2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5"
那么根据今天的日期 2017/6/5
函数将 return 4
因为有 4 个连续的日期从今天开始(包括今天) .
如果数组包含日期 "2017/6/2, 2017/6/3, 2017/6/4"
那么它将 return 0
因为数组不包含今天的日期。否则计数将在非连续日期中断。
List<Date> dateList = new ArrayList<Date>();
int count = 0;
Date todayDate = new Date();
for (int i=0; i<dateList.size(); i++){
// Check if dates within the array are consecutive from todayDate, if so then increment count by 1.
}
有很多方法可以写的更清楚:
- 使用新日期API;
- 使用图书馆;
但是,在这种情况下,使用旧日期 类,我会这样做:
public static void main(String[] args) {
long millisInDay = TimeUnit.DAYS.toMillis(1);
List<Date> dates = Arrays.asList(new Date("2017/6/2"), new Date("2017/6/3"), new Date("2017/6/4"), new Date("2017/6/5"));
System.out.println(getSequentialNumber(millisInDay, dates));
}
private static int getSequentialNumber(long millisInDay, List<Date> dates) {
int count = 0;
Date now = setMidnight(Calendar.getInstance().getTime());
for (int i = dates.size() - 1; i >= 0; i--) {
Date date = setMidnight(dates.get(i));
if (date.getTime() == now.getTime()) {
count++;
}
now.setTime(now.getTime() - millisInDay);
}
return count;
}
private static Date setMidnight(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.HOUR_OF_DAY, 0);
return calendar.getTime();
}
如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.
如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it
虽然您也可以使用 JodaTime,但它已经停产并被新的 API 取代,我不建议您使用 joda 开始一个新项目。即使在 joda's website 它说:"Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
因为你只想比较日期 (day/month/year),而不是时间 (hour/minute/second),最好的选择是使用 LocalDate
class .对于 java 8,这个 class 在 java.time
包中,而在 ThreeTen Backport 中,包在 org.threeten.bp
中。但是 classes 和方法名称是相同的。
代码应该是这样的:
public int count(List<LocalDate> dateList, LocalDate today) {
if (!dateList.contains(today)) { // today is not in the list, return 0
return 0;
}
int count = 0;
LocalDate prev = dateList.get(0); // get first date from list
for (int i = 1; i < dateList.size(); i++) {
LocalDate next = dateList.get(i);
if (prev.plusDays(1).equals(next)) {
// difference between dates is one day
count++;
} else {
// difference between dates is not 1
// Do what? return 0? throw exception?
}
prev = next;
}
return count + 1; // didn't count the first element, adding 1
}
测试此方法:
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
dateList.add(LocalDate.of(2017, 6, 5));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 4
另一个测试(当今天不在列表中时)
List<LocalDate> dateList = new ArrayList<>();
dateList.add(LocalDate.of(2017, 6, 2));
dateList.add(LocalDate.of(2017, 6, 3));
dateList.add(LocalDate.of(2017, 6, 4));
LocalDate today = LocalDate.now();
System.out.println(count(dateList, today)); // 0
备注:
- 由于没有指定当日子不连续时要做什么(return
0
或抛出异常),我将这部分留在评论中。但是将其添加到代码中应该很简单 如果你想把
java.util.Date
转换成LocalDate
,你可以按照下面的方式做(使用代码of this answer,完整的解释在这个link 如果您有任何问题):public LocalDate convert(Date date) { return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); } // if your Date has no toInstant method, try this: public LocalDate convert(Date date) { return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate(); }
我了解到您想检查 连续 天(因此,日期之间相差 1 天)。但是如果你想检查前一个日期是否在之前下一个(无论多少天),你可以将
if (prev.plusDays(1).equals(next))
更改为if (prev.isBefore(next))
我不确定是不是这样,但如果你愿意,你也可以将
String
直接解析为LocalDate
(所以你不需要创建大量Date
个对象),使用DateTimeFormatter
:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d"); LocalDate d = LocalDate.parse("2017/6/2", formatter); // 2017-06-02
如果我对要求的理解正确,你有一个 Date
对象数组,按日期排序,并保证同一天不会有两个 Date
对象,但可能有间隔那些日子。您的目标是 return 仅包含连续天数且还包括当前日期的最大子数组的长度,或者如果没有这样的子数组,则 return 0 。当前日期可能位于该子数组内的任何位置,不一定在开头或结尾。
不清楚您是否需要支持跨越年份界限,但我假设是这样。我还假设列表中的所有 Date
对象都针对同一时区,这也是您所在设备的时区 运行。如果不是这种情况,您应该参考 this answer 以获取有关测试两个 Date
对象是否指同一天的更多信息。
如果您使用 Calendar
个对象而不是 Date
个对象,则执行此操作相当简单。您不需要任何第三方库,因为 Date
和 Calendar
都是标准 Android API 的一部分。我建议分两个阶段执行此操作:首先在数组中搜索当前日期,然后在两个方向上扫描日期中的间隙或数组边界。然后数一数你在每个方向上能走多远。
public int getDateSpanCount(List<Date> dateList) {
final int n = dateList.size();
final Calendar today = Calendar.getInstance();
final Calendar other = Calendar.getInstance();
int count = 0;
// First search for today in the date array
int posToday = -1;
for (int i=0; i<n; i++) {
other.setTime(dateList.get(i));
if (areSameDay(today, other)) {
posToday = i;
break;
}
}
// If today is in the list, count the size of the sub-array containing today
if (posToday >= 0) {
count++; // count today, at least
final Calendar probe = Calendar.getInstance();
// scan backwards from position of today's date
for (int prevPos = posToday - 1; prevPos >= 0; prevPos--) {
final Date prev = dateList.get(prevPos);
probe.setTime(prev);
other.add(Calendar.DAY_OF_YEAR, -1);
if (areSameDay(probe, other)) {
count++;
other.setTime(prev);
} else {
break;
}
}
// reset the other time
other.setTime(today.getTime());
// scan forward from position of today's date
for (int nextPos = posToday + 1; nextPos < n; nextPos++) {
final Date next = dateList.get(nextPos);
probe.setTime(next);
other.add(Calendar.DAY_OF_YEAR, 1);
if (areSameDay(probe, other)) {
count++;
other.setTime(next);
} else {
break;
}
}
}
return count;
}
/** Test whether two Calendar objects are set to the same day */
private static boolean areSameDay(Calendar c1, Calendar c2) {
// see discussion above if dates may not all be for the local time zone
return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}