Skip to content

Datatype

Data types (Boolean, Date, DateTime, Duration).

Classes:

Name Description
Boolean

Handles conversion between ODF boolean string representation (‘true’, ‘false’)

Date

Handles conversion between ODF date string representation and Python’s datetime.date type.

DateTime

Handles conversion between ODF date-time string representation and Python’s datetime.datetime type.

Duration

Handles conversion between ODF duration string representation (ISO 8601 format)

Attributes:

Name Type Description
DATETIME_FORMAT
DATETIME_FORMAT_MICRO
DATE_FORMAT
DURATION_FORMAT

DATETIME_FORMAT module-attribute

DATETIME_FORMAT = DATE_FORMAT + 'T%H:%M:%S'

DATETIME_FORMAT_MICRO module-attribute

DATETIME_FORMAT_MICRO = DATETIME_FORMAT + '.%f'

DATE_FORMAT module-attribute

DATE_FORMAT = '%Y-%m-%d'

DURATION_FORMAT module-attribute

DURATION_FORMAT = 'PT%02dH%02dM%02dS'

Boolean

Handles conversion between ODF boolean string representation (‘true’, ‘false’) and Python’s native bool type.

Methods:

Name Description
decode

Decode an ODF boolean string to a Python boolean.

encode

Encode a Python boolean (or boolean-like string/bytes) to an ODF boolean string.

Source code in odfdo/datatype.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class Boolean:
    """Handles conversion between ODF boolean string representation ('true', 'false')
    and Python's native `bool` type.
    """

    @staticmethod
    def decode(data: str) -> bool:
        """Decode an ODF boolean string to a Python boolean.

        Args:
            data: The string to decode, expected to be 'true' or 'false'.

        Returns:
            bool: `True` if data is 'true', `False` if data is 'false'.

        Raises:
            ValueError: If the input string is not a valid ODF boolean ('true' or 'false').
        """
        if data == "true":
            return True
        elif data == "false":
            return False
        raise ValueError(f"boolean {data!r} is invalid")

    @staticmethod
    def encode(value: bool | str | bytes | int | None) -> str:
        """Encode a Python boolean (or boolean-like string/bytes) to an ODF boolean string.

        Args:
            value: The value to encode. Can be a Python `bool`, a string ('true', 'false' case-insensitive), or bytes.

        Returns:
            str: The ODF boolean string ('true' or 'false').

        Raises:
            TypeError: If the input value cannot be interpreted as a boolean.
        """
        if isinstance(value, bytes):
            value = value.decode()
        if value is True or str(value).lower() == "true":
            return "true"
        elif value is False or str(value).lower() == "false":
            return "false"
        raise TypeError(f"{value!r} is not a boolean")

decode staticmethod

decode(data: str) -> bool

Decode an ODF boolean string to a Python boolean.

Parameters:

Name Type Description Default
data str

The string to decode, expected to be ‘true’ or ‘false’.

required

Returns:

Name Type Description
bool bool

True if data is ‘true’, False if data is ‘false’.

Raises:

Type Description
ValueError

If the input string is not a valid ODF boolean (‘true’ or ‘false’).

Source code in odfdo/datatype.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@staticmethod
def decode(data: str) -> bool:
    """Decode an ODF boolean string to a Python boolean.

    Args:
        data: The string to decode, expected to be 'true' or 'false'.

    Returns:
        bool: `True` if data is 'true', `False` if data is 'false'.

    Raises:
        ValueError: If the input string is not a valid ODF boolean ('true' or 'false').
    """
    if data == "true":
        return True
    elif data == "false":
        return False
    raise ValueError(f"boolean {data!r} is invalid")

encode staticmethod

encode(value: bool | str | bytes | int | None) -> str

Encode a Python boolean (or boolean-like string/bytes) to an ODF boolean string.

Parameters:

Name Type Description Default
value bool | str | bytes | int | None

The value to encode. Can be a Python bool, a string (‘true’, ‘false’ case-insensitive), or bytes.

required

Returns:

Name Type Description
str str

The ODF boolean string (‘true’ or ‘false’).

Raises:

Type Description
TypeError

If the input value cannot be interpreted as a boolean.

Source code in odfdo/datatype.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@staticmethod
def encode(value: bool | str | bytes | int | None) -> str:
    """Encode a Python boolean (or boolean-like string/bytes) to an ODF boolean string.

    Args:
        value: The value to encode. Can be a Python `bool`, a string ('true', 'false' case-insensitive), or bytes.

    Returns:
        str: The ODF boolean string ('true' or 'false').

    Raises:
        TypeError: If the input value cannot be interpreted as a boolean.
    """
    if isinstance(value, bytes):
        value = value.decode()
    if value is True or str(value).lower() == "true":
        return "true"
    elif value is False or str(value).lower() == "false":
        return "false"
    raise TypeError(f"{value!r} is not a boolean")

