Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Работа с датами

Поиск:
Спасибо LSD за отличную статью

Работа с датами в Java.


Календарь - система времясчисления. Основанием К. служат астрономические явления: обращение земли вокруг солнца (солнечный К.), луны вокруг земли (лунный К.) и земли вокруг своей оси.
Малый энциклопедический словарь Брокгауза и Ефрона

В Древнем Риме К. сначала называли книги, в которые кредиторы записывали проценты, вносившиеся ежемесячно в дни календ (отсюда название), позднее К. стали именовать книги, расписывающие культовые празднества, дни рождения императоров, дни собраний сената.
Большая советская энциклопедия


Базовым классом для хранения даты в Java, является java.util.Date. От него унаследовано несколько классов java.sql.Date, java.sql.Time, java.sql.Timestamp, которые предназначены для работы с базами данных. Для манипулирования датами предназначен класс java.util.Calendar, он позволяет для любой даты узнать такие вещи как: день недели, месяц, номер недели в году и месяце, номер дня в году и т.п. Еще одним важным классом для работы с датой является java.util.TimeZone.

Класс TimeZone представляет собой временную зону. Временная зона характеризует насколько время в данном регионе, смещено относительно некой нулевой точки. В качестве нулевой точки выбрано время на гринвичском меридиане GMT (Greenwich Mean Time), иначе его называют - UTC (Universal Coordinated Time - всеобщее скоординированное время). Все остальные пояса указывают как смещение от GMT, например Москва это GMT+03:00, что означает, что для получения московского времени, надо к времени по Гринвичу прибавить 3 часа 0 минут. Временная зона это не одно и то же, что и часовой пояс: не везде временная зона совпадает с часовым поясом, в часовом поясе нет понятия зимнего/летнего времени. Часовой пояс характеризует только географическое положение территории, но не используемое на ней время. Существует несколько стандартных имен временных зон:
  • GMT (Greenwich Mean Time) - Гринвичское время (точка отсчета времени; GMT действует в Великобритании, Ирландии, Португалии)
  • UTC (Universal Coordinated Time) - всеобщее время, то же, что GMT
  • BST (British Summer Time) - Британское, английское летнее время
  • CET (Central European Time) - центральноевропейское время (+1 от Гринвича, вся континетальная западная Европа кроме Португалии)
  • EST (Eastern Standard Time) - восточное стандартное время (-5 часов от Гринвича, Атлантическое побережье США) или восточное стандартное время (+10 часов от Гринвича, Австралия)
  • EET (East European Time) - восточноевропейское поясное время (+2 часа от Гринвича)
  • MST (Mountain Standard Time) - стандартное горное время (-7 часов от Гринвича, горные штаты США)
  • CST (Central Standard Time) - центральное поясное время (-6 часов от Гринвича, центральные штаты США) или центральное поясное время (+9 часов 30 минут от Гринвича, Австралия)
  • PST (Pacific Standard Time)- стандартное тихоокеанское время (- 8 часов от Гринвича, тихоокеанское побережье США)
Получить список распознаваемых временных зон можно с помощью TimeZone.getAvailableIDs(). Плюс к этим временным зонам всегда распознаются зоны вида GMT+XX:XX. Для получения временной зоны по её названию надо использовать TimeZone.getTimeZone(<имя_временной_зоны>). По умолчанию временная зона устанавливается в соответсвии с настройками системы, изменить ее можно с помощью TimeZone.setDefault(<временная_зона>). В основном TimeZone предназначена для использования в классе Calendar, но есть несколько полезных методов:
  • getOffset() - возвращает смещение в миллисекундах временной зоны относительно GMT (сколько надо прибавить к времени в GMT, чтобы получить время в этой временной зоне)
  • useDaylightTime() - используется ли в данной временной зоне переход на летнее/зимнее время
  • inDaylightTime(<дата>) - указанная дата попадает на зимнее или летнее время
