SwiftDate:Swift日期处理的终极工具包

SwiftDate:Swift日期处理的终极工具包

【免费下载链接】SwiftDate 🐔 Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift. 【免费下载链接】SwiftDate 项目地址: https://gitcode.com/gh_mirrors/sw/SwiftDate

SwiftDate是一个功能强大的Swift日期和时间处理工具包,专门为解决iOS、macOS、watchOS、tvOS以及Linux平台上的日期时间处理难题而设计。作为Swift生态系统中下载量超过300万次的流行库,它提供了从简单日期操作到复杂业务逻辑处理的完整解决方案。

SwiftDate项目概述与核心价值

SwiftDate是一个功能强大的Swift日期和时间处理工具包,专门为解决iOS、macOS、watchOS、tvOS以及Linux平台上的日期时间处理难题而设计。作为Swift生态系统中下载量超过300万次的流行库,它提供了从简单日期操作到复杂业务逻辑处理的完整解决方案。

项目架构设计理念

SwiftDate采用模块化的架构设计,核心围绕DateDateInRegion两个主要类型构建:

mermaid

这种设计允许开发者在处理日期时明确区分时区、日历和区域设置,确保日期操作的准确性和一致性。

核心功能特性矩阵

SwiftDate提供了一系列强大的日期处理功能,下表展示了其主要功能模块:

