Опубликован: 11.12.2003 | Уровень: специалист | Доступ: платный
Лекция 14:

Пакет java.util

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >
Аннотация: Эта лекция посвящена пакету java.util, в котором содержится множество вспомогательных классов и интерфейсов. Они настолько удобны, что практически любая программа использует эту библиотеку. Центральную часть в изложении занимает тема контейнеров, или коллекций, - классов, хранящих упорядоченные ссылки на ряд объектов. Они были существенно переработаны в ходе создания версии Java2. Также рассматриваются классы для работы с датой, для генерации случайных чисел, обеспечения поддержки многих национальных языков в приложении и др.

Работа с датами и временем

Класс Date

Класс Date изначально предоставлял набор функций для работы с датой - для получения текущего года, месяца и т.д. Однако сейчас все перечисленные методы не рекомендованы к использованию и практически всю функциональность для этого предоставляет класс Calendar.

Существует несколько конструкторов класса Date, однако рекомендовано к использованию два:

Date() и Date(long date)

Второй конструктор принимает в качестве параметра значение типа long, указывающее на количество миллисекунд, прошедших с 1 января 1970 г., 00:00:00 по Гринвичу. Первый конструктор создает экземпляр, соответствующий текущему моменту. Фактически это эквивалентно второму варианту new Date(System.currentTimeMillis()). Можно уже после создания экземпляра класса Date использовать метод setTime(long time) для того, чтобы задать нужное время.

Для сравнения дат служат методы after(Date date) и before(Date date), которые возвращают булевское значение, в зависимости от того, выполнено условие или нет. Метод compareTo(Date anotherDate) возвращает значение типа int, которое равно -1, если дата меньше сравниваемой, 1 - если больше и 0 - если даты равны. Метод toString() возвращает строковое описание даты. Однако для более понятного и удобного преобразования даты в текст рекомендуется пользоваться классом SimpleDateFormat, определенным в пакете java.text.

Классы Calendar и GregorianCalendar

Более развитые средства для работы с датами представляет класс Calendar. Calendar является абстрактным классом. Для различных платформ реализуются конкретные подклассы календаря. На данный момент существует реализация Григорианского календаря - GregorianCalendar. Экземпляр этого класса получается путем вызова статического метода getInstance(), который возвращает экземпляр класса GregorianCalendar. Подклассы класса Calendar должны интерпретировать объект Date по-разному. В будущем предполагается реализовать также лунный календарь, используемый в некоторых странах.

Calendar обеспечивает набор методов, позволяющих манипулировать различными "частями" даты, т.е. получать и устанавливать дни, месяцы, недели и т.д.

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

YEAR = 1970, MONTH = JANUARY, DATE = 1 и т.д.

Для считывания и установки различных "частей" даты используются методы get(int field), set(int field, int value), add(int field, int amount), roll(int field, int amount), переменная типа int с именем field указывает на номер поля, с которым нужно произвести операцию. Для удобства все эти поля определены в Calendar как статические константы типа int.

Рассмотрим подробнее порядок выполнения перечисленных методов.

Метод set(int field,int value).

Как уже говорилось, данный метод производит установку какого-либо поля даты. На самом деле после вызова этого метода немедленного пересчета даты не производится. Пересчет даты будет осуществлен только после вызова методов get(), getTime() или getTimeInMillis(). Таким образом, последовательная установка нескольких полей не вызовет ненужных вычислений. Помимо этого, появляется еще один интересный эффект. Рассмотрим следующий пример. Предположим, что дата установлена на последний день августа. Необходимо перевести ее на последний день сентября. Если бы внутреннее представление даты изменялось после вызова метода set, то при последовательной установке полей мы получили бы вот такой эффект:

public class Test {
  public Test() {
  }
  public static void main(String[] args) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMMM dd HH:mm:ss");
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR,2002);
    cal.set(Calendar.MONTH,Calendar.AUGUST);
    cal.set(Calendar.DAY_OF_MONTH,31);
    System.out.println(" Initialy set date: " + sdf.format(cal.getTime()));
    cal.set(Calendar.MONTH,Calendar.SEPTEMBER);
    System.out.println(" Date with month changed : " + sdf.format(cal.getTime()));
    cal.set(Calendar.DAY_OF_MONTH,30);
    System.out.println(" Date with day changed : " + sdf.format(cal.getTime()));

  }
}
Пример 14.1.