Date

Handles conversion between ODF date string representation and Python’s datetime.date type. Assumes ISO 8601 format (YYYY-MM-DD) for ODF dates.

Methods:

Name Description
decode

Decode an ODF date string to a Python datetime object.

encode

Encode a Python datetime or date object to an ODF date string.

Source code in odfdo/datatype.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class Date:
    """Handles conversion between ODF date string representation and Python's `datetime.date` type.
    Assumes ISO 8601 format (YYYY-MM-DD) for ODF dates.
    """

    @staticmethod
    def decode(data: str) -> datetime:
        """Decode an ODF date string to a Python `datetime` object.

        Args:
            data: The date string to decode, expected in ISO 8601 format (YYYY-MM-DD).

        Returns:
            datetime: A `datetime` object representing the decoded date.
        """
        return datetime.fromisoformat(data)

    @staticmethod
    def encode(value: datetime | date) -> str:
        """Encode a Python `datetime` or `date` object to an ODF date string.

        The output string is formatted as "YYYY-MM-DD".

        Args:
            value: The `datetime` or `date` object to encode.

        Returns:
            str: The ODF date string (e.g., "2024-01-31").
        """
        if isinstance(value, datetime):
            return value.date().isoformat()
        # date instance
        return value.isoformat()

decode staticmethod

decode(data: str) -> datetime

Decode an ODF date string to a Python datetime object.

Parameters:

Name Type Description Default
data str

The date string to decode, expected in ISO 8601 format (YYYY-MM-DD).

required

Returns:

Name Type Description
datetime datetime

A datetime object representing the decoded date.

Source code in odfdo/datatype.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@staticmethod
def decode(data: str) -> datetime:
    """Decode an ODF date string to a Python `datetime` object.

    Args:
        data: The date string to decode, expected in ISO 8601 format (YYYY-MM-DD).

    Returns:
        datetime: A `datetime` object representing the decoded date.
    """
    return datetime.fromisoformat(data)

encode staticmethod

encode(value: datetime | date) -> str

Encode a Python datetime or date object to an ODF date string.

The output string is formatted as “YYYY-MM-DD”.

Parameters:

Name Type Description Default
value datetime | date

The datetime or date object to encode.

required

Returns:

Name Type Description
str str

The ODF date string (e.g., “2024-01-31”).

Source code in odfdo/datatype.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@staticmethod
def encode(value: datetime | date) -> str:
    """Encode a Python `datetime` or `date` object to an ODF date string.

    The output string is formatted as "YYYY-MM-DD".

    Args:
        value: The `datetime` or `date` object to encode.

    Returns:
        str: The ODF date string (e.g., "2024-01-31").
    """
    if isinstance(value, datetime):
        return value.date().isoformat()
    # date instance
    return value.isoformat()

DateTime

Handles conversion between ODF date-time string representation and Python’s datetime.datetime type. Assumes ISO 8601 format for ODF date-times.

Methods:

Name Description
decode

Decode an ODF date-time string to a Python datetime.datetime object.

encode

Encode a Python datetime.datetime object to an ODF date-time string.

Source code in odfdo/datatype.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
class DateTime:
    """Handles conversion between ODF date-time string representation and Python's `datetime.datetime` type.
    Assumes ISO 8601 format for ODF date-times.
    """

    @staticmethod
    def decode(data: str) -> datetime:
        """Decode an ODF date-time string to a Python `datetime.datetime` object.

        Handles various ISO 8601 formats and provides compatibility for Python 3.9/3.10
        specific `fromisoformat` behaviors.

        Args:
            data: The date-time string to decode, expected in ISO 8601 format.

        Returns:
            datetime: A `datetime.datetime` object representing the decoded date-time.
        """

        def _decode_39_310(data1: str) -> datetime:  # pragma: nocover
            if data1.endswith("Z"):
                data1 = data1[:-1] + "+00:00"
            try:
                return datetime.fromisoformat(data1)
            except ValueError as e:
                if "microsecond must be" in str(e) or "Invalid isoformat string" in str(
                    e
                ):
                    if len(data1) == 29:
                        return datetime.fromisoformat(data1[:26])
                    if len(data1) == 35:
                        return datetime.fromisoformat(data1[:26] + data1[-6:])
                raise

        try:
            return datetime.fromisoformat(data)
        except ValueError:
            # maybe python 3.9 pr 3.10
            if sys.version_info.minor in {9, 10}:  # pragma: nocover
                return _decode_39_310(data)
            raise

    @staticmethod
    def encode(value: datetime) -> str:
        """Encode a Python `datetime.datetime` object to an ODF date-time string.

        The output string is formatted in ISO 8601. UTC offsets (e.g., "+00:00")
        are converted to the canonical 'Z' representation.

        Args:
            value: The `datetime.datetime` object to encode.

        Returns:
            str: The ODF date-time string (e.g., "YYYY-MM-DDTHH:MM:SSZ").
        """
        text = value.isoformat()
        if text.endswith("+00:00"):
            # convert to canonical representation
            return text[:-6] + "Z"
        return text

