• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2010 Google Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""Simple protocol message types.
19
20Includes new message and field types that are outside what is defined by the
21protocol buffers standard.
22"""
23import datetime
24
25from apitools.base.protorpclite import messages
26from apitools.base.protorpclite import util
27
28__all__ = [
29    'DateTimeField',
30    'DateTimeMessage',
31    'VoidMessage',
32]
33
34
35class VoidMessage(messages.Message):
36    """Empty message."""
37
38
39class DateTimeMessage(messages.Message):
40    """Message to store/transmit a DateTime.
41
42    Fields:
43      milliseconds: Milliseconds since Jan 1st 1970 local time.
44      time_zone_offset: Optional time zone offset, in minutes from UTC.
45    """
46    milliseconds = messages.IntegerField(1, required=True)
47    time_zone_offset = messages.IntegerField(2)
48
49
50class DateTimeField(messages.MessageField):
51    """Field definition for datetime values.
52
53    Stores a python datetime object as a field.  If time zone information is
54    included in the datetime object, it will be included in
55    the encoded data when this is encoded/decoded.
56    """
57
58    type = datetime.datetime
59
60    message_type = DateTimeMessage
61
62    @util.positional(3)
63    def __init__(self,
64                 number,
65                 **kwargs):
66        super(DateTimeField, self).__init__(self.message_type,
67                                            number,
68                                            **kwargs)
69
70    def value_from_message(self, message):
71        """Convert DateTimeMessage to a datetime.
72
73        Args:
74          A DateTimeMessage instance.
75
76        Returns:
77          A datetime instance.
78        """
79        message = super(DateTimeField, self).value_from_message(message)
80        if message.time_zone_offset is None:
81            return datetime.datetime.utcfromtimestamp(
82                message.milliseconds / 1000.0)
83
84        # Need to subtract the time zone offset, because when we call
85        # datetime.fromtimestamp, it will add the time zone offset to the
86        # value we pass.
87        milliseconds = (message.milliseconds -
88                        60000 * message.time_zone_offset)
89
90        timezone = util.TimeZoneOffset(message.time_zone_offset)
91        return datetime.datetime.fromtimestamp(milliseconds / 1000.0,
92                                               tz=timezone)
93
94    def value_to_message(self, value):
95        value = super(DateTimeField, self).value_to_message(value)
96        # First, determine the delta from the epoch, so we can fill in
97        # DateTimeMessage's milliseconds field.
98        if value.tzinfo is None:
99            time_zone_offset = 0
100            local_epoch = datetime.datetime.utcfromtimestamp(0)
101        else:
102            time_zone_offset = util.total_seconds(
103                value.tzinfo.utcoffset(value))
104            # Determine Jan 1, 1970 local time.
105            local_epoch = datetime.datetime.fromtimestamp(-time_zone_offset,
106                                                          tz=value.tzinfo)
107        delta = value - local_epoch
108
109        # Create and fill in the DateTimeMessage, including time zone if
110        # one was specified.
111        message = DateTimeMessage()
112        message.milliseconds = int(util.total_seconds(delta) * 1000)
113        if value.tzinfo is not None:
114            utc_offset = value.tzinfo.utcoffset(value)
115            if utc_offset is not None:
116                message.time_zone_offset = int(
117                    util.total_seconds(value.tzinfo.utcoffset(value)) / 60)
118
119        return message
120