HCE Project Python language Distributed Tasks Manager Application, Distributed Crawler Application and client API bindings.  2.0.0-chaika
Hierarchical Cluster Engine Python language binding
PDateTimezonesHandler.py
Go to the documentation of this file.
1 # coding: utf-8
2 """
3 HCE project, Python bindings, Distributed Tasks Manager application.
4 PDateTimezonesHandler Class content main functional calculate offset use timezones.
5 
6 @package: dc_processor
7 @file PDateTimezonesHandler.py
8 @author Alexander Vybornyh <alexander.hce.cluster@gmail.com>
9 @link: http://hierarchical-cluster-engine.com/
10 @copyright: Copyright &copy; 2013-2016 IOIX Ukraine
11 @license: http://hierarchical-cluster-engine.com/license/
12 @since: 0.1
13 """
14 
15 
16 import datetime
17 import re
18 import time
19 import json
20 from app.DateTimeType import DateTimeType
21 
22 
23 # # Class PDateTimezonesProperties for save properties data
25  # #Constructor
26  def __init__(self, pattern, zoneTo, zoneFrom, condition):
27  self.pattern = pattern
28  self.zoneTo = zoneTo
29  self.zoneFrom = zoneFrom
30  self.condition = condition
31 
32 
33 # # Class DateTimeType for extract data
34 #
35 class PDateTimezonesHandler(object):
36  # #Constans used in class
37  PROPERTY_OPTION_PATTERN = 'pattern'
38  PROPERTY_OPTION_ZONE_TO = 'zone_to'
39  PROPERTY_OPTION_ZONE_FROM = 'zone_from'
40  PROPERTY_OPTION_CONDITION = 'condition'
41  # #Spetial value for 'zone_to'
42  VALUE_ZONE_FROM_LOCAL = 'LOCAL'
43  # #Constant of error messages
44  ERROR_BAD_PARAMETER = 'Bad parameter'
45  ERROR_LOAD_PROPERTIES = 'Load properties fail'
46  ERROR_MANDATORY_FIELDS = 'Missed some mandatory fields'
47  ERROR_WRONG_CONDITION = "Wrong '" + PROPERTY_OPTION_CONDITION + "' value"
48  # #Constants used in class
49  USE_PATTERN = 'Use pattern'
50 
51  patternListTimezoneOffset = [r'(?P<op>[+−-])(?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9])',
52  r'(?P<op>[+−-])(?P<hour>[0-9][0-9])(?P<min>[0-9][0-9])',
53  r'(?P<op>[+-])(?P<hour>\d{1,2})',
54  r'(?P<hour>[−][0-9][0-9])']
55  # #Constants
56  TRANSFORMATION_ALWAYS = 0 # always perform transformation
57  TRANSFORMATION_TIMEZONE_DEFINED = 1 # only if source date has timezone defined;
58  TRANSFORMATION_NO_TIMEZONE_DEFINED = 2 # only if source date has no timezone defined;
59  TRANSFORMATION_TIMEZONE_EQUAL = 3 # only if the source date timezone equal with zone_from value;
60  TRANSFORMATION_TIMEZONE_NOT_EQUAL = 4 # only if the source date timezone is not equal with zone_from value;
61  # #Constructor
62  def __init__(self):
63  pass
64 
65  # #Loag properties json string
66  #
67  # @param propertyString - input property string
68  # @param logger - instance of logger for log if necessary
69  # @return list of PDateTimezonesProperties instancies
70  @staticmethod
71  def loadProperties(propertyString, logger=None):
72  # variable for result
73  properties = []
74  try:
75  obj = json.loads(propertyString)
76  for elem in obj:
77  if PDateTimezonesHandler.PROPERTY_OPTION_PATTERN in elem and \
78  PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO in elem and \
79  PDateTimezonesHandler.PROPERTY_OPTION_ZONE_FROM in elem and \
80  PDateTimezonesHandler.PROPERTY_OPTION_CONDITION in elem:
81  properties.append(PDateTimezonesProperties(elem[PDateTimezonesHandler.PROPERTY_OPTION_PATTERN],
82  elem[PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO],
83  elem[PDateTimezonesHandler.PROPERTY_OPTION_ZONE_FROM],
84  elem[PDateTimezonesHandler.PROPERTY_OPTION_CONDITION]))
85  elif PDateTimezonesHandler.PROPERTY_OPTION_PATTERN in elem and \
86  PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO in elem and \
87  PDateTimezonesHandler.PROPERTY_OPTION_CONDITION in elem:
88  properties.append(PDateTimezonesProperties(elem[PDateTimezonesHandler.PROPERTY_OPTION_PATTERN],
89  elem[PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO],
90  None,
91  elem[PDateTimezonesHandler.PROPERTY_OPTION_CONDITION]))
92  else:
93  if logger is not None:
94  errorMessage = PDateTimezonesHandler.ERROR_MANDATORY_FIELDS + ': '
95  if PDateTimezonesHandler.PROPERTY_OPTION_PATTERN not in elem:
96  errorMessage += "'" + PDateTimezonesHandler.PROPERTY_OPTION_PATTERN + "',"
97 
98  if PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO not in elem:
99  errorMessage += "'" + PDateTimezonesHandler.PROPERTY_OPTION_ZONE_TO + "',"
100 
101  if PDateTimezonesHandler.PROPERTY_OPTION_CONDITION not in elem:
102  errorMessage += "'" + PDateTimezonesHandler.PROPERTY_OPTION_CONDITION + "',"
103 
104  logger.debug(errorMessage)
105 
106  except Exception, err:
107  if logger is not None:
108  logger.error(PDateTimezonesHandler.ERROR_LOAD_PROPERTIES + ': ' + str(err))
109 
110  return properties
111 
112 
113  # # Transform datetime accord to url pattern, timezones and conditions
114  #
115  # @param dt - input datetime before transformation
116  # @param utcOffset - input utcOffset if was detected
117  # @param propertyString - input property string
118  # @param urlString - url string for search use regular expression
119  # @param logger - instance of logger for log if necessary
120  # @return transformed datatime or None
121  @staticmethod
122  def transform(dt, utcOffset, propertyString, urlString, logger=None):
123  # variable for result
124  ret = None
125  try:
126  if not isinstance(dt, datetime.datetime):
127  raise Exception(PDateTimezonesHandler.ERROR_BAD_PARAMETER + " 'Datatime'")
128 
129  if (not isinstance(propertyString, str) and not isinstance(propertyString, unicode)) or not propertyString:
130  raise Exception(PDateTimezonesHandler.ERROR_BAD_PARAMETER + " 'propertyString'")
131 
132  if (not isinstance(urlString, str) and not isinstance(urlString, unicode)) or not urlString:
133  raise Exception(PDateTimezonesHandler.ERROR_BAD_PARAMETER + " 'urlString'")
134 
135  # try load properties
136  properties = PDateTimezonesHandler.loadProperties(propertyString, logger)
137  # Transform each element
138  for elem in properties:
139  match = re.search(elem.pattern, urlString)
140  if match:
141  if logger is not None:
142  logger.debug(PDateTimezonesHandler.USE_PATTERN + ": '" + str(elem.pattern) + "'")
143 
144  transformHandlers = {\
145  PDateTimezonesHandler.TRANSFORMATION_ALWAYS:\
146  PDateTimezonesHandler.transformationHandlerAlways, \
147  PDateTimezonesHandler.TRANSFORMATION_TIMEZONE_DEFINED:\
148  PDateTimezonesHandler.transformationHandlerTimezoneDefined, \
149  PDateTimezonesHandler.TRANSFORMATION_NO_TIMEZONE_DEFINED:\
150  PDateTimezonesHandler.transformationHandlerNoTimezoneDefined, \
151  PDateTimezonesHandler.TRANSFORMATION_TIMEZONE_EQUAL:\
152  PDateTimezonesHandler.transformationHandlerTimezoneEqual, \
153  PDateTimezonesHandler.TRANSFORMATION_TIMEZONE_NOT_EQUAL:\
154  PDateTimezonesHandler.transformationHandlerTimezoneNotEqual}
155 
156  if int(elem.condition) < len(transformHandlers) and int(elem.condition) >= 0:
157  # check value of zoneFrom
158  if elem.zoneFrom is None:
159  elem.zoneFrom = DateTimeType.getTimezone(dt) # from datetime
160  elif elem.zoneFrom == PDateTimezonesHandler.VALUE_ZONE_FROM_LOCAL:
161  elem.zoneFrom = PDateTimezonesHandler.getLocalOffset() # from local offset
162 
163  logger.debug('>>>>> d.isoformat: ' + str(dt.isoformat(' ')))
164  logger.debug('>>>>> elem.zoneFrom: ' + str(elem.zoneFrom))
165  logger.debug('>>>>> elem.zoneTo: ' + str(elem.zoneTo))
166  logger.debug('>>>>> elem.condition = ' + str(elem.condition))
167 
168  # Make excution accord to different algorithms
169  ret = transformHandlers[int(elem.condition)](dt, utcOffset, elem.zoneTo, elem.zoneFrom, logger)
170  else:
171  if logger is not None:
172  logger.debug(PDateTimezonesHandler.ERROR_WRONG_CONDITION + ': ' + str(elem.condition))
173 
174  except Exception, err:
175  if logger is not None:
176  logger.error(str(err))
177 
178  logger.debug("!!! ret: %s", str(ret))
179  return ret
180 
181 
182  # #Convert utcOffset to time instance
183  #
184  # @param utcOffset - input utcOffset if was detected
185  # @param logger - instance of logger for log if necessary
186  # @return operation, hour, minute
187  @staticmethod
188  def utcOffsetToNumeric(utcOffset, logger=None):
189  # variable for result
190  operation = None
191  hour = 0
192  minute = 0
193 
194  utcOffset = utcOffset.replace('−', '-')
195 
196  try:
197  for pattern in PDateTimezonesHandler.patternListTimezoneOffset:
198  match = re.search(pattern, utcOffset)
199  if match:
200  if 'op' in match.groupdict():
201  if match.groupdict()['op'] == '-':
202  operation = -1
203  else:
204  operation = 1
205 
206  if 'hour' in match.groupdict():
207  hour = int(match.groupdict()['hour'])
208 
209  if operation is None:
210  operation = 1
211 
212  if 'min' in match.groupdict():
213  minute = int(match.groupdict()['min'])
214  break
215 
216  if logger is not None:
217  logger.debug('operation: ' + str(operation) + ' hour: ' + str(hour) + ' minute: ' + str(minute))
218 
219  except Exception, err:
220  if logger:
221  logger.debug(str(err))
222 
223  return operation, int(hour), int(minute)
224 
225 
226  # #Get local timezone utc offset as string
227  #
228  # @param - None
229  # @return string timezome
230  @staticmethod
232  gmTime = time.gmtime()
233  locTime = time.localtime()
234 
235  tmMinute = (gmTime.tm_hour * 60 + gmTime.tm_min) - (locTime.tm_hour * 60 + locTime.tm_min)
236  hour = tmMinute // 60 * (-1)
237  minute = tmMinute - tmMinute // 60 * 60
238  # structTime = (time.strptime(str(hour) + ' ' + str(minute), "%H %M"))
239  # return str(time.strftime('%H:%M', structTime))
240 
241  return str(hour) + ':' + str(minute)
242 
243 
244  # # Compare two string contents offset data
245  #
246  # @param lhs - first string data
247  # @param rhs - second string data
248  # @param logger - instance of logger for log if necessary
249  # @return True is equal and False otherwise
250  @staticmethod
251  def isEqualOffset(lhs, rhs, logger=None):
252  # variable for result
253  ret = False
254  # extract lhs timezone offset
255  lUtcOffset = DateTimeType.extractUtcOffset(lhs, logger)
256  if lUtcOffset is not None:
257  # extract rhs timezome offset
258  rUtcOffset = DateTimeType.extractUtcOffset(rhs, logger)
259 
260  if rUtcOffset is not None:
261  lOperation, lHour, lMinute = PDateTimezonesHandler.utcOffsetToNumeric(lUtcOffset, logger)
262  rOperation, rHour, rMinute = PDateTimezonesHandler.utcOffsetToNumeric(rUtcOffset, logger)
263  # compare extracter numeric values
264  if lOperation and rOperation and lOperation == rOperation and lHour == rHour and lMinute == rMinute:
265  ret = True
266 
267  return ret
268 
269 
270  # # Transformation handler always perform
271  #
272  # @param dt - input datetime before transformation
273  # @param utcOffset - input utcOffset if was detected
274  # @param zoneTo - string defines zone to transform to
275  # @param zoneFrom - string defines zone to transform from
276  # @param logger - instance of logger for log if necessary
277  # @return datetime instance
278  @staticmethod
279  def transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger=None): # pylint: disable=W0613,W0612
280  # variable for result
281  ret = None
282  # local variables
283  tZoneTo = datetime.timedelta(hours=0)
284  tZoneFrom = datetime.timedelta(hours=0)
285  opZoneFrom = 1
286 
287  zoneToOffset = DateTimeType.extractUtcOffset(zoneTo, logger)
288  if zoneToOffset is not None:
289  zoneTo = str(zoneToOffset)
290 
291  zoneFromOffset = DateTimeType.extractUtcOffset(zoneFrom, logger)
292  if zoneFromOffset is not None:
293  zoneFrom = str(zoneFromOffset)
294 
295  opZoneTo, hour, minute = PDateTimezonesHandler.utcOffsetToNumeric(zoneTo, logger)
296  if opZoneTo is None:
297  opZoneTo = 1
298 
299  if opZoneTo is not None:
300  tZoneTo = datetime.timedelta(hours=hour, minutes=minute) # pylint: disable=R0204
301 
302  if zoneFrom is None:
303  tZoneFrom = datetime.timedelta(hours=0, minutes=0) # pylint: disable=R0204
304  opZoneFrom = 1
305  else:
306  opZoneFrom, hour, minute = PDateTimezonesHandler.utcOffsetToNumeric(zoneFrom, logger)
307  if opZoneFrom is None:
308  opZoneFrom = 1
309  tZoneFrom = datetime.timedelta(hours=hour, minutes=minute)
310 
311  if logger is not None:
312  logger.debug("!!! opZoneTo = %s, opZoneFrom = %s", str(opZoneTo), str(opZoneFrom))
313  logger.debug("!!! tZoneTo = %s, tZoneFrom = %s", str(tZoneTo), str(tZoneFrom))
314 
315  if opZoneTo is not None and opZoneFrom is not None:
316  if int(opZoneTo) >= 0 and int(opZoneFrom) >= 0:
317  # ret = dt - (max(tZoneTo, tZoneFrom * (-1)) - min(tZoneTo, tZoneFrom * (-1)))
318  ret = dt + tZoneFrom * (-1) + tZoneTo
319  if logger is not None:
320  logger.debug("!!! GOOD !!! ret = %s", str(ret))
321 
322  elif int(opZoneTo) < 0 and int(opZoneFrom) < 0:
323  # ret = dt + (max(tZoneTo, tZoneFrom * (-1)) - min(tZoneTo, tZoneFrom * (-1)))
324  ret = dt + tZoneFrom * (-1) + tZoneTo
325 
326  else:
327  ret = dt + (abs(tZoneTo) + abs(tZoneFrom)) * opZoneTo
328  # ret = dt + (tZoneFrom * (-1) + tZoneTo) * opZoneTo
329 
330  return ret
331 
332 
333  # # Transformation handler if timezone defined
334  #
335  # @param dt - input datetime before transformation
336  # @param utcOffset - input utcOffset if was detected
337  # @param zoneTo - string defines zone to transform to
338  # @param zoneFrom - string defines zone to transform from
339  # @param logger - instance of logger for log if necessary
340  # @return datetime instance
341  @staticmethod
342  def transformationHandlerTimezoneDefined(dt, utcOffset, zoneTo, zoneFrom, logger=None):
343  # variable for result
344  ret = None
345  sourceTimeZone = DateTimeType.extractUtcOffset(utcOffset, logger, True)
346  if sourceTimeZone is not None:
347  ret = PDateTimezonesHandler.transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger)
348 
349  return ret
350 
351 
352  # # Transformation handler if no timezone defined
353  #
354  # @param dt - input datetime before transformation
355  # @param utcOffset - input utcOffset if was detected
356  # @param zoneTo - string defines zone to transform to
357  # @param zoneFrom - string defines zone to transform from
358  # @param logger - instance of logger for log if necessary
359  # @return datetime instance
360  @staticmethod
361  def transformationHandlerNoTimezoneDefined(dt, utcOffset, zoneTo, zoneFrom, logger=None):
362  # variable for result
363  ret = None
364  sourceTimeZone = DateTimeType.extractUtcOffset(utcOffset, logger)
365  if sourceTimeZone is None:
366  ret = PDateTimezonesHandler.transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger)
367 
368  return ret
369 
370 
371  # # Transformation handler if timezone equal
372  #
373  # @param dt - input datetime before transformation
374  # @param utcOffset - input utcOffset if was detected
375  # @param zoneTo - string defines zone to transform to
376  # @param zoneFrom - string defines zone to transform from
377  # @param logger - instance of logger for log if necessary
378  # @return datetime instance
379  @staticmethod
380  def transformationHandlerTimezoneEqual(dt, utcOffset, zoneTo, zoneFrom, logger=None):
381  # variable for result
382  ret = None
383  if PDateTimezonesHandler.isEqualOffset(utcOffset, zoneFrom):
384  ret = PDateTimezonesHandler.transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger)
385 
386  return ret
387 
388 
389  # # Transformation handler if timezone not equal
390  #
391  # @param dt - input datetime before transformation
392  # @param utcOffset - input utcOffset if was detected
393  # @param zoneTo - string defines zone to transform to
394  # @param zoneFrom - string defines zone to transform from
395  # @param logger - instance of logger for log if necessary
396  # @return datetime instance
397  @staticmethod
398  def transformationHandlerTimezoneNotEqual(dt, utcOffset, zoneTo, zoneFrom, logger=None):
399  # variable for result
400  ret = None
401  if not PDateTimezonesHandler.isEqualOffset(utcOffset, zoneFrom):
402  ret = PDateTimezonesHandler.transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger)
403 
404  return ret
405 
406 
407  # # Apply timezone
408  #
409  # @param dt - datetime instance
410  # @param timezone - timezone as string for apply
411  # @param logger - instance of logger for log if necessary
412  # @return datetime instance after apply timezone offset
413  @staticmethod
414  def applyTimezone(dt, timezone, logger=None):
415  utcOffset = DateTimeType.extractUtcOffset(timezone, logger, True)
416  if logger is not None:
417  logger.debug("timezone: '%s', utcOffset: %s", str(timezone), str(utcOffset))
418 
419  res = PDateTimezonesHandler.transformationHandlerAlways(dt, utcOffset, timezone, None, logger)
420  if logger is not None:
421  logger.debug("result: %s", str(res))
422 
423  if res is not None:
424  dt = res
425 
426  return dt
def transformationHandlerTimezoneNotEqual(dt, utcOffset, zoneTo, zoneFrom, logger=None)
def transform(dt, utcOffset, propertyString, urlString, logger=None)
def __init__(self, pattern, zoneTo, zoneFrom, condition)
def transformationHandlerAlways(dt, utcOffset, zoneTo, zoneFrom, logger=None)
def transformationHandlerTimezoneDefined(dt, utcOffset, zoneTo, zoneFrom, logger=None)
def transformationHandlerTimezoneEqual(dt, utcOffset, zoneTo, zoneFrom, logger=None)
def transformationHandlerNoTimezoneDefined(dt, utcOffset, zoneTo, zoneFrom, logger=None)