decode staticmethod

decode(data: str) -> datetime

Decode an ODF date-time string to a Python datetime.datetime object.

Handles various ISO 8601 formats and provides compatibility for Python 3.9/3.10 specific fromisoformat behaviors.

Parameters:

Name Type Description Default
data str

The date-time string to decode, expected in ISO 8601 format.

required

Returns:

Name Type Description
datetime datetime

A datetime.datetime object representing the decoded date-time.

Source code in odfdo/datatype.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
@staticmethod
def decode(data: str) -> datetime:
    """Decode an ODF date-time string to a Python `datetime.datetime` object.

    Handles various ISO 8601 formats and provides compatibility for Python 3.9/3.10
    specific `fromisoformat` behaviors.

    Args:
        data: The date-time string to decode, expected in ISO 8601 format.

    Returns:
        datetime: A `datetime.datetime` object representing the decoded date-time.
    """

    def _decode_39_310(data1: str) -> datetime:  # pragma: nocover
        if data1.endswith("Z"):
            data1 = data1[:-1] + "+00:00"
        try:
            return datetime.fromisoformat(data1)
        except ValueError as e:
            if "microsecond must be" in str(e) or "Invalid isoformat string" in str(
                e
            ):
                if len(data1) == 29:
                    return datetime.fromisoformat(data1[:26])
                if len(data1) == 35:
                    return datetime.fromisoformat(data1[:26] + data1[-6:])
            raise

    try:
        return datetime.fromisoformat(data)
    except ValueError:
        # maybe python 3.9 pr 3.10
        if sys.version_info.minor in {9, 10}:  # pragma: nocover
            return _decode_39_310(data)
        raise

encode staticmethod

encode(value: datetime) -> str

Encode a Python datetime.datetime object to an ODF date-time string.

The output string is formatted in ISO 8601. UTC offsets (e.g., “+00:00”) are converted to the canonical ‘Z’ representation.

Parameters:

Name Type Description Default
value datetime

The datetime.datetime object to encode.

required

Returns:

Name Type Description
str str

The ODF date-time string (e.g., “YYYY-MM-DDTHH:MM:SSZ”).

Source code in odfdo/datatype.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
@staticmethod
def encode(value: datetime) -> str:
    """Encode a Python `datetime.datetime` object to an ODF date-time string.

    The output string is formatted in ISO 8601. UTC offsets (e.g., "+00:00")
    are converted to the canonical 'Z' representation.

    Args:
        value: The `datetime.datetime` object to encode.

    Returns:
        str: The ODF date-time string (e.g., "YYYY-MM-DDTHH:MM:SSZ").
    """
    text = value.isoformat()
    if text.endswith("+00:00"):
        # convert to canonical representation
        return text[:-6] + "Z"
    return text

Duration

Handles conversion between ODF duration string representation (ISO 8601 format) and Python’s datetime.timedelta type.

Methods:

Name Description
decode

Decode an ODF duration string (ISO 8601) to a Python datetime.timedelta object.

encode

Encode a Python datetime.timedelta object to an ODF duration string (ISO 8601).

