| Новости | 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ля]
]
]