美文网首页
iOS 时间字符串format跟随系统语言地区

iOS 时间字符串format跟随系统语言地区

作者: Yuency | 来源:发表于2022-09-29 16:08 被阅读0次

    前言:

    公司App做了个时间的改动。以前显示出来的时间使我们自己format出来的,也就是说,时间样式是我们自己定义的。比如,年份在月份前面,月份在日子前面。但是不同国家地区的人,他们有自己的日期显示习惯,

    有的是:日/月/年

    有的是:月/日/年

    有的是:年-月-日

    有的是:年.月.日.

    各种顺序分隔符都有。为了符合当地人习惯,我们不再指定显示格式,而是根据手机系统的格式来显示。

    需求:在App里显示的时间样式,根据用户的系统样式即可。也就是说,系统把时间显示成什么样,我们也显示成什么样。但是我们有选择地显示,有些地方只要 年月日,有些地方要 年月日时分秒,有些地方只要 时分。样式仍旧按照系统的来。

    代码地址:https://gitee.com/yuency/Autolayout
    示例代码类名 【LocalizeDateToPhoneSystemViewController】

    部分示例截图:


    示例.png

    直接上结论代码,Command + C,V

    func localizeDate(date: Date, destinationTimeZone: TimeZone? = TimeZone.current, destinationTemplate: DateTemplate) -> String {
        guard let destinationTimeZone = destinationTimeZone else {
            return "时区错误"
        }
        let formatter = DateFormatter()
        let formatTemplate = destinationTemplate.getTemplate()
        //用下面这个方法就可获得根据系统语言地区得到的格式化样式
        //fromTemplate: 传一个临时的样式,告诉DateFormatter你需要哪些时间字段, 如: "yyyyMMdd", "MMddHHss"等, 无需携带格式.
        formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: formatTemplate, options: 0, locale: NSLocale.current)
        formatter.timeZone = destinationTimeZone
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        let dateString = formatter.string(from: date)
        return dateString
    }
    
    输出打印示例:
    20210929134455 ->  13:44:55
    20210929134455 ->  29/09/2021
    20210929134455 ->  29/09/2021 13:44
    20210929134455 ->  29/09/2021 13:44:55
    
    2021-09-29 13:44:56 ->  13:44:56
    2021-09-29 13:44:56 ->  29/09/2021
    2021-09-29 13:44:56 ->  29/09/2021 13:44
    2021-09-29 13:44:56 ->  29/09/2021 13:44:56
    
    2021-09-29T13:44:57Z ->  13:44:57
    2021-09-29T13:44:57Z ->  29/09/2021
    2021-09-29T13:44:57Z ->  29/09/2021 13:44
    2021-09-29T13:44:57Z ->  29/09/2021 13:44:57
    
    2022-09-29 07:44:53 +0000 ->  15:44:53
    2022-09-29 07:44:53 +0000 ->  29/09/2022
    2022-09-29 07:44:53 +0000 ->  29/09/2022 15:44
    2022-09-29 07:44:53 +0000 ->  29/09/2022 15:44:53
    
    

    完整的测试代码如下:

    import UIKit
    
    class LocalizeDateToPhoneSystemViewController: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.white
            
            let label = UILabel()
            label.font = UIFont.systemFont(ofSize: 16)
            label.backgroundColor = .yellow
            label.textAlignment = .left
            label.numberOfLines = 0
            view.addSubview(label)
            label.snp.makeConstraints { make in
                make.left.equalTo(10)
                make.right.equalTo(-10)
                make.centerY.equalToSuperview()
            }
            
            label.text = """
                        不同语言地区组合时间格式化示例 (24小时制)
                        
                        zh-Hans_GL, 简体中文_格林兰
                        2021年09月29日 13:44:55
                        
                        en_GL, 英语_格林兰
                        2022-09-29, 14:28:42
                        
                        zh-Hans_FR, 简体中文_法国
                        29/09/2022 14:13:17
                        
                        en_FR, 英语_法国
                        29/09/2022 at 14:14:09
                        
                        zh-Hans_HR, 简体中文_克罗地亚
                        29. 09. 2022. 14:15:38
                        
                        en_HR, 英语_克罗地亚
                        29.09.2022., 14:20:32
                        
                        zh-Hans_MX, 简体中文_墨西哥
                        29/09/2022 14:24:41
                        
                        zh-Hans_MV, 简体中文_马尔代夫
                        2022年09月29日 14:25:43
                        
                        en_MV, 英语_马尔代夫
                        29-09-2022 14:26:18
                        
                        fi_FI, 芬兰语_芬兰
                        29.09.2022 klo 15.21.18
                                            
                        """
            //Locale.current 由两个部分组成, 为: "语言_地区" 用下划线分开语言和地区
            //Locale.current 这个取系统语言不是很准确,有时候把语言更换了,但是这里仍然没有改变,地区倒能改变
            print("当前的语言地区: \(Locale.current), 当前的时区: \(TimeZone.current)")
            
            print("\n\n")
            var dateString = "20210929134455"
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .simple, destinationTemplate: .HHmmss))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .simple, destinationTemplate: .yyyyMMdd))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .simple, destinationTemplate: .yyyyMMddHHmm))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .simple, destinationTemplate: .yyyyMMddHHmmss))")
            
            print("\n\n")
            dateString = "2021-09-29 13:44:56"
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .normal, destinationTemplate: .HHmmss))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .normal, destinationTemplate: .yyyyMMdd))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .normal, destinationTemplate: .yyyyMMddHHmm))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .normal, destinationTemplate: .yyyyMMddHHmmss))")
            
            print("\n\n")
            dateString = "2021-09-29T13:44:57Z"
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .utc, destinationTemplate: .HHmmss))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .utc, destinationTemplate: .yyyyMMdd))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .utc, destinationTemplate: .yyyyMMddHHmm))")
            print("\(dateString) ->  \(localizeDate(dateString: dateString, dateFormat: .utc, destinationTemplate: .yyyyMMddHHmmss))")
            
            print("\n\n")
            let date = Date()
            print("\(date) ->  \(localizeDate(date: date, destinationTemplate: .HHmmss))")
            print("\(date) ->  \(localizeDate(date: date, destinationTemplate: .yyyyMMdd))")
            print("\(date) ->  \(localizeDate(date: date, destinationTemplate: .yyyyMMddHHmm))")
            print("\(date) ->  \(localizeDate(date: date, destinationTemplate: .yyyyMMddHHmmss))")
            
            tongjigezhogngeshi()
        }
        
        /// 重要!
        func localizeDate(date: Date, destinationTimeZone: TimeZone? = TimeZone.current, destinationTemplate: DateTemplate) -> String {
            guard let destinationTimeZone = destinationTimeZone else {
                return "时区错误"
            }
            let formatter = DateFormatter()
            let formatTemplate = destinationTemplate.getTemplate()
            //用下面这个方法就可获得根据系统语言地区得到的格式化样式
            //fromTemplate: 传一个临时的样式,告诉DateFormatter你需要哪些时间字段, 如: "yyyyMMdd", "MMddHHss"等, 无需携带格式.
            formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: formatTemplate, options: 0, locale: NSLocale.current)
            formatter.timeZone = destinationTimeZone
            formatter.calendar = Calendar(identifier: .gregorian)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            let dateString = formatter.string(from: date)
            return dateString
        }
        
        /// 重要!
        func localizeDate(dateString: String, dateFormat: DateFormat, destinationTimeZone: TimeZone? = TimeZone.current, destinationTemplate: DateTemplate) -> String {
            guard let destinationTimeZone = destinationTimeZone  else {
                return "时区错误"
            }
            let formatter = DateFormatter()
            formatter.dateFormat = dateFormat.getFormat()
            
            //格式化为0时区, 那么可以写成GMT, 后面再看
            //formatter.timeZone = TimeZone(abbreviation: "GMT")
            
            //这里是不管啥时间串,直接格式化为东八区 (拿着手机跑到美国,看到的依然是东八区)
            //formatter.timeZone = TimeZone(secondsFromGMT: 8)
            
            //如果对时间字符串没有什么要求,可以按照当前时区格式化, 东八就东八, 东十就东十, 取决于手机
            formatter.timeZone = TimeZone.current
            
            formatter.calendar = Calendar(identifier: .gregorian)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            guard let gmtDate = formatter.date(from: dateString) else {
                return "格式化错误, 时间串:\(dateString), 格式串:\(formatter.dateFormat ?? "")"
            }
            let convertDateString = localizeDate(date: gmtDate, destinationTimeZone: destinationTimeZone, destinationTemplate: destinationTemplate)
            return convertDateString
        }
        
        /// 额外研究
        func qitajieshao() {
            //语言和地区,会互相影响最终的时间格式.
            //如果是一个有国际化的APP,那么有可能出现其他的形式,如,月份使用了英文 February 或者 中文 月
            //要让时间格式只收到地区影响,就要在 DateFormatter.dateFormat(fromTemplate: formatTemplate, options: 0, locale: NSLocale.current) 这一句里指定地区
            print("\n\n")
            let diqu = NSLocale.current.identifier.components(separatedBy: "_").last ?? "" //通过分隔符把最后的地域拿到
            let yuyan = "en"
            let yuyandiqu = "\(yuyan)_\(diqu)" //就硬再前面拼上语言,合成一个完整的 语言地域串
            //这个 `fromTemplate` 是可以带上格式的,但是会被忽略掉
            let fixedFormat = DateFormatter.dateFormat(fromTemplate: "yyyy MM dd HH mm ss", options: 0, locale: Locale(identifier: yuyandiqu))
            print("硬凑的语言地区: \(yuyandiqu),  修正后的格式: \(fixedFormat ?? "错误")")
        }
        
        /// 额外研究
        func tongjigezhogngeshi() {
            
            print("\n\n")
            print("🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩🌩")
            print("\n\n")
            
            var dic = [String: [String]]()
            
            //带有下划线的,认为是语言+地区的组合, 实际上对于下面的功能来说, 过滤与否, 结果条数都一样, 59条格式
            //var yuyandiquArray = Locale.availableIdentifiers.filter { string in
            //    string.contains("_")
            //}
            
            var yuyandiquArray = Locale.availableIdentifiers
            
            for s in yuyandiquArray {
                let fixedFormat = DateFormatter.dateFormat(fromTemplate: "yyyy MM dd HH mm ss", options: 0, locale: Locale(identifier: s)) ?? "错误"
                if dic.keys.contains(fixedFormat) == false {
                    dic[fixedFormat] = [String]()
                }
                dic[fixedFormat]?.append(s)
            }
            
            print("取得格式化结果 \(dic.keys.count) 条")
            
            for (key, array) in dic {
                print("\n")
                print("所用格式: \(key)")
                print("所在地区: \(array)")
            }
            
            print("\n\n")
            let sortFormat = dic.keys.sorted { a, b in
                a.count > b.count
            }
            print("格式化长短排序 \(sortFormat)") //马其顿稳居第一
        }
    }
    
    enum DateTemplate: String {
        case HHmmss
        case yyyyMMdd
        case yyyyMMddHHmm
        case yyyyMMddHHmmss
        
        func getTemplate() -> String {
            switch self {
            case .HHmmss:
                return "HHmmss"
            case .yyyyMMdd:
                return "yyyyMMdd"
            case .yyyyMMddHHmm:
                return "yyyyMMddHHmm"
            case .yyyyMMddHHmmss:
                return "yyyyMMddHHmmss"
            }
        }
    }
    
    enum DateFormat: String {
        case normal
        case utc
        case simple
        
        func getFormat() -> String {
            switch self {
            case .normal:
                return "yyyy-MM-dd HH:mm:ss"
            case .utc:
                return "yyyy-MM-dd'T'HH:mm:ss'Z'"
            case .simple:
                return "yyyyMMddHHmmss"
            }
        }
    }
    

    感谢以下iOS玩家的文章:
    iOS15.4 NSDateformatter 12小时制日期格式问题及解决
    iOS DateFormatter dateFormat fromTemplate 中允许的格式说明符是什么?
    苹果官方
    swift DateFormatter

    结语

    又是这么一个小小的时间。

    相关文章

      网友评论

          本文标题:iOS 时间字符串format跟随系统语言地区

          本文链接:https://www.haomeiwen.com/subject/gtjkartx.html