I have a logic that schedules reminders using AlarmManager. I need to implement the following:
Logic 1: when the user changes time zone, eg he travels from UK (UTC+0) to central Europe (UTC+1), alarms should follow the time zone.
Example, a reminder scheduled at 3PM UTC+0 should fire at 4PM UTC+1
Logic 2: when a time shift occurs, eg time shifts to daylight saving time in spring (from UTC+1 to UTC+2), alarms should keep the original time
Example, a reminder scheduled at 3PM UTC+1 should fire at 3PM UTC+2
How can I achieve this? As of now I have no particular logic in place and all the alarms follow Logic 1. I have found no way to identify when a time shift happens.
Scheduling logic is very simple:
LocalDateTime reminderTime = LocalDateTime.of(...)
ZoneOffset currentOffsetForMyZone = ZoneId.systemDefault().getRules().getOffset(Instant.now());
reminderTime.toInstant(currentOffsetForMyZone).toEpochMilli();
alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
For each alarm store the time of day and the time zone in which it was set. This suffices for firing the alarm at the right time no matter if the user is currently in a different time zone. And Java will take summer time (DST) into account.
Your example times and UTC offsets correspond to standard time in those time zones, so let’s start with an example date in standard time even though it was a couple of days ago now:
LocalTime alarmTime = LocalTime.of(15, 0);
ZoneId alarmTimeZone = ZoneId.of("Europe/London");
// Travel to Paris and see the alarm go off at 4, assuming standard time
ZoneId currentTimeZone = ZoneId.of("Europe/Paris");
Instant actualAlarmTime = LocalDate.of(2021, Month.MARCH, 18)
.atTime(alarmTime)
.atZone(alarmTimeZone)
.toInstant();
ZonedDateTime timeOnLocation = actualAlarmTime.atZone(currentTimeZone);
System.out.format("Scheduled at %s or %d millis, goes off at %s local time%n",
actualAlarmTime, actualAlarmTime.toEpochMilli(), timeOnLocation);
The code prints:
Scheduled at 2021-03-18T15:00:00Z or 1616079600000 millis, goes off at
2021-03-18T16:00+01:00[Europe/Paris] local time
Let’s also try a date in the summer time part of the year. I have changed Paris to London and MARCH to APRIL:
// Stay back home in the UK
ZoneId currentTimeZone = ZoneId.of("Europe/London");
Instant actualAlarmTime = LocalDate.of(2021, Month.APRIL, 18)
.atTime(alarmTime)
.atZone(alarmTimeZone)
.toInstant();
Scheduled at 2021-04-18T14:00:00Z or 1618754400000 millis, goes off at
2021-04-18T15:00+01:00[Europe/London] local time
The basic trick is: don’t use the current offset for the time zone where you set the alarm. Let Java automatically apply the offset for the date and time where the alarm is to go off.
If anyone is interested, the fix was to apply the correct offset for the date and time where the alarm is to go of, as pointed out by Ole. My silly mistake was to apply always the current timezone.
LocalDateTime alarmTime = LocalDateTime.of(...)
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime zoneDateTime = ZonedDateTime.of(alarmTime , zone);
long startAtMillis = zoneDateTime.toInstant().toEpochMilli();
//Fire alarm
notificationAlarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startAtMillis, pendingIntent);
Related
I am making a simple alarm clock application that mimics the default alarm app that comes with Android Lollipop.
The set*( ) methods of an AlarmManager require the date on which the alarm should be fired as a Unix epoch.
The UI is rather simple with a TimePicker.
So, given the current time and the time the user has selected from TimePicker, how do I figure out the time in milliseconds when the alarm should be fired?
Update:
There are two cases I run into:
Selecting the time that is after the current time:
Assume it is 11am and the user selects the time from the time picker as 03pm. In this case, I know that the alarm should be scheduled for the same day.
Selecting the time that is before the current time:
Assume it is 11am and the user selects the time from the time picker as 10am. In this case, I know that the alarm should be scheduled for the next day's 10am.
Ok here you go:
// Get the current time
final Date currentTime = new Date();
// Set the hours and minutes from the time picker against todays date
final Calendar selectedTime = Calendar.getInstance();
selectedTime.set(Calendar.HOUR_OF_DAY, hourFromTimePicker);
selectedTime.set(Calendar.MINUTE, minuteFromTimePicker);
// If the current date is greater than the hour and minute from time picker add one day
if (currentTime.getTime() > selectedTime.getTime().getTime()) {
selectedTime.add(Calendar.DAY_OF_YEAR, 1);
}
// Schedule the alarm
//AlarmManager.set(selectedTime.getTime().getTime());
if you store data in java Date object:
long getTime( )
Returns the number of milliseconds that have elapsed since January 1, 1970.just subtract.
Another way if you look only at time within day:
int ms = 1000*SECONDS + 1000*60*MINUTES + 1000*60*60*HOURS
I would use a android.text.format.Time class
call the setters on the Time class to set the Hour, Minute, Second, etc. The Hours are in 24H time, so if the current hour > selected hour then you know to increment days by 1
Finally, call Time#toMillis(boolean ignoreDst) to get the system time in millis, and pass that to AlarmManager
EDIT: GregorianCalendar should be used instead.
i have a general question to set an alarm in android.
at the moment, the user can chose a date and time with a date picker (which is a date in the future).
then i will set the the delay time for the alarm.
I convert the chosen date & time in milliseconds and subtract System.currentTimeMillis() = this difference I set for the delay of my alarm.
my question is, if this the best way to calculate the delay or is there a better solution?
this calculation i use for update an alarm, too
Look at the Android Alarm Manager class. You can use it to set alarms at specific times, without calculating the delay.
final AlarmManager am = (AlarmManager) App.instance.getSystemService(Context.ALARM_SERVICE);
Calendar c = GregorianCalendar.getInstance();
c.set(2015, 12, 20, 10, 30);
am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), operation);
where operation is a PendingIntent of the action you want to do when the alarm triggers.
My solution for you here:
You should convert normal datetime to timestemp formart
You can calculate by subtract future timestemp (which is set by alarm time) with current timestemp of phone to get return value then convert it to hour, munite and day number.
If after calculate get result is zezo then you notify alarm to user.
else nothing to do
I am using (maybe incorrectly) a Joda DateTime to store a reminder time in a reminders app. When the timezone changes on the device (because of DST or just moving to a different TZ) I want to be able to reset the reminders to use the current timezone but keep the time.
For example: I set a reminder for Friday May 15th, at 15:00. If the timezone changes before that time, I want to be able to recreate the reminder for Friday May 15th at 15:00 local time.
I have already tried to use withZoneRetainFields but I haven't been able to keep the time unchanged:
new DateTime(reminderTimeMillis, DateTimeZone.forID(previousTimeZone)).withZoneRetainFields(DateTimeZone.getDefault())
Well, that method should basically work:
#Test
public void change_timezone_of_reminder() {
DateTime originalReminderDateTime = LocalDateTime.parse("2015-05-15T15:00").toDateTime(
DateTimeZone.forID("Europe/Berlin"));
assertThat(originalReminderDateTime.toString(), equalTo("2015-05-15T15:00:00.000+02:00"));
long reminderMillis = originalReminderDateTime.getMillis();
DateTime updatedTime = new DateTime(reminderMillis, DateTimeZone.forID("Europe/Berlin"))
.withZoneRetainFields(DateTimeZone.forID("America/New_York"));
assertThat(updatedTime.toLocalDateTime(), equalTo(LocalDateTime.parse("2015-05-15T15:00")));
assertThat(updatedTime.toString(), equalTo("2015-05-15T15:00:00.000-04:00"));
}
So, how are you evaluating that the time field is not changing?
Also, if you are storing the reminder time as millis-after-epoch, you don't need to adjust it for DST shifts, if you're calculating the millis using a full timezone ("Europe/Berlin") rather than a fixed offset.
I want to popup an alarm on selected day i.e. Monday ,Tuesday and so on. And at selected time on every week. I've an idea about interval but I don't know how to get the next day and popup alarm ?
You need to use the AlarmManager and get a WakeClock while processing the Intent in Service (make sure to release it and chose the right kind).
Here is a great example :
https://stackoverflow.com/a/8801990/220710
To get the day current day of the week, look at this question :
Android: how to get the current day of the week (Monday, etc...) in the user's language?
Then you would use :
setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation)
Schedule a repeating alarm
that has inexact trigger time requirements; for example, an alarm that
repeats every hour, but not necessarily at the top of every hour.
Then you would need to set :
type = RTC_WAKEUP
intervalMillis = ms in a week
triggerAtMillis = System.currentTimeMillis() + ms to the next Monday, Tuesday or
whatever
intent = the intent you want to fire to a Service that
will process it.
I have tried to do this without bothering the experts and have read numerous threads here and on other sites. It is clearly my brain not understanding what needs to be done in order for this to work.
My goal is that the app allows the user to enter a time and one or more days in a week. All of the GUI side and storing of the dates and times I have done, however to get the alarm manager to repeat, lets say every Monday at 14:00 and then can send at 14:02 . I have used the java Calendar object to hold the times and days of the week or even used date and day of the week of the month. These are then , as needed, converted to milliseconds for it to be read in by the alarm manager.
I then have used either the alarm manager set or set repeat methods to repeat the event. All I am able to do is get it to occur once and then if I change the emulator date and time to another Monday nothing happens.
The GUI holds the hours and minutes in required variables and then these are used against the calendar objects.
The alarm manager calls a broadcast receiver for the event to occur.
Please can someone simply give an example on how to set specific days such as Monday , Wednesday Friday. I know that separate alarm managers are needed for each day and at the moment I have just focused on Monday as my main test.
Links viewed:
How can i Repeat the Alarm in android for only Monday, Tuesday and Friday
How to repeat the alarm for "n" days at a particular time
how to repeat alarm after 1 day in android
Managed to figure this out now and so follows my answer:
The following code calculates the remaining days between now and the day needed for the scheduled task. the variable whichday is passed via parameter from the method this code belongs to. In the understanding of this whichday represents days of the week 1 through to 7 where 1 is Sunday , 2 is Monday and so .
//This gets the current day of the week as of TODAY / NOW
int checkcurrentday = getcurtime.get(Calendar.DAY_OF_WEEK);
// This calculates the days between now and the day needed which is represented by whichday.
int numberofdays = Calendar.SATURDAY + whichday - checkcurrentday;
//Now add NOT set the difference of the days to your Calendar object
tMondayOn.add(Calendar.DATE, numberofdays);
Well, you need to first use the Java Calendar API (or Joda!) to figure out when the next monday is. Set the alarm to to that time in milliseconds then use setRepeating and pass in a long that represents the interval of one week.