【问题标题】:Date to milliseconds and back to date in SwiftSwift 中的日期到毫秒和回溯到日期
【发布时间】:2017-03-01 06:24:15
【问题描述】:

我正在使用 UTC 中的当前时间,并将其放入纳秒,然后我需要采用纳秒并返回到本地时间的日期。我可以将时间精确到纳秒,然后返回到日期字符串,但是当我从字符串到日期时,时间会变得很复杂。

    //Date to milliseconds
     func currentTimeInMiliseconds() -> Int! {
            let currentDate = NSDate()
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = format
            dateFormatter.timeZone = NSTimeZone(name: "UTC") as TimeZone!
            let date = dateFormatter.date(from: dateFormatter.string(from: currentDate as Date))
            let nowDouble = date!.timeIntervalSince1970
            return Int(nowDouble*1000)
        }

    //Milliseconds to date
    extension Int {
        func dateFromMilliseconds(format:String) -> Date {
            let date : NSDate! = NSDate(timeIntervalSince1970:Double(self) / 1000.0)
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = format
            dateFormatter.timeZone = TimeZone.current
            let timeStamp = dateFormatter.string(from: date as Date)

let formatter = DateFormatter()
            formatter.dateFormat = format
            return ( formatter.date( from: timeStamp ) )!
        }
    }

//时间戳正确但返回日期不正确

【问题讨论】:

  • 将日期转换为字符串并立即返回日期(在currentTimeInMiliseconds())的目的是什么?
  • 我工作的公司以毫秒为单位存储所有日期
  • 但是什么是双重转换日期 -> 字符串 -> 日期以及代码中缺少的给定 format 是什么?
  • 您正在将日期转换为字符串,然后又回到相同的日期,这似乎没用。再一次,format 使用的日期格式是什么?其实Int(Date().timeIntervalSince1970 * 1000)可以替换整个功能。 NSDate 只是一个不考虑时区的双精度值
  • 当您的意思似乎是 毫秒 时,您似乎在问题中使用了 nanoseconds。一毫秒有一百万纳秒,所以这不是一个无关紧要的错误。

标签: ios swift date time


【解决方案1】:
let dateTimeStamp = NSDate(timeIntervalSince1970:Double(currentTimeInMiliseconds())/1000)  //UTC time  //YOUR currentTimeInMiliseconds METHOD
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone() 
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.dateStyle = NSDateFormatterStyle.FullStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle


let strDateSelect = dateFormatter.stringFromDate(dateTimeStamp)
print("Local Time", strDateSelect) //Local time


let dateFormatter2 = NSDateFormatter()
dateFormatter2.timeZone = NSTimeZone(name: "UTC") as NSTimeZone!
dateFormatter2.dateFormat = "yyyy-MM-dd"

let date3 = dateFormatter.dateFromString(strDateSelect)
print("DATE",date3)

