Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Работа с датами, Статья для FAQ 
:(
    Опции темы
LSD
Дата 13.9.2005, 20:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Работа с датами в 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 и его функции по работе с полями.
Добавлено @ 20:57
Готов выслушать пожелания по даработке статьи smile

Это сообщение отредактировал(а) LSD - 14.9.2005, 09:17


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
batigoal
Дата 13.9.2005, 22:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 24
Всего: 151



Для начала выслушай "спасибу" smile

Если через пару дней никаких изменений не предложат, добаим в ФАК.


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
LSD
Дата 13.9.2005, 23:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(Lamer @ 13.9.2005, 23:50)
Для начала выслушай "спасибу"

Bitte smile


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Zandr
Дата 14.9.2005, 08:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 433
Регистрация: 16.7.2004
Где: Новосибирск

Репутация: 9
Всего: 13



Здорово!
Замечены (возможные) очепятки:
Цитата
Основанием Е. служат астрономические явления:

Цитата
если для 31 января выполнить roll(Calendar.DAY_OF_MONTH, 1), то получим 28 февраля

PM MAIL   Вверх
LSD
Дата 14.9.2005, 09:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Цитата(Zandr @ 14.9.2005, 09:39)
Замечены (возможные) очепятки:
...

Fixed smile
Добавлено @ 09:19
Сюда желательно добавить какие нибудь примерчики, но у меня нет идей по этому поводу. Может кто что подкинет?


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
AntonSaburov
Дата 14.9.2005, 11:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 51
Всего: 118



Статья приятная. Наверно в качестве примеров надо привести возможности по обработке дат.
Например:
1. Какая дата была 10 дней назад (послезавтра)
2. Какой день недели был такого-то числа.
3. Получить разницу дат в днях/месяцах/годах/часах
PM MAIL WWW ICQ   Вверх
Stampede
Дата 14.9.2005, 21:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Гносеолог
**


Профиль
Группа: Участник Клуба
Сообщений: 963
Регистрация: 25.4.2005
Где: Calgary, Alberta, Canada

Репутация: 24
Всего: 144



Отличная статья, спасибо.

Цитата(LSD @ 13.9.2005, 20:56)
проблемы двухтысячного года для Java никогда не было, но есть проблема 292 278 994 года


Смеялся smile

Цитата(LSD @ 14.9.2005, 09:18)
Сюда желательно добавить какие нибудь примерчики, но у меня нет идей по этому поводу. Может кто что подкинет?


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

- составление даты по числу, месяцу и году;

- округление текущей даты до года, месяца и т. д.

- получение максимальной даты заданного месяца.

Вот такие пожелания.



--------------------
"If you want something done right, do it yourself"
По секрету: выучить английский - реально!
PM WWW   Вверх
jer1
Дата 16.9.2005, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 123
Регистрация: 24.1.2005
Где: Харьков Украина

Репутация: 4
Всего: 5



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

следующая программа это демонстрирует:
Код

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



а вот ещё одна программа по работе с датой:

Код

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




--------------------
:w!q
PM MAIL   Вверх
LSD
Дата 18.9.2005, 01:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Итак примерчики:

Посчитать дату на 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();
  }

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



--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
batigoal
Дата 18.9.2005, 09:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 24
Всего: 151



В FAQ можно добавлять только один пост, поэтому, если не возражаете, я объединю все в одно сообщение:


Работа с датами в 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()));
    }
}


Это сообщение отредактировал(а) Lamer George - 18.9.2005, 12:15


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
LSD
Дата 18.9.2005, 10:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Да, еще одно дополнение, в конец метода set(int,int), надо вставить fireTableStructureChanged() иначе JTable не узнает что надо обновиться.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
batigoal
Дата 18.9.2005, 12:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 24
Всего: 151



Добавил.


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
AntonSaburov
Дата 19.9.2005, 12:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 51
Всего: 118



Статья находится в FAQ - Java->Статьи
PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1273 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.