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"
24 # this one (datetime.isoformat) would work too but that's less readable - we support this input though
25 iso_format = "%Y-%m-%dT%H:%M:%S"
26 # sometimes it's convenient to understand more formats
27 input_formats = [ sql_format,
34 # for timestamps we usually accept either an int, or an ISO string,
35 # the datetime.datetime stuff can in general be used locally,
36 # but not sure it can be marshalled over xmlrpc though
40 return Mixed (Parameter (int, doc + " (unix timestamp)"),
41 Parameter (str, doc + " (formatted as %s)"%Timestamp.sql_format),
45 def sql_validate (input, timezone=False, check_future = False):
47 Validates the specified GMT timestamp, returns a
48 standardized string suitable for SQL input.
50 Input may be a number (seconds since UNIX epoch back in 1970,
51 or a string (in one of the supported input formats).
53 If timezone is True, the resulting string contains
54 timezone information, which is hard-wired as 'UTC'
56 If check_future is True, raises an exception if timestamp is in
59 Returns a GMT timestamp string suitable to feed SQL.
62 if not timezone: output_format = Timestamp.sql_format
63 else: output_format = Timestamp.sql_format_utc
65 if Timestamp.debug: print 'sql_validate, in:',input,
66 if isinstance(input, StringTypes):
68 # calendar.timegm() is the inverse of time.gmtime()
69 for time_format in Timestamp.input_formats:
71 timestamp = calendar.timegm(time.strptime(input, time_format))
72 sql = time.strftime(output_format, time.gmtime(timestamp))
74 # wrong format: ignore
75 except ValueError: pass
78 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
79 elif isinstance (input,(int,long,float)):
81 timestamp = long(input)
82 sql = time.strftime(output_format, time.gmtime(timestamp))
84 raise PLCInvalidArgument, "Timestamp %r not recognized -- %r"%(input,e)
86 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
88 if check_future and input < time.time():
89 raise PLCInvalidArgument, "'%s' not in the future" % sql
91 if Timestamp.debug: print 'sql_validate, out:',sql
95 def sql_validate_utc (timestamp):
96 "For convenience, return sql_validate(intput, timezone=True, check_future=False)"
97 return Timestamp.sql_validate (timestamp, timezone=True, check_future=False)
101 def cast_long (input):
103 Translates input timestamp as a unix timestamp.
105 Input may be a number (seconds since UNIX epoch, i.e., 1970-01-01
106 00:00:00 GMT), a string (in one of the supported input formats above).
109 if Timestamp.debug: print 'cast_long, in:',input,
110 if isinstance(input, StringTypes):
112 for time_format in Timestamp.input_formats:
114 result=calendar.timegm(time.strptime(input, time_format))
115 if Timestamp.debug: print 'out:',result
117 # wrong format: ignore
118 except ValueError: pass
119 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
120 elif isinstance (input,(int,long,float)):
122 if Timestamp.debug: print 'out:',result
125 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
128 """ Translate a string into a time using dateutil.parser.parse but make
129 sure it's in UTC time and strip the timezone, so that it's compatible
130 with normal datetime.datetime objects.
132 For safety this can also handle inputs that are either timestamps, or
135 # prepare the input for the checks below by
136 # casting strings ('1327098335') to ints
137 if isinstance(input, StringTypes):
143 if isinstance (input, datetime.datetime):
145 elif isinstance (input, StringTypes):
146 t = dateutil.parser.parse(input)
147 if t.utcoffset() is not None:
148 t = t.utcoffset() + t.replace(tzinfo=None)
150 elif isinstance (input, (int,float,long)):
151 return datetime.datetime.fromtimestamp(input)
156 return datetime.datetime.strftime(Timestamp.utcparse(input), Timestamp.sql_format)
158 # utility for displaying durations
159 # be consistent in avoiding the datetime stuff
167 def to_string(duration):
170 (days,left) = divmod(left,Duration.DAY)
171 if days: result.append("%d d)"%td.days)
172 (hours,left) = divmod (left,Duration.HOUR)
173 if hours: result.append("%d h"%hours)
174 (minutes, seconds) = divmod (left, Duration.MINUTE)
175 if minutes: result.append("%d m"%minutes)
176 if seconds: result.append("%d s"%seconds)
177 if not result: result = ['void']
178 return "-".join(result)
181 def validate (duration):
182 # support seconds only for now, works for int/long/str
184 return long (duration)
186 raise PLCInvalidArgument, "Could not parse duration %r"%duration