Результатом будет:

Initialy set date: 2002 August 31 22:57:47
Date with month changed : 2002 October 01 22:57:47
Date with day changed : 2002 October 30 22:57:47
Пример 14.2.

Как мы видим, в данном примере при изменении месяца день месяца остался неизменным и было унаследовано его предыдущее значение. Но поскольку в сентябре 30 дней, дата автоматически была переведена на 1 октября, и когда было бы установлено 30 число, оно относилось бы уже к октябрю. В следующем примере считывание даты не производится, соответственно, ее вычисление не выполняется до тех пор, пока все поля не установлены:

public class Test {
  public Test() {
  }
  public static void main(String[] args) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMMM dd HH:mm:ss");

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR,2002);
    cal.set(Calendar.MONTH,Calendar.AUGUST);
    cal.set(Calendar.DAY_OF_MONTH,31);
    System.out.println(" Initialy set date: " + sdf.format(cal.getTime()));
   cal.set(Calendar.MONTH,Calendar.SEPTEMBER);
   cal.set(Calendar.DAY_OF_MONTH,30);
   System.out.println(" Date with day and month changed : " + sdf.format(cal.getTime()));
  }
}
Пример 14.3.

Результатом будет:

Initialy set date: 2002 August 31 23:03:51
Date with day and month changed: 2002 September 30 23:03:51
Пример 14.4.
Метод add(int field,int delta).

Добавляет некоторое смещение к существующей величине поля. В принципе, то же самое можно сделать с помощью set(f, get(f) + delta).

В случае использования метода add следует помнить о двух правилах:

  1. Если величина поля изменения выходит за диапазон возможных значений данного поля, то производится деление по модулю данной величины, частное суммируется со следующим по старшинству полем.
  2. Если изменяется одно из полей, причем, после изменения младшее по отношению к изменяемому полю принимает некорректное значение, то оно изменяется на то, которое максимально близко к "старому".
public class Test {

   public Test() {
   }
   public static void main(String[] args) {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMMM dd HH:mm:ss");
      Calendar cal = Calendar.getInstance();
      cal.set(Calendar.YEAR,2002);
      cal.set(Calendar.MONTH,Calendar.AUGUST);
      cal.set(Calendar.DAY_OF_MONTH,31);
      cal.set(Calendar.HOUR_OF_DAY,19);
      cal.set(Calendar.MINUTE,30);
      cal.set(Calendar.SECOND,00);
      System.out.println("Current date: " + sdf.format(cal.getTime()));
      cal.add(Calendar.SECOND,75);
      System.out.println("Current date: " + sdf.format(cal.getTime()));
      cal.add(Calendar.MONTH,1);
      System.out.println("Current date: " + sdf.format(cal.getTime()));
   }
}
Пример 14.5.

Результатом будет:

Current date: 2002 August 31 19:30:00
Current date: 2002 August 31 19:31:15
Current date: 2002 September 30 19:31:15
Пример 14.6.
Метод roll(int field,int delta).

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

public class Test {

   public Test() {
   }
   public static void main(String[] args) {
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMMM dd HH:mm:ss");
      Calendar cal = Calendar.getInstance();
      cal.set(Calendar.YEAR,2002);
      cal.set(Calendar.MONTH,Calendar.AUGUST);
      cal.set(Calendar.DAY_OF_MONTH,31);
      cal.set(Calendar.HOUR_OF_DAY,19);
      cal.set(Calendar.MINUTE,30);
      cal.set(Calendar.SECOND,00);
      System.out.println("Current date: " + sdf.format(cal.getTime()));
      cal.roll(Calendar.SECOND,75);
      System.out.println("Rule 1: " + sdf.format(cal.getTime()));
      cal.roll(Calendar.MONTH,1);
      System.out.println("Rule 2: " + sdf.format(cal.getTime()));
   }
}
Пример 14.7.

