2 # Utilities to handle timestamps / durations from/to integers and strings
9 # datetime.{datetime,timedelta} are powerful tools, but these objects are not
10 # natively marshalled over xmlrpc
13 from types import StringTypes
17 from PLC.Faults import *
18 from PLC.Parameter import Parameter, Mixed
20 # a dummy class mostly used as a namespace
26 # this is how we expose times to SQL
27 sql_format = "%Y-%m-%d %H:%M:%S"
28 sql_format_utc = "%Y-%m-%d %H:%M:%S UTC"
29 # this one (datetime.isoformat) would work too but that's less readable - we support this input though
30 iso_format = "%Y-%m-%dT%H:%M:%S"
31 # sometimes it's convenient to understand more formats
32 input_formats = [ sql_format,
39 # for timestamps we usually accept either an int, or an ISO string,
40 # the datetime.datetime stuff can in general be used locally,
41 # but not sure it can be marshalled over xmlrpc though
45 return Mixed (Parameter (int, doc + " (unix timestamp)"),
46 Parameter (str, doc + " (formatted as %s)"%Timestamp.sql_format),
50 def sql_validate (input, timezone=False, check_future = False):
52 Validates the specified GMT timestamp, returns a
53 standardized string suitable for SQL input.
55 Input may be a number (seconds since UNIX epoch back in 1970,
56 or a string (in one of the supported input formats).
58 If timezone is True, the resulting string contains
59 timezone information, which is hard-wired as 'UTC'
61 If check_future is True, raises an exception if timestamp is in
64 Returns a GMT timestamp string suitable to feed SQL.
67 if not timezone: output_format = Timestamp.sql_format
68 else: output_format = Timestamp.sql_format_utc
70 if Timestamp.debug: print 'sql_validate, in:',input,
71 if isinstance(input, StringTypes):
73 # calendar.timegm() is the inverse of time.gmtime()
74 for time_format in Timestamp.input_formats:
76 timestamp = calendar.timegm(time.strptime(input, time_format))
77 sql = time.strftime(output_format, time.gmtime(timestamp))
79 # wrong format: ignore
80 except ValueError: pass
83 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
84 elif isinstance (input,(int,long,float)):
86 timestamp = long(input)
87 sql = time.strftime(output_format, time.gmtime(timestamp))
89 raise PLCInvalidArgument, "Timestamp %r not recognized -- %r"%(input,e)
91 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
93 if check_future and input < time.time():
94 raise PLCInvalidArgument, "'%s' not in the future" % sql
96 if Timestamp.debug: print 'sql_validate, out:',sql
100 def sql_validate_utc (timestamp):
101 "For convenience, return sql_validate(intput, timezone=True, check_future=False)"
102 return Timestamp.sql_validate (timestamp, timezone=True, check_future=False)
106 def cast_long (input):
108 Translates input timestamp as a unix timestamp.
110 Input may be a number (seconds since UNIX epoch, i.e., 1970-01-01
111 00:00:00 GMT), a string (in one of the supported input formats above).
114 if Timestamp.debug: print 'cast_long, in:',input,
115 if isinstance(input, StringTypes):
117 for time_format in Timestamp.input_formats:
119 result=calendar.timegm(time.strptime(input, time_format))
120 if Timestamp.debug: print 'out:',result
122 # wrong format: ignore
123 except ValueError: pass
124 raise PLCInvalidArgument, "Cannot parse timestamp %r - not in any of %r formats"%(input,Timestamp.input_formats)
125 elif isinstance (input,(int,long,float)):
127 if Timestamp.debug: print 'out:',result
130 raise PLCInvalidArgument, "Timestamp %r - unsupported type %r"%(input,type(input))
133 # utility for displaying durations
134 # be consistent in avoiding the datetime stuff
142 def to_string(duration):
145 (days,left) = divmod(left,Duration.DAY)
146 if days: result.append("%d d)"%td.days)
147 (hours,left) = divmod (left,Duration.HOUR)
148 if hours: result.append("%d h"%hours)
149 (minutes, seconds) = divmod (left, Duration.MINUTE)
150 if minutes: result.append("%d m"%minutes)
151 if seconds: result.append("%d s"%seconds)
152 if not result: result = ['void']
153 return "-".join(result)
156 def validate (duration):
157 # support seconds only for now, works for int/long/str
159 return long (duration)
161 raise PLCInvalidArgument, "Could not parse duration %r"%duration