mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-11 22:55:55 +05:45
137 lines
4.7 KiB
Swift
137 lines
4.7 KiB
Swift
//
|
|
// Copyright RevenueCat Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the MIT License (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://opensource.org/licenses/MIT
|
|
//
|
|
// ISODurationFormatter.swift
|
|
//
|
|
// Created by Facundo Menzella on 10/2/25.
|
|
|
|
import Foundation
|
|
|
|
/// A representation of an ISO 8601 duration.
|
|
///
|
|
/// This struct represents both date and time-based components of an ISO 8601 duration string.
|
|
/// ISO 8601 durations use the format `PnYnMnWnDTnHnMnS`, where each part is optional:
|
|
/// - `P` indicates the duration starts.
|
|
/// - `nY` for years.
|
|
/// - `nM` for months.
|
|
/// - `nW` for weeks.
|
|
/// - `nD` for days.
|
|
/// - `T` separates the date part from the time part.
|
|
/// - `nH` for hours.
|
|
/// - `nM` for minutes.
|
|
/// - `nS` for seconds.
|
|
///
|
|
/// Example duration strings:
|
|
/// - `"P1Y2M3DT4H5M6S"`: 1 year, 2 months, 3 days, 4 hours, 5 minutes, 6 seconds.
|
|
/// - `"P3W"`: 3 weeks.
|
|
/// - `"PT15M"`: 15 minutes.
|
|
public struct ISODuration: Equatable {
|
|
/// The number of years in the duration.
|
|
///
|
|
/// Example: For `"P1Y"`, this will be `1`.
|
|
public let years: Int
|
|
|
|
/// The number of months in the duration.
|
|
///
|
|
/// Example: For `"P2M"`, this will be `2`.
|
|
public let months: Int
|
|
|
|
/// The number of weeks in the duration.
|
|
///
|
|
/// Example: For `"P3W"`, this will be `3`.
|
|
public let weeks: Int
|
|
|
|
/// The number of days in the duration.
|
|
///
|
|
/// Example: For `"P4D"`, this will be `4`.
|
|
public let days: Int
|
|
|
|
/// The number of hours in the duration.
|
|
///
|
|
/// Example: For `"PT5H"`, this will be `5`.
|
|
public let hours: Int
|
|
|
|
/// The number of minutes in the duration.
|
|
///
|
|
/// Example: For `"PT6M"`, this will be `6`.
|
|
public let minutes: Int
|
|
|
|
/// The number of seconds in the duration.
|
|
///
|
|
/// Example: For `"PT7S"`, this will be `7`.
|
|
public let seconds: Int
|
|
}
|
|
|
|
@available(iOS 11.2, macOS 10.13.2, tvOS 11.2, *)
|
|
enum ISODurationFormatter {
|
|
|
|
// swiftlint:disable:next line_length
|
|
static let pattern = #"([-+]?)P(?:([-+]?\d+)Y)?(?:([-+]?\d+)M)?(?:([-+]?\d+)W)?(?:([-+]?\d+)D)?(?:T(?:([-+]?\d+)H)?(?:([-+]?\d+)M)?(?:([-+]?\d+)S)?)?"#
|
|
|
|
/// Parses an ISO 8601 duration string and returns an `ISODuration` object.
|
|
static func parse(from periodString: String) -> ISODuration? {
|
|
guard let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) else {
|
|
return nil
|
|
}
|
|
|
|
let nsString = periodString as NSString
|
|
let match = regex.firstMatch(
|
|
in: periodString,
|
|
options: [],
|
|
range: NSRange(location: 0, length: nsString.length))
|
|
|
|
guard let match = match else {
|
|
print("Failed to parse ISO duration: \(periodString)")
|
|
return nil
|
|
}
|
|
|
|
let negate = nsString.substring(with: match.range(at: 1)) == "-" ? -1 : 1
|
|
|
|
let years = getIntValue(from: nsString, match: match, at: 2) * negate
|
|
let months = getIntValue(from: nsString, match: match, at: 3) * negate
|
|
let weeks = getIntValue(from: nsString, match: match, at: 4) * negate
|
|
let days = getIntValue(from: nsString, match: match, at: 5) * negate
|
|
let hours = getIntValue(from: nsString, match: match, at: 6) * negate
|
|
let minutes = getIntValue(from: nsString, match: match, at: 7) * negate
|
|
let seconds = getIntValue(from: nsString, match: match, at: 8) * negate
|
|
|
|
return ISODuration(
|
|
years: years,
|
|
months: months,
|
|
weeks: weeks,
|
|
days: days,
|
|
hours: hours,
|
|
minutes: minutes,
|
|
seconds: seconds)
|
|
}
|
|
|
|
/// Converts an `ISODuration` object back to an ISO 8601 duration string.
|
|
static func string(from duration: ISODuration) -> String {
|
|
var result = "P"
|
|
if duration.years != 0 { result += "\(duration.years)Y" }
|
|
if duration.months != 0 { result += "\(duration.months)M" }
|
|
if duration.weeks != 0 { result += "\(duration.weeks)W" }
|
|
if duration.days != 0 { result += "\(duration.days)D" }
|
|
if duration.hours != 0 || duration.minutes != 0 || duration.seconds != 0 {
|
|
result += "T"
|
|
if duration.hours != 0 { result += "\(duration.hours)H" }
|
|
if duration.minutes != 0 { result += "\(duration.minutes)M" }
|
|
if duration.seconds != 0 { result += "\(duration.seconds)S" }
|
|
}
|
|
return result
|
|
}
|
|
|
|
private static func getIntValue(from nsString: NSString, match: NSTextCheckingResult, at index: Int) -> Int {
|
|
guard match.range(at: index).location != NSNotFound else {
|
|
return 0
|
|
}
|
|
return Int(nsString.substring(with: match.range(at: index))) ?? 0
|
|
}
|
|
}
|