日历:不允许过去的时间戳

时间:2017-06-24 12:58:38

标签: java android datetime calendar

我安排重复闹钟,每周二早上08:30响铃。

由于打盹,我无法使用AlarmManager.setRepeating因为闹钟的时间不准确(需要的是,我理解对电池的影响)。

因此,我使用此代码来安排第一个警报:

Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.setFirstDayOfWeek(Calendar.WEDNESDAY); // Trick not to get a "tuesday" timestamp in the past if today is wednesday+ !
calendar.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long scheduledTime = calendar.getTimeInMillis();

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    alarmManager.setExact(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
} else {
    alarmManager.set(AlarmManager.RTC_WAKEUP, scheduledTime, pendingIntent);
}

它完美无瑕。

问题是,我必须在星期二上午8:30在Intent消耗的BroadcastReceiver时重新安排闹钟。

但是在星期二08:30到星期二23:59之间,这个方法会给我一个"过去"的时间戳,这是不正确的。

是否有任何方法而不是"如果提供的时间戳是过去的,则为其添加1周"解决这个问题?

3 个答案:

答案 0 :(得分:4)

我建议您使用新的java.time API进行此计算。 ThreeTenABP project是针对Android的该库的改编。

以下是使用LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); now.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)) .withHour(8) .withMinute(30) .withSecond(0)); API获取每周二上午8:30的示例:

{{1}}

答案 1 :(得分:2)

旧类(DateCalendarSimpleDateFormat)有lots of problemsdesign issues,并且它们被新API取代

在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端,以及ThreeTenABP(更多关于如何使用它here })。

虽然您也可以使用Joda-Time,但它处于维护模式并被新API取代,因此我不建议您使用它来启动新项目。即使在joda's website它也说:"请注意,Joda-Time被认为是一个很大程度上“完成”的项目。没有计划重大改进。如果使用Java SE 8,请迁移到java.time(JSR-310)。"

以下所有课程均在org.threeten.bp包下。

首先,我检查了AlarmManager的文档,它似乎与时间戳(long方法中的set参数)一起使用,这是一个数字1970-01-01T00:00Z的毫秒数。

所以,你需要ZonedDateTime(特定时区的日期和时间),因为你需要知道日期,时间和时区来获得特定的时间戳毫秒:

import org.threeten.bp.DayOfWeek;
import org.threeten.bp.LocalTime;
import org.threeten.bp.ZonedDateTime;
import org.threeten.bp.temporal.TemporalAdjusters;

// current date/time at system's default timezone
ZonedDateTime now = ZonedDateTime.now();

// get next Tuesday at 08:30 AM
ZonedDateTime nextTuesday = now
    // get the next Tuesday
    .with(TemporalAdjusters.next(DayOfWeek.TUESDAY))
    // at 08:30 AM
    .with(LocalTime.of(8, 30));
System.out.println(nextTuesday);

// get the millis timestamp
long scheduledTime = nextTuesday.toInstant().toEpochMilli();

TemporalAdjusters.next()方法可以找到下周二的技巧。如果当前日期已经是星期二,并且您不希望获得下周二的星期二,则可以将其替换为TemporalAdjusters.nextOrSame()

LocalTime.of(8, 30)将小时设置为08:30 AM(已将毫秒设置为零)。

上面的代码使用系统的默认时区(可能是设备中配置的时区)获取当前日期/时间。如果您需要特定时区,可以使用org.threeten.bp.ZoneIdorg.threeten.bp.ZoneOffset类:

// current date/time at London
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/London"));

// current date/time in UTC
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);

请注意,API使用IANA timezones names(始终采用Continent/City格式,如America/Sao_PauloEurope/London。 避免使用3个字母的缩写(例如CSTPST),因为它们是ambiguous and not standard

如果您已有java.util.Calendar,则可以使用org.threeten.bp.DateTimeUtils类转换为新API:

// convert calendar to ZonedDateTime, using system's default timezone
ZonedDateTime z = DateTimeUtils.toInstant(calendar).atZone(ZoneId.systemDefault());

// convert back to calendar
Calendar c = DateTimeUtils.toGregorianCalendar(z);

答案 2 :(得分:0)

您可以使用此代码计算下周二上午8:30。 它使用JodaTime Library。

在回答这个问题时,这是要添加到build.gradle的依赖性:compile 'joda-time:joda-time:2.9.9'

public DateTime getNextTuesday830(DateTime now){
        DateTime rtn = now;

        int thisDay = now.getDayOfWeek();
        int deltaNexTuesday = -1;

        if(thisDay >= DateTimeConstants.TUESDAY){
            deltaNexTuesday =  DateTimeConstants.TUESDAY + 7 - thisDay;
        }else{
            deltaNexTuesday = DateTimeConstants.TUESDAY - thisDay;
        }

        rtn = rtn.plusDays(deltaNexTuesday).withHourOfDay(8).withMinuteOfHour(30).withSecondOfMinute(0);

        return rtn;
    }

要计算 now 参数:

DateTime now = new DateTime(System.currentTimeMillis());

从DateTime获取Date()对象:

Date d = now.toDate();