Класс Date хранит в себе дату и время. Дата хранится в виде количества миллисекунд с 00:00:00.000 01 Январь 1970 GMT. Примечание: время храниться в UTC, таким образом решается проблема с различными часовыми поясами и зимним/летним временем. Именно поэтому для System.out.println(new Date(0)); мы получим, что-то типа Thu Jan 01 03:00:00 MSK 1970, а не Thu Jan 01 00:00:00 GMT 1970. Данные хранятся в переменной типа long, что позволяет хранить время от 02 Декабрь 292 269 055 до н.э. до 17 Август 292 278 994 н.э., т.е. проблемы двухтысячного года для Java никогда не было, но есть проблема 292 278 994 года. Методы по работе с элементами даты, как то getYear(), getMonth() и т.д. были deprecated, и данная функциональность была возложена на Calendar. Из полезных методов в Date есть after() и before() которые позволяют сравнить две даты в хронологическом порядке.

Класс Calendar предназначен для манипулирования датами. Сам класс Calendar абстрактный, и служит базовым классом для реализации других календарей. Получить умолчальный календарь можно с помощью Calendar.getInstance(), можно получить календарь для указанного часового пояса и региона (Locale). У Calendar есть список полей, которые характеризуют дату, это:
  • Calendar.ERA - эра (до н.э./н.э., две константы GregorianCalendar.AD и GregorianCalendar.BC)
  • Calendar.YEAR - год (отсчет идет от 1, нулевого года не существует)
  • Calendar.MONTH - месяц (отсчет месяцев идет от 0)
  • Calendar.DAY_OF_YEAR - день года (отсчет идет от 1)
  • Calendar.DAY_OF_MONTH - день месяца (отсчет идет от 1)
  • Calendar.DATE - синоним для Calendar.DAY_OF_MONTH
  • Calendar.DAY_OF_WEEK - день недели (отсчет идет от 1, но никак не коррелирует с getFirstDayOfWeek(), поэтому надо использовать константы SUNDAY, MONDAY и т.д.)
  • Calendar.DAY_OF_WEEK_IN_MONTH - день недели в месяце (отсчет идет от 1, похоже на номер недели в месяце, но не зависит от getFirstDayOfWeek() и getMinimalDaysInFirstWeek(), для дней с 1 по 7 выдает 1, для 8-14 выдает 2 и т.д.)
  • Calendar.WEEK_OF_YEAR - неделя в году (отсчет идет от 1, зависит от getFirstDayOfWeek() и getMinimalDaysInFirstWeek())
  • Calendar.WEEK_OF_MONTH - неделя в месяце (отсчет идет от 1, зависит от getFirstDayOfWeek() и getMinimalDaysInFirstWeek())
  • Calendar.HOUR_OF_DAY - час дня (отсчет идет от 0, часы в 24-х часовом формате)
  • Calendar.HOUR - час (отсчет идет от 0, часы в 12-и часовом формате, полдню и полночи соответствует 0)
  • Calendar.AM_PM - время суток (до полудня или после, две константы Calendar.AM и Calendar.PM)
  • Calendar.MINUTE - минуты (отсчет от 0)
  • Calendar.SECOND - секунды (отсчет от 0)
  • Calendar.MILLISECOND - миллисекунды (отсчет от 0)
  • Calendar.ZONE_OFFSET - смещение часовой зоны (смещение, выдаваемое TimeZone.getRawOffset(), установленной для данного календаря)
получить значение поля можно с помощью get(<поле>), а установить set(<поле>,<значение>). Для поля можно получить диапазон допустимых значений, для этого есть две пары методов. Первая getMaximum(<поле>)/getMinimum(<поле>), возвращает максимально/минимально достижимое значение для данного поля. Вторая пара getActualMaximum(<поле>)/getActualMinimum(<поле>), возвращает максимальное/минимальное значение, с учетом состояния других полей, например если месяц установлен в февраль, а год високосный, то getActualMaximum(Calendar.DAY_OF_MONTH) вернет 29. Есть еще два метода: getGreatestMinimum(<поле>) - максимальное значение, которое может вернуть getActualMinimum(<поле>), getLeastMaximum(<поле>) - минимальное значение которое может вернуть getActualMaximum(<поле>).
Теперь собственно о том, как происходит работа с датой, установить дату можно двумя способами. Первый методами setTime(<дата>) и setTimeInMillis(<миллисекунды>), можно просто установить время, при этом будут вычислены и установлены все поля. Потом можно будет получить или изменить любое поле. Другой способ состоит в том, чтобы устанавливать значения полей и затем на основе этих данных получить дату. Поля могут принимать взаимоисключающие значения, тогда во внимание принимается последнее изменение, например можно установить месяц, а затем установить день в году, тогда даты будет считаться исходя из номера дня в году, независимо от того какой месяц мы до этого установили. А вот если попробовать наоборот, то тут посложнее, когда мы установим день года, то автоматом будут вычислены месяц и день месяца, изменив месяц, день месяца останется неизменным. Если какое то поле не было установлено, явно или опосредованно, то оно принимает значение по умолчанию, как правило это первое число, первый месяц и т.д.