Результатом будет:

Current date: 2002 August 31 19:30:00
Rule 1: 2002 August 31 19:30:15
Rule 2: 2002 September 30 19:30:15
Пример 14.8.

Как видно из результатов работы приведенного выше кода, действие правила 1 изменилось по сравнению с методом add, а правило 2 действует так же.

Класс TimeZone

Класс TimeZone предназначен для совместного использования с классами Calendar и DateFormat. Класс абстрактный, поэтому от него порождать объекты нельзя. Вместо этого определен статический метод getDefault(), который возвращает экземпляр наследника TimeZone с настройками, взятыми из операционной системы, под управлением которой работает JVM. Для того, чтобы указать произвольные параметры, можно воспользоваться статическим методом getTimeZone(String ID), в качестве параметра которому передается наименование конкретного временного пояса, для которого необходимо получить объект TimeZone. Набор полей, определяющих возможный набор параметров для getTimeZone, нигде явно не описывается. Вместо этого определен статический метод String[] getAvailableIds(), который возвращает возможные значения для параметра getTimeZone. Так можно определить набор возможных параметров для конкретного временного пояса (рассчитывается относительно Гринвича) String[] getAvailableIds(int offset).

Рассмотрим пример, в котором на консоль последовательно выводятся:

  • временная зона по умолчанию;
  • список всех возможных временных зон;
  • список временных зон, которые совпадают с текущей временной зоной.
public class Test {
   public Test() {
   }
   public static void main(String[] args) {
      Test test = new Test();
      TimeZone tz = TimeZone.getDefault();
      int rawOffset = tz.getRawOffset();
      System.out.println("Current TimeZone" + tz.getDisplayName() + tz.getID() + "\n\n");
      // Display all available TimeZones
      System.out.println("All Available TimeZones \n");
      String[] idArr = tz.getAvailableIDs();
      for(int cnt=0;cnt < idArr.length;cnt++){
      tz = TimeZone.getTimeZone(idArr[cnt]);
      System.out.println(test.padr(tz.getDisplayName() + 
         tz.getID(),64) + " raw offset=" + tz.getRawOffset() + 
         ";hour offset=(" + tz.getRawOffset()/ (1000 * 60 * 60 ) + ")");
      }
      // Display all available TimeZones same as for Moscow
      System.out.println("\n\n TimeZones same as for Moscow \n");
      idArr = tz.getAvailableIDs(rawOffset);
      for(int cnt=0;cnt < idArr.length;cnt++){
         tz = TimeZone.getTimeZone(idArr[cnt]);
         System.out.println(test.padr(tz.getDisplayName()+ 
            tz.getID(),64) + " raw offset=" + tz.getRawOffset() + 
            ";hour offset=(" + tz.getRawOffset()/ (1000 * 60 * 60 ) + ")");
      }
   }
   String padr(String str,int len){
      if(len - str.length() > 0){
         char[] buf = new char[len - str.length()];
         Arrays.fill(buf,' ');
         return str + new String(buf);
      }else{
         return str.substring(0,len);
      }
   }
}
Пример 14.9.

Результатом будет:

Current TimeZone Moscow Standard TimeEurope/Moscow
TimeZones same as for Moscow 
Eastern African TimeAfrica/Addis_Aba raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Asmera raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Dar_es_Sa raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Djibouti raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Kampala raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Khartoum raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Mogadishu raw offset=10800000;hour offset=(3)
Eastern African TimeAfrica/Nairobi raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Aden raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Baghdad raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Bahrain raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Kuwait raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Qatar raw offset=10800000;hour offset=(3)
Arabia Standard TimeAsia/Riyadh raw offset=10800000;hour offset=(3)
Eastern African TimeEAT raw offset=10800000;hour offset=(3)
Moscow Standard TimeEurope/Moscow raw offset=10800000;hour offset=(3)
Eastern African TimeIndian/Antananar raw offset=10800000;hour offset=(3)
Eastern African TimeIndian/Comoro raw offset=10800000;hour offset=(3)
Eastern African TimeIndian/Mayotte raw offset=10800000;hour offset=(3)
Пример 14.10.

