Coverage for pycommons / strings / string_conv.py: 100%

86 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-11 03:04 +0000

1"""Converting stuff to and from strings.""" 

2 

3from datetime import datetime 

4from math import isfinite, isnan 

5from typing import Callable, Final, cast 

6 

7from pycommons.math.int_math import __try_int 

8from pycommons.strings.chars import NBDASH, NBSP 

9from pycommons.types import type_error 

10 

11#: fast call to :meth:`str.__len__` 

12__LEN: Final[Callable[[str], int]] = cast("Callable[[str], int]", str.__len__) 

13 

14 

15def float_to_str(value: float) -> str: 

16 """ 

17 Convert `float` to a string. 

18 

19 The floating point value `value` is converted to a string. 

20 

21 :param value: the floating point value 

22 :returns: the string representation 

23 :raises TypeError: if `value` is not a `float` 

24 :raises ValueError: if `value` is not a number 

25 

26 >>> float_to_str(1.3) 

27 '1.3' 

28 >>> float_to_str(1.0) 

29 '1' 

30 >>> float_to_str(1e-5) 

31 '1e-5' 

32 

33 >>> try: 

34 ... float_to_str(1) 

35 ... except TypeError as te: 

36 ... print(te) 

37 value should be an instance of float but is int, namely 1. 

38 

39 >>> try: 

40 ... float_to_str(None) 

41 ... except TypeError as te: 

42 ... print(te) 

43 value should be an instance of float but is None. 

44 

45 >>> from math import nan 

46 >>> try: 

47 ... float_to_str(nan) 

48 ... except ValueError as ve: 

49 ... print(ve) 

50 nan => 'nan' is not a permitted float. 

51 

52 >>> from math import inf 

53 >>> float_to_str(inf) 

54 'inf' 

55 >>> float_to_str(-inf) 

56 '-inf' 

57 >>> float_to_str(1e300) 

58 '1e300' 

59 >>> float_to_str(-1e300) 

60 '-1e300' 

61 >>> float_to_str(-1e-300) 

62 '-1e-300' 

63 >>> float_to_str(1e-300) 

64 '1e-300' 

65 >>> float_to_str(1e1) 

66 '10' 

67 >>> float_to_str(1e5) 

68 '100000' 

69 >>> float_to_str(1e10) 

70 '10000000000' 

71 >>> float_to_str(1e20) 

72 '1e20' 

73 >>> float_to_str(1e030) 

74 '1e30' 

75 >>> float_to_str(0.0) 

76 '0' 

77 >>> float_to_str(-0.0) 

78 '0' 

79 """ 

80 if not isinstance(value, float): 

81 raise type_error(value, "value", float) 

82 if value == 0.0: # fast track for 0 

83 return "0" 

84 s = float.__repr__(value) 

85 if isnan(value): # nan is not permitted 

86 raise ValueError(f"{value!r} => {s!r} is not a permitted float.") 

87 if not isfinite(value): # +/-inf can be returned directly 

88 return s 

89 return str.replace(str.replace(str.replace( # simplify/remove clutter 

90 s, "e-0", "e-"), "e+0", "e"), "e+", "e").removesuffix(".0") 

91 

92 

93def bool_to_str(value: bool) -> str: 

94 """ 

95 Convert a Boolean value to a string. 

96 

97 This function is the inverse of :func:`str_to_bool`. 

98 

99 :param value: the Boolean value 

100 :returns `"T"`: if `value == True` 

101 :returns `"F"`: if `value == False` 

102 :raises TypeError: if `value` is not a `bool` 

103 

104 >>> print(bool_to_str(True)) 

105 T 

106 >>> print(bool_to_str(False)) 

107 F 

108 

109 >>> try: 

110 ... bool_to_str("t") 

111 ... except TypeError as te: 

112 ... print(te) 

113 value should be an instance of bool but is str, namely 't'. 

114 

115 >>> try: 

116 ... bool_to_str(None) 

117 ... except TypeError as te: 

118 ... print(te) 

119 value should be an instance of bool but is None. 

120 """ 

