RFC1123DateFormatter.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import NIO
  2. import Foundation
  3. /// Performant method for generating RFC1123 date headers.
  4. internal final class RFC1123DateCache {
  5. static func on(_ eventLoop: EventLoop) -> RFC1123DateCache {
  6. assert(eventLoop.inEventLoop)
  7. if let existing = thread.currentValue {
  8. return existing
  9. } else {
  10. let new = RFC1123DateCache()
  11. let fracSeconds = 1.0 - Date().timeIntervalSince1970.truncatingRemainder(dividingBy: 1)
  12. let msToNextSecond = Int64(fracSeconds * 1000) + 1
  13. eventLoop.scheduleRepeatedTask(initialDelay: .milliseconds(msToNextSecond), delay: .seconds(1)) { task in
  14. new.updateTimestamp()
  15. }
  16. self.thread.currentValue = new
  17. return new
  18. }
  19. }
  20. /// Thread-specific RFC1123
  21. private static let thread: ThreadSpecificVariable<RFC1123DateCache> = .init()
  22. /// Currently cached time components.
  23. private var cachedTimeComponents: (key: time_t, components: tm)?
  24. /// Currently cached timestamp.
  25. private var timestamp: String
  26. /// Creates a new `RFC1123DateCache`.
  27. private init() {
  28. self.timestamp = ""
  29. self.updateTimestamp()
  30. }
  31. func currentTimestamp() -> String {
  32. return self.timestamp
  33. }
  34. /// Updates the current RFC 1123 date string.
  35. func updateTimestamp() {
  36. // get the current time
  37. var date = time(nil)
  38. // generate a key used for caching
  39. // this key is a unique id for each day
  40. let key = date / secondsInDay
  41. // get time components
  42. let dateComponents: tm
  43. if let cached = self.cachedTimeComponents, cached.key == key {
  44. dateComponents = cached.components
  45. } else {
  46. var tc = tm.init()
  47. gmtime_r(&date, &tc)
  48. dateComponents = tc
  49. self.cachedTimeComponents = (key: key, components: tc)
  50. }
  51. // parse components
  52. let year: Int = numericCast(dateComponents.tm_year) + 1900 // years since 1900
  53. let month: Int = numericCast(dateComponents.tm_mon) // months since January [0-11]
  54. let monthDay: Int = numericCast(dateComponents.tm_mday) // day of the month [1-31]
  55. let weekDay: Int = numericCast(dateComponents.tm_wday) // days since Sunday [0-6]
  56. // get basic time info
  57. let t: Int = date % secondsInDay
  58. let hours: Int = numericCast(t / 3600)
  59. let minutes: Int = numericCast((t / 60) % 60)
  60. let seconds: Int = numericCast(t % 60)
  61. // generate the RFC 1123 formatted string
  62. var rfc1123 = ""
  63. rfc1123.reserveCapacity(30)
  64. rfc1123.append(dayNames[weekDay])
  65. rfc1123.append(", ")
  66. rfc1123.append(stringNumbers[monthDay])
  67. rfc1123.append(" ")
  68. rfc1123.append(monthNames[month])
  69. rfc1123.append(" ")
  70. rfc1123.append(stringNumbers[year / 100])
  71. rfc1123.append(stringNumbers[year % 100])
  72. rfc1123.append(" ")
  73. rfc1123.append(stringNumbers[hours])
  74. rfc1123.append(":")
  75. rfc1123.append(stringNumbers[minutes])
  76. rfc1123.append(":")
  77. rfc1123.append(stringNumbers[seconds])
  78. rfc1123.append(" GMT")
  79. // cache the new timestamp
  80. self.timestamp = rfc1123
  81. }
  82. }
  83. // MARK: Private
  84. private let dayNames = [
  85. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  86. ]
  87. private let monthNames = [
  88. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  89. ]
  90. private let stringNumbers = [
  91. "00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
  92. "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
  93. "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
  94. "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
  95. "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
  96. "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
  97. "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
  98. "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
  99. "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
  100. "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"
  101. ]
  102. private let secondsInDay = 60 * 60 * 24