Теперь рассмотрим один тонкий момент, при установки одного значения значение другого поля может стать не корректным. Например, был дата 31 января, мы установили месяц в февраль, 31 число месяца стало не действительным. Что при этом произойдет от того, какое значение имеет свойство lenient, если оно установлено в true, то лишние дни будут "перенесены" на следующий месяц (в нашем примере мы получим 3 марта, для не високосного года), если оно установлено в false, то будет вызвано исключение IllegalArgumentException. По умолчанию lenient установлено в true.

Есть еще несколько вспомогательных методов по работе с полями. Метод clear(<поле>) устанавливает поле в умолчальное значение, clear() устанавливает все поля в умолчальные значения (в результате мы получим 00:00:00.000 31 Январь 2005 н.э. GMT). Узнать было ли поле установлено явно или не явно, можно с помощью isSet(<поле>), false возвращается для полей, которые вообще не были установлены явно или не явно. Функция add(<поле>,<значение>) добавляет к указанному полю, указанную величину (прибавлять можно и отрицательные величины), при этом могут измениться и другие поля, например если для 31 января вызвать add(Calendar.DAY_OF_MONTH, 2), то получим 2 февраля. Есть функция roll(<поле>,<значение>), схожая с add, но отличающаяся тем что при изменении поля "старшие" поля меняться не будут, в нашем примере roll(Calendar.DAY_OF_MONTH, 2) поменяет дату с 31 января на 2 января. Несмотря на то что старшие поля не меняются, младшие будут меняться если для 31 января выполнить roll(Calendar.MONTH, 1), то получим 28 февраля (или 29 для високостного года).

Это основные понятия по работе с датой, осталось рассмотреть временные интервалы. В Java нет особого класса для работы с временными интервалами. Если нужно вычислить время прошедшее между двумя датами, то это делается достаточно просто: (dateTill.getTime() - dateFrom.getTime()) и получим количество миллисекунд прошедших с первого момента времени до второго. Чтобы получить например количество часов, надо полученную величину разделить на 3 600 000. Если же надо вычислить количество календарных суток, прошедших с первого момента до второго, то надо использовать Calendar и его функции по работе с полями.

Примеры использования:

Посчитать дату на N дней вперед/назад от указанной
Код
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_YEAR, days);
return calendar.getTime();


Установить временную составляющую в начало суток:
Код
private static final int[] TIME_FIELDS =
{
  Calendar.HOUR_OF_DAY,
  Calendar.HOUR,
  Calendar.AM_PM,
  Calendar.MINUTE,
  Calendar.SECOND,
  Calendar.MILLISECOND
};
...
calendar.setTime(date);
for(int i : TIME_FIELDS)
  calendar.clear(i);
return calendar.getTime();


Получить день недели:
Код
calendar.setTime(date);
return calendar.get(Calendar.DAY_OF_WEEK);


Получить день недели в текстовом виде, на французком языке:
Код
calendar.setTime(date);
DateFormatSymbols dfs = new DateFormatSymbols(Locale.FRANCE);
return dfs.getWeekdays()[calendar.get(Calendar.DAY_OF_WEEK)];


Получить дату по году, месяцу и дню месяца:
Код
calendar.clear();
calendar.set(year, month, day);
return calendar.getTime();


Получить максимальный день месяца:
Код
calendar.clear();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);


Ну и на последок простенькая TableModel отображающая календарь на указанный месяц:
Код
public class CalendarModel extends AbstractTableModel
{
  private int columnCount;
  private int rowCount;
  private Integer[][] days;
  private Calendar calendar;
  private String[] dayNames;
  private int year;
  private int month;

  public CalendarModel(int year, int month)
  {
    set(year, month);
  }
  