121 if not isinstance(value, bool): 

122 raise type_error(value, "value", bool) 

123 return "T" if value else "F" 

124 

125 

126def str_to_bool(value: str) -> bool: 

127 """ 

128 Convert a string to a boolean value. 

129 

130 This function is the inverse of :func:`bool_to_str`. 

131 

132 :param value: the string value 

133 :returns `True`: if `value == "T"` 

134 :returns `False`: if `value == "F"` 

135 :raises TypeError: if `value` is not a string 

136 :raises ValueError: if `value` is neither `T` nor `F` 

137 

138 >>> str_to_bool("T") 

139 True 

140 >>> str_to_bool("F") 

141 False 

142 

143 >>> try: 

144 ... str_to_bool("x") 

145 ... except ValueError as v: 

146 ... print(v) 

147 Expected 'T' or 'F', but got 'x'. 

148 

149 >>> try: 

150 ... str_to_bool(1) 

151 ... except TypeError as te: 

152 ... print(te) 

153 descriptor '__len__' requires a 'str' object but received a 'int' 

154 

155 >>> try: 

156 ... str_to_bool(None) 

157 ... except TypeError as te: 

158 ... print(te) 

159 descriptor '__len__' requires a 'str' object but received a 'NoneType' 

160 """ 

161 if __LEN(value) == 1: 

162 if value == "T": 

163 return True 

164 if value == "F": 

165 return False 

166 raise ValueError(f"Expected 'T' or 'F', but got {value!r}.") 

167 

168 

169def num_to_str(value: int | float) -> str: 

170 """ 

171 Transform a numerical value which is either `int` or`float` to a string. 

172 

173 If `value` is an instance of `int`, the result of its conversion via `str` 

174 will be returned. 

175 If `value` is an instance of `bool`, a `TypeError` will be raised. 

176 Otherwise, the result of :func:`~float_to_str` is returned. 

177 This means that `nan` will yield a `ValueError` and anything that is 

178 neither an `int`, `bool`, or `float` will incur a `TypeError`. 

179 

180 :param value: the value 

181 :returns: the string 

182 :raises TypeError: if `value` is a `bool` (notice that `bool` is a 

183 subclass of `int`) or any other type that is neither `int` nor 

184 `float`. 

185 :raises ValueError: if `value` is not-a-number 

186 

187 >>> num_to_str(1) 

188 '1' 

189 >>> num_to_str(1.5) 

190 '1.5' 

191 

192 >>> try: 

193 ... num_to_str(True) 

194 ... except TypeError as te: 

195 ... print(te) 

196 value should be an instance of any in {float, int} but is bool, \ 

197namely True. 

198 

199 >>> try: 

200 ... num_to_str(False) 

201 ... except TypeError as te: 

202 ... print(te) 

203 value should be an instance of any in {float, int} but is bool, \ 

204namely False. 

205 

206 >>> try: 

207 ... num_to_str("x") 

208 ... except TypeError as te: 

209 ... print(te) 

210 value should be an instance of float but is str, namely 'x'. 

211 

212 >>> try: 

213 ... num_to_str(None) 

214 ... except TypeError as te: 

215 ... print(te) 

216 value should be an instance of float but is None. 

217 

218 >>> from math import inf, nan 

219 >>> try: 

220 ... num_to_str(nan) 

221 ... except ValueError as ve: 

222 ... print(ve) 

223 nan => 'nan' is not a permitted float. 

224 

225 >>> num_to_str(inf) 

226 'inf' 

227 >>> num_to_str(-inf) 

228 '-inf' 

229 """ 

230 if isinstance(value, bool): 

231 raise type_error(value, "value", (int, float)) 

232 return int.__str__(value) if isinstance(value, int) \ 

233 else float_to_str(value) 