【讨论】:

    【解决方案2】:
    //Date to milliseconds
    func currentTimeInMiliseconds() -> Int {
        let currentDate = Date()
        let since1970 = currentDate.timeIntervalSince1970
        return Int(since1970 * 1000)
    }
    
    //Milliseconds to date
    extension Int {
        func dateFromMilliseconds() -> Date {
            return Date(timeIntervalSince1970: TimeInterval(self)/1000)
        }
    }
    

    我通过字符串和所有那些随机的! 删除了看似无用的转换。

    【讨论】:

    • 这很好,但我需要 currentTimeInMilliseconds 将本地时间转换为 UTC,我需要 dateFromMilliseconds 将 UTC 转换为本地时间
    • Timestamp is in UTC already.。 > The number of seconds from the reference date (00:00:00 **UTC** on 1 January 1970)
    • 为什么要*1000,我想我们失去了毫秒
    • 不会丢失毫秒精度,但会丢失亚毫秒精度。除非绝对需要将时间间隔表示为整数,否则请考虑我在下面的答案。
    • @PeterSchorn,是的,答案中的这段代码只是问题中代码的清理版本。问题有Int,因此答案也有Int
    【解决方案3】:

    我不明白你为什么要对字符串做任何事情......

    extension Date {
        var millisecondsSince1970:Int64 {
            Int64((self.timeIntervalSince1970 * 1000.0).rounded())
        }
        
        init(milliseconds:Int64) {
            self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
        }
    }
    
    
    Date().millisecondsSince1970 // 1476889390939
    Date(milliseconds: 0) // "Dec 31, 1969, 4:00 PM" (PDT variant of 1970 UTC)
    

    【讨论】:

    • 我正在做字符串,因为我需要将毫秒Since1970 设为 UTC 并将 init(milliseconds:Int) 放回本地时间。
    • 日期对象始终采用 UTC。我的示例中的这些值是 UTC(注意描述方法如何呈现从 PDT 开始的 UTC 纪元)。
    • 最好使用 init(milliseconds:Double) 而不是 Int 否则在转换回来时会丢失毫秒数。
    • 这个方案不会失去毫秒精度吗?
    • davis:如果我理解正确,timeIntervalSince1970 是双精度数,因此具有毫秒精度。乘数和强制转换可以将其转换为 int。
    【解决方案4】:

    @Travis 解决方案有效,但在某些情况下

    var millisecondsSince1970:Int 会导致应用崩溃

    有错误

    双精度值无法转换为 Int,因为如果发生,结果将大于 Int.max 请使用 Int64 更新您的答案

    这里是更新的答案

    extension Date {
     var millisecondsSince1970:Int64 {
            return Int64((self.timeIntervalSince1970 * 1000.0).rounded()) 
            //RESOLVED CRASH HERE
        }
    
        init(milliseconds:Int) {
            self = Date(timeIntervalSince1970: TimeInterval(milliseconds / 1000))
        }
    }
    

    About Int definitions.

    在 32 位平台上,Int 与 Int32 大小相同,在 64 位平台上,Int 与 Int64 大小相同。

    一般情况下,我在iPhone 5 遇到这个问题,它在32 位环境中运行。新设备现在运行 64 位环境。他们的Int 将是Int64

    希望对遇到同样问题的朋友有所帮助

    【讨论】:

    • 为什么会出现这个错误?自 1970 年以来的时间间隔是一个 32 位数字。至少在我们做到这一点之前:en.wikipedia.org/wiki/Year_2038_problem
    • @DoesData 如果您看到我们将数字乘以 1000。这会导致超出 Int32 的范围
    • 一些旧的 iOS 设备在 32 位环境中运行。这是IntInt32。但是新的 iOS 设备,例如:iPhone 6,IntInt64。因此,32 位问题可能会导致旧 iOS 设备崩溃。
    • @AechoLiu 感谢您指出这种情况应该如何解决?你能帮我更新我的答案吗
    • 我在 32 位系统 (iPad 2) 上也遇到了这个问题。这应该是正确的答案。
    【解决方案5】:

    @Travis 解决方案是正确的,但是在生成 Date 时它会丢失毫秒。我添加了一行以将毫秒包含在日期中:

    如果您不需要这种精度,请使用 Travis 解决方案,因为它会更快。

    extension Date {
    
        func toMillis() -> Int64! {
            return Int64(self.timeIntervalSince1970 * 1000)
        }
    
        init(millis: Int64) {
            self = Date(timeIntervalSince1970: TimeInterval(millis / 1000))
            self.addTimeInterval(TimeInterval(Double(millis % 1000) / 1000 ))
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      注意是否要在转换后比较日期!

      例如,我得到了日期为 TimeInterval(366144731.9) 的模拟器资产,使用

      转换为毫秒 Int64(1344451931900) 并返回 TimeInterval(366144731.9000001)
      func convertToMilli(timeIntervalSince1970: TimeInterval) -> Int64 {
          return Int64(timeIntervalSince1970 * 1000)
      }
      
      func convertMilliToDate(milliseconds: Int64) -> Date {
          return Date(timeIntervalSince1970: (TimeInterval(milliseconds) / 1000))
      }
      

      我尝试通过 creationDate 获取资产,但它没有找到资产,正如您所想象的那样,数字不一样。

      我尝试了多种解决方案来降低 double 的小数精度,例如 round(interval*1000)/1000、使用 NSDecimalNumber 等...均未成功。

      我最终按间隔 -1

      可能有更好的解决方案!?

      【讨论】:

        【解决方案7】:

        @Prashant Tukadiya 回答有效。但是,如果您想将值保存在 UserDefaults 中,然后将其与其他日期进行比较,则会将 int64 截断,因此可能会导致问题。我找到了解决办法。

        斯威夫特 4:

        您可以在 UserDefaults 中将 int64 保存为字符串:

        let value: String(Date().millisecondsSince1970)
        let stringValue = String(value)
        UserDefaults.standard.set(stringValue, forKey: "int64String")
        

        就像你避免 Int 截断一样。

        然后你就可以恢复原来的值了:

        let int64String = UserDefaults.standard.string(forKey: "int64String")
        let originalValue = Int64(int64String!)
        

        这允许您将其与其他日期值进行比较:

        let currentTime = Date().millisecondsSince1970
        let int64String = UserDefaults.standard.string(forKey: "int64String")
        let originalValue = Int64(int64String!) ?? 0 
        
        if currentTime < originalValue {
             return false
        } else {
             return true
        }
        

        希望对遇到同样问题的人有所帮助

        【讨论】:

        • Date().millisecondsSince1970 显示为未解决
        • @javadba 试试Date().timeIntervalSince1970。返回值是Double,表示自 1970 年以来的秒数——精确到“亚毫秒精度”。见这里:developer.apple.com/documentation/foundation/timeinterval
        • @Jase Thx 是的,我终于在不久之后遇到了这个问题。
        • @javadba Date().millisecondsSince1970 是该问题帖子中的自定义扩展。它不在 Foundation 中。
        【解决方案8】:

        这是 Swift 5/iOS 13 中的一个简单解决方案。

        extension Date {
            
            func toMilliseconds() -> Int64 {
                Int64(self.timeIntervalSince1970 * 1000)
            }
        
            init(milliseconds:Int) {
                self = Date().advanced(by: TimeInterval(integerLiteral: Int64(milliseconds / 1000)))
            }
        }
        

        但是,这假设您已经计算了 UTF 时间和本地时间之间的差异,并以毫秒为单位进行了调整和计算。对于日历的外观

        var cal = Calendar.current
        cal.timeZone = TimeZone(abbreviation: "UTC")!
        let difference = cal.compare(dateGiven, to: date, toGranularity: .nanosecond)
        

        【讨论】:

          【解决方案9】:

          除非您绝对必须将日期转换为整数,否则请考虑使用Double 来表示时间间隔。毕竟这是timeIntervalSince1970返回的类型。所有转换为整数的答案都会失去亚毫秒级的精度,但这种解决方案要准确得多(尽管由于浮点不精确,您仍然会损失一些精度)。

          public extension Date {
              
              /// The interval, in milliseconds, between the date value and
              /// 00:00:00 UTC on 1 January 1970.
              /// Equivalent to `self.timeIntervalSince1970 * 1000`.
              var millisecondsSince1970: Double {
                  return self.timeIntervalSince1970 * 1000
              }
          
              /**
               Creates a date value initialized relative to 00:00:00 UTC
               on 1 January 1970 by a given number of **milliseconds**.
               
               equivalent to
               ```
               self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
               ```
               - Parameter millisecondsSince1970: A time interval in milliseconds.
               */
              init(millisecondsSince1970 milliseconds: Double) {
                  self.init(timeIntervalSince1970: TimeInterval(milliseconds) / 1000)
              }
          
          }
          

          【讨论】:

            【解决方案10】:

            在 UInt64 中获取时间令牌的简单一行代码

            let time = UInt64(Date().timeIntervalSince1970 * 1000)
            
            print(time) <----- prints time in UInt64
            

            补充提示:

            对于具有 自 1970 年以来用于 API 调用的 10 位毫秒的时间戳然后

            let timeStamp = Date().timeIntervalSince1970
            
            print(timeStamp) <-- prints current time stamp
            

            【讨论】:

              猜你喜欢
              • 2014-10-12
              • 1970-01-01
              • 1970-01-01
              • 2018-11-21
              • 1970-01-01
              • 2016-03-26
              • 1970-01-01
              • 1970-01-01
              • 2018-12-22
              相关资源
              最近更新 更多