iOS Swift - 获取当前本地时间和日期时间戳

88

我正在尝试开发一款考勤应用程序,但我对iOS和Firebase中的日期和时间非常困惑。

我使用日期作为关键字,在Firebase数据库中的结构如下:

--Employees
  --Unique_ID
     --Details
          Name: John
     --Attendance
          --dateToday
              Timein: 8:00 AM
              Timeout: 5:00 PM
              BreakStart: 12:00 PM
              BreakFinish: 1:00 PM

这是我用作键的日期时间戳获取代码

 override func viewDidLoad() {
     super.viewDidLoad()

     let now = NSDate()
     let nowTimeStamp = self.getCurrentTimeStampWOMiliseconds(dateToConvert: now)

     // I save this dateToday as Key in Firebase
     dateToday = nowTimeStamp
}


func getCurrentTimeStampWOMiliseconds(dateToConvert: NSDate) -> String {
    let objDateformat: DateFormatter = DateFormatter()
    objDateformat.dateFormat = "yyyy-MM-dd"
    let strTime: String = objDateformat.string(from: dateToConvert as Date)
    let objUTCDate: NSDate = objDateformat.date(from: strTime)! as NSDate
    let milliseconds: Int64 = Int64(objUTCDate.timeIntervalSince1970)
    let strTimeStamp: String = "\(milliseconds)"
    return strTimeStamp
}
但是当我将其转换回日期时,我得到的是2017-09-22 16:00:00 +0000,这是错误的,因为在我的位置上是9月23日。
请问正确的代码是什么,以便我可以获得正确的日期时间戳和时间戳?

3
如果你的时区是UTC+0800,那么这个说法是正确的。print始终以UTC时间显示日期。而且,2017-09-22 16:00:00 +00002017-09-23 00:00:00 +0800实际上表示同一时刻。顺便提一下,在Swift 3中完全不要使用NSDate - vadian
DateFormatter 是一个创建起来比较耗费资源的对象,因此如果你需要经常调用该函数,建议将其放在函数作用域之外。 - ha100
9个回答

147

为了将当前时间保存到Firebase数据库中,我使用了Unix Epoch转换:

let timestamp = NSDate().timeIntervalSince1970

并且用于将Unix Epoch时间解码为日期()。

let myTimeInterval = TimeInterval(timestamp)
let time = NSDate(timeIntervalSince1970: TimeInterval(myTimeInterval))

NSDate().timeIntervalSince1970会计算时区吗?还是有不同时区的方法可以做到这一点? - Spire
1
纪元时间(Epoch time)的定义是在协调世界时(UTC)中。它是自1970年1月1日午夜以来经过的秒数/毫秒数,以UTC时区为准。时区信息仅对以特定人类语言显示有用,而不适用于存储或计算任何内容。 - Andrew Koster

66

如果您只需要Unix时间戳,请创建一个扩展程序:

extension Date {
    func currentTimeMillis() -> Int64 {
        return Int64(self.timeIntervalSince1970 * 1000)
    }
}

然后您可以像在其他编程语言中一样使用它:

let timestamp = Date().currentTimeMillis()

6
迄今为止最好的答案。 - barrylachapelle

25

首先,我建议您将时间戳存储为 NSNumber 而不是存储为 String 在 Firebase 数据库中。

这里值得一提的另一件事是,如果您想使用 Swift 操作日期,最好使用 Date 而不是 NSDate,除非您在应用程序中与某些 Obj-C 代码交互。

当然,您可以同时使用两者,但文档指出:

Date 桥接到 NSDate 类。您可以在与 Objective-C API 交互的代码中互换使用它们。

现在回答您的问题,我认为问题出在时区上。

例如,如果您执行 print(Date()),目前会得到:

2017-09-23 06:59:34 +0000

这是格林威治标准时间(GMT)。

因此,根据您所在的位置(或您的用户所在的位置),您需要在存储您的Date之前(或之后,例如当您尝试访问数据时)调整时区:

    let now = Date()

    let formatter = DateFormatter()

    formatter.timeZone = TimeZone.current

    formatter.dateFormat = "yyyy-MM-dd HH:mm"

    let dateString = formatter.string(from: now)

然后你有了格式正确的 String,反映了你所在位置的当前时间,你可以随意使用它 :)(将其转换为 Date / NSNumber,或直接将其作为 String 存储在数据库中。。)。

1
日期字符串最终是正确的。但是,如果我在末尾添加 formatter.date(from: dateString),我会再次得到格林威治标准时间。 - Michael McKenna

20

Swift 5