234 

235 

236def bool_or_num_to_str(value: int | float | bool) -> str: 

237 """ 

238 Convert a `bool` or number to string. 

239 

240 :param value: the number or `bool` 

241 :returns: the string 

242 :raises TypeError: if the number is neither `bool`, `float`, or `int`. 

243 

244 >>> bool_or_num_to_str(True) 

245 'T' 

246 >>> bool_or_num_to_str(False) 

247 'F' 

248 >>> bool_or_num_to_str(12.0) 

249 '12' 

250 >>> bool_or_num_to_str(12) 

251 '12' 

252 >>> bool_or_num_to_str(12.5) 

253 '12.5' 

254 >>> try: 

255 ... bool_or_num_to_str("x") 

256 ... except TypeError as te: 

257 ... print(te) 

258 value should be an instance of float but is str, namely 'x'. 

259 """ 

260 return bool_to_str(value) if isinstance(value, bool) else ( 

261 int.__str__(value) if isinstance(value, int) else float_to_str(value)) 

262 

263 

264def num_or_none_to_str(value: int | float | None) -> str: 

265 """ 

266 Convert a numerical type (`int`, `float`) or `None` to a string. 

267 

268 If `value is None`, then `""` is returned. 

269 Otherwise, the result of :func:`~num_to_str` is returned. 

270 

271 :param value: the value 

272 :returns: the string representation, `""` for `None` 

273 :returns `""`: if `value is None` 

274 :returns `num_to_str(value)`: otherwise 

275 :raises TypeError: if `value` not `Nont` but instead is a `bool` 

276 (notice that `bool` is a subclass of `int`) or any other type that 

277 is neither `int` nor `float`. 

278 :raises ValueError: if `value` is not-a-number 

279 

280 >>> print(repr(num_or_none_to_str(None))) 

281 '' 

282 >>> print(num_or_none_to_str(12)) 

283 12 

284 >>> print(num_or_none_to_str(12.3)) 

285 12.3 

286 

287 >>> try: 

288 ... num_or_none_to_str(True) 

289 ... except TypeError as te: 

290 ... print(te) 

291 value should be an instance of any in {float, int} but is bool, \ 

292namely True. 

293 

294 >>> try: 

295 ... num_or_none_to_str(False) 

296 ... except TypeError as te: 

297 ... print(te) 

298 value should be an instance of any in {float, int} but is bool, \ 

299namely False. 

300 

301 >>> from math import nan 

302 >>> try: 

303 ... num_to_str(nan) 

304 ... except ValueError as ve: 

305 ... print(ve) 

306 nan => 'nan' is not a permitted float. 

307 """ 

308 return "" if value is None else num_to_str(value) 

309 

310 

311def int_or_none_to_str(value: int | None) -> str: 

312 """ 

313 Convert an integer or `None` to a string. 

314 

315 If `value is None`, `""` is returned. 

316 If `value` is an instance of `bool`, a `TypeError` is raised. 

317 If `value` is an `int`, `str(val)` is returned. 

318 Otherwise, a `TypeError` is thrown. 

319 

320 :param value: the value 

321 :returns: the string representation, `''` for `None` 

322 :returns `""`: if `value is None` 

323 :returns `int.__str__(value)`: otherwise 

324 :raises TypeError: if `value` is a `bool` (notice that `bool` is a 

325 subclass of `int`) or any other non-`int` type. 

326 

327 >>> print(repr(int_or_none_to_str(None))) 

328 '' 

329 >>> print(int_or_none_to_str(12)) 

330 12 

331 

332 >>> try: 

333 ... int_or_none_to_str(True) 

334 ... except TypeError as te: 

335 ... print(te) 

336 value should be an instance of int but is bool, namely True. 

337 

338 >>> try: 

339 ... int_or_none_to_str(False) 

340 ... except TypeError as te: 

341 ... print(te) 

342 value should be an instance of int but is bool, namely False. 

343 

344 >>> print(int_or_none_to_str(-10)) 

345 -10 

346 

347 >>> try: 

348 ... int_or_none_to_str(1.0) 

349 ... except TypeError as te: 

350 ... print(te) 

351 value should be an instance of int but is float, namely 1.0. 

352 """ 

