Date time field which handles microseconds exactly and internally stores the timestamp as number of microseconds since the unix epoch. Note: We need to do that because mongoengine serializes this field as comma delimited string which breaks sorting.
| 78 | |
| 79 | |
| 80 | class ComplexDateTimeField(LongField): |
| 81 | """ |
| 82 | Date time field which handles microseconds exactly and internally stores |
| 83 | the timestamp as number of microseconds since the unix epoch. |
| 84 | |
| 85 | Note: We need to do that because mongoengine serializes this field as comma |
| 86 | delimited string which breaks sorting. |
| 87 | """ |
| 88 | |
| 89 | def _convert_from_datetime(self, val): |
| 90 | """ |
| 91 | Convert a `datetime` object to number of microseconds since epoch representation |
| 92 | (which will be stored in MongoDB). This is the reverse function of |
| 93 | `_convert_from_db`. |
| 94 | """ |
| 95 | result = self._datetime_to_microseconds_since_epoch(value=val) |
| 96 | return result |
| 97 | |
| 98 | def _convert_from_db(self, value): |
| 99 | result = self._microseconds_since_epoch_to_datetime(data=value) |
| 100 | return result |
| 101 | |
| 102 | def _microseconds_since_epoch_to_datetime(self, data): |
| 103 | """ |
| 104 | Convert a number representation to a `datetime` object (the object you |
| 105 | will manipulate). This is the reverse function of |
| 106 | `_convert_from_datetime`. |
| 107 | |
| 108 | :param data: Number of microseconds since the epoch. |
| 109 | :type data: ``int`` |
| 110 | """ |
| 111 | result = datetime.datetime.utcfromtimestamp(data // SECOND_TO_MICROSECONDS) |
| 112 | microseconds_reminder = data % SECOND_TO_MICROSECONDS |
| 113 | result = result.replace(microsecond=microseconds_reminder) |
| 114 | result = date_utils.add_utc_tz(result) |
| 115 | return result |
| 116 | |
| 117 | def _datetime_to_microseconds_since_epoch(self, value): |
| 118 | """ |
| 119 | Convert datetime in UTC to number of microseconds from epoch. |
| 120 | |
| 121 | Note: datetime which is passed to the function needs to be in UTC timezone (e.g. as returned |
| 122 | by ``datetime.datetime.utcnow``). |
| 123 | |
| 124 | :rtype: ``int`` |
| 125 | """ |
| 126 | # Verify that the value which is passed in contains UTC timezone |
| 127 | # information. |
| 128 | if not value.tzinfo or (value.tzinfo.utcoffset(value) != datetime.timedelta(0)): |
| 129 | raise ValueError( |
| 130 | "Value passed to this function needs to be in UTC timezone" |
| 131 | ) |
| 132 | |
| 133 | seconds = calendar.timegm(value.timetuple()) |
| 134 | microseconds_reminder = value.time().microsecond |
| 135 | result = int(seconds * SECOND_TO_MICROSECONDS) + microseconds_reminder |
| 136 | return result |
| 137 |
no outgoing calls