Monday, July 1, 2013

Форматирование даты/времени в Android приложении

Одним из аспектов локализации приложения является представление времени и даты в соответствии с настройками устройства. Различные страны/регионы используют разные форматы, как следствие, нужно учитывать ряд моментов.

Первая мысль, которая приходит в голову - это использование java.text.DateFormat(и как частный случай SimpleDateFormat):

Calendar cal = new GregorianCalendar(2013, 11, 20);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(cal.getTime());
// date == "2013-12-20"

Данный подход хорошо работает, скажем, для вэб сервиса, но он совсем неприемлем для мобильных приложений. Т.к. с большой долей вероятности для конечного пользователя шаблон форматирования не является привычным. Например, не ясно когда часы(суточное время) должны быть представлены в 12-и или 24-х часовом формате.

Более правильный решение - это использовать android.text.format.DateFormat. Класс имеет ряд методов, возвращающих шаблон представления даты/времени в соответствии с системной локалью: getDateFormat(), getTimeFormat() и т.д.

Calendar cal = new GregorianCalendar(2013, 11, 20);
DateFormat df = android.text.format.DateFormat.getDateFormat(this); 
String date = df.format(cal.getTime());
// date == "12/20/2013"

Недостаток - это полное отсутствие гибкости. Скажем, что если я не хочу показывать год или наоборот - добавить день недели? Как можно догадаться, применять такое форматирование можно лишь в частном случае, когда нет жестких требований.

Мы подошли вплотную к правильному решению - DateUtils. Класс предоставляет семейство методов formatDateTime() и formatDateRange, принимающие флаги в качестве параметров, указывающие, какие поля нужно включить в шаблон. Преимущество в том, что форматирование осуществляется автоматически с учетом локали пользователя, избавляя нас от обработки всех тонкостей вручную:

Calendar cal = new GregorianCalendar(2013, 11, 20);
String date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20"
date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR);
// date == "12/20/2013"
date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME);
// date == "00:00, 12/20/2013"

DateUtils.formatDateRange() необходим для форматирования временного диапазона(например, "Jan 5 - Feb 12"). Может возникнуть логичный вопрос. Для чего это нужно, если можно выполнить конкатенацию двух дат с использованием formatDateTime()? Помимо того что он проще, в опр. условиях будет выполнена оптимизация представления даты за счет уменьшения количества отображаемых полей(например, если год/месяц не меняется в рамках диапазона):

Calendar cal1 = new GregorianCalendar(2013, 11, 20);
Calendar cal2 = new GregorianCalendar(2013, 11, 25);
Calendar cal3 = new GregorianCalendar(2014, 0, 5);
String date = DateUtils.formatDateRange(this, cal1.getTimeInMillis(), cal2.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20 - 24"
date = DateUtils.formatDateRange(this, cal1.getTimeInMillis(), cal3.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20, 2013 - January 4, 2014"

Единственная вещь в formatDateRange() на которую следует обратить внимание - округления даты. Возможно вы заметили в примере выше, что верхняя граница диапазона была округлена в меньшую сторону(до 24 декабря вместо 25-го). Это произошло из-за того, что было выполнено отсечение по суточной границе(оригинальный текст - that's because it cuts off at midnight). Если добавить миллисекунды, то диапазон будет представлен верно.

Чтобы ваше приложение правильно представляло дату/время и по прежнему имело возможность контролировать формат DateUtils - хорошая отправная точка. Зная и умело используя флаги, можно добиться определенной гибкости.

Замечания и доп. материалы

  • Примеры приведенные выше отображают дату/время с учетом текущих(моих) настроек локали. В вашем случае, отформатированные данные, могут выглядеть иначе.
  • Оригинальная статья

No comments:

Post a Comment