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 # utility for displaying durations
129 # be consistent in avoiding the datetime stuff
137 def to_string(duration):
140 (days,left) = divmod(left,Duration.DAY)
141 if days: result.append("%d d)"%td.days)
142 (hours,left) = divmod (left,Duration.HOUR)
143 if hours: result.append("%d h"%hours)
144 (minutes, seconds) = divmod (left, Duration.MINUTE)
145 if minutes: result.append("%d m"%minutes)
146 if seconds: result.append("%d s"%seconds)
147 if not result: result = ['void']
148 return "-".join(result)
151 def validate (duration):
152 # support seconds only for now, works for int/long/str
154 return long (duration)
156 raise PLCInvalidArgument, "Could not parse duration %r"%duration