2012-06-12 1 views
2

Для повторных событий я хочу показать количество дней, оставшихся до следующего события в приложении для Android-календаря.-1469913 осталось дней при расчете повторяющихся событий в Android

Пример:

Today: 2012-06-12 
Reoccurring event: 19th June 
=> 13 days left 

Для того, чтобы достичь этого, я сохранить первое вхождение в объект типа данных Calendar:

private Calendar cal; 
... 
cal = new GregorianCalendar(); 
cal.set(Calendar.YEAR, USER_INPUT_YEAR); 
cal.set(Calendar.MONTH, USER_INPUT_MONTH); 
... 

Для расчета дней осталось ли использовать этот функции:

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); 
    } 
} 

который использует эту функцию:

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(); 
    } 
} 

Кстати, чтобы получить начальную дату, я ожидаю, чтобы найти значение YYYY-MM-DD и разобрать его, как это:

(new SimpleDateFormat("yyyy-MM-dd")).parse(INPUT_DATE_STRING) 

Это нормально работает в большинстве случаев, но некоторые пользователи сообщают, что они видят цифры, такие как -1469913 как «дни, оставшиеся дни». Как это может произойти?

Я думал, что дата (cal) может быть не установлена ​​или недействительна, но тогда она будет показывать -1 или что-то в этом роде, так как есть нулевые проверки во всех частях, не так ли?

-1469913 означает что-то вроде -4027 лет назад! Поскольку это повторяющееся событие, я думал, что информация, оставленная в «дни слева», всегда должна быть между 0 и 366. Что может заставить этот код создать такой номер? Означает ли это, что getNextOccurrence() возвращает данные, которые были 4027 лет назад? Я не могу объяснить это поведение.

Надеюсь, вы можете мне помочь. Огромное спасибо заранее!

Edit: Как это может быть полезно: год неверных дат всегда выводятся как 1 при использовании DateFormat.getDateInstance().format(), например, Jan 3, 1. Тем не менее, результат getDaysLeft() составляет примерно 4 тыс. Лет.

Редактировать # 2: Я выяснил, что дата, как 1--22199-1, является той, которая производит выход «4k years left». Тем не менее, он успешно анализируется (new SimpleDateFormat("yyyy-MM-dd")).parse(). Аналогично, -1-1-1-91- правильно разобран как Jan 1, 2.

Редактировать # 3: Оказалось, что такая простая дата, как «0000-01-03», вызвала все проблемы. Когда я вывожу время в миллисекундах, он говорит -62167222800000. Когда я потом вывожу его в строку GMT, он говорит 0001-01-03 - странно, не так ли? И когда я установил год 1900, время в миллисе внезапно -122095040400000. Зачем?

+0

Используйте Joda время API. – adatapost

+0

Джода прикомандирован - но разве это было бы довольно легко отлаживать? –

+0

Почему Джода? Класс «Календарь» на самом деле довольно полезен и прост в обращении, не так ли? Но это непросто отлаживать для меня, поскольку у меня нет этих входных данных для тестирования. Но это может быть только недопустимая дата, поскольку повторяющиеся события всегда будут 366 дней в будущем или меньше, или я бегу против базовой математики? – caw

ответ

1

Работая с датами, это может быть очень сложно выяснить эти неясные ошибки, прежде чем они произойдут с пользователем в дикой природе.Во многих случаях, может быть, стоит потратить время, чтобы сделать небольшой единичный тест, который выдает несколько десятков миллионов дат в машинном оборудовании и выясняет, появляются ли какие-либо экстремальные ответы.

Также this, возможно, стоит прочитать. Вы не поймете, насколько плох класс даты java, прежде чем вы попробуете что-то, что лучше. :)

EDIT: Если пользователи дают очень высокое входное значение, то может быть переполнение числа, когда вы бросаете свой результат до целого числа в getDaysLeft(). Просто держи его как долго. Или еще лучше: принимать только разумное значение входного сигнала, предупреждает пользователя, если они ввести 20120 год или что-то подобное :)

EDIT2: Я был неправ в моем последнем редактирования, .ceil() защищает от числа переполняется. Честно говоря, я больше не знаю, как может произойти эта ошибка.

EDIT3: В ответ на ваше третье редактирование: Помните, что Date и Calendar используют Unix time. Это означает, что время, представленное нулем, равно 1970 году. Все до 1970 года будет представлено отрицательным значением.

EDIT4: Помните, что javas calendar-classes сосет. Этот фрагмент кода показывает, что ошибка на самом деле в календарном классе:

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()); 

Выход:

-125629491600000 
1325545200000 

однако это будет очень трудно отследить точную ошибку, которая вызывает это. Причина, по которой все эти ошибки остаются, заключается в том, что их устранение может привести к поломке устаревших систем по всему миру. Я предполагаю, что ошибка возникает из-за неспособности дать отрицательные годы. Это, например, даст выход «2013»:

Calendar next = new GregorianCalendar(); 
next.set(Calendar.YEAR, -2012); 
System.out.println(next.get(Calendar.YEAR)); 

Я просто рекомендую вам, чтобы не допустить таких экстремальных значений в вашем входе. Определите приемлемый диапазон и укажите сообщение об ошибке, если значение находится за пределами этих границ. Если вы хотите обрабатывать все возможные даты в каком-либо другом приложении, просто используйте joda time. Вы не пожалеете об этом :)

+0

Спасибо :) Тест-единица с десятками миллионов дат - хорошая идея в целом. Но поскольку 'DateFormat.parse()' может создавать только «null» или действительный объект «Date», неудобный ввод, который вызывает эту ошибку, может быть только строкой даты, которая по ошибке интерпретируется как дата 4k лет назад, правильно? – caw

+0

Кажется, что getNextOccurrence() должен возвращать null, если дата в прошлом, так что этого недостаточно, чтобы объяснить большое отрицательное значение. Это также защищает от переполнения числа, которое происходит, если cal установлен в год Integer.MAX_VALUE. – pgsandstrom

+0

Извините! Это утверждение if не было в исходном коде, я просто добавил при попытке отладки приложения и забыл удалить его снова. Так что в коде, который произвел отрицательные числа, этого не было. Поэтому результат 'getNextOccurrence()', который раньше, чем текущее время, определенно является причиной. – caw

0

У вас есть отрицательные значения в днях, которые могут быть вызваны тем, что пользователь ввел дату следующего события, которое является любой предшествующей датой.

Я думаю, вы должны рассчитать daysLeft как таким образом,

  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 
      } 
+0

Спасибо! Но если вы посмотрите на мое последнее редактирование вопроса, вы увидите, что это не похоже на решение. – caw

+0

@MarcoW. По моим словам, вы получаете отрицательные значения daysleft, это может быть только из-за неправильных вычислений, поэтому я на 100% уверен, что эта проблема лежит только в той части кода, действительно ли вы попробовали мой ответ? или просто упустил это из виду, поскольку он только делает расчеты. Все равно попытается проанализировать ваш код/​​вопрос больше и опубликует что-нибудь, если я получу. – MKJParekh

+0

Ну, вы видели мои изменения на вопрос? Как вы можете видеть, класс «Календарь», кажется, испортит и вычисляет неправильные метки времени. В противном случае 'getNextOccurrence()' не может быть раньше, чем сегодня. – caw