formatter.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, division
  3. import calendar
  4. import re
  5. from dateutil import tz as dateutil_tz
  6. from arrow import locales, util
  7. class DateTimeFormatter(object):
  8. # This pattern matches characters enclosed in square brackets are matched as
  9. # an atomic group. For more info on atomic groups and how to they are
  10. # emulated in Python's re library, see https://stackoverflow.com/a/13577411/2701578
  11. _FORMAT_RE = re.compile(
  12. r"(\[(?:(?=(?P<literal>[^]]))(?P=literal))*\]|YYY?Y?|MM?M?M?|Do|DD?D?D?|d?dd?d?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?Z?|a|A|X|x|W)"
  13. )
  14. def __init__(self, locale="en_us"):
  15. self.locale = locales.get_locale(locale)
  16. def format(cls, dt, fmt):
  17. return cls._FORMAT_RE.sub(lambda m: cls._format_token(dt, m.group(0)), fmt)
  18. def _format_token(self, dt, token):
  19. if token and token.startswith("[") and token.endswith("]"):
  20. return token[1:-1]
  21. if token == "YYYY":
  22. return self.locale.year_full(dt.year)
  23. if token == "YY":
  24. return self.locale.year_abbreviation(dt.year)
  25. if token == "MMMM":
  26. return self.locale.month_name(dt.month)
  27. if token == "MMM":
  28. return self.locale.month_abbreviation(dt.month)
  29. if token == "MM":
  30. return "{:02d}".format(dt.month)
  31. if token == "M":
  32. return str(dt.month)
  33. if token == "DDDD":
  34. return "{:03d}".format(dt.timetuple().tm_yday)
  35. if token == "DDD":
  36. return str(dt.timetuple().tm_yday)
  37. if token == "DD":
  38. return "{:02d}".format(dt.day)
  39. if token == "D":
  40. return str(dt.day)
  41. if token == "Do":
  42. return self.locale.ordinal_number(dt.day)
  43. if token == "dddd":
  44. return self.locale.day_name(dt.isoweekday())
  45. if token == "ddd":
  46. return self.locale.day_abbreviation(dt.isoweekday())
  47. if token == "d":
  48. return str(dt.isoweekday())
  49. if token == "HH":
  50. return "{:02d}".format(dt.hour)
  51. if token == "H":
  52. return str(dt.hour)
  53. if token == "hh":
  54. return "{:02d}".format(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
  55. if token == "h":
  56. return str(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
  57. if token == "mm":
  58. return "{:02d}".format(dt.minute)
  59. if token == "m":
  60. return str(dt.minute)
  61. if token == "ss":
  62. return "{:02d}".format(dt.second)
  63. if token == "s":
  64. return str(dt.second)
  65. if token == "SSSSSS":
  66. return str("{:06d}".format(int(dt.microsecond)))
  67. if token == "SSSSS":
  68. return str("{:05d}".format(int(dt.microsecond / 10)))
  69. if token == "SSSS":
  70. return str("{:04d}".format(int(dt.microsecond / 100)))
  71. if token == "SSS":
  72. return str("{:03d}".format(int(dt.microsecond / 1000)))
  73. if token == "SS":
  74. return str("{:02d}".format(int(dt.microsecond / 10000)))
  75. if token == "S":
  76. return str(int(dt.microsecond / 100000))
  77. if token == "X":
  78. # TODO: replace with a call to dt.timestamp() when we drop Python 2.7
  79. return str(calendar.timegm(dt.utctimetuple()))
  80. if token == "x":
  81. # TODO: replace with a call to dt.timestamp() when we drop Python 2.7
  82. ts = calendar.timegm(dt.utctimetuple()) + (dt.microsecond / 1000000)
  83. return str(int(ts * 1000000))
  84. if token == "ZZZ":
  85. return dt.tzname()
  86. if token in ["ZZ", "Z"]:
  87. separator = ":" if token == "ZZ" else ""
  88. tz = dateutil_tz.tzutc() if dt.tzinfo is None else dt.tzinfo
  89. total_minutes = int(util.total_seconds(tz.utcoffset(dt)) / 60)
  90. sign = "+" if total_minutes >= 0 else "-"
  91. total_minutes = abs(total_minutes)
  92. hour, minute = divmod(total_minutes, 60)
  93. return "{}{:02d}{}{:02d}".format(sign, hour, separator, minute)
  94. if token in ("a", "A"):
  95. return self.locale.meridian(dt.hour, token)
  96. if token == "W":
  97. year, week, day = dt.isocalendar()
  98. return "{}-W{:02d}-{}".format(year, week, day)