Освой Java играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Date
Calendar
GregorianCalendar
Класс DateFormat
Класс SimpleDateFormat
TimeZone
Класс Date предназначен для работы с текущими датой и временем и позволяет отталкиваться от них для решения своих задач. При выходе новых версий Java часть методов класса была перемещена в классы Calendar и DateFormat.
При импорте выбирайте java.util.Date, а не java.sql.Date.
У класса есть два конструктора:
Date() Date(long milliseconds)
Первый конструктор без параметров инициализирует объект текущей датой и временем. Во втором конструкторе вы можете указать количество миллисекунд, прошедших с полуночи 1 января 1970 года.
Методы:
Если вы посмотрите документацию, то увидите, что существует множество методов для получения или установки отдельных компонентов времени и даты, например, getMinutes()/setMinutes() и др. Все они являются устаревшими и вместо них следует использовать класс Calendar.
Простой пример вывода даты на экран.
// Создадим объект Date
Date date = new Date();
mInfoTextView.setText(date.toString());
С помощью метода getTime() можно отобразить количество миллисекунд, прошедших с 1 января 1970 года. Обновим пример
// Создадим объект Date
Date date = new Date();
long millis = date.getTime();
infoTextView.setText(String.valueOf(millis));
Или так:
long msTime = System.currentTimeMillis();
Date curDateTime = new Date(msTime);
mInfoTextView.setText(curDateTime.toString());
Впрочем, предыдущий код избыточен, так как конструктор Date() без параметров уже содержит текущее время и дату.
Date anotherCurDate = new Date();
mInfoTextView.setText(anotherCurDate.toString());
При работе с датами важно учитывать локаль (регион), если хотим увидеть вывод даты на нужном языке. Локаль можно задать в строковых функциях. Выводим дату на немецком языке.
Locale locale = new Locale("de");
Locale.setDefault(locale);
Date now = new Date();
mInfoTextView.setText(
String.format(locale, "%tc\n", now) + // длинная строка
String.format(locale, "%tD\n", now) + //(MM/DD/YY)
String.format(locale, "%tF\n", now) + //(YYYY-MM-DD)
String.format(locale, "%tr\n", now) + //Full 12-hour time
String.format(locale, "%tz\n", now) + //Time zone GMT offset
String.format(locale, "%tZ\n", now) //Localized time zone abbreviation
);
Абстрактный класс Calendar позволяет преобразовать время в миллисекундах в более удобном виде - год, месяц, день, часы, минуты, секунды. Существуют также подклассы, например, GregorianCalendar.
Переменная типа boolean под именем areFieldsSet указывает, были установлены компоненты времени. Переменная fields - это массив целочисленных значений, содержащий компоненты времени. Переменная isSet - массив типа boolean, указывающий, был ли установлен специфический компонент времени. Переменная time (тип long) содержит текущее время объекта. Переменная isTimeSet (тип boolean) указывает, что было установлено текущее время.
У класса много методов. Вкратце опишем часть из них:
Также в календаре определены много различных констант: AUGUST и другие месяцы, SATURDAY и другие дни недели, HOUR и т.д.
С помощью специальных классов DateFormat и др., мы можем получить отформатированную строку (см. примеры ниже). Но мы можем и вручную обрабатывать получаемый результат.
Получим текущую дату.
Calendar now = Calendar.getInstance();
mInfoTextView.setText(now.toString());
Мы получим очень длинную строку, не слишком удобную для чтения: java.util.GregorianCalendar[time=1654754513863,areFieldsSet=true,lenient=true,zone=Europe/Moscow,firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=5,WEEK_OF_YEAR=24,WEEK_OF_MONTH=2,DAY_OF_MONTH=9,DAY_OF_YEAR=160,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=9,HOUR_OF_DAY=9,MINUTE=1,SECOND=53,MILLISECOND=863,ZONE_OFFSET=10800000,DST_OFFSET=0].
Можно получить более удобный вывод даты, применив функцию getTime().
Calendar now = Calendar.getInstance();
mInfoTextView.setText(now.getTime().toString());
Получим более вразумительный ответ: Thu Jun 09 09:13:36 GMT+03:00 2022
Получить текущий месяц можно, например, так:
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH);
mInfoTextView.setText(String.valueOf(month));
Помните, что отсчёт месяцев идёт от 0. Например, если хотите получить правильные номера месяцев (добавим также небольшое форматирование с ведущим нулём):
Calendar calendar = Calendar.getInstance();
String month = calendar.get(Calendar.MONTH) + 1 + "";
if (month.length() < 2) {
month = "0" + month;
}
mInfoTextView.setText(month);
Даже профессиональные программисты забывают добавлять единицу к месяцу. Однажды в одной версий Android была проблема с месяцами, когда кто-то из команды Google забыл про правило.
Получим текущий день и текущую минуту:
Calendar calendar = Calendar.getInstance();
// текущая минута
int currentMinute = calendar.get(Calendar.MINUTE);
mInfoTextView.setText(String.valueOf(currentMinute));
Оформим в виде метода получение сегодняшней даты в определённом формате.
public void onClick(View view) {
mInfoTextView.setText(getToday());
}
private String getToday() {
// Сегодняшняя дата в формате MM/DD/YYYY
final Calendar calendar = Calendar.getInstance();
return (new StringBuilder().append(calendar.get(Calendar.MONTH) + 1)
.append("/").append(calendar.get(Calendar.DAY_OF_MONTH)).append("/")
.append(calendar.get(Calendar.YEAR)).append(" ")).toString();
}
Текущее время.
public void onClick(View view) {
mInfoTextView.setText(getCurrentTime());
}
private String getCurrentTime() {
// Текущее время в формате HH:MM:SS
final Calendar calendar = Calendar.getInstance();
return (new StringBuilder().append(calendar.get(Calendar.HOUR_OF_DAY))
.append(":").append(calendar.get(Calendar.MINUTE)).append(":")
.append(calendar.get(Calendar.SECOND)).append(" ")).toString();
}
Напишем вспомогательную функцию для вывода основных данных, которые можно получить из класса Calendar:
private String getCalendarInfo() {
Calendar calendar = Calendar.getInstance();
return calendar + "\n" +
calendar.get(Calendar.YEAR)
+ "-"
+ calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT,
Locale.US) + "-" + calendar.get(Calendar.DATE) + "\n" +
"День года: " + calendar.get(Calendar.DAY_OF_YEAR) + "\n" +
"День месяца: " + calendar.get(Calendar.DAY_OF_MONTH) + "\n" +
"День недели: " + calendar.get(Calendar.DAY_OF_WEEK) + "\n" +
"getTime(): " + calendar.getTime() + "\n" +
"getTimeInMillis(): " + calendar.getTimeInMillis() + "\n";
}
Класс GregorianCalendar является подклассом Calendar, который представляет обычный Григорианский календарь. Метод getInstance() класса Calendar обычно возвращает объект класса GregorianCalendar, инициированный текущей датой и временем согласно региональным настройкам.
У класса есть два поля AD и BC - до нашей эры и наша эра.
Кроме стандартных методов, которые есть в классе Calendar, у GregorianCalendar есть метод isLeapYear() для проверки високосного года.
boolean isLeapYear(int year)
Если год високосный, то возвращается true.
Отсчёт месяцев идёт от нуля, поэтому декабрь будет одиннадцатым месяцем. Чтобы не путаться с такими случаями, проще использовать понятные константы:
GregorianCalendar catDay = new GregorianCalendar(2022, Calendar.MARCH,
1);
int dayOfWeek = catDay.get(Calendar.DAY_OF_WEEK);
mInfoTextView.setText("1 марта 2022 года " + (--dayOfWeek) + " день недели");
А получать нужные отрезки времени можно через метод get(). Например, узнать, какой месяц содержится в созданной нами дате можно так:
GregorianCalendar catDay = new GregorianCalendar(2022, Calendar.MARCH,
1);
int month = catDay.get(Calendar.MONTH);
mInfoTextView.setText(String.valueOf(month)); // не забываем затем прибавить единицу в реальном проекте
Изменить состояние объекта можно через метод set(). Например, установим новую дату у нашего объекта.
GregorianCalendar calendar = new GregorianCalendar(1975,
Calendar.DECEMBER, 31);
calendar.set(1976, Calendar.FEBRUARY, 23);
// Убедимся, что возвращает 1 - февраль
mInfoTextView.setText(String.valueOf(calendar.get(Calendar.MONTH)));
Можно сдвинуть дату на определённый период с помощью метода add(). Отодвинем дату на два месяца.
GregorianCalendar calendar = new GregorianCalendar(1975,
Calendar.DECEMBER, 31);
calendar.add(Calendar.MONTH, 2);
mInfoTextView.setText(String.valueOf(calendar.get(Calendar.MONTH)));
Методы getTime() и setTime() работают с объектами Date и полезны для преобразования.
GregorianCalendar calendar = new GregorianCalendar(year, month, day);
Date hireDay = calendar.getTime();
Будьте осторожны, существуют два класса в разных пакетах: java.text.format.DateFormat/android.text.format.DateFormat и java.text.DateFormat/android.text.format.DateFormat. И иногда возникает конфликт имён при сокращённой записи. Поэтому нужно быть осторожным и использовать правильные имена. Методы getXXXFormat() относятся к классу android.text.format.DateFormat, а возвращаемый результат к java.text.DateFormat.
Класс DateFormat является абстрактным классом, с помощью которого можно форматировать и анализировать показания даты и времени. метод getDateInstance() возвращает экземпляр класса DateFormat, который может форматировать информацию о дате.
Чаще всего используется метод format(), позволяющий вывести дату в нужном формате.
Date now = Calendar.getInstance().getTime();
String formattedDate = DateFormat.getDateInstance().format(now);
mInfoTextView.setText(formattedDate);
Результат (текущая дата на запуск примера): 9 июня 2022 г.
Можно получить больше информации, например, получить день недели. Для этого вызываем перегруженную версию getDateInstance().
String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(now);
Результат: четверг, 9 июня 2022 г.
Мы получили строку, где данные разделены запятой. Строковая функция split() с использованием регулярного выражения поможет разбить строку на подстроки.
Date now = Calendar.getInstance().getTime();
String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(now);
String[] splitDate = formattedDate.split(",");
mInfoTextView.setText(splitDate[0]); // выводим первый элемент массива
Результат: четверг
Правила форматирования зависят от записи. Небольшая шпаргалка для общего понимания (для июля).
M -> 7 MM -> 07 MMM -> Июл MMMM -> Июль
Для минут (7 минут)
m -> 7 mm -> 07 mmm -> 007 mmmm -> 0007
Подставляя нужный вариант, вы формируете дату по вашему вкусу. Для дней недели используются E, EEEE. Посторонние слова, которые не будут распознаны как элементы даты, будут выводиться без изменений.
Метод System.currentTimeMillis() возвращает текущую дату в миллисекундах. С помощью метода DateFormat.format() можно привести дату в нужный формат. Например:
CharSequence date = DateFormat.format("MM/dd/yy h:mmaa", System.currentTimeMillis());
mInfoTextView.setText(date);
Усложним пример, добавим собственную информацию. Допустим, сегодня у нас родился котёнок, фиксируем событие.
CharSequence date = DateFormat.format(
"День рождения кота: EEEE MMMM/dd/yy h:mmaa",
System.currentTimeMillis());
mInfoTextView.setText(date);
Формально мы можем использовать DateFormat из пакета Java, а не Android. Пример работает, но лучше избегать такого кода.
public void onClick(View view) {
java.text.DateFormat timeFormat = DateFormat.getTimeFormat(this); // 18:10
java.text.DateFormat numericDateFormat = DateFormat.getDateFormat(this); // 11.11.2016
java.text.DateFormat mediumDateFormat = DateFormat.getMediumDateFormat(this); // 11 нояб. 2016 г.
java.text.DateFormat longDateFormat = DateFormat.getLongDateFormat(this); // 11 ноября 2016 г.
mInfoTextView.setText(timeFormat.format(new Date()) + "\n" +
numericDateFormat.format(new Date()) + "\n" +
mediumDateFormat.format(new Date()) + "\n" +
longDateFormat.format(new Date()));
}
Класс SimpleDateFormat является подклассом класса DateFormat и позволяет определять собственные шаблоны форматирования для отображения даты и времени.
Символы форматирования строки
и так далее.
Количество повторений символа определяет способ представления даты. Например, можно указать hh:mm:ss, а можно h:m:s. В первом случае будет отображаться ноль перед цифрой.
Напомним, что в Java дата хранится в миллисекундах, которые отсчитываются от 1 января 1970 года. Простейший способ перевести непонятное большое число типа long в нормальную дату (есть и другие способы):
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
String dateString = formatter.format(new Date(dateInMillis)));
Соответственно, получить текущее время и дату можно так:
Calendar calendar = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = formatter.format(calendar.getTime());
mInfoTextView.setText(formattedDate);
Результат: 2022-06-09 15:50:59
Класс TimeZone позволяет работать с часовыми поясами, смещёнными относительно Гринвича, известного как универсальное глобальное время (UTC). Класс также учитывает летнее время.
Если необходимо определить установленный текущий часовой пояс на устройстве, то воспользуйтесь методом TimeZone.getDefault():
TimeZone timezone = TimeZone.getDefault();
String timeZoneName = timezone.getDisplayName();
int timeZoneOffset = timezone.getRawOffset() / (60 * 60 * 1000);
mInfoTextView.setText("Мой часовой пояс " + timeZoneName + ": " + timeZoneOffset);
Метод getAvailableIDs() возвращает список установленных в системе идентификаторов часовых поясов, из которых можно извлечь полезную информацию, в частности через метод getTimeZone(). Наш испытательный стенд для этой задачи не слишком подойдёт для примера, поэтому временно изменим макет приложения.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/timezone_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Получаемый список поместим в Spinner - список получается очень большой.
package ru.alexanderklimov.expresscourse;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.TimeZone;
public class MainActivity extends AppCompatActivity {
private TextView timeZoneTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Spinner spinner = findViewById(R.id.spinner);
timeZoneTextView = findViewById(R.id.timezone_text);
String[] idArray = TimeZone.getAvailableIDs();
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, idArray);
spinnerAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);
spinner
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
String selectedId = (String) (parent
.getItemAtPosition(position));
TimeZone timezone = TimeZone.getTimeZone(selectedId);
String TimeZoneName = timezone.getDisplayName();
int TimeZoneOffset = timezone.getRawOffset()
/ (60 * 60 * 1000);
timeZoneTextView.setText(TimeZoneName + ": " + TimeZoneOffset);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
}
}
Класс SimpleTimeZone - подкласс класса TimeZone и позволяет работать с часовыми поясами в Григорианском календаре.