numbers.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # Copyright (c) 2010-2024 openpyxl
  2. import re
  3. from openpyxl.descriptors import (
  4. String,
  5. Sequence,
  6. Integer,
  7. )
  8. from openpyxl.descriptors.serialisable import Serialisable
  9. BUILTIN_FORMATS = {
  10. 0: 'General',
  11. 1: '0',
  12. 2: '0.00',
  13. 3: '#,##0',
  14. 4: '#,##0.00',
  15. 5: '"$"#,##0_);("$"#,##0)',
  16. 6: '"$"#,##0_);[Red]("$"#,##0)',
  17. 7: '"$"#,##0.00_);("$"#,##0.00)',
  18. 8: '"$"#,##0.00_);[Red]("$"#,##0.00)',
  19. 9: '0%',
  20. 10: '0.00%',
  21. 11: '0.00E+00',
  22. 12: '# ?/?',
  23. 13: '# ??/??',
  24. 14: 'mm-dd-yy',
  25. 15: 'd-mmm-yy',
  26. 16: 'd-mmm',
  27. 17: 'mmm-yy',
  28. 18: 'h:mm AM/PM',
  29. 19: 'h:mm:ss AM/PM',
  30. 20: 'h:mm',
  31. 21: 'h:mm:ss',
  32. 22: 'm/d/yy h:mm',
  33. 37: '#,##0_);(#,##0)',
  34. 38: '#,##0_);[Red](#,##0)',
  35. 39: '#,##0.00_);(#,##0.00)',
  36. 40: '#,##0.00_);[Red](#,##0.00)',
  37. 41: r'_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)',
  38. 42: r'_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)',
  39. 43: r'_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)',
  40. 44: r'_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)',
  41. 45: 'mm:ss',
  42. 46: '[h]:mm:ss',
  43. 47: 'mmss.0',
  44. 48: '##0.0E+0',
  45. 49: '@', }
  46. BUILTIN_FORMATS_MAX_SIZE = 164
  47. BUILTIN_FORMATS_REVERSE = dict(
  48. [(value, key) for key, value in BUILTIN_FORMATS.items()])
  49. FORMAT_GENERAL = BUILTIN_FORMATS[0]
  50. FORMAT_TEXT = BUILTIN_FORMATS[49]
  51. FORMAT_NUMBER = BUILTIN_FORMATS[1]
  52. FORMAT_NUMBER_00 = BUILTIN_FORMATS[2]
  53. FORMAT_NUMBER_COMMA_SEPARATED1 = BUILTIN_FORMATS[4]
  54. FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-'
  55. FORMAT_PERCENTAGE = BUILTIN_FORMATS[9]
  56. FORMAT_PERCENTAGE_00 = BUILTIN_FORMATS[10]
  57. FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd'
  58. FORMAT_DATE_YYMMDD = 'yy-mm-dd'
  59. FORMAT_DATE_DDMMYY = 'dd/mm/yy'
  60. FORMAT_DATE_DMYSLASH = 'd/m/y'
  61. FORMAT_DATE_DMYMINUS = 'd-m-y'
  62. FORMAT_DATE_DMMINUS = 'd-m'
  63. FORMAT_DATE_MYMINUS = 'm-y'
  64. FORMAT_DATE_XLSX14 = BUILTIN_FORMATS[14]
  65. FORMAT_DATE_XLSX15 = BUILTIN_FORMATS[15]
  66. FORMAT_DATE_XLSX16 = BUILTIN_FORMATS[16]
  67. FORMAT_DATE_XLSX17 = BUILTIN_FORMATS[17]
  68. FORMAT_DATE_XLSX22 = BUILTIN_FORMATS[22]
  69. FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss'
  70. FORMAT_DATE_TIME1 = BUILTIN_FORMATS[18]
  71. FORMAT_DATE_TIME2 = BUILTIN_FORMATS[19]
  72. FORMAT_DATE_TIME3 = BUILTIN_FORMATS[20]
  73. FORMAT_DATE_TIME4 = BUILTIN_FORMATS[21]
  74. FORMAT_DATE_TIME5 = BUILTIN_FORMATS[45]
  75. FORMAT_DATE_TIME6 = BUILTIN_FORMATS[21]
  76. FORMAT_DATE_TIME7 = 'i:s.S'
  77. FORMAT_DATE_TIME8 = 'h:mm:ss@'
  78. FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss'
  79. FORMAT_DATE_YYMMDDSLASH = 'yy/mm/dd@'
  80. FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-'
  81. FORMAT_CURRENCY_USD = '$#,##0_-'
  82. FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-'
  83. COLORS = r"\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]"
  84. LITERAL_GROUP = r'".*?"' # anything in quotes
  85. LOCALE_GROUP = r'\[(?!hh?\]|mm?\]|ss?\])[^\]]*\]' # anything in square brackets, except hours or minutes or seconds
  86. STRIP_RE = re.compile(f"{LITERAL_GROUP}|{LOCALE_GROUP}")
  87. TIMEDELTA_RE = re.compile(r'\[hh?\](:mm(:ss(\.0*)?)?)?|\[mm?\](:ss(\.0*)?)?|\[ss?\](\.0*)?', re.I)
  88. # Spec 18.8.31 numFmts
  89. # +ve;-ve;zero;text
  90. def is_date_format(fmt):
  91. if fmt is None:
  92. return False
  93. fmt = fmt.split(";")[0] # only look at the first format
  94. fmt = STRIP_RE.sub("", fmt) # ignore some formats
  95. return re.search(r"(?<![_\\])[dmhysDMHYS]", fmt) is not None
  96. def is_timedelta_format(fmt):
  97. if fmt is None:
  98. return False
  99. fmt = fmt.split(";")[0] # only look at the first format
  100. return TIMEDELTA_RE.search(fmt) is not None
  101. def is_datetime(fmt):
  102. """
  103. Return date, time or datetime
  104. """
  105. if not is_date_format(fmt):
  106. return
  107. DATE = TIME = False
  108. if any((x in fmt for x in 'dy')):
  109. DATE = True
  110. if any((x in fmt for x in 'hs')):
  111. TIME = True
  112. if DATE and TIME:
  113. return "datetime"
  114. if DATE:
  115. return "date"
  116. return "time"
  117. def is_builtin(fmt):
  118. return fmt in BUILTIN_FORMATS.values()
  119. def builtin_format_code(index):
  120. """Return one of the standard format codes by index."""
  121. try:
  122. fmt = BUILTIN_FORMATS[index]
  123. except KeyError:
  124. fmt = None
  125. return fmt
  126. def builtin_format_id(fmt):
  127. """Return the id of a standard style."""
  128. return BUILTIN_FORMATS_REVERSE.get(fmt)
  129. class NumberFormatDescriptor(String):
  130. def __set__(self, instance, value):
  131. if value is None:
  132. value = FORMAT_GENERAL
  133. super().__set__(instance, value)
  134. class NumberFormat(Serialisable):
  135. numFmtId = Integer()
  136. formatCode = String()
  137. def __init__(self,
  138. numFmtId=None,
  139. formatCode=None,
  140. ):
  141. self.numFmtId = numFmtId
  142. self.formatCode = formatCode
  143. class NumberFormatList(Serialisable):
  144. count = Integer(allow_none=True)
  145. numFmt = Sequence(expected_type=NumberFormat)
  146. __elements__ = ('numFmt',)
  147. __attrs__ = ("count",)
  148. def __init__(self,
  149. count=None,
  150. numFmt=(),
  151. ):
  152. self.numFmt = numFmt
  153. @property
  154. def count(self):
  155. return len(self.numFmt)
  156. def __getitem__(self, idx):
  157. return self.numFmt[idx]