Класс SimpleTimeZone

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

В классе SimpleTimeZone определено три конструктора. Рассмотрим наиболее полный с точки зрения функциональности вариант, который, помимо временной зоны, задает летнее и зимнее время.

public SimpleTimeZone(int rawOffset,
      String ID,
      int startMonth,
      int startDay,
      int startDayOfWeek,
      int startTime,
      int endMonth,
      int endDay,
      int endDayOfWeek,
   int endTime)

rawOffset - временное смещение относительно гринвича;

ID - идентификатор временной зоны (см. пред.параграф);

startMonth - месяц перехода на летнее время;

startDay - день месяца перехода на летнее время*;

startDayOfWeek - день недели перехода на летнее время*;

startTime - время перехода на летнее время (указывается в миллисекундах);

endMonth - месяц окончания действия летнего времени;

endDay - день окончания действия летнего времени*;

endDayOfWeek - день недели окончания действия летнего времени*;

endTime - время окончания действия летнего времени (указывается в миллисекундах).

Перевод часов на зимний и летний вариант исчисления времени определяется специальным правительственным указом. Обычно переход на летнее время происходит в 2 часа в последнее воскресенье марта, а переход на зимнее время - в 3 часа в последнее воскресенье октября.

Алгоритм расчета таков:

  • если startDay=1 и установлен день недели, то будет вычисляться первый день недели startDayOfWeek месяца startMonth (например, первое воскресенье);
  • если startDay=-1 и установлен день недели, то будет вычисляться последний день недели startDayOfWeek месяца startMonth (например, последнее воскресенье);
  • если день недели startDayOfWeek установлен в 0, то будет вычисляться число startDay конкретного месяца startMonth ;
  • для того, чтобы установить день недели после конкретного числа, специфицируется отрицательное значение дня недели. Например, чтобы указать первый понедельник после 23 февраля, используется вот такой набор: startDayOfWeek=-MONDAY, startMonth=FEBRUARY, startDay=23
  • для того, чтобы указать последний день недели перед каким-либо числом, указывается отрицательное значение этого числа и отрицательное значение дня недели. Например, для того, чтобы указать последнюю субботу перед 23 февраля, необходимо задать такой набор параметров: startDayOfWeek=-SATURDAY, startMonth=FEBRUARY, startDay=-23;
  • все вышеперечисленное относится также и к окончанию действия летнего времени.

Рассмотрим пример получения текущей временной зоны с заданием перехода на зимнее и летнее время для России по умолчанию.

public class Test {
   public Test() {
   }
   public static void main(String[] args) {
      Test test = new Test();
      SimpleTimeZone stz = new SimpleTimeZone(
         TimeZone.getDefault().getRawOffset()
         ,TimeZone.getDefault().getID()
         ,Calendar.MARCH
         ,-1
         ,Calendar.SUNDAY
         ,test.getTime(2,0,0,0)
         ,Calendar.OCTOBER
         ,-1
         ,Calendar.SUNDAY
         ,test.getTime(3,0,0,0)
         );
   System.out.println(stz.toString());
   }
   int getTime(int hour,int min,int sec,int ms){
      return hour * 3600000 + min * 60000 + sec * 1000 + ms;
   }
}
Пример 14.11.

Результатом будет:

java.util.SimpleTimeZone[id=Europe/Moscow,offset=10800000,dstSavings=3600000,useDaylight=true,
startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=7200000,startTimeMode=0,
endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=10800000,endTimeMode=0]
Пример 14.12.
< Лекция 13 || Лекция 14: 12345 || Лекция 15 >
Вадим Кудаев
Вадим Кудаев

Добрый день! Начал проходить курс "Программирование на Java". Как я понимаю,курс создавался приблизительно в 2015 году. Не потерял ли данный курс свою актуальность? Стоит ли проходить его в 2023 году, или же лучше найти что-то более новое?

Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Данила Некрасов
Данила Некрасов
Россия, Пермь, ПНИПУ
Сергей Федоров
Сергей Федоров
Россия