<commit_before>//
//  AFDateHelper.swift
//  https://github.com/melvitax/DateHelper
//  Version 4.4.1
//
//  Created by <NAME> on 7/15/14.
//  Copyright (c) 2014. All rights reserved.
//

import Foundation


public extension Date {
    
    // MARK: - Convert from String
    
    /*
        Creates a new Date based on a string of a specified format. Supports optional timezone and locale.
    */
    init?(fromString string: String, format:DateFormatType, timeZone: TimeZoneType = .local, locale: Locale = Foundation.Locale.current, isLenient: Bool = true) {
        guard !string.isEmpty else {
            return nil
        }
        var string = string
        switch format {
            case .dotNet:
                let pattern = "\\\\?/Date\\((\\d+)(([+-]\\d{2})(\\d{2}))?\\)\\\\?/"
                let regex = try! NSRegularExpression(pattern: pattern)
                guard let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) else {
                    return nil
                }
                 #if swift(>=4.0)
                let dateString = (string as NSString).substring(with: match.range(at: 1))
                #else
                let dateString = (string as NSString).substring(with: match.rangeAt(1))
                #endif
                let interval = Double(dateString)! / 1000.0
                self.init(timeIntervalSince1970: interval)
                return
            case .rss, .altRSS:
                if string.hasSuffix("Z") {
                    string = string[..<string.index(string.endIndex, offsetBy: -1)].appending("GMT")
                }
            default:
                break
        }
        let formatter = Date.cachedDateFormatters.cachedFormatter(
            format.stringFormat,
            timeZone: timeZone.timeZone,
            locale: locale, 
            isLenient: isLenient)
        guard let date = formatter.date(from: string) else {
            return nil
        }
        self.init(timeInterval:0, since:date)
    }
    
    /*
        Creates a new Date based on the first date detected on a string using data dectors.
    */
    init?(detectFromString string: String) {
        let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
        let matches = detector?.matches(in: string, options: [], range: NSMakeRange(0, string.utf16.count))
        if let date = matches?.first?.date {
            self.init()
            self = date
        } else {
            return nil
        }
    }
    
    // MARK: - Convert to String
    
    /// Converts the date to string using the short date and time style.
    func toString(style:DateStyleType = .short) -> String {
        switch style {
        case .short:
            return self.toString(dateStyle: .short, timeStyle: .short, isRelative: false)
        case .medium:
            return self.toString(dateStyle: .medium, timeStyle: .medium, isRelative: false)
        case .long:
            return self.toString(dateStyle: .long, timeStyle: .long, isRelative: false)
        case .full:
            return self.toString(dateStyle: .full, timeStyle: .full, isRelative: false)
        case .ordinalDay:
            let formatter = Date.cachedDateFormatters.cachedNumberFormatter()
            if #available(iOSApplicationExtension 9.0, *) {
                formatter.numberStyle = .ordinal
            }
            return formatter.string(from: component(.day)! as NSNumber)!
        case .weekday:
            let weekdaySymbols = Date.cachedDateFormatters.cachedFormatter().weekdaySymbols!
            let string = weekdaySymbols[component(.weekday)!-1] as String
            return string
        case .shortWeekday:
            let shortWeekdaySymbols = Date.cachedDateFormatters.cachedFormatter().shortWeekdaySymbols!
            return shortWeekdaySymbols[component(.weekday)!-1] as String
        case .veryShortWeekday:
            let veryShortWeekdaySymbols = Date.cachedDateFormatters.cachedFormatter().veryShortWeekdaySymbols!
            return veryShortWeekdaySymbols[component(.weekday)!-1] as String
        case .month:
            let monthSymbols = Date.cachedDateFormatters.cachedFormatter().monthSymbols!
            return monthSymbols[component(.month)!-1] as String
        case .shortMonth:
            let shortMonthSymbols = Date.cachedDateFormatters.cachedFormatter().shortMonthSymbols!
            return shortMonthSymbols[component(.month)!-1] as String
        case .veryShortMonth:
            let veryShortMonthSymbols = Date.cachedDateFormatters.cachedFormatter().veryShortMonthSymbols!
            return veryShortMonthSymbols[component(.month)!-1] as String
        }
    }
    
    /// Converts the date to string based on a date format, optional timezone and optional locale.
    func toString(format: DateFormatType, timeZone: TimeZoneType = .local, locale: Locale = Locale.current) -> String {
        var useLocale = locale
        
        switch format {
        case .dotNet:
            let offset = Foundation.NSTimeZone.default.secondsFromGMT() / 3600
            let nowMillis = 1000 * self.timeIntervalSince1970
            return String(format: format.stringFormat, nowMillis, offset)
        case .isoDateTimeMilliSec, .isoDateTimeSec, .isoDateTime,
             .isoYear,. isoDate, .isoYearMonth:
            if #available(iOS 11.0, watchOS 3, tvOS 10, macOS 13, *) {
                return formatIsoDate(format: format, timeZone: timeZone)
            } else {
                useLocale = Locale(identifier: "en_US_POSIX")
            }
        default:
            break
        }
        let formatter = Date.cachedDateFormatters.cachedFormatter(format.stringFormat, timeZone: timeZone.timeZone, locale: useLocale)
        return formatter.string(from: self)
    }
    
    /// Converts to ISO format using the new API
    @available(iOS 11.0, watchOS 3, tvOS 10, macOS 13, *)
    func formatIsoDate(format: DateFormatType, timeZone: TimeZoneType = .local) -> String {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = timeZone.timeZone
        
        var options: ISO8601DateFormatter.Options = []
        switch format {
        case .isoDate:
            options = [.withFullDate]
        case .isoYearMonth:<commit_msg>Increased minimum watchOS version for ISO Date (#101)<commit_after>//
//  AFDateHelper.swift
//  https://github.com/melvitax/DateHelper
//  Version 4.4.1
//
//  Created by <NAME> on 7/15/14.
//  Copyright (c) 2014. All rights reserved.
//

import Foundation


public extension Date {
    
    // MARK: - Convert from String
    
    /*
        Creates a new Date based on a string of a specified format. Supports optional timezone and locale.
    */
    init?(fromString string: String, format:DateFormatType, timeZone: TimeZoneType = .local, locale: Locale = Foundation.Locale.current, isLenient: Bool = true) {
        guard !string.isEmpty else {
            return nil
        }
        var string = string
        switch format {
            case .dotNet:
                let pattern = "\\\\?/Date\\((\\d+)(([+-]\\d{2})(\\d{2}))?\\)\\\\?/"
                let regex = try! NSRegularExpression(pattern: pattern)
                guard let match = regex.firstMatch(in: string, range: NSRange(location: 0, length: string.utf16.count)) else {
                    return nil
                }
                 #if swift(>=4.0)
                let dateString = (string as NSString).substring(with: match.range(at: 1))
                #else
                let dateString = (string as NSString).substring(with: match.rangeAt(1))
                #endif
                let interval = Double(dateString)! / 1000.0
                self.init(timeIntervalSince1970: interval)
                return
            case .rss, .altRSS:
                if string.hasSuffix("Z") {
                    string = string[..<string.index(string.endIndex, offsetBy: -1)].appending("GMT")
                }
            default:
                break
        }
        let formatter = Date.cachedDateFormatters.cachedFormatter(
            format.stringFormat,
            timeZone: timeZone.timeZone,
            locale: locale, 
            isLenient: isLenient)
        guard let date = formatter.date(from: string) else {
            return nil
        }
        self.init(timeInterval:0, since:date)
    }
    
    /*
        Creates a new Date based on the first date detected on a string using data dectors.
    */
    init?(detectFromString string: String) {
        let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
        let matches = detector?.matches(in: string, options: [], range: NSMakeRange(0, string.utf16.count))
        if let date = matches?.first?.date {
            self.init()
            self = date
        } else {
            return nil
        }
    }
    
    // MARK: - Convert to String
    
    /// Converts the date to string using the short date and time style.
    func toString(style:DateStyleType = .short) -> String {
        switch style {
        case .short:
            return self.toString(dateStyle: .short, timeStyle: .short, isRelative: false)
        case .medium:
            return self.toString(dateStyle: .medium, timeStyle: .medium, isRelative: false)
        case .long:
            return self.toString(dateStyle: .long, timeStyle: .long, isRelative: false)
        case .full:
            return self.toString(dateStyle: .full, timeStyle: .full, isRelative: false)
        case .ordinalDay:
            let formatter = Date.cachedDateFormatters.cachedNumberFormatter()
            if #available(iOSApplicationExtension 9.0, *) {
                formatter.numberStyle = .ordinal
            }
            return formatter.string(from: component(.day)! as NSNumber)!
        case .weekday:
            let weekdaySymbols = Date.cachedDateFormatters.cachedFormatter().weekdaySymbols!
            let string = weekdaySymbols[component(.weekday)!-1] as String
            return string
        case .shortWeekday:
            let shortWeekdaySymbols = Date.cachedDateFormatters.cachedFormatter().shortWeekdaySymbols!
            return shortWeekdaySymbols[component(.weekday)!-1] as String
        case .veryShortWeekday:
            let veryShortWeekdaySymbols = Date.cachedDateFormatters.cachedFormatter().veryShortWeekdaySymbols!
            return veryShortWeekdaySymbols[component(.weekday)!-1] as String
        case .month:
            let monthSymbols = Date.cachedDateFormatters.cachedFormatter().monthSymbols!
            return monthSymbols[component(.month)!-1] as String
        case .shortMonth:
            let shortMonthSymbols = Date.cachedDateFormatters.cachedFormatter().shortMonthSymbols!
            return shortMonthSymbols[component(.month)!-1] as String
        case .veryShortMonth:
            let veryShortMonthSymbols = Date.cachedDateFormatters.cachedFormatter().veryShortMonthSymbols!
            return veryShortMonthSymbols[component(.month)!-1] as String
        }
    }
    
    /// Converts the date to string based on a date format, optional timezone and optional locale.
    func toString(format: DateFormatType, timeZone: TimeZoneType = .local, locale: Locale = Locale.current) -> String {
        var useLocale = locale
        
        switch format {
        case .dotNet:
            let offset = Foundation.NSTimeZone.default.secondsFromGMT() / 3600
            let nowMillis = 1000 * self.timeIntervalSince1970
            return String(format: format.stringFormat, nowMillis, offset)
        case .isoDateTimeMilliSec, .isoDateTimeSec, .isoDateTime,
             .isoYear,. isoDate, .isoYearMonth:
            if #available(iOS 11.0, watchOS 4, tvOS 10, macOS 13, *) {
                return formatIsoDate(format: format, timeZone: timeZone)
            } else {
                useLocale = Locale(identifier: "en_US_POSIX")
            }
        default:
            break
        }
        let formatter = Date.cachedDateFormatters.cachedFormatter(format.stringFormat, timeZone: timeZone.timeZone, locale: useLocale)
        return formatter.string(from: self)
    }
    
    /// Converts to ISO format using the new API
    @available(iOS 11.0, watchOS 4, tvOS 10, macOS 13, *)
    func formatIsoDate(format: DateFormatType, timeZone: TimeZoneType = .local) -> String {
        let formatter = ISO8601DateFormatter()
        formatter.timeZone = timeZone.timeZone
        
        var options: ISO8601DateFormatter.Options = []
        switch format {
        case .isoDate:
            options = [.withFullDate]
        case .isoYearMonth:
{'fraction_non_alphanumeric': 0.09695487535500158, 'fraction_numerical': 0.012701167560744714, 'mean_word_length': 2.0694915254237287, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4192659', 'n_tokens_mistral': 3560, 'n_tokens_neox': 3360, 'n_words': 878}