ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Дата и времяВведениеВремя и дата - очень важные величины, и с ними необходимо уметь работать. "Сколько пользователей регистрировалось за последний месяц?", "Сколько секунд я должен проспать, чтобы проснуться к полудню?" и "Не истек ли срок действия пароля данного пользователя?" - вопросы кажутся тривиальными, однако ответ на них потребует на удивление нетривиальных операций. В Peri моменты времени представлены в виде интервалов, измеряемых в секундах с некоторого момента, называемого началом эпохи. В UNIX и многих других системах начало эпохи соответствует 00 часов 00 минут 1 января 1970 года по Гринвичу (GMT1). На Macintosh дата и время измеряется в местном часовом поясе. Функция gmtime возвращает правильное время по Гринвичу, основанное на смещении местного часового пояса. Помните об этом, рассматривая рецепты этой главы. На Macintosh количество секунд с начала эпохи позволяет отсчитывать время в интервале от 00:00 1 января 1904 года до 06:28:15 6 февраля 2040 года. Говоря о времени и датах, мы часто путаем две разные концепции: момент времени (дата, время) и интервал между двумя моментами (недели, дни, месяцы и т. д.). При отсчете секунд с начала эпохи интервалы и моменты представляются в одинаковых единицах, поэтому с ними можно выполнять простейшие математические операции. Однако люди не привыкли измерять время в секундах с начала эпохи. Мы предпочитаем работать с конкретным годом, месяцем, днем, часом, минутой и секундой. Более того, название месяца может быть как полным, так и сокращенным. Число может указываться как перед месяцем, так и после него. Использование разных форматов затрудняет вычисления, поэтому введенная пользователем или В наши дни время по Гринвичу также часто обозначается сокращением UTC (Universal Coordinated Time). прочитанная из списка строка даты/времени обычно преобразуется в количество секунд с начала эпохи, с ней производятся необходимые операции, после чего секунды снова преобразуются для вывода. Для удобства вычислении количество секунд с начала эпохи всегда измеряется по Гринвичу. В любых преобразованиях всегда необходимо учитывать, представлено ли время по Гринвичу или в местном часовом поясе. Различные функции преобразования позволяют перейти от гринвичского времени в местное, и наоборот. Функция Peri time возвращает количество секунд, прошедших с начала эпохи... более или менее' точно. Для преобразования секунд с начала эпохи в конкретные дни, месяцы, годы, часы, минуты и секунды используются функции localtime и gmtime. В списковом контексте эти функции возвращают список, состоящий из девяти элементов.Переменная Значение Интервал $sec Секунды 0-60 $min Минуты 0-59 $hours Часы 0-23 $mday День месяца 1-31 $month Месяц 0-11,0 == январь $уеаг Год, начиная с 1900 1-138 (и более) $wday День недели 0-6, 0 == воскресенье $yday День года 1-366 $isdst________0 или 1___________true, если действует летнее время Секунды изменяются в интервале 0-60 с учетом возможных корректировок;
под влиянием стандартов в любой момент может возникнуть лишняя секунда.
В дальнейшем совокупность "день/месяц/год/час/минута/секунда" будет обозначаться
выражением "полное время" - хотя бы потому, что писать каждый раз "отдельные
значения дня, месяца, года, часа, минут и секунд" довольно утомительно.
Сокращение не связано с конкретным порядком возвращаемых значений. Peri
не возвращает данные о годе в виде числа из двух цифр. Он возвращает разность
между текущим годом и 1900, которая до 1999 года представляет собой число
из двух цифр. У Peri нет своей "проблемы 2000 года", если только вы не
изобретете ее сами (впрочем, у вашего компьютера и Peri может возникнуть
проблема 2038 года, если к тому времени еще будет использоваться 32-разрядная
адресация). Для получения полного значения года прибавьте к его представлению
1900. Не пользуйтесь конструкцией "19$уеаг", или вскоре ваши программы
начнут выдавать "год 19102". Мы не можем точно зафиксировать интервал года,
потому что все зависит от размера целого числа, используемого вашей системой
для представления секунд с начала эпохи. Малые числа дают небольшой интервал;
большие (64-разрядные) числа означают огромные интервалы. Скорее, менее.
В момент написания книги функция возвращала на 21 секунду меньше. В соответствии
со стандартом POSIX функция time не должна возвращать секунды, которые
накапливаются из-за замедления вращения Земли, обусловленного воздействием
приливов. За дополнительной информацией обращайтесь к разделу 3 sci. astro
FAQno адресу http://astrosun.tn.cornell.edu/students/ lazio.sci.astro.S.FAQ.
В скалярном контексте localtime и gmtime возвращают дату и время, отформатированные
в виде ASCII-строкн:
print "Today is day ", (localtime()[7], " of the current year.\n";
Чтобы преобразовать список в количество секунд с начала эпохи, воспользуйтесь стандартным модулем Time::Local. В нем имеются функции timelocal и timegm, которые получают список из девяти элементов и возвращают целое число. Элементы списка и интервалы допустимых значений совпадают с теми, которые возвращаются функциями localtime и gettime. Количество секунд с начала эпохи ограничивается размером целого числа. Беззнаковое 32-разрядное целое позволяет представить время по Гринвичу от 20:45:52 13 декабря 1901 года до 03:14:07 19 января 2038 года включительно. Предполагается, что к 2038 году в компьютерах должны использоваться целые числа большей разрядности. Во всяком случае, будем надеяться на это. Чтобы работать с временем за пределами этого интервала, вам придется воспользоваться другим представлением или выполнять операции со отдельными значениями года, месяца и числа. Модули Date::Calc и Date::Manip с CPAN работают с этими отдельными значениями, но учтите - они не всегда вычитают из года 1900, как это делает localtime, а нумерация месяцев и недель в них не всегда начинается с 0. Как всегда, в страницах руководства можно найти достоверные сведения о том, какая информация передается модулю, а какая - возвращается им. Только представьте, как будет неприятно, если рассчитанные вами финансовые показатели уйдут на 1900 лет в прошлое! 3.1. Определение текущей датыПроблемаТребуется определить год, месяц и число для текущей даты.РешениеВоспользуйтесь функцией localtime. Без аргументов она возвращает текущую дату и время. Вы можете вызвать localtime и извлечь необходимую информацию из полученного списка:($DAY, $MONTH, $YEAR) = (localtime)[3,4,5]; Модуль Timc::localtimc переопределяет localtime так, чтобы функция возвращала объект Time::tm: use Time::localtime;
КомментарийВывод текущей даты в формате ГПТ-ММ-ДД с использованием стандартной функции localtime выполняется следующим образом:($day, $month, $year) = (localtime)[3,4,5]; printf("The current date is %04d %02d %02\n", $year+1900, $month+1, $day): The current date is 1999 04 28 Нужные ноля из списка, возвращаемого localtime, извлекаются с помощью
среза. Запись могла выглядеть иначе:
use Time: : localtline;
В короткой программе объектный интерфейс выглядит неуместно. Однако
при большом объеме вычислений с отдельными компонентами даты обращения
по имени заметно упрощают чтение программы. То же самое можно сделать и
хитроумным способом, не требующим создания временных переменных:
Кроме того, в модуле POSIX имеется функция strftime, упоминаемая в рецепте 3.8: use POSIX qw(strftime):
Функция gmtime работает аналогично localtime, но возвращает время но Гринвичу, а не для местного часового пояса. [> Смотри также --------------------------------
3.2. Преобразование полного времени в секунды с начала эпохиПроблемаТребуется преобразовать дату/время, выраженные отдельными значениями дня, месяца, года и т. д. в количество секунд с начала эпохи,РешениеВоспользуйтесь функцией timelocal или timegm стандартного модуля Time::Local. Выбор зависит от того, относится ли дата/время к текущему часовому поясу или Гринвичскому меридиану:use Time::Local;
КомментарийВстроенная функция localtime преобразует количество секунд с начала эпохи в компоненты полного времени; процедура timelocal из стандартного модуля Time::Local преобразует компоненты полного времени в секунды. Следующий пример показывает, как определяется количество секунд с начала эпохи для текущей даты. Значения дня, месяца и года получаются от localtime:# $hours, $niinntes и $seconds задают время для текущей даты U и текущего часового пояса use Time::Local: $time = timelocal($seconds, $mlnutes, $hours, (localtime)[3.4,5]); Если функции timelocal передаются месяц н год, они должны принадлежать тем же интервалам, что и значения, возвращаемые localtime. А именно, нумерация месяцев начинается с 0, а из года вычитается 1900. Функция timelocal предполагает, что компоненты полного времени соответствуют текущему часовому поясу. Модуль Time::Local также экспортирует процедуру timegm, для которой компоненты полного времени задаются для Гринвичского меридиана. К сожалению, удобных средств для работы с другими часовыми поясами, кроме текущего или Гринвичского, не существует. Лучшее, что можно сделать, - преобразовать время к Гринвичскому и вычесть или прибавить смещение часового пояса в секундах. В следующем фрагменте демонстрируется как применение timegm, так и настройка интервалов года и месяца: # $day - день месяца (1-31)
use Time::Local;
> Смотри также --------------------------------
3.3. Преобразование секунд с начала эпохи в полное времяПроблемаТребуется преобразовать количество секунд с начала эпохи в отдельные значения дня, месяца, года и т. д.РешениеВоспользуйтесь функцией localtime или gmtime в зависимости от того, хотите ли вы получить дату/время для текущего часового пояса или для Гринвичского меридиана.($seconds, Sminutes, $hours, $day_of_month, $year, $wday, $yday, $isdst) = localtime($TIME); Стандартные модули Time::timelocal н Tirne::gmtirne переопределяют функции localtime и gmtime так, чтобы к компонентам можно было обращаться по именам: use Time::localtlme; # или Time::gmtime $tm = localtime($TIME); # или gmtime($TIME) $seconds = $tm->sec; "..." КомментарийФункции localtime и gettime возвращают несколько странную информацию о годе и месяце; из года вычитается 1900, а нумерация месяцев начинается с 0 (январь). Не забудьте исправить полученные величины, как это делается в следующем примере:($seconds, $minutes. $hours, $day_of_month, $month, $year, $wday, $yday, $isdst) = localtime($time); printf("Dateline: %02d:%02d:%02d-%04d/%02d/%02d\n", $hours, $minutes, $seconds, $year+1900, $month+1, $day_of_month); Модуль Time::localtime позволяет избавиться от временных переменных:
> Смотри также ----------------
3.4. Операции сложения и вычитания для датПроблемаИмеется значение даты/времени. Требуется определить дату/время, отделенную от них некоторым промежутком в прошлом или будущем.РешениеПроблема решается простым сложением или вычитанием секунд с начала эпохи:$when = $now + $difference; $then = $now - Sdifference; Если у вас имеются отдельные компоненты полного времени, воспользуйтесь модулем Date::Calc с CPAN. Если вычисления выполняются только с целыми днями, примените функцию Add_Delta_Days (смещение $offset может представлять собой как положительное, так и отрицательное целое количество дней): use Date::Calc qw(Add_Delta_Days); ($у2, $m2, $d2) = Add_Delta_Days($y, $m, $d, $offset); Если и вычислениях используются часы, минуты и секунды (то есть не только дата, но и время), воспользуйтесь функцией Add_Delta_DHMS: use Date::DateCalc qw(Add_Delta_DHMS); ($year2, $month2, $day2, $h2, $m2, $s2) ,= Add_Delta_DHMS( $year, $month,o$day, $hour, $minute, $seconds, $days_offset, $hour_offset, $minute_offset, $seconds_offset ); КомментарийВычисления с секундами от начала эпохи выполняются проще всего (если не считать усилий па преобразования даты/времени в секунды и обратно). В следующем фрагменте показано, как прибавить смещение (в данном примере - 55 дней, 2 часа, 17 минут и 5 секунд) к заданной базовой дате и времени:$birthtime = 96176750; # 18 января 1973 года, 03:45:50 $interval = 5 + # 5 секунд 17 * 60 + # 17 минут ' 2 * 60 * 60 + # 2 часа 55 * 60 * 60 * 24; # и 55 дней $fhen = $birthtime + $interval; print "Then is ", scalar(localtime($then)), "\n"; Then is Wed Mar 14 06:02:55 1973 Мы также могли воспользоваться функцией Add_Delta_DHMS и обойтись без преобразований к секундам с начала эпохи и обратно: use Date::Calc qw(Add_Delta_DHMS): ($year, $month, $day, $hh, $mm, $ss) = Add_Delta_DHMS( 1973, 1, 18, 3, 45, 50, # 18 января 1973 года, 03:45:50 55, 2, 17, 5); # 55 дней, 2 часа, 17 минут, 5 секунд print "To be prcise: $hh:$miTi:$ss, $month/$day/$year\n"; To be precise: 6:2:55, 3/14/1973 Как обычно, необходимо проследить, чтобы аргументы функции находились
в правильных интервалах. Add_Delta_DHMS получает полное значение года (без
вычитания 1900). Нумерация месяцев начинается с 1, а не с 0. Аналогичные
параметры передаются и функции Add_Delta_Days модуля Date::DateCalc:
> Смотри также -------------------------------
3.5. Вычисление разности между датамиПроблемаТребуется определить количество дней между двумя датами или моментами времени.РешениеЕсли даты представлены в виде секунд с начала эпохи и принадлежат интервалу от 20:45:52 13 декабря 1901 года до 03:14:07 19 января 2038 года включительно, достаточно вычесть одну дату из другой и преобразовать полученные секунды в дни: $seconds = $recent = $earlier: Если вы работаете с отдельными компонентами полного времени или беспокоитесь об ограничениях интервалов для секунд с начала эпохи, воспользуйтесь модулем Date::Calc с CPAN. On позволяет вычислять разность дат:use Date::Calc qw(Delta_DHMS); ($days, $hours, $minutes, $seconds) = Delta_DHMS( $year1, $month1, $day1, $hour1, $minute1, $seconds1, # Ранний # момент $year2, $month2, $day2, $hour2, $minute2, $seconds2, # Поздний # момент КомментарийОдна из проблем, связанных с секундами с начала эпохи, - преобразование больших целых чисел в форму, понятную для человека. Следующим пример демонстрирует один из способов преобразования секунд с начала эпохи в привычные недели, дни, часы, минуты и секунды:$bree = 361535725; # 04:35:25 16 июня 1981 года $nat = 96201950; # 03:45:50 18 января 1973 года $difference = $bree - $nat; print "There were $difference seconds between Nat and Bree\n"; There were 266802575 seconds between Nat and Bree $seconds = $difference % 60; $difference = ($difference - $seconds) / 60; $minutes = $difference % 60; $difference = ($difference - $minutes) / 60; $hours = $difference $ 24; $diff'erence = ($difference - $hours) / 24; $days = $difference % 7; $weeks = ($difference - $days) / 7; print "($weeks weeks, $days days, $hours:$minutes:$seconds)\n"; (441 weeks, 0 days, 23: 49: 35) Функции модуля Date::Calc упрощают подобные вычисления. Delta_Days возвращает количество дней между двумя датами. Даты передаются ei'i в виде списка "год/месяц/день" в хронологическом порядке, то есть начиная с более ранней. > Смотри также -------------------------------
|