extension Date {
    static var currentTimeStamp: Int64{
        return Int64(Date().timeIntervalSince1970 * 1000)
    }
}

像这样进行调用:

let timeStamp = Date.currentTimeStamp
print(timeStamp)

感谢 @lenooh


11

创建当前时间戳的简单方法如下所示:

func generateCurrentTimeStamp () -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
    return (formatter.string(from: Date()) as NSString) as String
}

you can call like this:

let timeStmp = generateCurrentTimeStamp()
print("time stamp: \(timeStmp)")

3
如果您编写的是iOS 13.0或更高版本的代码,并且想要一个时间戳,那么可以使用以下代码:
let currentDate = NSDate.now

这不是10位时间戳。 - djdance

2

在扩展@MacacoAzul的答案上,这是我的当前工作示例:

import SwiftUI

struct TimestampDemo: View {
    var body: some View {
        
        Text(getActualTimeStamp(1))
            .padding(10)
       
        Text(getActualTimeStamp(2))
            .padding(10)
        
        Text(getActualTimeStamp(3))
            .padding(10)
        
        Text(getActualTimeStamp(4))
            .padding(10)
    }
    
    
    func getActualTimeStamp(_ tipo:Int) -> String {
            let date = Date()
            let formatter = DateFormatter()
            if tipo == 1{
                formatter.dateFormat = "dd/MM/yyyy"
            } else if tipo == 2{
                formatter.dateFormat = "yyyy-MM-dd HH:mm"
            }else if tipo == 3{
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            }
            else if tipo == 4  {
                formatter.dateFormat = "dd-MM-yyyy"
            }

            return formatter.string(from: date)
        }
}

struct TimestampDemo_Previews: PreviewProvider {
    static var previews: some View {
        TimestampDemo()
    }
}

enter image description here

Swift语言版本:5


1
你甚至可以创建一个函数,根据你的需求返回不同的时间戳:
func dataatual(_ tipo:Int) -> String {
        let date = Date()
        let formatter = DateFormatter()
        if tipo == 1{
            formatter.dateFormat = "dd/MM/yyyy"
        } else if tipo == 2{
            formatter.dateFormat = "yyyy-MM-dd HH:mm"
        } else {
            formatter.dateFormat = "dd-MM-yyyy"
        }

        return formatter.string(from: date)
    } 

1
当我们将一个UTC时间戳(2017-11-06 20:15:33 -08:00)转换成一个Date对象时,时区会被归零为GMT。对于计算时间间隔来说,这不是问题,但对于在UI中呈现时间可能会有影响。
我倾向于使用RFC3339格式(2017-11-06T20:15:33-08:00),因为它具有普适性。Swift中的日期格式为yyyy-MM-dd'T'HH:mm:ssXXXXX,但RFC3339允许我们利用ISO8601DateFormatter
func getDateFromUTC(RFC3339: String) -> Date? {
    let formatter = ISO8601DateFormatter()
    return formatter.date(from: RFC3339)
}

RFC3339还使时区提取变得简单:

func getTimeZoneFromUTC(RFC3339: String) -> TimeZone? {

    switch RFC3339.suffix(6) {

    case "+05:30":
        return TimeZone(identifier: "Asia/Kolkata")

    case "+05:45":
        return TimeZone(identifier: "Asia/Kathmandu")

    default:
        return nil

    }

}

还有大约37个其他时区需要考虑,由您确定哪些时区,因为没有明确的列表。一些标准计算较少的时区,一些计算更多的时区。大多数时区按小时分割,有些按半小时分割,有些按0:45,有些按0:15分割。

我们可以将上述两种方法合并成以下内容:

func getFormattedDateFromUTC(RFC3339: String) -> String? {

    guard let date = getDateFromUTC(RFC3339: RFC3339),
        let timeZone = getTimeZoneFromUTC(RFC3339: RFC3339) else {
            return nil
    }

    let formatter = DateFormatter()
    formatter.dateFormat = "h:mma EEE, MMM d yyyy"
    formatter.amSymbol = "AM"
    formatter.pmSymbol = "PM"
    formatter.timeZone = timeZone // preserve local time zone
    return formatter.string(from: date)

}

因此,代表加德满都某处下午5:00的字符串"2018-11-06T17:00:00+05:45"将打印出5:00PM Tue, Nov 6 2018,显示本地时间,无论机器在哪里。

另外,我建议远程存储日期为字符串(包括具有本地日期对象的Firestore),因为我认为远程数据应该是不可知的,以尽可能减少服务器和客户端之间的摩擦。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接