Source code in odfdo/datatype.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
class Duration:
    """Handles conversion between ODF duration string representation (ISO 8601 format)
    and Python's `datetime.timedelta` type.
    """

    @staticmethod
    def decode(data: str) -> timedelta:
        """Decode an ODF duration string (ISO 8601) to a Python `datetime.timedelta` object.

        Args:
            data: The duration string to decode (e.g., "PT1H30M0S", "-P5D").

        Returns:
            timedelta: A `datetime.timedelta` object representing the decoded duration.

        Raises:
            ValueError: If the input string is not a valid ISO 8601 duration format.
        """
        if data.startswith("P"):
            sign = 1
        elif data.startswith("-P"):
            sign = -1
        else:
            raise ValueError(f"duration not valid {data!r}")

        days = 0
        hours = 0
        minutes = 0
        seconds = 0

        buffer = ""
        for c in data:
            if c.isdigit():
                buffer += c
            elif c == "D":
                days = int(buffer)
                buffer = ""
            elif c == "H":
                hours = int(buffer)
                buffer = ""
            elif c == "M":
                minutes = int(buffer)
                buffer = ""
            elif c == "S":
                seconds = int(buffer)
                buffer = ""
                break
        if buffer != "":
            raise ValueError(f"duration not valid {data!r}")

        return timedelta(
            days=sign * days,
            hours=sign * hours,
            minutes=sign * minutes,
            seconds=sign * seconds,
        )

    @staticmethod
    def encode(value: timedelta) -> str:
        """Encode a Python `datetime.timedelta` object to an ODF duration string (ISO 8601).

        Args:
            value: The `datetime.timedelta` object to encode.

        Returns:
            str: The ODF duration string (e.g., "PT1H30M0S", "-P5D").

        Raises:
            TypeError: If the input value is not a `datetime.timedelta` object.
        """
        if not isinstance(value, timedelta):
            raise TypeError(f"duration must be a timedelta: {value!r}")

        days = value.days
        if days < 0:
            microseconds = -(
                (days * 24 * 60 * 60 + value.seconds) * 1000000 + value.microseconds
            )
            sign = "-"
        else:
            microseconds = (
                days * 24 * 60 * 60 + value.seconds
            ) * 1000000 + value.microseconds
            sign = ""

        hours = microseconds / (60 * 60 * 1000000)
        microseconds %= 60 * 60 * 1000000

        minutes = microseconds / (60 * 1000000)
        microseconds %= 60 * 1000000

        seconds = microseconds / 1000000

        return sign + DURATION_FORMAT % (hours, minutes, seconds)

decode staticmethod

decode(data: str) -> timedelta

Decode an ODF duration string (ISO 8601) to a Python datetime.timedelta object.

Parameters:

Name Type Description Default
data str

The duration string to decode (e.g., “PT1H30M0S”, “-P5D”).

required

Returns:

Name Type Description
timedelta timedelta

A datetime.timedelta object representing the decoded duration.

Raises:

Type Description
ValueError

If the input string is not a valid ISO 8601 duration format.

Source code in odfdo/datatype.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
@staticmethod
def decode(data: str) -> timedelta:
    """Decode an ODF duration string (ISO 8601) to a Python `datetime.timedelta` object.

    Args:
        data: The duration string to decode (e.g., "PT1H30M0S", "-P5D").

    Returns:
        timedelta: A `datetime.timedelta` object representing the decoded duration.

    Raises:
        ValueError: If the input string is not a valid ISO 8601 duration format.
    """
    if data.startswith("P"):
        sign = 1
    elif data.startswith("-P"):
        sign = -1
    else:
        raise ValueError(f"duration not valid {data!r}")

    days = 0
    hours = 0
    minutes = 0
    seconds = 0

    buffer = ""
    for c in data:
        if c.isdigit():
            buffer += c
        elif c == "D":
            days = int(buffer)
            buffer = ""
        elif c == "H":
            hours = int(buffer)
            buffer = ""
        elif c == "M":
            minutes = int(buffer)
            buffer = ""
        elif c == "S":
            seconds = int(buffer)
            buffer = ""
            break
    if buffer != "":
        raise ValueError(f"duration not valid {data!r}")

    return timedelta(
        days=sign * days,
        hours=sign * hours,
        minutes=sign * minutes,
        seconds=sign * seconds,
    )

encode staticmethod

encode(value: timedelta) -> str

Encode a Python datetime.timedelta object to an ODF duration string (ISO 8601).

Parameters:

Name Type Description Default
value timedelta

The datetime.timedelta object to encode.

required

Returns:

Name Type Description
str str

The ODF duration string (e.g., “PT1H30M0S”, “-P5D”).

Raises:

Type Description
TypeError

If the input value is not a datetime.timedelta object.

Source code in odfdo/datatype.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
@staticmethod
def encode(value: timedelta) -> str:
    """Encode a Python `datetime.timedelta` object to an ODF duration string (ISO 8601).

    Args:
        value: The `datetime.timedelta` object to encode.

    Returns:
        str: The ODF duration string (e.g., "PT1H30M0S", "-P5D").

    Raises:
        TypeError: If the input value is not a `datetime.timedelta` object.
    """
    if not isinstance(value, timedelta):
        raise TypeError(f"duration must be a timedelta: {value!r}")

    days = value.days
    if days < 0:
        microseconds = -(
            (days * 24 * 60 * 60 + value.seconds) * 1000000 + value.microseconds
        )
        sign = "-"
    else:
        microseconds = (
            days * 24 * 60 * 60 + value.seconds
        ) * 1000000 + value.microseconds
        sign = ""

    hours = microseconds / (60 * 60 * 1000000)
    microseconds %= 60 * 60 * 1000000

    minutes = microseconds / (60 * 1000000)
    microseconds %= 60 * 1000000

    seconds = microseconds / 1000000

    return sign + DURATION_FORMAT % (hours, minutes, seconds)