353 if value is None: 

354 return "" 

355 if isinstance(value, bool) or (not isinstance(value, int)): 

356 raise type_error(value, "value", int) 

357 return int.__str__(value) 

358 

359 

360def __str_to_num_or_none(value: str | None, 

361 none_is_ok: bool) -> int | float | None: 

362 """ 

363 Convert a string to an `int` or `float`. 

364 

365 If `value is None` and `none_is_ok == True`, then `None` is returned. 

366 If `value` is not an instance of `str`, a `TypeError` will be raised. 

367 If `value` becomes an empty string after stripping, then `None` is 

368 returned if `none_is_ok == True` and else an `ValueError` is raised. 

369 If the value `value` can be converted to an integer without loss of 

370 precision, then an `int` with the corresponding value is returned. 

371 If the value `value` can be converted to a `float`, a `float` with the 

372 appropriate value is returned. 

373 Otherwise, a `ValueError` is thrown. 

374 

375 :param value: the string value 

376 :returns: the `int` or `float` or `None` corresponding to `value` 

377 

378 >>> print(type(__str_to_num_or_none("15.0", False))) 

379 <class 'int'> 

380 >>> print(type(__str_to_num_or_none("15.1", False))) 

381 <class 'float'> 

382 >>> __str_to_num_or_none("inf", False) 

383 inf 

384 >>> __str_to_num_or_none(" -inf ", False) 

385 -inf 

386 

387 >>> try: 

388 ... __str_to_num_or_none(21, False) 

389 ... except TypeError as te: 

390 ... print(te) 

391 descriptor 'strip' for 'str' objects doesn't apply to a 'int' object 

392 

393 >>> try: 

394 ... __str_to_num_or_none("nan", False) 

395 ... except ValueError as ve: 

396 ... print(ve) 

397 NaN is not permitted, but got 'nan'. 

398 

399 >>> try: 

400 ... __str_to_num_or_none("12-3", False) 

401 ... except ValueError as ve: 

402 ... print(ve) 

403 Invalid numerical value '12-3'. 

404 

405 >>> __str_to_num_or_none("1e34423", False) 

406 inf 

407 >>> __str_to_num_or_none("-1e34423", False) 

408 -inf 

409 >>> __str_to_num_or_none("-1e-34423", False) 

410 0 

411 >>> __str_to_num_or_none("1e-34423", False) 

412 0 

413 

414 >>> try: 

415 ... __str_to_num_or_none("-1e-34e4423", False) 

416 ... except ValueError as ve: 

417 ... print(ve) 

418 Invalid numerical value '-1e-34e4423'. 

419 

420 >>> try: 

421 ... __str_to_num_or_none("T", False) 

422 ... except ValueError as ve: 

423 ... print(ve) 

424 Invalid numerical value 'T'. 

425 

426 >>> try: 

427 ... __str_to_num_or_none("F", False) 

428 ... except ValueError as ve: 

429 ... print(ve) 

430 Invalid numerical value 'F'. 

431 

432 >>> try: 

433 ... __str_to_num_or_none(None, False) 

434 ... except TypeError as te: 

435 ... print(te) 

436 descriptor 'strip' for 'str' objects doesn't apply to a 'NoneType' object 

437 

438 >>> try: 

439 ... __str_to_num_or_none(" ", False) 

440 ... except ValueError as ve: 

441 ... print(ve) 

442 Value ' ' becomes empty after stripping, cannot be converted to a number. 

443 

444 >>> try: 

445 ... __str_to_num_or_none("", False) 

446 ... except ValueError as ve: 

447 ... print(ve) 

448 Value '' becomes empty after stripping, cannot be converted to a number. 

449 

450 >>> print(__str_to_num_or_none(" ", True)) 

451 None 

452 >>> print(__str_to_num_or_none("", True)) 

453 None 

454 >>> print(__str_to_num_or_none(None, True)) 

455 None 

456 """ 

