Новости | FAQ | Авторы | Документация | В действии | Библиотека |
Инструменты | Полезные ссылки | Хостинги | Скачать | Примеры | Форум |
serglif 23.11.2004 13:51
Написал класс для работы с датами расширенного диапазона. Если кому нужно - пользуйтесь на здоровье. Буду очень рад замечаниям. Но по хорошему в конструктор нужно вставить регулярное выражение, проверяющее правильность ввода даты и разбивающее дату на годы, дни, часы и т.д. Изучать RCPE пока некогда... если у кого есть, может поделитесь? Варианты строки с датой как в date::create[]. Код класса:#========================================================================================== # Класс для работы с датами расширенного диапазона. (v1.0) Автор: С. Лифшиц (serlif@mail.ru) #========================================================================================== # # Метод print и массивы локальных названий месяцев взяты из класса dtf.p # (http://www.parser.ru/examples/date/) Михаила Петрушина (http://misha.design.ru/). # Алгоритмы работы с модифицированными юлианскими датами взяты на http://alglib.manual.ru/ # # Диапазон класса: 17.11.1858 - 23.12.54078. # # Верхний предел занижен, так как за ним начинаются ошибки в вычислениях (связанные # видимо с переполнением int). Некогда разбираться... Дальше наверное нужно только астрономам. :) # # Класс в первую очередь быд предназначен для обработки дат, хранящихся # в СУБД и выходящих за стандартный диапазон. Поэтому я обошелся без регулярного выражения # в конструкторе. # # Поля класса аналогичны Parser-овскому (www.parser.ru/docs/lang/datefields.htm): # day, month, year, hour, minute, second, weekday, yearday, daylightsaving # # Дополнительные поля: millisecond - миллисекунды, julian - дата в модифицированном # юлианском формате (количество дней, прошедших с 17.11.1858), russian - дата по русски # в виде "ДД Месяц ГГГГ, ДеньНедели", timestamp- количество секунд, прошедших с начала # текущих суток # # Примечание: Поскольку зимнее и летнее время в любом случае порождает # неопределенности (попробуйте сказать какое время 2004-10-31 02:00:01), # то поле daylightsaving вычисляется и для параметра без времени, говоря # в этом случае какое в этот день используется время (здесь неопределенности для # последнего воскресения марта и октября) # # Методы: # # @create[sql-date] - конструктор # # @roll[target;offset] - сдвигает дату на заданное количество единиц (в target: year, month, day) # # @print[sql-date;format;locale] - вывод даты, используя форматную строку и локальные названия месяцев # # @dayofweek[year;month;day] - определение дня недели для введенной даты # # @daysbetween[year1;month1;day1;year2;month2;day2] - вычисление количества дней между датами # # @getjulian[year;month;day] - переводит дату в модифицированную юлианскую # # @fromjulian[julian] - переводит дату из модифицированной юлианской в обычную # #========================================================================================== @CLASS cdate #========================================================================================== # Конструктор класса #========================================================================================== @create[sql-date][date;cdate][tmp] $count[^sql-date.split[ ]] $date[^sql-date.split[ ;h]] # Заполняем поля, относящиеся к дате $cdate[^date.0.split[-;h]] $year($cdate.0) $month($cdate.1) $day($cdate.2) $hour(0) $minute(0) $second(0) $millisecond(0) # Переводим дату в юлианскую $julian(^getjulian[$year;$month;$day]) # Если вышли за диапазон класса - выдаем ошибку ^if($julian<0 || $julian>19073000){ ^throw[bad.command;cdate:create;date '${year}-${month}-${day}' is out of valid range] } # Корректируем значения для ошибочных дат (ex. 2000-02-31) $tmp[^fromjulian[$julian]] $year($tmp.year) $month($tmp.month) $day($tmp.day) # Если в параметре есть время - заполняем поля ^if(^count.count[]>1){ $time[^date.1.split[.;h]] $count[^date.1.split[.]] $ctime[^time.0.split[:;h]] $timestamp(^ctime.2.int(0)+^ctime.1.int(0)*60+^ctime.0.int(0)*3600) ^if(^count.count[]>1){ $timestamp($timestamp+^time.1.int(0)\1000+(^time.1.int(0)%1000)/1000) } # Корректируем значения для ошибочного времени (ex. 26:69:84 = Сутки + 3:10:24) $julian($julian+$timestamp\86400) $timestamp($timestamp%86400) $tmp[^fromjulian[$julian]] $year($tmp.year) $month($tmp.month) $day($tmp.day) # Заполняем время $hour($timestamp\3600) $minute(($timestamp%3600)\60) $second(^math:trunc(($timestamp%3600)%60)) ^if(^count.count[]>1){$millisecond(^math:frac($timestamp)*1000)} } ^advancedfields[] #========================================================================================== # Вычисляет расширенные поля: день недели, день года и т.д. #========================================================================================== @advancedfields[][marth;october] $weekday[^dayofweek[$year;$month;$day]] $yearday[^daysbetween[$year;1;1;$year;$month;$day]] # Вычисляем принадлежит ли день периоду летнего времени $marth(32-^dayofweek[$year;03;25]) $marth[^daysbetween[$year;1;1;$year;3;$marth]] $october(32-^dayofweek[$year;10;25]) $october[^daysbetween[$year;1;1;$year;10;$october]] $daylightsaving(^if($yearday>=$marth && $yearday<=$october){1}{0}) # Уточнение для летнего и зимнего времени ^if($yearday==$marth && $hour==2){^hour.dec[]} ^if($daylightsaving && def $time){ ^if($yearday==$marth && $hour<2){$daylightsaving(0)} ^if($yearday==$october && $hour>=2){$daylightsaving(0)} } ^leapyear[] $russian[^print[;%e %h %Y, %A]] #========================================================================================== # Сдвигает дату на заданное количество единиц #========================================================================================== @roll[target;offset][tmp] # Годы ^if($target eq year){ $year($year+^offset.int(0)) } # Месяцы ^if($target eq month){ $month($month+^offset.int(0)%12) $year($year+^offset.int(0)\12) ^if($month<=0){ $month(12+$month) ^year.dec[] } ^if($month>12){ $month($month-12) ^year.inc[] } } # Дни ^if($target eq day){ $julian($julian+^offset.int(0)) $tmp[^fromjulian[$julian]] $year($tmp.year) $month($tmp.month) $day($tmp.day) } # Перевычисляем доп. поля для обновленной даты ^leapyear[] ^if($day>$dayinmonth.$month){$day($dayinmonth.$month)} $julian[^getjulian[$year;$month;$day]] # Если вышли за диапазон класса - выдаем ошибку ^if($julian<0 || $julian>19073000){ ^throw[rolled.out;cdate:roll;bad resulting date '${year}-${month}-${day}'(rolled out of valid date range)] } ^advancedfields[] #========================================================================================== # Коррекция количества дней в феврале для високосных лет #========================================================================================== @leapyear[] ^if($year%4==0 && ($year%100!=0 || $year%400==0)){$dayinmonth.2[29]}{$dayinmonth.2[28]} #========================================================================================== # Вычисляет день недели #========================================================================================== @dayofweek[y;m;d][jul] $jul(^getjulian[$y;$m;$d]) $result(($jul%7+3)%7) #========================================================================================== # Вычисляет количество дней между датами #========================================================================================== @daysbetween[y1;m1;d1;y2;m2;d2][date1;date2] $result(^getjulian[$y2;$m2;$d2]-^getjulian[$y1;$m1;$d1]) #========================================================================================== # Переводит дату в модифицированную юлианскую #========================================================================================== @getjulian[y;m;d][a;b] $y(^y.int(0)) $m(^m.int(0)) $d(^d.int(0)) $a(10000*$y+100*$m+$d) ^if($m<=2){^m.inc(12) ^y.dec(1)} ^if($a<=15821004){ $b(-2+($y+4716)\4-1179) }{ $b($y\400-$y\100+$y\4) } $a(365*$y-679004) $result($a+$b+306001*($m+1)\10000+$d) #========================================================================================== # Переводит дату из модифицированной юлианской в обычную #========================================================================================== @fromjulian[mjd][jd;b;c;d;e;f;da;mo;ye] $jd($mjd+2400001) ^if($jd<2299161){ $b(0) $c($jd+1524) }{ $b(($jd*100-186721625)\3652425) $c($jd+($b-$b\4)+1525) } $d((100*$c-12210)\36525) $e(365*$d+$d\4) $f(($c-$e)*10000\306001) $da(^math:trunc($c-$e+0.5)-^math:trunc($f*30.6001)) $mo($f-1-12*($f\14)) $ye($d-4715-(7+$mo)\10) $result[$.day($da) $.month($mo) $.year($ye)] #========================================================================================== # Вывод даты, используя форматную строку. # Формат строки понятен из вариантов switch #========================================================================================== @print[sql-date;format;locale][tmp;temp;year;month;day;hour;minute;second] # Если дата не задана - печатаем для текущего объекта ^if(!def ${sql-date}){ $year($self.year) $month($self.month) $day($self.day) $hour($self.hour) $minute($self.minute) $second($self.second) $millisecond($self.millisecond) }{ $temp[^cdate::create[$sql-date]] $year($temp.year) $month($temp.month) $day($temp.day) $hour($temp.hour) $minute($temp.minute) $second($temp.second) $millisecond($temp.millisecond) } ^if(!def $locale){$locale[$rr-locale]} $result[ ^format.match[%(.)][g]{ ^switch[$match.1]{ ^case[%]{%} ^case[e]{$day} ^case[d]{^day.format[%02d]} ^case[c]{$month} ^case[m]{^month.format[%02d]} ^case[h]{$locale.month.[$month]} ^case[Y]{$year} ^case[y]{$tmp($year % 100)^tmp.format[%02d]} ^case[w]{$weekday} ^case[a;A]{$locale.weekday.[$weekday]} ^case[D]{^month.format[%02d]/^day.format[%02d]/$year} ^case[H]{^hour.format[%02d]} ^case[M]{^minute.format[%02d]} ^case[i]{^minute.format[%02d]} ^case[S]{^second.format[%02d]} ^case[T]{^hour.format[%02d]:^minute.format[%02d]:^second.format[%02d]} } } ] #========================================================================================== # Вспомогательные переменные #========================================================================================== @auto[] $dayinmonth[ $.1[31] $.2[28] $.3[31] $.4[30] $.5[31] $.6[30] $.7[31] $.8[31] $.9[30] $.10[31] $.11[30] $.12[31] ] $rr-locale[ $.month[ $.1[Января] $.2[Февраля] $.3[Марта] $.4[Апреля] $.5[Мая] $.6[Июня] $.7[Июля] $.8[Августа] $.9[Сентября] $.10[Октября] $.11[Ноября] $.12[Декабря] ] $.weekday[ $.0[Воскресенье] $.1[Понедельник] $.2[Вторник] $.3[Среда] $.4[Четверг] $.5[Пятница] $.6[Суббота] $.7[Воскресенье] ] ] $ri-locale[ $.month[ $.1[Январь] $.2[Февраль] $.3[Март] $.4[Апрель] $.5[Май] $.6[Июнь] $.7[Июль] $.8[Август] $.9[Сентябрь] $.10[Октябрь] $.11[Ноябрь] $.12[Декабрь] ] $.weekday[ $.0[Воскресенье] $.1[Понедельник] $.2[Вторник] $.3[Среда] $.4[Четверг] $.5[Пятница] $.6[Суббота] $.7[Воскресенье] ] ] $rs-locale[ $.month[ $.1[Янв] $.2[Фев] $.3[Мар] $.4[Апр] $.5[Май] $.6[Июн] $.7[Июл] $.8[Авг] $.9[Сен] $.10[Окт] $.11[Ноя] $.12[Дек] ] $.weekday[ $.0[Вс] $.1[Пн] $.2[Вт] $.3[Ср] $.4[Чт] $.5[Пт] $.6[Сб] $.7[Вс] ] ] # английская locale $es-locale[ $.month[ $.1[Jan] $.2[Feb] $.3[Mar] $.4[Apr] $.5[May] $.6[Jun] $.7[Jul] $.8[Aug] $.9[Sep] $.10[Oct] $.11[Nov] $.12[Dec] ] $.weekday[ $.0[Sun] $.1[Mon] $.2[Tue] $.3[Wed] $.4[Thu] $.5[Fri] $.6[Sat] $.7[Sun] ] ] $ei-locale[ $.month[ $.1[January] $.2[Febrary] $.3[March] $.4[April] $.5[May] $.6[June] $.7[July] $.8[August] $.9[September] $.10[October] $.11[November] $.12[December] ] $.weekday[ $.0[Sunday] $.1[Monday] $.2[Tuesday] $.3[Wednsday] $.4[Thusday] $.5[Friday] $.6[Satarday] $.7[Sunday] ] ] # украинская locale $us-locale[ $.month[ $.1[Сiч] $.2[Лют] $.3[Бер] $.4[Квi] $.5[Тра] $.6[Чер] $.7[Лип] $.8[Сер] $.9[Вер] $.10[Жов] $.11[Лис] $.12[Гру] ] $.weekday[ $.0[Нед] $.1[Пон] $.2[Вiв] $.3[Сер] $.4[Чет] $.5[П'я] $.6[Суб] $.7[Нед] ] ] $ui-locale[ $.month[ $.1[Сiчень] $.2[Лютий] $.3[Березень] $.4[Квiтень] $.5[Травень] $.6[Червень] $.7[Липень] $.8[Серпень] $.9[Вересень] $.10[Жовтень] $.11[Листопад] $.12[Грудень] ] $.weekday[ $.0[Недiля] $.1[Понедiлок] $.2[Вiвторок] $.3[Середа] $.4[Четвер] $.5[П'ятниця] $.6[Субота] $.7[Недiля] ] ]