功能类别核心特性应用场景
日期解析支持15+种自动识别格式(ISO8601、RSS、.NET等)多格式日期字符串转换
日期操作自然语言时间单位运算(3.months + 2.days日期加减计算
日期比较20+种粒度比较函数(.isToday, .isSameWeek日期关系判断
区域支持完整的时区、日历、区域设置转换国际化应用开发
衍生日期20+种相关日期生成(.nextWeekday, .endOfMonth业务日期计算
组件提取区域感知的时间组件获取日期信息展示
格式化自定义格式、相对时间格式化(140+语言)用户界面显示
Codable支持完整的编码解码支持数据持久化

技术实现优势

SwiftDate在技术实现上具有以下显著优势:

1. 线程安全的格式化机制

// 使用线程本地存储优化日期格式化性能
public var customFormatter: DateFormatter? {
    get {
        let formatter: DateFormatter? = getAssociatedValue(key: AssociatedKeys.customDateFormatter.rawValue, object: self as AnyObject)
        return formatter
    }
    set {
        set(associatedValue: newValue, key: AssociatedKeys.customDateFormatter.rawValue, object: self as AnyObject)
    }
}

2. 统一的区域处理模型

// Region类型封装了日历、时区和区域设置
public struct Region {
    public let calendar: Calendar
    public let timeZone: TimeZone
    public let locale: Locale
}

3. 扩展的日期操作语法

// 自然语言式的时间操作
let futureDate = Date() + 3.hours + 15.minutes
let duration = (date2 - date1).inDays

多平台兼容性支持

SwiftDate具备出色的跨平台兼容性:

平台支持版本特性完整性
iOS9.0+完整支持
macOS10.10+完整支持
watchOS2.0+完整支持
tvOS10.0+完整支持
LinuxSwift 4+基本功能支持

开发体验优化

SwiftDate通过以下方式显著提升开发体验:

智能日期解析

// 自动识别多种日期格式
let date1 = "2010-05-20 15:30:00".toDate() // ISO格式
let date2 = "19 Nov 2015 22:20:40 +0100".toRSS(alt: true) // RSS格式
let date3 = "2017-09-17T11:59:29+02:00".toISODate() // ISO8601带时区

丰富的比较操作

// 多种粒度的时间比较
let isSameHour = dateA.compare(toDate: dateB, granularity: .hour) == .orderedSame
let isInRange = dateC.isInRange(date: dateA, and: dateB, granularity: .day)
let isNextWeek = date.compare(.isNextWeek)

完整的国际化支持

// 多语言相对时间格式化
let relativeTime = (Date() - 3.minutes).toRelative(style: .twitterStyle(), locale: .italian)
// 输出: "3 min fa"

SwiftDate的核心价值在于它将复杂的日期时间处理抽象为简单直观的API,同时保持了高度的灵活性和准确性。无论是处理简单的日期显示还是复杂的跨时区业务逻辑,SwiftDate都能提供优雅的解决方案,大大减少了开发者在日期处理上的心智负担和潜在错误。

项目架构与主要组件解析

SwiftDate采用模块化架构设计,将复杂的日期时间处理功能分解为多个职责清晰的组件。整个库围绕核心的DateInRegionRegion概念构建,提供了从基础日期操作到高级时间区间管理的完整解决方案。

核心架构设计

SwiftDate的架构采用分层设计,主要分为以下几个层次:

mermaid

主要组件详解

1. DateInRegion - 区域化日期核心

DateInRegion是SwiftDate的核心组件,它将日期与特定的区域信息(时区、日历、语言环境)绑定在一起,解决了原生Date类型缺乏上下文信息的问题。

// DateInRegion 结构定义示例
public struct DateInRegion: DateRepresentable {
    public let region: Region
    public let absoluteDate: Date
    // 丰富的属性和方法...
}

主要功能特性:

  • 区域感知:每个日期都关联特定的时区、日历和语言环境
  • 组件提取:提供year、month、day等属性,自动处理区域转换
  • 数学运算:支持日期加减、比较等操作
  • 格式化输出:根据区域设置自动格式化日期字符串
2. Region - 区域配置管理

Region组件定义了日期处理的上下文环境,包含三个核心要素:

组件作用示例值
TimeZone时区配置Zones.europeRome
Calendar日历系统Calendars.gregorian
Locale语言环境Locales.english
// Region 创建示例
let romeRegion = Region(
    calendar: Calendars.gregorian,
    zone: Zones.europeRome, 
    locale: Locales.italian
)
3. 支持系统组件

SwiftDate提供了丰富的支持组件来简化日期操作:

TimeUnit扩展

// 时间单位字面量支持
let threeHours = 3.hours
let fiveDays = 5.days
let interval = 2.hours + 30.minutes

预定义区域常量

// 内置时区、日历、语言环境常量
Zones.americaNewYork
Calendars.chinese
Locales.japanese
4. 格式化器组件

SwiftDate包含多种日期格式化器,支持主流日期格式:

格式化器类型支持格式主要方法
ISOFormatterISO8601格式toISODate()
DotNetFormatter.NET日期格式toDotNET()
RSSFormatterRSS/AltRSS格式toRSS()
SQLFormatterSQL日期格式toSQL()
// 格式化使用示例
let date = DateInRegion()
let isoString = date.toISODate()  // "2023-11-15T10:30:45+01:00"
let dotNetString = date.toDotNET() // "/Date(1700044245000+0100)/"
5. TimePeriod时间区间管理

SwiftDate提供了强大的时间区间管理功能,支持时间区间的创建、比较和操作:

mermaid

6. 扩展工具组件

Foundation扩展

  • DateComponents+Extras: 增强的日期组件操作
  • String+Parser: 字符串到日期的解析功能
  • TimeInterval+Formatter: 时间间隔格式化
  • Int+DateComponents: 整型到时间单位的转换

支持工具

  • AssociatedValues: 关联值管理
  • Calendars: 日历系统支持
  • Locales: 语言环境管理
  • Zones: 时区配置

架构优势分析

SwiftDate的架构设计具有以下显著优势:

  1. 模块化设计:各组件职责单一,易于维护和扩展
  2. 区域感知:彻底解决时区、日历、语言环境问题
  3. 类型安全:丰富的类型系统防止错误使用
  4. 性能优化:高效的日期计算和缓存机制
  5. 可扩展性:易于添加新的格式化器和功能组件

通过这种架构设计,SwiftDate能够提供强大而灵活的日期时间处理能力,同时保持代码的清晰性和可维护性。

日期解析与格式化功能详解

SwiftDate提供了强大而灵活的日期解析和格式化功能,让开发者能够轻松处理各种日期字符串格式,并将日期对象转换为所需的字符串表示。本节将深入探讨SwiftDate在日期解析和格式化方面的核心功能。

日期解析:从字符串到日期对象

SwiftDate支持多种日期字符串格式的自动解析,包括ISO8601、RSS、.NET、SQL等标准格式,同时也支持自定义格式。

自动格式识别

SwiftDate内置了20多种常见的日期格式,可以自动识别并解析:

// 自动识别多种格式
let date1 = "2018-01-01 15:00".toDate()
let date2 = "15:40:50".toDate("HH:mm:ss")
let date3 = "2015-01-01 at 14".toDate("yyyy-MM-dd 'at' HH", region: romeRegion)

SwiftDate的自动解析机制通过DateFormats.autoFormats数组实现,该数组包含了所有支持的格式,按优先级顺序进行匹配尝试。

支持的标准格式

SwiftDate支持以下标准日期格式的解析:

格式类型示例解析方法
ISO8601"2017-09-17T11:59:29+02:00".toISODate()
RSS"Sat, 22 Jul 2017 18:27:02 +0200".toRSSDate(alt: false)
Alt RSS"22 Jul 2017 18:27:02 +0200".toRSSDate(alt: true)
.NET"/Date(1500740822000+0200)/".toDotNETDate()
SQL"2016-04-14T11:58:58.000+02".toSQLDate()
区域设置的重要性

在解析日期时,区域设置(Region)至关重要,特别是当日期字符串包含本地化的月份或星期名称时:

let italianRegion = Region(calendar: .gregorian, zone: .europeRome, locale: .italian)
let englishRegion = Region(calendar: .gregorian, zone: .europeRome, locale: .english)

let dateString = "July 15 - 15:30"

// 使用英语区域解析成功
let englishDate = dateString.toDate(["yyyy-MM-dd","MMM dd '-' HH:mm"], region: englishRegion)

// 使用意大利语区域解析失败(因为"July"是英语月份名称)
let italianDate = dateString.toDate(["yyyy-MM-dd","MMM dd '-' HH:mm"], region: italianRegion)

日期格式化:从日期对象到字符串

SwiftDate提供了丰富的格式化选项,可以将日期对象转换为各种格式的字符串表示。

自定义格式格式化

使用toFormat()方法可以按照自定义格式输出日期字符串:

let romeRegion = Region(calendar: .gregorian, zone: .europeRome, locale: .italian)
let date = DateInRegion(year: 2015, month: 1, day: 15, hour: 20, minute: 0, second: 5, region: romeRegion)

// 自定义格式输出
let formatted = date.toFormat("MMM dd yyyy", locale: Locales.english) // "Jan 15 2015"
标准格式输出

SwiftDate支持多种标准格式的输出:

// ISO8601格式
let isoString = date.toISO() // "2017-07-22T00:00:00+02:00"

// RSS格式
let rssString = date.toRSS(alt: false) // "Tue, 20 Jun 2017 14:49:19 +0200"

// .NET格式
let dotNetString = date.toDotNET() // "/Date(1497962959000+0200)/"

// SQL格式
let sqlString = date.toSQL() // "2015-11-19T22:20:40.000+01"
样式化输出

SwiftDate提供了预定义的样式来格式化日期:

// 使用预定义样式
let dateStyle = date.toString(.date(.full)) // "Monday, February 27, 2017"
let timeStyle = date.toString(.time(.medium)) // "2:22:06 PM"
let dateTimeStyle = date.toString(.dateTime(.long)) // "February 27, 2017 at 2:22:06 PM EST"

// 混合样式
let mixedStyle = date.toString(.dateTimeMixed(dateStyle: .full, timeStyle: .short))
// "Monday, February 27, 2017 at 2:22 PM"

ISO8601格式的高级支持

SwiftDate对ISO8601格式提供了特别强大的支持,包括多种变体和选项:

// ISO8601格式化选项
let options: ISOFormatter.Options = [.withInternetDateTimeExtended]
let isoExtended = date.toISO(options) // "2017-07-22T00:00:00.000+02:00"

// 自定义ISO选项
let customOptions: ISOFormatter.Options = [.withYear, .withMonth, .withDay, .withSpaceBetweenDateAndTime]
let customISO = date.toISO(customOptions) // "2017-07-22 00:00:00+02:00"

ISO8601选项支持包括:

  • withYearwithMonthwithDay:包含年、月、日
  • withTimewithTimeZone:包含时间和时区
  • withInternetDateTime:RFC 3339标准格式
  • withInternetDateTimeExtended:包含毫秒的扩展格式
  • withSpaceBetweenDateAndTime:用空格代替'T'分隔符

相对时间格式化

SwiftDate支持将日期格式化为相对时间表达,如"3分钟前"、"2天前"等:

let fiveMinutesAgo = DateInRegion() - 5.minutes
let relativeString = fiveMinutesAgo.toRelative(style: .default) // "5 minutes ago"

// Twitter风格的相对时间
let twitterStyle = fiveMinutesAgo.toRelative(style: .twitter) // "5m"

// 支持多语言
let italianRelative = fiveMinutesAgo.toRelative(style: .default, locale: .italian) // "5 minuti fa"

格式化流程解析

SwiftDate的格式化过程遵循清晰的流程:

mermaid

性能优化建议

  1. 明确指定格式:如果知道输入格式,明确指定可以显著提高解析性能
  2. 重用Region对象:避免频繁创建Region实例
  3. 使用线程共享的Formatter:SwiftDate自动使用线程共享的DateFormatter实例
  4. 预定义格式列表:可以自定义SwiftDate.autoFormats来优化自动解析顺序

实用示例代码

// 解析各种日期字符串
let dates = [
    "2023-12-25T14:30:00+08:00", // ISO8601
    "25 Dec 2023 14:30:00 +0800", // RSS
    "/Date(1703485800000+0800)/", // .NET
    "2023-12-25T14:30:00.000+08"  // SQL
]

for dateString in dates {
    if let date = dateString.toDate() {
        print("解析成功: \(date.toFormat("yyyy-MM-dd HH:mm:ss"))")
    }
}

// 多语言格式化示例
let sampleDate = DateInRegion("2023-12-25 14:30:00", region: Region.UTC)!
let locales = [Locales.english, Locales.chinese, Locales.japanese, Locales.german]

for locale in locales {
    let formatted = sampleDate.toFormat("yyyy MMMM dd EEEE", locale: locale)
    print("\(locale.identifier): \(formatted)")
}

通过SwiftDate的强大解析和格式化功能,开发者可以轻松处理各种日期字符串格式,实现多语言支持,并确保日期处理的准确性和一致性。

实际应用场景与最佳实践

SwiftDate作为Swift生态中最强大的日期时间处理库,在实际开发中有着广泛的应用场景。通过合理的实践方法,可以充分发挥其强大功能,同时避免常见的陷阱和性能问题。

国际化应用中的日期处理

在全球化应用中,正确处理不同时区、语言和日历系统至关重要。SwiftDate的Region机制为此提供了完美解决方案。

// 定义不同地区的区域配置
let shanghaiRegion = Region(
    calendar: Calendars.gregorian,
    zone: Zones.asiaShanghai, 
    locale: Locales.chineseChina
)

let newYorkRegion = Region(
    calendar: Calendars.gregorian,
    zone: Zones.americaNewYork,
    locale: Locales.englishUnitedStates
)

let tokyoRegion = Region(
    calendar: Calendars.gregorian, 
    zone: Zones.asiaTokyo,
    locale: Locales.japanese
)

// 创建基于特定区域的日期
let meetingTime = DateInRegion("2024-03-15 14:30:00", region: shanghaiRegion)!

// 转换为其他时区显示
let nyTime = meetingTime.convertTo(region: newYorkRegion)
let tokyoTime = meetingTime.convertTo(region: tokyoRegion)

print("上海: \(meetingTime.toFormat("yyyy-MM-dd HH:mm:ss"))")
print("纽约: \(nyTime.toFormat("yyyy-MM-dd HH:mm:ss"))") 
print("东京: \(tokyoTime.toFormat("yyyy-MM-dd HH:mm:ss"))")

企业级业务逻辑处理

在企业应用中,经常需要处理复杂的日期计算和业务规则。

// 计算项目截止日期
func calculateProjectDeadline(startDate: DateInRegion, durationInWeeks: Int) -> DateInRegion {
    return startDate + durationInWeeks.weeks
}

// 检查工作日(排除周末)
func isWorkday(_ date: DateInRegion) -> Bool {
    return !date.isWeekend
}

// 获取下一个工作日
func nextWorkday(after date: DateInRegion) -> DateInRegion {
    var nextDate = date + 1.days
    while nextDate.isWeekend {
        nextDate = nextDate + 1.days
    }
    return nextDate
}

// 计算两个日期之间的工作日数量
func workdaysBetween(_ start: DateInRegion, _ end: DateInRegion) -> Int {
    var count = 0
    var current = start
    while current <= end {
        if isWorkday(current) {
            count += 1
        }
        current = current + 1.days
    }
    return count
}

金融和计费系统应用

在金融领域,精确的日期计算对于利息计算、账单周期等至关重要。

// 计算信用卡账单周期
func calculateBillingCycle(for date: DateInRegion, cycleDay: Int) -> (start: DateInRegion, end: DateInRegion) {
    let calendar = date.region.calendar
    
    // 获取当前月的账单日开始日期
    let cycleStart = date.dateAt(.startOfMonth) + (cycleDay - 1).days
    
    // 如果当前日期早于账单日,则周期从上个月开始
    let actualStart = date < cycleStart ? 
        cycleStart - 1.months : cycleStart
    
    // 周期结束是下个账单日前一天
    let cycleEnd = actualStart + 1.months - 1.days
    
    return (actualStart, cycleEnd)
}

// 计算日利息
func calculateDailyInterest(principal: Double, annualRate: Double, 
                           startDate: DateInRegion, endDate: DateInRegion) -> Double {
    let days = endDate.getInterval(toDate: startDate, component: .day)
    let dailyRate = annualRate / 365.0
    return principal * dailyRate * Double(days)
}

社交媒体和时间显示优化

在社交应用中,友好的时间显示能够显著提升用户体验。

// 智能相对时间显示
func smartRelativeTimeString(for date: DateInRegion) -> String {
    let now = DateInRegion()
    let diff = now - date
    
    if diff < 1.minutes {
        return "刚刚"
    } else if diff < 1.hours {
        let minutes = diff.in(.minute)!
        return "\(minutes)分钟前"
    } else if date.isToday {
        let hours = diff.in(.hour)!
        return "\(hours)小时前"
    } else if date.isYesterday {
        return "昨天 \(date.toFormat("HH:mm"))"
    } else if date.isThisYear {
        return date.toFormat("MM-dd HH:mm")
    } else {
        return date.toFormat("yyyy-MM-dd")
    }
}

// 聊天消息时间分组
func shouldGroupWithPreviousMessage(current: DateInRegion, previous: DateInRegion) -> Bool {
    return current.getInterval(toDate: previous, component: .minute) < 5
}

数据分析和报表生成

在大数据处理中,日期分组和聚合是常见需求。

// 按时间维度分组数据
enum TimeGrouping {
    case hourly, daily, weekly, monthly, quarterly, yearly
}

func groupDataByTime<T>(_ data: [(date: DateInRegion, value: T)], 
                       grouping: TimeGrouping) -> [String: [T]] {
    var result: [String: [T]] = [:]
    
    for item in data {
        let key: String
        
        switch grouping {
        case .hourly:
            key = item.date.toFormat("yyyy-MM-dd HH:00")
        case .daily:
            key = item.date.toFormat("yyyy-MM-dd")
        case .weekly:
            key = "\(item.date.year)-W\(item.date.weekOfYear)"
        case .monthly:
            key = item.date.toFormat("yyyy-MM")
        case .quarterly:
            let quarter = (item.date.month - 1) / 3 + 1
            key = "\(item.date.year)-Q\(quarter)"
        case .yearly:
            key = "\(item.date.year)"
        }
        
        result[key, default: []].append(item.value)
    }
    
    return result
}

// 生成时间序列
func generateTimeSeries(start: DateInRegion, end: DateInRegion, 
                       interval: DateComponents) -> [DateInRegion] {
    return DateInRegion.enumerateDates(from: start, to: end, increment: interval)
}

性能优化最佳实践

在处理大量日期操作时,性能考虑至关重要。

// 重用DateFormatter实例(重要性能优化)
class DateFormatterCache {
    static let shared = DateFormatterCache()
    private var formatters: [String: DateFormatter] = [:]
    
    func formatter(for format: String, region: Region) -> DateFormatter {
        let key = "\(format)-\(region.description)"
        if let formatter = formatters[key] {
            return formatter
        }
        
        let formatter = DateFormatter()
        formatter.dateFormat = format
        formatter.timeZone = region.timeZone
        formatter.locale = region.locale
        formatters[key] = formatter
        
        return formatter
    }
}

// 批量日期处理优化
func processDatesInBatch(_ dates: [DateInRegion], 
                        processor: (DateInRegion) -> Void) {
    // 预先设置默认区域避免重复配置
    let originalRegion = SwiftDate.defaultRegion
    SwiftDate.defaultRegion = dates.first?.region ?? Region.UTC
    
    dates.forEach(processor)
    
    // 恢复原始设置
    SwiftDate.defaultRegion = originalRegion
}

// 使用值类型避免不必要的拷贝
func efficientDateManipulation() {
    var dates = [DateInRegion]()
    
    // 使用map和lazy优化大规模操作
    let processedDates = dates.lazy.map { date in
        return date + 1.days
    }
    
    // 使用filter进行高效筛选
    let workdays = dates.filter { !$0.isWeekend }
}

错误处理和边界情况

健壮的日期处理需要考虑各种边界情况。

// 安全的日期解析
func safeDateParse(_ string: String, 
                  formats: [String] = DateFormats.builtInAutoFormats,
                  region: Region = SwiftDate.defaultRegion) -> DateInRegion? {
    
    for format in formats {
        if let date = DateInRegion(string, format: format, region: region) {
            return date
        }
    }
    
    // 尝试自动解析
    return DateInRegion(string, region: region)
}

// 处理闰年和时区转换
func handleLeapYearAndDST(_ date: DateInRegion) -> DateInRegion {
    // 检查是否是闰年
    if date.region.calendar.isDateInLeapYear(date.date) {
        // 特殊处理闰年逻辑
    }
    
    // 处理夏令时转换
    if date.region.timeZone.isDaylightSavingTime(for: date.date) {
        // 调整夏令时逻辑
    }
    
    return date
}

// 验证日期范围
func validateDateRange(_ date: DateInRegion, 
                      min: DateInRegion? = nil, 
                      max: DateInRegion? = nil) -> Bool {
    if let minDate = min, date < minDate {
        return false
    }
    if let maxDate = max, date > maxDate {
        return false
    }
    return true
}

测试策略和Mock数据

完善的测试是保证日期处理正确性的关键。

// 日期相关的单元测试工具
class DateTestingUtils {
    // 创建固定的测试日期
    static func createTestDate(year: Int, month: Int, day: Int, 
                              hour: Int = 0, minute: Int = 0,
                              region: Region = Region.UTC) -> DateInRegion {
        return DateInRegion(
            year: year, month: month, day: day,
            hour: hour, minute: minute,
            region: region
        )
    }
    
    // 生成测试日期范围
    static func generateTestDateRange(count: Int, 
                                     start: DateInRegion,
                                     interval: DateComponents) -> [DateInRegion] {
        var dates = [start]
        for i in 1..<count {
            dates.append(dates[i-1] + interval)
        }
        return dates
    }
    
    // 验证日期相等性(考虑精度)
    static func areDatesEqual(_ date1: DateInRegion, 
                             _ date2: DateInRegion,
                             granularity: Calendar.Component = .second) -> Bool {
        return date1.compare(toDate: date2, granularity: granularity) == .orderedSame
    }
}

// 模拟时间源用于测试
protocol TimeSource {
    func now() -> DateInRegion
}

class TestTimeSource: TimeSource {
    private var currentTime: DateInRegion
    
    init(initialTime: DateInRegion) {
        self.currentTime = initialTime
    }
    
    func now() -> DateInRegion {
        return currentTime
    }
    
    func advance(by components: DateComponents) {
        currentTime = currentTime + components
    }
}

通过上述实际应用场景和最佳实践,开发者可以充分利用SwiftDate的强大功能,构建出健壮、高效且易于维护的日期时间处理逻辑。这些实践涵盖了从基础操作到高级业务逻辑的各个方面,为各种类型的应用提供了可靠的日期处理解决方案。

总结

通过SwiftDate的强大解析和格式化功能,开发者可以轻松处理各种日期字符串格式,实现多语言支持,并确保日期处理的准确性和一致性。SwiftDate的核心价值在于它将复杂的日期时间处理抽象为简单直观的API,同时保持了高度的灵活性和准确性。无论是处理简单的日期显示还是复杂的跨时区业务逻辑,SwiftDate都能提供优雅的解决方案,大大减少了开发者在日期处理上的心智负担和潜在错误。

【免费下载链接】SwiftDate 🐔 Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift. 【免费下载链接】SwiftDate 项目地址: https://gitcode.com/gh_mirrors/sw/SwiftDate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值