  public void set(int year, int month)
  {
    this.year = year;
    this.month = month;
    init();
    fireTableStructureChanged();
  }

  private void init()
  {
    calendar = Calendar.getInstance();
    calendar.setMinimalDaysInFirstWeek(7);
    calendar.clear();
    calendar.set(Calendar.YEAR, year);
    calendar.set(Calendar.MONTH, month);
    columnCount = calendar.getActualMaximum(Calendar.DAY_OF_WEEK) - calendar.getActualMinimum(Calendar.DAY_OF_WEEK) + 1;
    rowCount = calendar.getActualMaximum(Calendar.WEEK_OF_MONTH) - calendar.getActualMinimum(Calendar.WEEK_OF_MONTH) + 1;
    days = new Integer[rowCount][columnCount];
    dayNames = new String[columnCount];
    int currDay = calendar.getFirstDayOfWeek();
    String[] d = new DateFormatSymbols().getShortWeekdays();
    for(int i = 0; i < dayNames.length; i++)
    {
      dayNames[i] = d[currDay];
      currDay++;
      if(currDay > calendar.getActualMaximum(Calendar.DAY_OF_WEEK))
        currDay = calendar.getActualMinimum(Calendar.DAY_OF_WEEK);
    }
    
    calendar.set(Calendar.WEEK_OF_MONTH, calendar.getActualMinimum(Calendar.WEEK_OF_MONTH));
    calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
    for(int r = 0; r < rowCount; r++)
    {
      for(int c = 0; c < columnCount; c++)
      {
        if(calendar.get(Calendar.MONTH) == month)
        {
          days[r][c] = calendar.get(Calendar.DAY_OF_MONTH);
        }
        else
        {
          days[r][c] = null;
        }
        calendar.add(Calendar.DAY_OF_YEAR, 1);
      }
    }
  }

  public Object getValueAt(int rowIndex, int columnIndex)
  {
    return days[rowIndex][columnIndex];
  }

  public Class getColumnClass(int columnIndex)
  {
    return Integer.class;
  }

  public int getRowCount()
  {
    return rowCount;
  }

  public int getColumnCount()
  {
    return columnCount;
  }

  public String getColumnName(int columnIndex)
  {
    return dayNames[columnIndex];
  }

  public static void main(String[] args)
  {
    CalendarModel model = new CalendarModel(2005, Calendar.SEPTEMBER);
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(new JScrollPane(new JTable(model)));
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}


При работе с датой/временем возникает задача перевода оной в текстовый вид и обратно. Для облегчения работы с текстом сущесвует пакет java.text, для нашего случая это классы данного пакета: java.text.DateFormat, java.text.SimpleDateFormat
Кроме того, в JDK 5.0 появился класс java.util.Formatter, который также можно использовать для представления даты в нужном текстовом виде.

Пример 1:

Код

import static java.lang.System.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Calendar;

public class DateFormatter {
    static final String timePattern = "HH:mm:ss dd.MM.yyyy";
    static final DateFormat dateFormatter =  new SimpleDateFormat(timePattern);
    static final DateFormat dateParser =   dateFormatter;
    
    public static void main ( String[] args ) throws Exception{
        Date now = new Date();
        String timeInString = dateFormatter.format(now);
        out.println("println new string from Date: " + timeInString);
        out.println("println new Date from string: " + dateParser.parse(timeInString));
        out.printf("System.out.printf: %1$tH:%1$tM:%1$tS %1$td.%1$tm.%1$tY %n", now);
        out.format("System.out.format: %1$tH:%1$tM:%1$tS %1$td.%1$tm.%1$tY %n", now);
    }
}



Пример 2:

Код

import static java.lang.System.*;
import java.util.Calendar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

/*По году, месяцу и дню возвращает день недели*/
public class BirthdayDay {
    public static void main ( String[] args ) throws Exception{
        int year = new Integer(args[0]);
        int month = new Integer(args[1]) - 1; // отсчёт с 0
        int day = new Integer(args[2]);
        
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month);
        cal.set(Calendar.DAY_OF_MONTH, day);
        
        DateFormat formatter = new SimpleDateFormat("EEEE");
        out.println(formatter.format(cal.getTime()));
    }
}
Автор: LSD






Просмотров: 16429

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную