我安排重复闹钟,每周二早上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周"解决这个问题?
答案 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)
旧类(Date
,Calendar
和SimpleDateFormat
)有lots of problems和design 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.ZoneId
或org.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_Paulo
或Europe/London
。
避免使用3个字母的缩写(例如CST
或PST
),因为它们是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();