457 if (value is None) and none_is_ok: 

458 return None 

459 vv: Final[str] = str.lower(str.strip(value)) 

460 if __LEN(vv) <= 0: 

461 if none_is_ok: 

462 return None 

463 raise ValueError(f"Value {value!r} becomes empty after stripping, " 

464 "cannot be converted to a number.") 

465 try: 

466 return int(vv) 

467 except ValueError: 

468 pass 

469 res: float 

470 try: 

471 res = float(vv) 

472 except ValueError as ve: 

473 raise ValueError(f"Invalid numerical value {value!r}.") from ve 

474 if isnan(res): 

475 raise ValueError(f"NaN is not permitted, but got {value!r}.") 

476 return __try_int(res) 

477 

478 

479def str_to_num(value: str) -> int | float: 

480 """ 

481 Convert a string to an `int` or `float`. 

482 

483 If `value` is not an instance of `str`, a `TypeError` will be raised. 

484 If the value `value` can be converted to an integer, then an `int` with 

485 the corresponding value is returned. 

486 If the value `value` can be converted to a `float`, a `float` with the 

487 appropriate value is returned. 

488 Otherwise, a `ValueError` is thrown. 

489 

490 :param value: the string value 

491 :returns: the `int` or `float`: Integers are preferred to be used whereever 

492 possible 

493 :raises TypeError: if `value` is not a `str` 

494 :raises ValueError: if `value` is a `str` but cannot be converted to an 

495 integer (base-10) or converts to a `float` which is not a number 

496 

497 >>> print(type(str_to_num("15.0"))) 

498 <class 'int'> 

499 >>> print(type(str_to_num("15.1"))) 

500 <class 'float'> 

501 >>> str_to_num("inf") 

502 inf 

503 >>> str_to_num(" -inf ") 

504 -inf 

505 >>> try: 

506 ... str_to_num(21) 

507 ... except TypeError as te: 

508 ... print(te) 

509 descriptor 'strip' for 'str' objects doesn't apply to a 'int' object 

510 >>> try: 

511 ... str_to_num("nan") 

512 ... except ValueError as ve: 

513 ... print(ve) 

514 NaN is not permitted, but got 'nan'. 

515 

516 >>> try: 

517 ... str_to_num("12-3") 

518 ... except ValueError as ve: 

519 ... print(ve) 

520 Invalid numerical value '12-3'. 

521 

522 >>> str_to_num("1e34423") 

523 inf 

524 >>> str_to_num("-1e34423") 

525 -inf 

526 >>> str_to_num("-1e-34423") 

527 0 

528 >>> str_to_num("1e-34423") 

529 0 

530 >>> try: 

531 ... str_to_num("-1e-34e4423") 

532 ... except ValueError as ve: 

533 ... print(ve) 

534 Invalid numerical value '-1e-34e4423'. 

535 

536 >>> try: 

537 ... str_to_num("T") 

538 ... except ValueError as ve: 

539 ... print(ve) 

540 Invalid numerical value 'T'. 

541 

542 >>> try: 

543 ... str_to_num("F") 

544 ... except ValueError as ve: 

545 ... print(ve) 

546 Invalid numerical value 'F'. 

547 

548 >>> try: 

549 ... str_to_num(None) 

550 ... except TypeError as te: 

551 ... print(te) 

552 descriptor 'strip' for 'str' objects doesn't apply to a 'NoneType' object 

553 >>> try: 

554 ... str_to_num("") 

555 ... except ValueError as ve: 

556 ... print(ve) 

557 Value '' becomes empty after stripping, cannot be converted to a number. 

558 """ 

