2 # Utilities to handle timestamps / durations from/to integers and strings
4 # datetime.{datetime,timedelta} are powerful tools, but these objects are not
5 # natively marshalled over xmlrpc
8 from types import StringTypes
12 from PLC.Faults import *
13 from PLC.Parameter import Parameter, Mixed
15 # a dummy class mostly used as a namespace
21 # this is how we expose times to SQL
22 sql_format = "%Y-%m-%d %H:%M:%S"
23 sql_format_utc = "%Y-%m-%d %H:%M:%S UTC"
25 # this one (datetime.isoformat) would work too but that's less readable - we support this input though
26 iso_format = "%Y-%m-%dT%H:%M:%S"
27 # sometimes it's convenient to understand more formats
28 input_formats = [ sql_format,
33 "%Y-%m-%d %H:%M:%S.%f"
36 # for timestamps we usually accept either an int, or an ISO string,
37 # the datetime.datetime stuff can in general be used locally,
38 # but not sure it can be marshalled over xmlrpc though
42 return Mixed (Parameter (int, doc + " (unix timestamp)"),
43 Parameter (str, doc + " (formatted as %s)"%Timestamp.sql_format),
47 def sql_validate (input, timezone=False, check_future = False):
49 Validates the specified GMT timestamp, returns a
50 standardized string suitable for SQL input.
52 Input may be a number (seconds since UNIX epoch back in 1970,
53 or a string (in one of the supported input formats).
55 If timezone is True, the resulting string contains
56 timezone information, which is hard-wired as 'UTC'
58 If check_future is True, raises an exception if timestamp is in
61 Returns a GMT timestamp string suitable to feed SQL.
64 if not timezone: output_format = Timestamp.sql_format
65 else: output_format = Timestamp.sql_format_utc
67 if Timestamp.debug: print 'sql_validate, in:',input,
68 if isinstance(input, StringTypes):
70 # calendar.timegm() is the inverse of time.gmtime()
71 for time_format in Timestamp.input_formats:
73 timestamp = calendar.timegm(time.strptime(input, time_format))
74 sql = time.strftime(output_format, time.gmtime(timestamp))
76 # wrong format: ignore
77 except ValueError: pass
80 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
81 elif isinstance (input,(int,long,float)):
83 timestamp = long(input)
84 sql = time.strftime(output_format, time.gmtime(timestamp))
86 raise PLCInvalidArgument, "Timestamp %r not recognized -- %r"%(input,e)
88 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
90 if check_future and input < time.time():
91 raise PLCInvalidArgument, "'%s' not in the future" % sql
93 if Timestamp.debug: print 'sql_validate, out:',sql
97 def sql_validate_utc (timestamp):
98 "For convenience, return sql_validate(intput, timezone=True, check_future=False)"
99 return Timestamp.sql_validate (timestamp, timezone=True, check_future=False)
103 def cast_long (input):
105 Translates input timestamp as a unix timestamp.
107 Input may be a number (seconds since UNIX epoch, i.e., 1970-01-01
108 00:00:00 GMT), a string (in one of the supported input formats above).
111 if Timestamp.debug: print 'cast_long, in:',input,
112 if isinstance(input, StringTypes):
114 for time_format in Timestamp.input_formats:
116 result=calendar.timegm(time.strptime(input, time_format))
117 if Timestamp.debug: print 'out:',result
119 # wrong format: ignore
120 except ValueError: pass
121 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
122 elif isinstance (input,(int,long,float)):
124 if Timestamp.debug: print 'out:',result
127 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
130 """ Translate a string into a time using dateutil.parser.parse but make
131 sure it's in UTC time and strip the timezone, so that it's compatible
132 with normal datetime.datetime objects.
134 For safety this can also handle inputs that are either timestamps, or
137 # prepare the input for the checks below by
138 # casting strings ('1327098335') to ints
139 if isinstance(input, StringTypes):
145 if isinstance (input, datetime.datetime):
147 elif isinstance (input, StringTypes):
148 t = dateutil.parser.parse(input)
149 if t.utcoffset() is not None:
150 t = t.utcoffset() + t.replace(tzinfo=None)
152 elif isinstance (input, (int,float,long)):
153 return datetime.datetime.fromtimestamp(input)
158 return datetime.datetime.strftime(Timestamp.utcparse(input), Timestamp.sql_format)
160 # utility for displaying durations
161 # be consistent in avoiding the datetime stuff
169 def to_string(duration):
172 (days,left) = divmod(left,Duration.DAY)
173 if days: result.append("%d d)"%td.days)
174 (hours,left) = divmod (left,Duration.HOUR)
175 if hours: result.append("%d h"%hours)
176 (minutes, seconds) = divmod (left, Duration.MINUTE)
177 if minutes: result.append("%d m"%minutes)
178 if seconds: result.append("%d s"%seconds)
179 if not result: result = ['void']
180 return "-".join(result)
183 def validate (duration):
184 # support seconds only for now, works for int/long/str
186 return long (duration)
188 raise PLCInvalidArgument, "Could not parse duration %r"%duration