Package pytils :: Module dt
[hide private]

Source Code for Module pytils.dt

  1  # -*- coding: utf-8 -*- 
  2  # -*- test-case-name: pytils.test.test_dt -*- 
  3  # PyTils - simple processing for russian strings 
  4  # Copyright (C) 2006-2007  Yury Yurevich 
  5  # 
  6  # http://gorod-omsk.ru/blog/pythy/projects/pytils/ 
  7  # 
  8  # This program is free software; you can redistribute it and/or 
  9  # modify it under the terms of the GNU General Public License 
 10  # as published by the Free Software Foundation, version 2 
 11  # of the License. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  """ 
 18  Russian dates without locales 
 19  """ 
 20   
 21  __id__ = __revision__ = "$Id: dt.py 63 2007-01-02 09:22:16Z the.pythy $" 
 22  __url__ = "$URL: https://pythy.googlecode.com/svn/trunk/pytils/pytils/dt.py $" 
 23   
 24  import datetime 
 25   
 26  from pytils import numeral, utils 
 27   
 28  DAY_ALTERNATIVES = { 
 29      1: (u"вчера", u"завтра"), 
 30      2: (u"позавчера", u"послезавтра") 
 31      }  #: Day alternatives (i.e. one day ago -> yesterday) 
 32   
 33  DAY_VARIANTS = ( 
 34      u"день", 
 35      u"дня", 
 36      u"дней", 
 37      )  #: Forms (1, 2, 5) for noun 'day' 
 38   
 39  HOUR_VARIANTS = ( 
 40      u"час", 
 41      u"часа", 
 42      u"часов", 
 43      )  #: Forms (1, 2, 5) for noun 'hour' 
 44   
 45  MINUTE_VARIANTS = ( 
 46      u"минуту", 
 47      u"минуты", 
 48      u"минут", 
 49      )  #: Forms (1, 2, 5) for noun 'minute' 
 50   
 51  PREFIX_IN = u"через"  #: Prefix 'in' (i.e. B{in} three hours) 
 52  SUFFIX_AGO = u"назад"  #: Prefix 'ago' (i.e. three hours B{ago}) 
 53   
 54  MONTH_NAMES = ( 
 55      (u"янв", u"январь", u"января"), 
 56      (u"фев", u"февраль", u"февраля"), 
 57      (u"мар", u"март", u"марта"), 
 58      (u"апр", u"апрель", u"апреля"), 
 59      (u"май", u"май", u"мая"), 
 60      (u"июн", u"июнь", u"июня"), 
 61      (u"июл", u"июль", u"июля"), 
 62      (u"авг", u"август", u"августа"), 
 63      (u"сен", u"сентябрь", u"сентября"), 
 64      (u"окт", u"октябрь", u"октября"), 
 65      (u"ноя", u"ноябрь", u"ноября"), 
 66      (u"дек", u"декабрь", u"декабря"), 
 67      )  #: Month names (abbreviated, full, inflected) 
 68   
 69  DAY_NAMES = ( 
 70      (u"пн", u"понедельник", u"понедельник"), 
 71      (u"вт", u"вторник", u"вторник"), 
 72      (u"ср", u"среда", u"среду"), 
 73      (u"чт", u"четверг", u"четверг"), 
 74      (u"пт", u"пятница", u"пятницу"), 
 75      (u"сб", u"суббота", u"субботу"), 
 76      (u"вск", u"воскресенье", u"субботу"), 
 77      )  #: Day names (abbreviated, full, inflected) 
 78   
 79   
80 -def distance_of_time_in_words(from_time, accuracy=1, to_time=None):
81 """ 82 Represents distance of time in words 83 84 @param from_time: source time (in seconds from epoch) 85 @type from_time: C{int}, C{float} or C{datetime.datetime} 86 87 @param accuracy: level of accuracy (1..3), default=1 88 @type accuracy: C{int} 89 90 @param to_time: target time (in seconds from epoch), 91 default=None translates to current time 92 @type to_time: C{int}, C{float} or C{datetime.datetime} 93 94 @return: distance of time in words 95 @rtype: unicode 96 97 @raise TypeError: input parameters' check failed 98 @raise ValueError: accuracy is lesser or equal zero 99 """ 100 current = False 101 102 if to_time is None: 103 current = True 104 to_time = datetime.datetime.now() 105 106 utils.check_type('from_time', (int, float, datetime.datetime)) 107 utils.check_type('to_time', (int, float, datetime.datetime)) 108 utils.check_type('accuracy', int) 109 utils.check_positive('accuracy', strict=True) 110 111 if not isinstance(from_time, datetime.datetime): 112 from_time = datetime.datetime.fromtimestamp(from_time) 113 114 if not isinstance(to_time, datetime.datetime): 115 to_time = datetime.datetime.fromtimestamp(to_time) 116 117 dt_delta = to_time - from_time 118 difference = dt_delta.days*86400 + dt_delta.seconds 119 120 seconds_orig = int(abs(difference)) 121 minutes_orig = int(abs(difference)/60.0) 122 hours_orig = int(abs(difference)/3600.0) 123 days_orig = int(abs(difference)/86400.0) 124 in_future = from_time > to_time 125 126 words = [] 127 values = [] 128 alternatives = [] 129 130 days = days_orig 131 hours = hours_orig - days_orig*24 132 133 words.append(u"%d %s" % (days, numeral.choose_plural(days, DAY_VARIANTS))) 134 values.append(days) 135 136 words.append(u"%d %s" % \ 137 (hours, numeral.choose_plural(hours, HOUR_VARIANTS))) 138 values.append(hours) 139 140 hours == 1 and current and alternatives.append(u"час") 141 142 minutes = minutes_orig - hours_orig*60 143 144 words.append(u"%d %s" % (minutes, 145 numeral.choose_plural(minutes, MINUTE_VARIANTS))) 146 values.append(minutes) 147 148 minutes == 1 and current and alternatives.append(u"минуту") 149 150 real_words = words 151 # убираем из values и words конечные нули 152 while values and not values[-1]: 153 values.pop() 154 words.pop() 155 # убираем из values и words начальные нули 156 while values and not values[0]: 157 values.pop(0) 158 words.pop(0) 159 limit = min(accuracy, len(words)) 160 real_words = words[:limit] 161 real_values = values[:limit] 162 # снова убираем конечные нули 163 while real_values and not real_values[-1]: 164 real_values.pop() 165 real_words.pop() 166 limit -= 1 167 168 real_str = u" ".join(real_words) 169 170 # альтернативные варианты нужны только если в real_words одно значение 171 # и, вдобавок, если используется текущее время 172 alter_str = limit == 1 and current and \ 173 alternatives and alternatives[0] 174 _result_str = alter_str or real_str 175 result_str = in_future and u"%s %s" % (PREFIX_IN, _result_str) \ 176 or u"%s %s" % (_result_str, SUFFIX_AGO) 177 178 # если же прошло менее минуты, то real_words -- пустой, и поэтому 179 # нужно брать alternatives[0], а не result_str 180 zero_str = minutes == 0 and not real_words and \ 181 (in_future and u"менее чем через минуту" \ 182 or u"менее минуты назад") 183 184 # нужно использовать вчера/позавчера/завтра/послезавтра 185 # если days 1..2 и в real_words одно значение 186 day_alternatives = DAY_ALTERNATIVES.get(days, False) 187 alternate_day = day_alternatives and current and limit == 1 and \ 188 ((in_future and day_alternatives[1]) \ 189 or day_alternatives[0]) 190 191 final_str = not real_words and zero_str or alternate_day or result_str 192 193 return final_str
194 195
196 -def ru_strftime(format=u"%d.%m.%Y", date=None, inflected=False, inflected_day=False):
197 """ 198 Russian strftime without locale 199 200 @param format: strftime format, default=u'%d.%m.%Y' 201 @type format: C{unicode} 202 203 @param date: date value, default=None translates to today 204 @type date: C{datetime.date} or C{datetime.datetime} 205 206 @return: strftime string 207 @rtype: unicode 208 209 @raise TypeError: input parameters' check failed 210 """ 211 if date is None: 212 date = datetime.datetime.today() 213 utils.check_type('date', (datetime.date, datetime.datetime)) 214 utils.check_type('format', unicode) 215 216 midx = inflected and 2 or 1 217 didx = inflected_day and 2 or 1 218 219 format = format.replace(u'%a', DAY_NAMES[date.weekday()][0]) 220 format = format.replace(u'%A', DAY_NAMES[date.weekday()][didx]) 221 format = format.replace(u'%b', MONTH_NAMES[date.month-1][0]) 222 format = format.replace(u'%B', MONTH_NAMES[date.month-1][midx]) 223 224 # strftime must be str, so encode it to utf8: 225 s_format = format.encode("utf-8") 226 s_res = date.strftime(s_format) 227 # and back to unicode 228 u_res = s_res.decode("utf-8") 229 230 return u_res
231