559 return __str_to_num_or_none(value, False) 

560 

561 

562def str_to_num_or_none(value: str | None) -> int | float | None: 

563 """ 

564 Convert a string to an `int` or `float` or `None`. 

565 

566 If the value `value` is `None`, then `None` is returned. 

567 If the vlaue `value` is empty or entirely composed of white space, `None` 

568 is returned. 

569 If the value `value` can be converted to an integer, then an `int` with 

570 the corresponding value is returned. 

571 If the value `value` can be converted to a `float`, a `float` with the 

572 appropriate value is returned. 

573 Otherwise, a `ValueError` is thrown. 

574 

575 :param value: the string value 

576 :returns: the `int` or `float` or `None` 

577 :raises TypeError: if `value` is neither a `str` nor `None` 

578 :raises ValueError: if `value` is a `str` but cannot be converted to an 

579 integer (base-10) or converts to a `float` which is not a number 

580 

581 >>> print(type(str_to_num_or_none("15.0"))) 

582 <class 'int'> 

583 >>> print(type(str_to_num_or_none("15.1"))) 

584 <class 'float'> 

585 >>> str_to_num_or_none("inf") 

586 inf 

587 >>> str_to_num_or_none(" -inf ") 

588 -inf 

589 >>> try: 

590 ... str_to_num_or_none(21) 

591 ... except TypeError as te: 

592 ... print(te) 

593 descriptor 'strip' for 'str' objects doesn't apply to a 'int' object 

594 >>> try: 

595 ... str_to_num_or_none("nan") 

596 ... except ValueError as ve: 

597 ... print(ve) 

598 NaN is not permitted, but got 'nan'. 

599 

600 >>> try: 

601 ... str_to_num_or_none("12-3") 

602 ... except ValueError as ve: 

603 ... print(ve) 

604 Invalid numerical value '12-3'. 

605 

606 >>> str_to_num_or_none("1e34423") 

607 inf 

608 >>> str_to_num_or_none("-1e34423") 

609 -inf 

610 >>> str_to_num_or_none("-1e-34423") 

611 0 

612 >>> str_to_num_or_none("1e-34423") 

613 0 

614 >>> try: 

615 ... str_to_num_or_none("-1e-34e4423") 

616 ... except ValueError as ve: 

617 ... print(ve) 

618 Invalid numerical value '-1e-34e4423'. 

619 

620 >>> try: 

621 ... str_to_num_or_none("T") 

622 ... except ValueError as ve: 

623 ... print(ve) 

624 Invalid numerical value 'T'. 

625 

626 >>> try: 

627 ... str_to_num_or_none("F") 

628 ... except ValueError as ve: 

629 ... print(ve) 

630 Invalid numerical value 'F'. 

631 

632 >>> print(str_to_num_or_none("")) 

633 None 

634 >>> print(str_to_num_or_none(None)) 

635 None 

636 >>> print(type(str_to_num_or_none("5.0"))) 

637 <class 'int'> 

638 >>> print(type(str_to_num_or_none("5.1"))) 

639 <class 'float'> 

640 """ 

641 return __str_to_num_or_none(value, True) 

642 

643 

644def str_to_int_or_none(value: str | None) -> int | None: 

645 """ 

646 Convert a string to an `int` or `None`. 

647 

648 If the value `value` is `None`, then `None` is returned. 

649 If the vlaue `value` is empty or entirely composed of white space, `None` 

650 is returned. 

651 If the value `value` can be converted to an integer, then an `int` with 

652 the corresponding value is returned. 

653 Otherwise, a `ValueError` is thrown. 

654 

655 :param value: the string value, or `None` 

656 :returns: the int or None 

657 :raises TypeError: if `value` is neither a `str` nor `None` 

658 :raises ValueError: if `value` is a `str` but cannot be base-10 converted 

659 to an integer 

660 

661 >>> print(str_to_int_or_none("")) 

662 None 

663 >>> print(str_to_int_or_none("5")) 

664 5 

665 >>> print(str_to_int_or_none(None)) 

666 None 

667 >>> print(str_to_int_or_none(" ")) 

668 None 

669 >>> try: 

670 ... print(str_to_int_or_none(1.3)) 

671 ... except TypeError as te: 

672 ... print(te) 

673 value should be an instance of str but is float, namely 1.3. 

674 

675 >>> try: 

676 ... print(str_to_int_or_none("1.3")) 

677 ... except ValueError as ve: 

678 ... print(ve) 

679 invalid literal for int() with base 10: '1.3' 

680 """ 

