Can anyone explain why 1 of 4 calendars shows Time=? - java
I have 4 calendars, startCal, endCal, reminderCal and today. today simply gets a Calendar.getInstance() for the current date/time, whereas the 3 others go through the same method for selecting date&time using Androids date/time pickers.
startCal and endCal are working just fine
if(startCal.after(endCal)){ // handle error }
however, reminderCal.before(today) doesn't return true, even when I purposely set the reminderCal before the current date.
Also, when printing all the times, the reminderCal doesn't have any time (Time=?) and areFieldsSet=false, but if I submit a second time the time gets updated, although the before() method still doesn't work!
Everything seems to work except for the the else if (reminderCal.before(today))
public boolean validInput(){
if(startCal.after(endCal)){
//THIS WORKS
timeError.setText(getString(R.string.error_event_end_early));
isOk = false;
Log.e(TAG, "validInput: Event ends before it starts");
}
if(hasReminder.isChecked()){
if(reminderDate.getText().toString().matches("")) {
// THIS WORKS
reminderError.setText(R.string.error_no_reminder);
isOk = false;
Log.e(TAG, "validInput: No reminder set");
} else if (reminderCal.before(today)){
// THIS DOESN'T WORK. Why?
reminderError.setText(R.string.error_reminder_early);
isOk = false;
Log.e(TAG, "validInput: Reminder too early " + reminderCal.toString());
} else if(reminderCal.after(startCal)){
//THIS WORKS
reminderError.setText(R.string.error_reminder_late);
isOk = false;
Log.e(TAG, "validInput: Reminder too late");
}
return isOk;
}
Log.e(TAG, "Debugging: \n" +
"strCalandar: " + startCal.toString() + "\n" +
"endCalandar: " + endCal.toString() + "\n" +
"remCalandar: " + reminderCal.toString() + "\n" +
"todCalandar: " + today.toString() + "\n");
}
today = Calander.getInstance(); with no further adjustments. The other calendars get the date/time from these methods.
public void showDatePickerDialog(final EditText text, final Calendar cal){
Log.d(TAG, "showDatePickerDialog: Open");
DialogFragment datePicker = new DatePickerFragment();
((DatePickerFragment) datePicker).setOnDateChosenListener(new DatePickerFragment.OnDateChosenListener() {
#Override
public void onDateChosen(int year, int month, int day) {
text.setText(String.format("%02d/%02d/%04d", day, month, year));
cal.set(year, month, day);
}
});
datePicker.show(getSupportFragmentManager(), "DatePicker");
}
public void showTimePickerDialog(final EditText text, final Calendar cal){
Log.d(TAG, "showTimePickerDialog: Open");
DialogFragment timePicker = new TimePickerFragment();
((TimePickerFragment) timePicker).setOnTimeChosenListener(new TimePickerFragment.OnTimeChosenListener() {
#Override
public void onTimeChosen(int hour, int min) {
text.setText(String.format("%02d:%02d", hour, min));
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, min);
}
});
timePicker.show(getSupportFragmentManager(), "TimePicker");
}
Output in Logcat:
strCalandar: java.util.GregorianCalendar[time=1566152201478,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=libcore.util.ZoneInfo[id="Europe/London",mRawOffset=0,mEarliestRawOffset=0,mUseDst=true,mDstSavings=3600000,transitions=242],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=7,WEEK_OF_YEAR=33,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=230,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=7,HOUR_OF_DAY=19,MINUTE=16,SECOND=41,MILLISECOND=478,ZONE_OFFSET=0,DST_OFFSET=3600000]
endCalandar: java.util.GregorianCalendar[time=1566152201478,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=libcore.util.ZoneInfo[id="Europe/London",mRawOffset=0,mEarliestRawOffset=0,mUseDst=true,mDstSavings=3600000,transitions=242],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=7,WEEK_OF_YEAR=33,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=230,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=7,HOUR_OF_DAY=19,MINUTE=16,SECOND=41,MILLISECOND=478,ZONE_OFFSET=0,DST_OFFSET=3600000]
remCalandar: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=libcore.util.ZoneInfo[id="Europe/London",mRawOffset=0,mEarliestRawOffset=0,mUseDst=true,mDstSavings=3600000,transitions=242],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=7,WEEK_OF_YEAR=31,WEEK_OF_MONTH=1,DAY_OF_MONTH=2,DAY_OF_YEAR=214,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=18,SECOND=41,MILLISECOND=478,ZONE_OFFSET=0,DST_OFFSET=3600000]
todCalandar: java.util.GregorianCalendar[time=1562775401478,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=libcore.util.ZoneInfo[id="Europe/London",mRawOffset=0,mEarliestRawOffset=0,mUseDst=true,mDstSavings=3600000,transitions=242],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=6,WEEK_OF_YEAR=28,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=191,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=16,SECOND=41,MILLISECOND=478,ZONE_OFFSET=0,DST_OFFSET=3600000]
reminderCal.before(today) should return true if reminder date is set before current date
They have tried to explain it in this part of the docs:
set(f, value) changes calendar field f to value. In addition, it sets an internal member variable to indicate that
calendar field f has been changed. Although calendar field f is
changed immediately, the calendar's time value in milliseconds is not
recomputed until the next call to get(), getTime(),
getTimeInMillis(), add(), or roll() is made. Thus, multiple
calls to set() do not trigger multiple, unnecessary computations. As
a result of changing a calendar field using set(), other calendar
fields may also change, depending on the calendar field, the calendar
field value, and the calendar system. In addition, get(f) will not
necessarily return value set by the call to the set method after the
calendar fields have been recomputed. The specifics are determined by
the concrete calendar class.
Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling set(Calendar.MONTH, Calendar.SEPTEMBER) sets the date to September 31, 1999. This is a temporary internal representation that
resolves to October 1, 1999 if getTime()is then called. However, a
call to set(Calendar.DAY_OF_MONTH, 30) before the call to getTime()
sets the date to September 30, 1999, since no recomputation occurs
after set() itself.
So since you call cal.set(Calendar.HOUR_OF_DAY, hour); and cal.set(Calendar.MINUTE, min);, the time gets temporarily undefined, which is why the question mark is printed from toString.
I have not been able to reproduce the reported behaviour of before and after, but it seems reasonable to assume that they also don’t work correctly until get or one of the mentioned getXxx methods is called.
This is only one of the very confusing aspects of the design of the Calendar class. The good solution is to switch to using java.time, the modern Java date and time API.
Related
Finding date of next Thursday in android studio
I'm trying to make a app which includes telling the time of next Thursday. App crashes every time i open that class. #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_authorised); button.setOnClickListener(new View.OnClickListener(){ #Override public void onClick(View v) { nextThursday(); } }); } void nextThursday(){ String nextThursday = getNext(DayOfWeek.THURSDAY).format(DateTimeFormatter.ofPattern("MMM, dd yyyy", Locale.ENGLISH)); nextThurs.setText(nextThursday); } public static LocalDate getNext(DayOfWeek dayOfWeek) { // get the reference day for the word "next" (that is the current day) LocalDate today = LocalDate.now(); // start with tomorrow LocalDate next = today.plusDays(1); // as long as the desired day of week is not reached while (next.getDayOfWeek() != dayOfWeek) { // add one day and try again next = next.plusDays(1); } // then return the result return next; } } Is anyone able to help?
This answer uses java.time, which is the datetime API to be used since the Joda Time project stopped further development. It basically uses an algorithm that may be realizable in Joda Time, too, but I don't know exactly if and how, so I show you a way in java.time. Define a method that returns the date of the next given day of week: public static LocalDate getNext(DayOfWeek dayOfWeek) { // get the reference day for the word "next" (that is the current day) LocalDate today = LocalDate.now(); // start with tomorrow LocalDate next = today.plusDays(1); // as long as the desired day of week is not reached while (next.getDayOfWeek() != dayOfWeek) { // add one day and try again next = next.plusDays(1); } // then return the result return next; } and use it in a main() just to print it out: public static void main(String[] args) { System.out.println("Next Thursday is " + getNext(DayOfWeek.THURSDAY) .format(DateTimeFormatter.ofPattern("MMM, dd yyyy", Locale.ENGLISH))); } which results in the output when executed Friday, 15th of May 2020: Next Thursday is May, 21 2020 Of course, the format is just an example and can easily be adjusted according to your needs.
It's quite simple using a predefined TemporalAdjuster: LocalDate date = LocalDate.now() .with(TemporalAdjusters.next(DayOfWeek.THURSDAY)); This is natively supported since Java 8 or Android API level 26. To target previous API levels, use the ThreeTen Android Backport. Joda Time is in maintenance mode, and they're suggesting that you use java.time instead.
Deleted all code in that activity. Still crashed. Error was not in the code. Something else got messed up instead.
Calendar (GregorianCalendar) .complete() method not visible?
So I read through a few different threads but none of them seem to directly address how I fix my issue. I'm trying to create a Calendar (Gregorian) and then use the .complete() method so that in my classes using this (Paycheck) class I can find relative dates and create new Calendar(s) from those dates to determine wages payed and wages owed. However, it's telling me that .complete() .computeTime() and .computeFields() are all not visible. From what I've read, this seems to be because they are protected methods and even though I import the java.util for them, I can't access them because that class is not in my package. How do I get this so that I can call the .complete() method? import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone; public class Paycheck { //fields protected double grossAmount; protected Calendar paymentDate; protected Calendar payPeriodStart; public Paycheck(double grossAmount, int iYear, int iMonth, int iDay, int sYear, int sMonth, int sDay) { this.grossAmount = grossAmount; TimeZone tz1 = TimeZone.getTimeZone("America/Chicago"); this.paymentDate = new GregorianCalendar(iYear, iMonth, iDay); this.paymentDate.setTimeZone(tz1); this.paymentDate.complete(); //says "method not visible" this.payPeriodStart = new GregorianCalendar(sYear, sMonth, sDay); this.payPeriodStart.setTimeZone(tz1); }
In a comment you wrote: I don't care about the actual time, I just want it to give me the date so I can determine the dayofweek (important based on various state laws). That is very easy to do, and don't need any call to internal methods. Calendar cal = new GregorianCalendar(2016, Calendar.SEPTEMBER, 22); cal.setTimeZone(TimeZone.getTimeZone("America/Chicago")); System.out.println("Day of week (1=Sun, ..., 7=Sat): " + cal.get(Calendar.DAY_OF_WEEK)); Output Day of week (1=Sun, ..., 7=Sat): 5 Output is 5 because today is Thu 9/22/2016, and that's the date that was given.
Let's compare the typing effort: this.paymentDate = new GregorianCalendar(iYear, iMonth, iDay); this.paymentDate.complete(); // Why do you need to call this one? vs the code having the same effect this.paymentDate = new GregorianCalendar(iYear, iMonth, iDay, 0, 0, 0); // constructor taking extra hours, minutes, seconds ----------^ So why do you try the almost-impossible of calling a protected method? In some cases, very limited in number, one really needs to call a protected/private method and this may be possible. Exemplifying: Class<GregorianCalendar> clazz=GregorianCalendar.class; Method completeMethod = clazz.getDeclaredMethod("complete"); // this does the trick if it doesn't throw security exceptions completeMethod.setAccessible(true); completeMethod.invoke(this.paymentDate); See the Javadoc for AccesibleObject.setAccessible, Method being derived from AccessibleObject
Can you use variables when calling GergorianCalendar.isLeapYear()?
If I use: GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); boolean yearIsLeapYear = cal.isLeapYear(2016); Then my varialbe yearIsLeapYear is correctly set to true. However, if I use a variable in place of 2016 it doesn't not work. int year = 2016; GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); boolean yearIsLeapYear = cal.isLeapYear(year); Am I missing something or is it not possible to pass a variable into the isLeapyYear() method? In the program I'm writing the value in the year field can change depending on user input and the final algorithm I'm implementing needs to behave differently when the current year is a leap year or the next year is a leap year. I thought this would be simple way to perform the check. Edit showing full code Fields are: private int year; private boolean yearIsLeapYear , nextYearIsLeapYear, previousYearIsLeapYear; I have a constructor as follows: public FirstDayOfSummer(int currentYear) { year = currentYear; checkForLeapYears(); } And the following method which I am calling in the constructor: private void checkForLeapYears(){ GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); //checking for a leap year using the current value of "year" if(cal.isLeapYear(year)){ yearIsLeapYear = true; } else{ yearIsLeapYear = false; } //checking for a leap year using the value of "year" + 1 if(cal.isLeapYear(year + 1){ nextYearIsLeapYear = true; } else{ nextYearIsLeapYear = false; } //checking for a leap year using the value of "year" - 1 if(cal.isLeapYear(year - 1){ previousYearIsLeapYear = true; } else{ previousYearIsLeapYear = false; } }
The error was in calling my constructor. FirstDayOfSummer currentYearFirstDayOfSummer = new FirstDayOfSummer(Calendar.getInstance().get(Calendar.YEAR)); currentYearFirstDayOfSummer.setYear(2015); I was accidently using the current year in the constructor and then trying to use a mutator method to change it to something else. Because my method to check for a leap year was only being called in the constructor and not also in the mutator it was never updating my booleans! Thanks for asking good questions!
How to determine if the specific time is between given range?
Problem: I have a list containg hours, for example: 08:15:00 08:45:00 09:00:00 12:00:00 ... application is allowing user to make an appointment for a specific hour let'say: 8:15:00, each meeting takes half an hour. Question: How to determine if there is a slot needed for appointment like this? I know that Calendar class have methods before() nad after(), but it doesn'solve my problem. I mean if there is appointment at 12:00 and another one at 12:00, how to prevent before making another one at 12:15? edit: I've tried using methods I mentioned before, like: Calendar cal1 = Calendar.getInstance(); // for example 12:00:00 Calendar cal2 = Calendar.getInstance(); // for exmaple 12:30:00 Calendar userTime = Calendar.getInstance(); // time to test: 12:15:00 if(user.after(cal1)&& user.before(cal2)){ ... // do sth }
Check if the date to check is between the two provided: SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm"); Date before = sdf.parse("07/05/2012 08:00"); Date after = sdf.parse("07/05/2012 08:30"); Date toCheck = sdf.parse("07/05/2012 08:15"); //is toCheck between the two? boolean isAvailable = (before.getTime() < toCheck.getTime()) && after.getTime() > toCheck.getTime(); To book for a determinate hour, I would do a class with two dates and a method to check this: public class Appointment{ private Date start; private Date end; public boolean isBetween(Date toCheck){....} } Then you can simply do an Schedule class extending ArrayList, adding a method isDateAvailable(Date toCheck), iterating the list of Appointments and checking that there is no one conflicting.
I'd have some kind of appointment class with either a start timestamp and a duration or a start time and an end time. Then when adding new appointments to the schedule, check that the appointment with the start time before the new appointment doesn't run over the start time of the proposed new appointment.
Well how you would do it specifically depends on how you are storing your data, format, etc., but generally what you would do is simply check if there is an appointment for any time between the requested time to the requested time + requested length. // Example (using int time(1 = 1 minute), assuming that appointments can only be at 15min intervals) boolean isHalfHourTimeSlotAvaliable(int time) { for (int i = 0; i < appointments.size(); i++) { if (appointments.get(i).time == time || appointments.get(i).time == time + 15) { return false; } } return true; }
Guava's Ranges.asSet outputting infinite list
I am trying to get a date range using Guava's new Range functionality, via Range<Date> dateRange = Ranges.range(start, BoundType.CLOSED, end, BoundType.CLOSED); My goal is to get the hours in this date range. So I have created a DiscreteDomain like such: private static final DiscreteDomain<Date> HOURS = new DiscreteDomain<Date>() { public Date next(Date value) { return addHours(value, 1); } private Date addHours(Date value, int i) { Calendar cal = Calendar.getInstance(); cal.setTime(value); cal.add(Calendar.HOUR_OF_DAY, i); return cal.getTime(); } public Date previous(Date value) { return addHours(value, -1); } public long distance(Date start, Date end) { Calendar cal1 = Calendar.getInstance(); cal1.setTime(start); Calendar cal2 = Calendar.getInstance(); cal2.setTime(end); return cal2.getTimeInMillis() - cal1.getTimeInMillis(); } public Date minValue() { return new Date(Long.MIN_VALUE); } public Date maxValue() { return new Date(Long.MAX_VALUE); } }; If I merely sysout the output, I get the closed set [Thu Feb 24 00:00:00 EST 2011..Thu Feb 24 00:02:00 EST 2011] I really want to see each hour in the range, however, so I try a for loop: for (Date hour : hours) { System.out.println(hour); } When running this block, I seem to get an infinite set, beginning at the left side of the range, but not stopping at the right side, making me kill the IDE. What am I doing wrong?
I think this might be due to the behavior of the Iterator returned by the ContiguousSet (returned by Range.asSet()): #Override public UnmodifiableIterator<C> iterator() { return new AbstractLinkedIterator<C>(first()) { final C last = last(); #Override protected C computeNext(C previous) { return equalsOrThrow(previous, last) ? null : domain.next(previous); } }; } private static boolean equalsOrThrow(Comparable<?> left, #Nullable Comparable<?> right) { return right != null && compareOrThrow(left, right) == 0; } private static int compareOrThrow(Comparable left, Comparable right) { return left.compareTo(right); } It only stops when the next computed value is equal to the right bound of the range. In your case, have you tried calling it using Thu Feb 24 02:00:00 instead of Thu Feb 24 00:02:00 for the right bound of your range? I think this behavior is problematic, and it might be worth asking if equalsOrThrow() could be changed to check for left <= right instead of left == right Also, your distance() method is incorrect. It should return the distance in hours, not in milliseconds, according to the method contract. EDIT All this being said, I believe the real problem is that, according to the DiscreteDomain's javadoc: A discrete domain always represents the entire set of values of its type; it cannot represent partial domains such as "prime integers" or "strings of length 5." In your case, you are attempting to create a discrete domain over hourly dates, which is a partial domain of all dates. This is, I think, the root cause of the problem. When you have a partial domain, the equalsOrThrow method becomes unreliable, and it can "miss" the right bound of your range.
I just tried this and it worked fine for me. #eneveu already pointed out the issue with your distance method as well. I'm also guessing that there's some minor difference at the millisecond level between start and end which means that you'll never actually get a Date equal to end by adding hours to start. However, that's all just symptoms of using the classes in a way they aren't designed to work. The Javadoc for DiscreteDomain states: A discrete domain always represents the entire set of values of its type; it cannot represent partial domains such as "prime integers" or "strings of length 5." A DiscreteDomain of "hours" does not represent the domain of all possible Date objects and as such breaks its contract.