parserALT
Страницы ветки: 1 2 | Дальше → | одной страницей

Меняю код на Parser на регулярное выражение... ;)

#1serglif
23.11.04 13:51
www.parser.ru → | ответить → | в избранное →

Меняю код на Parser на регулярное выражение... ;)

Написал класс для работы с датами расширенного диапазона. Если кому нужно - пользуйтесь на здоровье. Буду очень рад замечаниям. Но по хорошему в конструктор нужно вставить регулярное выражение, проверяющее правильность ввода даты и разбивающее дату на годы, дни, часы и т.д. Изучать RCPE пока некогда... если у кого есть, может поделитесь? Варианты строки с датой как в «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ля]
	]
]
#2serglif
→ serglif [#1] | 23.11.04 13:53
www.parser.ru → | ответить → | в избранное →

Код для тестирования класса

 $s[1998-02-01 26:69:84.121]

 $sd[^date::create[$s]]
 $cd[^cdate::create[$s]]
 
 ^sd.roll[day](-529)
 ^cd.roll[day](-529)

<br/><br/>
<table border="1" align="center">
 <tr><td>&nbsp^;</td><td>Класс Parser</td><td>Класс cdate</td></tr>
 <tr><td>День</td><td>$sd.day</td><td>$cd.day</td></tr>
 <tr><td>Месяц</td><td>$sd.month</td><td>$cd.month</td></tr>
 <tr><td>Год</td><td>$sd.year</td><td>$cd.year</td></tr>
 <tr><td>Час</td><td>$sd.hour</td><td>$cd.hour</td></tr>
 <tr><td>Минуты</td><td>$sd.minute</td><td>$cd.minute</td></tr>
 <tr><td>Секунды</td><td>$sd.second</td><td>$cd.second</td></tr>
 <tr><td>Миллисекунды</td><td>-</td><td>$cd.millisecond</td></tr>
 <tr><td>День недели</td><td>$sd.weekday</td><td>$cd.weekday</td></tr>
 <tr><td>День года</td><td>$sd.yearday</td><td>$cd.yearday</td></tr>
 <tr><td>Летнее время</td><td>$sd.daylightsaving</td><td>$cd.daylightsaving</td></tr>
 <tr><td>Юлианская</td><td>-</td><td>$cd.julian</td></tr>
 <tr><td>По русски</td><td>-</td><td>$cd.russian</td></tr>
 <tr><td>Секунд с нач. суток</td><td>-</td><td>$cd.timestamp</td></tr>
</table>
#3redactor
→ serglif [#2] | 23.11.04 14:39
www.parser.ru → | ответить → | в избранное →

Спасибо огромное:)

Только вот вопрос. Просто интересно.
Зачем было реализовывать юлианские даты?
#4serglif
→ redactor [#3] | 23.11.04 14:52
www.parser.ru → | ответить → | в избранное →
Посмотрите реализацию метода для расстояния между датами или дня недели и все станет ясно... :) И в других местах тоже очень удобно...
#5redactor
→ serglif [#4] | 23.11.04 15:09
www.parser.ru → | ответить → | в избранное →

Вопрос отпал :)

#6Кодер
→ serglif [#1] | 23.11.04 15:16
www.parser.ru → | ответить → | в избранное →

2 Misha v.3: можеть быть это в примеры добавить?

#7redactor
→ serglif [#1] | 23.11.04 15:19
www.parser.ru → | ответить → | в избранное →

добавочка

Предлагаю дополнить
#==========================================================================================
# Конструктор класса
#==========================================================================================
@create[sql-date][date;cdate][tmp]
 ^if(!def $sql-date){

     $nowdate[^date::now[]]
     $sql-date[^nowdate.sql-string[]]
 
 }
 $count[^sql-date.split[ ]]
 $date[^sql-date.split[ ;h]]
 
# Заполняем поля, относящиеся к дате

#8redactor
→ Кодер [#6] | 23.11.04 15:23
www.parser.ru → | ответить → | в избранное →

Ага

И еще предложение по форуму:
Классы, написанные Парсере, перенести в раздел "скачать", в котором сделать одноименный подраздел.
#9Misha v.3
→ Кодер [#6] | 23.11.04 15:27
www.parser.ru → | ответить → | в избранное →

для переноса в примеры мне нужно сначала разобраться в коде, а прямо сейчас у меня нету на это времени.

#10redactor
→ redactor [#7] | 23.11.04 15:29
www.parser.ru → | ответить → | в избранное →

плюс

в @advancedfields[]добавить
 $ukrainian[^print[;%e %h %Y, %A;$ui-locale]]
 $english[^print[;%e %h %Y, %A;$ei-locale]]


А то хэш с этими локалями только место занимает :)
#11Кодер
→ Misha v.3 [#9] | 23.11.04 15:30
www.parser.ru → | ответить → | в избранное →

Будем ждать :)

#12redactor
→ redactor [#7] | 23.11.04 17:28
www.parser.ru → | ответить → | в избранное →

поправка к добавочке

Не хочет просто так работать с $sql-date.
Название не нравится :)
Делаем так: ${sql-date}
#==========================================================================================
# Конструктор класса
#==========================================================================================
@create[sql-date][date;cdate][tmp]
 ^if(!def ${sql-date}){

     $nowdate[^date::now[]]
     $sql-date[^nowdate.sql-string[]]
 
 }
 $count[^sql-date.split[ ]]
 $date[^sql-date.split[ ;h]]
 
# Заполняем поля, относящиеся к дате

#13serglif
→ redactor [#10] | 24.11.04 12:11
www.parser.ru → | ответить → | в избранное →

Спасибо за предложения

Единственно я не уверен, что для стандартного английского и украинского варианта подходит форматная строка %e %h %Y, %A. Насколько я знаю в английском принято писать типа "НазваниеМесяца ДД, ГГГГ (ДеньНедели)" - т.е %h %e, %Y (%A)? В украинском вроде как и у нас. И кстати мне помогли просклонять названия в украинском.
$.month[
		$.1[Сiчня]
		$.2[Лютня]
		$.3[Березеня]
		$.4[Квiтня]
		$.5[Травня]
		$.6[Червня]
		$.7[Липня]
		$.8[Серпня]
		$.9[Вересня]
		$.10[Жовтня]
		$.11[Листопада]
		$.12[Грудня]
	]


P.S. А насчет предложения из названия первого поста никто не поможет?
#14Никита Козин (Wonder)
→ serglif [#13] | 24.11.04 13:19
www.parser.ru → | ответить → | в избранное →

Посмотрите в примерах у Миши, может быть это то, что нужно?

#15serglif
→ Никита Козин (Wonder) [#14] | 24.11.04 13:40
www.parser.ru → | ответить → | в избранное →

не нашел ничего похожего... ткните пальцем, плиз...

#16Misha v.3
→ serglif [#15] | 24.11.04 13:52
www.parser.ru → | ответить → | в избранное →

отправил комментарии и REGEXP

#17Misha v.3
→ redactor [#10] | 24.11.04 14:56
www.parser.ru → | ответить → | в избранное →

я бы не добавлял это...

по моему основное назначение класса - делать ^cdate:print[$from_sql.datetime;формат;локаль]

поэтому незачем при каждом create (который вызывается из print) делать мало кому обычно нужные телодвижения.

возможно стоит сделать как у меня в dtf: установку текущей locale. если мы делаем на сайт английском, то выполнили один раз set locale и у нас по умолчанию все на английском, в ком числе в переменной где строка с полной датой (только тогда её надо обозвать не $russian).
#18Misha v.3
→ Кодер [#6] | 03.12.04 13:10
www.parser.ru → | ответить → | в избранное →

добавил.

#19AK666
→ serglif [#13] | 17.02 23:53
www.parser.ru → | ответить → | в избранное →
$.2[Лютня] -> $.2[Лютого]

в примерах тоже надо исправить (сейчас по поиску "+Лютня -музыка -инструмент" можно выцепить всех кто использует этот класс :))
#20Misha v.3
→ serglif [#1] | 09.05 06:21
www.parser.ru → | ответить → | в избранное →

внёс правки и выложил архив с обновлённым классом

- исправлены ошибки написания дней недели и месяцев в английском варианте
- конструктор в качестве входного параметра теперь также понимает объект класса cdate, int, double
- УДАЛЕНО создание внутренней переменной $local (с текстовым представлением даты) при конструировании объекта
- при ^obj.print[;формат] теперь не происходит ещё одного, внутреннего, создания объекта cdate
- ускорена работа конструктора в случае ^cdate::create[date-object]
- для разбора строковых дат используется единожды созданные внутренний объект класса regex (нет множественной компиляций pcre-шаблона в случае парсинга большого количества дат)
- добавлен метод sql-string
- мелкие правки
Страницы ветки: 1 2 | Дальше → | одной страницей