681 if value is None: 

682 return None 

683 if not isinstance(value, str): 

684 raise type_error(value, "value", str) 

685 vv: Final[str] = value.strip().lower() 

686 if len(vv) <= 0: 

687 return None 

688 return int(vv) 

689 

690 

691#: the format for time 

692__DATE_FORMAT: Final[str] = f"%Y{NBDASH}%m{NBDASH}%d" 

693#: the format for date and time 

694__DATE_TIME_FORMAT_NO_TZ: Final[str] = f"{__DATE_FORMAT}{NBSP}%H:%M" 

695#: the format for date and time 

696__DATE_TIME_FORMAT_TZ: Final[str] = f"{__DATE_TIME_FORMAT_NO_TZ}{NBSP}%Z" 

697 

698 

699def datetime_to_date_str(date: datetime) -> str: 

700 """ 

701 Convert a datetime object to a date string. 

702 

703 :param date: the date 

704 :returns: the date string 

705 :raises TypeError: if `date` is not an instance of 

706 :class:`datetime.datetime`. 

707 

708 >>> datetime_to_date_str(datetime(1999, 12, 21)) 

709 '1999\u201112\u201121' 

710 >>> try: 

711 ... datetime_to_date_str(None) 

712 ... except TypeError as te: 

713 ... print(te) 

714 date should be an instance of datetime.datetime but is None. 

715 

716 >>> try: 

717 ... datetime_to_date_str(1) 

718 ... except TypeError as te: 

719 ... print(te) 

720 date should be an instance of datetime.datetime but is int, namely 1. 

721 """ 

722 if not isinstance(date, datetime): 

723 raise type_error(date, "date", datetime) 

724 return date.strftime(__DATE_FORMAT) 

725 

726 

727def datetime_to_datetime_str(dateandtime: datetime) -> str: 

728 r""" 

729 Convert a datetime object to a date-time string. 

730 

731 :param dateandtime: the date and time 

732 :returns: the date-time string 

733 :raises TypeError: if `dateandtime` is not an instance of 

734 :class:`datetime.datetime`. 

735 

736 >>> datetime_to_datetime_str(datetime(1999, 12, 21, 13, 42, 23)) 

737 '1999\u201112\u201121\xa013:42' 

738 >>> from datetime import timezone 

739 >>> datetime_to_datetime_str(datetime(1999, 12, 21, 13, 42, 

740 ... tzinfo=timezone.utc)) 

741 '1999\u201112\u201121\xa013:42\xa0UTC' 

742 >>> try: 

743 ... datetime_to_datetime_str(None) 

744 ... except TypeError as te: 

745 ... print(te) 

746 dateandtime should be an instance of datetime.datetime but is None. 

747 

748 >>> try: 

749 ... datetime_to_datetime_str(1) 

750 ... except TypeError as te: 

751 ... print(str(te)[:60]) 

752 dateandtime should be an instance of datetime.datetime but i 

753 """ 

754 if not isinstance(dateandtime, datetime): 

755 raise type_error(dateandtime, "dateandtime", datetime) 

756 return dateandtime.strftime( 

757 __DATE_TIME_FORMAT_NO_TZ if dateandtime.tzinfo is None 

758 else __DATE_TIME_FORMAT_TZ)