I am having problems figuring this out. This is my query:
SELECT * FROM events WHERE date('2012-9-4') >= start_date AND
date('2012-9-4') <= end_date
Now as you can see from above, I am only getting the rows that match that day in some way. In my case, any events that are on that day or came from previous days. The problem is, the case where, say, the previous day has an event from 10:00PM to 12:00AM (2012-9-3), shows up on the next day (2012-9-4). So I need to keep the case like that from happening.
This is where I am having trouble. I have no idea how to get it done correctly. I have tried different things, but they all fail. I need a way to make a statement in the where clause only run when the end date > start date, the end date == current day, and the end time == 00:00. Any advice would be greatly apprecited!!
Don't you just need to turn around the first parts of the where clauses
Where start_date >= date ('2012-9-4') and end_date <= date('2012-9-4')
Assuming your data always has end_date later than start_date (if not, then adding an OR with the reverse to pick them up to).
If, (confused here from the Q) you actually want intersecting dates (overlaps), then you need two intersecting clauses:
where (start_date >= date('2012-09-03 00:00:00') and start_date < date('2012-09-04 00:00:00')) or (end_date >= date('2012-09-03 00:00:00') and end_date < date('2012-09-04 00:00:00'))
I finally figured it out (pretty sure)!
SELECT * FROM events WHERE date('2012-9-4') >= start_date AND
date('2012-9-5') <= end_date AND datetime(end_date,end_time) != datetime(2012-9-5,'00:00')
After 2 days of thinking, I have no idea why using the time as well never occurred to me. I guess I need more practice with databases. In any case, the part I added filters the events listed by date and time. Example would be like my query dates, lets say 11:00PM the 4th to 12:00AM the 5th. The AND datetime(end_date,end_time) != datetime(2012-9-5,'00:00') essentially says "only output when its not the end date with a time of midnight." It will only filter out the 5th and not the 4th because midnight is technically another day. Thats the only case I ever want to filter out events anymore than just between the event periods. I appreciate Wolf5370 for the hints which helped.
Related
I wanted to get the TimeInMillis value for alternate dates for the next 30 days from any selected date. I tried to get value for the dates using a for loop. But the code always returns the last 3 dates as wrong. Is there any alternate better way to get what i'm doing?
fun calculateDates(startDate: Long) {
val singeDateDifference = 86400000
val cancelledDates = arrayListOf<Long>()
for (i in 2..30 step 2) {
val difference = i * singeDateDifference
cancelledDates.add(startDate + difference)
}
}
The answer i'm getting for the above is this. I have logged the Long value as dates for better understanding.
startDate: 01/01/2022
[02/01/2022, 04/01/2022, 06/01/2022, 08/01/2022, 10/01/2022, 12/01/2022, 14/01/2022, 16/01/2022, 18/01/2022, 20/01/2022, 22/01/2022, 24/01/2022, 07/12/2021, 09/12/2021, 11/12/2021]
I'm always getting the last 3 dates from previous month. I can't figure out what is wrong with the code.
In the line
val difference = i * singeDateDifference
you are using integer types (Int in Kotlin), so for large enough results of your multiplication you will get an integer overflow (because Int.MAX_VALUE is 2147483647), which means that the result of the multiplication will be a negative value.
That happens for the last 3 difference values, which causes the last 3 dates to be 1 month behind.
Just ensure that the multiplication is done with Long types, one simple way to do that would be to set the date difference factor to Long.
// ensure we use Long when calculating with epoch millis
val singeDateDifference = 86400000L
This will cause the multiplication to be performed with the Long values and the result will also be a Long value.
That will fix the bug and print the dates as you would expect.
In general cases time, dates and calendars are not easy to get right and years of work have been put into APIs such as the Time API.
When working with times and dates, my suggestion would be, to use the Time API classes and methods instead. They are in the package java.time (java.time.*).
The documentation is here: https://developer.android.com/reference/java/time/package-summary
That will prevent bugs like the one you had, because it allows you to work with higher-level concepts, such as .plusHours(24) or .plusDays(1) (which is not the same in all cases!) instead of doing all the calculations manually. Even if you have your own tests, sooner or later you will get something wrong with time, dates and calendars. Not all days are 24 hours long, shocking, I know :) And this is just the first one on a long list of things that we believe about time, that are simply not true.
If you have to support older Android API levels (lower than 26) you need Android Gradle plugin of at least v4.0.0+ for the Time API to be available (support for core library desugaring).
If you cannot use that for some reason, add the JSR-310 Android Backport (ThreeTen ABP) as a dependency.
This will add the same Time API under the package org.threeten.bp.* with same classes and methods as the Time API, which is otherwise only available on Android from API level 26+ (without support for core library desugaring). Note that the recommended way is to switch to the newest version of the Android Gradle plugin, because this library's support is winding down.
See here:
https://github.com/JakeWharton/ThreeTenABP
I want to delete SharedPreferences, if they exist, after one month automatically. I could not find any solutions. Is this possible to make?
Thanks a lot.
It depends.
The easiest way is to delete it when the user starts the app.
When the apps is created, you check the SharedPreferences for the last updated time.
If it's null (the first time), you save the current time in milliseconds as a long.
If it's not null, you read it and compare it against the current time. If it less than a month, you do nothing. If it's more than a month, you clear the shared preferences and, after clearing it, insert the new time.
Something like:
long lastUpdate = sharedPreferences.getLong(LAST_UPDATE, -1);
if(lastUpdate == -1) {
//First time
sharedPreferences.edit().putLong(LAST_UPDATE, System.currentTimeMillis()).apply();
} else {
boolean isMoreThanAMonth = //Here you should do the math. it depends, you want to consider a month like 30 days, or you want to know if it was in another month... somehthing like that
if(isMoreThanAMonth) {
sharedPreferences.edit().clear().apply()
}
}
Of course, if you want to clear the SharedPreferences even if the user does not open the app you should use a Service. It's more complex and expensive for the OS, so you should try to go for the first one if it fits your requirement.
long installed = context
.getPackageManager()
.getPackageInfo(context.getPackageName(), 0)
.firstInstallTime
;
public long firstInstallTime
The time at which the app was first installed. Units are as per System.currentTimeMillis().
Now you can compare two date and get months diffrent by using GregorianCalendar
after you get one month different do as you want..clear sharedPrefrence.edit().clear().commit()
One possible way,
1. get the calendar instance.
2. Get maximum day of month.
3. Store in a var1 string in format of dd/mm/yyyy.
4. Get the current date from some calendar object and store in same way from point 3 but in var2.
5. Compare two strings.
6. If match then it will be last day of month and call delete() on your files.
Done.
I have a problem with adding event to Google Calendar programmaticly.
Here is code:
private void addToGoogleCalendar(long beginTime) {
ContentValues event = new ContentValues();
Observable.from(dataKeeper.getCalendarIds().keySet()).filter(key -> dataKeeper.getCalendarIds().get(key).equals(defaultCalendarName.getText().toString())).forEach(key ->
event.put(CalendarContract.Reminders.CALENDAR_ID, Integer.parseInt(key))
);
event.put(CalendarContract.Reminders.TITLE, reminderName.getText().toString());
if (!TextUtils.isEmpty(commentEdittext.getText().toString()))
event.put(CalendarContract.Reminders.DESCRIPTION, commentEdittext.getText().toString());
event.put(CalendarContract.Reminders.EVENT_TIMEZONE, TimeZone.getDefault().getID());
event.put(CalendarContract.Reminders.DTSTART, beginTime);
event.put(CalendarContract.Reminders.DTEND, dataKeeper.getEnd());
event.put(CalendarContract.Reminders.EVENT_COLOR, getResources().getColor(R.color.primary));
// http://tools.ietf.org/html/rfc2445
if (dataKeeper.getPeriod() > 0 && dataKeeper.getPeriod() != 7)
event.put(CalendarContract.Reminders.RRULE, String.format("FREQ=DAILY;INTERVAL=%d;UNTIL=%s", dataKeeper.getPeriod() - 1,
new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(dataKeeper.getEnd())));
else if (dataKeeper.getPeriod() == 7)
event.put(CalendarContract.Reminders.RRULE, String.format("FREQ=WEEKLY;INTERVAL=1;UNTIL=%s",
new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(dataKeeper.getEnd())));
event.put(CalendarContract.Reminders.ALL_DAY, 1); // 0 for false, 1 for true
event.put(CalendarContract.Reminders.STATUS, CalendarContract.Events.STATUS_CONFIRMED);
String eventUriString = "content://com.android.calendar/events";
getContentResolver().insert(Uri.parse(eventUriString), event);
}
Inrerval = 1
So, when I add event, it adds as need, but in few seconds I see that event adds recursive. First day one time, next day two times and so on.
Method called only ones, so I have no idea WTF happens.
You've set the end of the master instance to the end of the recurrence rule.
The first instance has a duration of dataKeeper.getEnd() - beginTime. According to the RRULE this instance is recurs every day until dataKeeper.getEnd()
Now if dataKeeper.getEnd() is several days after beginTime it happens that although the fist instance is still ongoing on the second day (because it has a duration of multiple days), another instance starts on that day.
On the third day, the first two instances are still ongoing and the rule says another one should start.
So there is one instance added until the UNTIL date of the RRULE, after that the number of instances decreases by one each day.
To fix it make sure that the first instance is just a day long. Replace
event.put(CalendarContract.Reminders.DTEND, dataKeeper.getEnd());
by
event.put(CalendarContract.Events.DURATION, "P1D");
This sets the duration of each instance to 1 day. You should always use DURATION for recurring events anyway, see Events.
Btw, you should also use CalendarContract.Events instead of CalendarContract.Reminders. Although Reminders also implements EventsColumns it's meant for alarms. I guess it contains the event columns because it effectively reads from a join over both tables.
For reoccurring events, I want to show the number of days left until the next occurrence in my Android calendar application.
Example:
Today: 2012-06-12
Reoccurring event: 19th June
=> 13 days left
In order to achieve this, I save the first occurrence in an object of data type Calendar:
private Calendar cal;
...
cal = new GregorianCalendar();
cal.set(Calendar.YEAR, USER_INPUT_YEAR);
cal.set(Calendar.MONTH, USER_INPUT_MONTH);
...
To calculate the days left I use this function:
public int getDaysLeft() {
Date next = this.getNextOccurrence();
if (next == null) {
return -1;
}
else {
long differenceInMilliseconds = next.getTime()-System.currentTimeMillis();
double differenceInDays = (double) differenceInMilliseconds/DateUtils.DAY_IN_MILLIS;
return (int) Math.ceil(differenceInDays);
}
}
Which uses this function:
public Date getNextOccurrence() {
if (this.cal == null) {
return null;
}
else {
Calendar today = new GregorianCalendar();
Calendar next = new GregorianCalendar();
next.setTime(this.cal.getTime());
next.set(Calendar.YEAR, today.get(Calendar.YEAR));
if ((today.get(Calendar.MONTH) > this.cal.get(Calendar.MONTH)) || ((today.get(Calendar.MONTH) == this.cal.get(Calendar.MONTH)) && (today.get(Calendar.DAY_OF_MONTH) > this.cal.get(Calendar.DAY_OF_MONTH)))) {
next.add(Calendar.YEAR, 1);
}
return next.getTime();
}
}
By the way, to get the initial date, I expect to find a YYYY-MM-DD value and parse it like this:
(new SimpleDateFormat("yyyy-MM-dd")).parse(INPUT_DATE_STRING)
This works fine in most cases, but some users report that they see numbers such as -1469913 as "days left". How can this happen?
I thought the date (cal) might be not set or invalid, but then it would show -1 or something like this, as there are null checks in all parts, right?
-1469913 means something like -4027 years ago! As it is a reoccurring event, I thought the "days left" information should always be between 0 and 366. What could cause this code to produce such a number? Does this mean that getNextOccurrence() returns a data that is 4027 years in the past? I can't explain this behaviour.
I hope you can help me. Thank you so much in advance!
Edit: As it may be helpful: The wrong dates' year is always output as 1 when using DateFormat.getDateInstance().format(), e.g. Jan 3, 1. Nevertheless, the result of getDaysLeft() is something like 4k years.
Edit #2: I found out a date like 1--22199-1 is one that produces the output of "4k years left". Nevertheless, it is successfully parsed by (new SimpleDateFormat("yyyy-MM-dd")).parse(). Similarly, -1-1-1-91- is correctly parsed as Jan 1, 2.
Edit #3: It turned out that a date as simple as "0000-01-03" was causing all the trouble. When I output the time in milliseconds it says -62167222800000. When I then output it to a GMT string it says 0001-01-03 - strange, isn't it? And when I set the year to 1900 the time in millis is suddenly -122095040400000. Why?
Working with dates it can be really difficult to figure out those obscure errors before they happen to a user in the wild. In many cases, it can be worth your time to make a little unit test that throws a few tens of million dates in the machinery and see if any extreme answers pop up.
Also this might be worth reading. You wont realize how bad the java date-class are before you have tried something that is way better. :)
EDIT: If the users give a very high input value, then there can be a number overflow when you throw your result to an integer in getDaysLeft(). Just keep it as a long. Or even better: Only accept sensible input value, warn the user if they input the year 20120 or something like that :)
EDIT2: I was wrong in my last edit, .ceil() protects against number overflows. To be honest I have no longer any idea how this bug can happen.
EDIT3: Responding to your third edit: Remember, Date and Calendar uses Unix time. That means that the time represented by a zero is 1970. Everything before 1970 will be represented by a negative value.
EDIT4: Remember that javas calendar-classes sucks. This code snippet demonstrates that the error is in fact in the Calendar-class:
Calendar next = new GregorianCalendar();
long date1 = -62167222800000L;
long date2 = -62135600400000L;
next.setTimeInMillis(date1);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());
next.setTimeInMillis(date2);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());
Output:
-125629491600000
1325545200000
It will however be very hard to track down the exact bug that causes this. The reason all those bugs remains are because fixing them might break legacy systems all over the world. My guess is that the bug originates from the inability to give negative years. This, for example, will give the output "2013":
Calendar next = new GregorianCalendar();
next.set(Calendar.YEAR, -2012);
System.out.println(next.get(Calendar.YEAR));
I would simply recommend you to not allow such extreme values in your input. Decide on an acceptable span and give an error message if the value is outside of those boundaries. If you would like to handle all possible dates in some futher application, just use joda time. You wont regret it :)
You got negative values in days that might be because user have entered the date of next occurance which is any previous date.
I think you should calculate your daysLeft like this way,
String inputDateString = "19/06/2012";
Calendar calCurr = Calendar.getInstance();//current date
Calendar calNext = Calendar.getInstance();// for next date
calNext.setTime(new Date(inputDateString)); // or do set Day,Month,Year like in your Question
if(calNext.after(calCurr)) // if the next date is after current
{
long timeDiff = calNext.getTimeInMillis() - calCurr.getTimeInMillis(); // time of next year if today is 15 june and some one enter 16 june for next occurance
int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
}
else
{
long timeDiff = calCurr.getTimeInMillis() - calNext.getTimeInMillis();
timeDiff = DateUtils.YEAR_IN_MILLIS - timeDiff; // time of next year if today is 15 june and some one enter 14 june for next occurance
int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
}
i have to select a interval of time.
i have two timepickers on my app, i need to check if timepicker1 selected time is less than timepicker2 selected time. If not, i have to show a toast to told the user the error.
I also need to do this with two datepickers,not with times in that case, but with dates.
please can someone give me some code examples for do this?
You just need to compare the individual components of the date or time in the correct order. For time:
if (time1.getCurrentHour() < time2.getCurrentHour() || (time1.getCurrentHour() == time2.getCurrentHour() && time1.getCurrentMinute() < time1.getCurrentMinute())) {
//time 1 is earlier.
}
You might need to add in a bit of complexity depending on if you are showing 24 hour time or not.
For dates, its the same, just compare first the year then the month then the day.