Showing preview only (685K chars total). Download the full file or copy to clipboard to get everything.
Repository: malcommac/SwiftDate
Branch: master
Commit: 5d943224c3bb
Files: 93
Total size: 651.0 KB
Directory structure:
gitextract_nwg7vbkr/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── .sourcery/
│ └── LinuxMain.stencil
├── .swiftlint.yml
├── .swiftpm/
│ └── xcode/
│ └── package.xcworkspace/
│ └── contents.xcworkspacedata
├── .travis.yml
├── Configs/
│ ├── SwiftDate.plist
│ └── SwiftDateTests.plist
├── Documentation/
│ ├── 0.Informations.md
│ ├── 1.Introduction.md
│ ├── 10.Upgrading_SwiftDate4.md
│ ├── 11.Related_Projects.md
│ ├── 12.Timer_Periods.md
│ ├── 2.Date_Parsing.md
│ ├── 3.Manipulate_Date.md
│ ├── 4.Compare_Dates.md
│ ├── 5.Date_Formatting.md
│ ├── 6.TimeInterval_Formatting.md
│ ├── 7.Format_UnicodeTable.md
│ ├── 8.Customize_ColloquialFormatter.md
│ ├── 9.ColloquialSupportedLanguages.md
│ └── Index.md
├── LICENSE
├── Package.swift
├── Playgrounds/
│ └── SwiftDate.playground/
│ ├── Contents.o
│ ├── Pages/
│ │ ├── Compare Dates.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── Date Formatting.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── Date Manipulation.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── Date Parsing.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ └── Introduction.xcplaygroundpage/
│ │ └── Contents.swift
│ └── contents.xcplayground
├── README.md
├── Sources/
│ └── SwiftDate/
│ ├── Date/
│ │ ├── Date+Compare.swift
│ │ ├── Date+Components.swift
│ │ ├── Date+Create.swift
│ │ ├── Date+Math.swift
│ │ └── Date.swift
│ ├── DateInRegion/
│ │ ├── DateInRegion+Compare.swift
│ │ ├── DateInRegion+Components.swift
│ │ ├── DateInRegion+Create.swift
│ │ ├── DateInRegion+Math.swift
│ │ ├── DateInRegion.swift
│ │ └── Region.swift
│ ├── DateRepresentable.swift
│ ├── Formatters/
│ │ ├── DotNetParserFormatter.swift
│ │ ├── Formatter+Protocols.swift
│ │ ├── ISOFormatter.swift
│ │ └── ISOParser.swift
│ ├── Foundation+Extras/
│ │ ├── DateComponents+Extras.swift
│ │ ├── Int+DateComponents.swift
│ │ ├── String+Parser.swift
│ │ └── TimeInterval+Formatter.swift
│ ├── Supports/
│ │ ├── AssociatedValues.swift
│ │ ├── Calendars.swift
│ │ ├── Commons.swift
│ │ ├── Locales.swift
│ │ ├── TimeStructures.swift
│ │ └── Zones.swift
│ ├── SwiftDate.swift
│ └── TimePeriod/
│ ├── Groups/
│ │ ├── TimePeriodChain.swift
│ │ ├── TimePeriodCollection.swift
│ │ └── TimePeriodGroup.swift
│ ├── TimePeriod+Support.swift
│ ├── TimePeriod.swift
│ └── TimePeriodProtocol.swift
├── SwiftDate.podspec
├── SwiftDate.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata/
│ └── xcschemes/
│ ├── SwiftDate-iOS Tests.xcscheme
│ ├── SwiftDate-iOS.xcscheme
│ ├── SwiftDate-macOS.xcscheme
│ ├── SwiftDate-tvOS.xcscheme
│ └── SwiftDate-watchOS.xcscheme
├── TestApplication/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── ViewController.swift
├── Tests/
│ ├── LinuxMain.swift
│ └── SwiftDateTests/
│ ├── TestDataStructures.swift
│ ├── TestDate.swift
│ ├── TestDateInRegion+Compare.swift
│ ├── TestDateInRegion+Components.swift
│ ├── TestDateInRegion+Create.swift
│ ├── TestDateInRegion+Math.swift
│ ├── TestDateInRegion.swift
│ ├── TestFormatters.swift
│ ├── TestRegion.swift
│ └── TestSwiftDate.swift
└── generateLinuxTests.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
custom: https://www.paypal.com/paypalme2/danielemargutti
================================================
FILE: .gitignore
================================================
*~
.DS_Store
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
.build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
.build/
# Bundler
.bundle
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
# Carthage/Build
Carthage
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
fastlane/report.xml
fastlane/screenshots
.DS_
================================================
FILE: .sourcery/LinuxMain.stencil
================================================
@testable import SwiftDateTests
import XCTest
// swiftlint:disable line_length file_length
{% for type in types.classes|based:"XCTestCase" %}
extension {{ type.name }} {
static var allTests: [(String, ({{ type.name }}) -> () throws -> Void)] = [
{% for method in type.methods where method.parameters.count == 0 and method.shortName|hasPrefix:"test" and method|!annotated:"skipTestOnLinux" %} ("{{ method.shortName }}", {{ method.shortName }}){% if not forloop.last %},{% endif %}
{% endfor %}]
}
{% endfor %}
XCTMain([
{% for type in types.classes|based:"XCTestCase" %} testCase({{ type.name }}.allTests){% if not forloop.last %},{% endif %}
{% endfor %}])
================================================
FILE: .swiftlint.yml
================================================
disabled_rules: # rule identifiers to exclude from running
- line_length
- function_body_length
- cyclomatic_complexity
- multiple_closures_with_trailing_closure
- xctfail_message
- vertical_parameter_alignment
- identifier_name
# Swift 3 rules that do not make sense for Swift 2.3
- implicit_getter
identifier_name:
allowed_symbols: "_"
min_length:
warning: 1
error: 1
max_length:
warning: 60
error: 80
excluded:
- id
type_name:
allowed_symbols: "_"
max_length:
warning: 60
error: 100
type_body_length:
warning: 400
error: 500
file_length:
warning: 700
error: 800
excluded:
- Unit Tests/GeneratedCode
- Pods
function_parameter_count:
warning: 7
error: 10
large_tuple:
warning: 3
opt_in_rules: # some rules are only opt-in
- closure_end_indentation
- closure_spacing
- syntactic_sugar
- redundant_nil_coalescing
- number_separator
- sorted_imports
- overridden_super_call
- object_literal
- explicit_init
- first_where
- operator_usage_whitespace
number_separator:
minimum_length: 7
custom_rules:
double_space: # from https://github.com/IBM-Swift/Package-Builder
include: "*.swift"
name: "Double space"
regex: '([a-z,A-Z] \s+)'
message: "Double space between keywords"
match_kinds: keyword
severity: warning
comments_space: # from https://github.com/brandenr/swiftlintconfig
name: "Space After Comment"
regex: '(^ *//\w+)'
message: "There should be a space after //"
severity: warning
empty_line_after_guard: # from https://github.com/brandenr/swiftlintconfig
name: "Empty Line After Guard"
regex: '(^ *guard[ a-zA-Z0-9=?.\(\),><!]*\{[ a-zA-Z0-9=?.\(\),><!]*\}\n *(?!(?:return|guard))\S+)'
message: "There should be an empty line after a guard"
severity: warning
unnecessary_type: # from https://github.com/brandenr/swiftlintconfig
name: "Unnecessary Type"
regex: '[ a-zA-Z0-9]*(?:let|var) [ a-zA-Z0-9]*: ([a-zA-Z0-9]*)[ ]*= \1'
message: "Type Definition Not Needed"
severity: warning
================================================
FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: .travis.yml
================================================
language: objective-c
osx_image: xcode10.2
env:
global:
- LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8
- PROJECT=SwiftDate.xcodeproj
- IOS_FRAMEWORK_SCHEME="SwiftDate-iOS"
- MACOS_FRAMEWORK_SCHEME="SwiftDate-macOS"
- TVOS_FRAMEWORK_SCHEME="SwiftDate-tvOS"
- WATCHOS_FRAMEWORK_SCHEME="SwiftDate-watchOS"
matrix:
- DESTINATION="OS=12.2,name=iPhone X" SCHEME="$IOS_FRAMEWORK_SCHEME"
- DESTINATION="OS=12.2,name=Apple TV 4K" SCHEME="$TVOS_FRAMEWORK_SCHEME"
- DESTINATION="arch=x86_64" SCHEME="$MACOS_FRAMEWORK_SCHEME"
script:
# Ensures that the return code from xcodebuild is passed along to xcpretty
- set -o pipefail
# Runs the tests in Debug and Release configurations | xcpretty
- xcodebuild -project "$PROJECT" -scheme "$SCHEME" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty
- xcodebuild -project "$PROJECT" -scheme "$SCHEME" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty
================================================
FILE: Configs/SwiftDate.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 Daniele Margutti. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
================================================
FILE: Configs/SwiftDateTests.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
================================================
FILE: Documentation/0.Informations.md
================================================

<a name="introduction"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Next Chapter**: Introduction](#1.Introduction.md)
## Info & Install
- [Methods Documentation](0.Informations.md#methods)
- [Unit Tests](0.Informations.md#unittest)
- [Linux Compatibility](0.Informations.md#linux)
- [Future Plans (ToDo List)](0.Informations.md#futureplans)
- [Communication](0.Informations.md#communication)
- [Requirements](0.Informations.md#compatibility)
- [Installation (CocoaPods,Carthage,SwiftPM](0.Informations.md#installation)
- [License](0.Informations.md#license)
<a name="methods"/>
## Methods Documentation
All methods and variables have been documented and are available for option+click inspection, just like the SDK classes. This includes an explanation of the methods as well as what their input and output parameters are for. Please raise an issue if you ever feel documentation is confusing or misleading and we will get it fixed up!
<a name="unittest"/>
## Unit Tests
Unit tests were performed on all the major classes in the library for quality assurance.
You can find theses under the "Tests" folder at the top of the library.
Currently SwiftDate has ~90$ of code coverage.
If you ever find a test case that is incomplete, please open an issue so we can get it fixed.
<a name="futureplans"/>
<a name="linux"/>
## Linux Compatibility
Since SwiftDate 5.0.13 the package compile successfully on Linux environment with the latest Swift 4.2 snapshot.
However there is potential issue with Calendar's compare to granularity method; to know more follow the [issue #596](https://github.com/malcommac/SwiftDate/issues/568) and the radar posted on Swift's group [SR-9101](https://bugs.swift.org/browse/SR-9101).
Last Update: 2018-10-28
## Future Plans (ToDo List)
Current plans for 5.5 are:
- [x] 100% Code Coverage
- [x] Support for Recurrences support (rrule)
Any other suggestion is welcomed!
<a name="compatibility"/>
## Requirements
SwiftDate 5.x is compatible with Swift 4.x+ and the following platforms:
- iOS 9+
- macOS 10.10+
- watchOS 2.0+
- tvOS 10.0+
- any Linux platform which supports Swift 4+
<a name="communication"/>
## Communication
- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/swiftdate). (Tag 'swiftdate')
- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/swiftdate).
- If you **found a bug**, open an issue.
- If you **have a feature request**, open an issue.
- If you **want to contribute**, submit a pull request.
<a name="installation"/>
## Installation
### CocoaPods
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
```bash
$ gem install cocoapods
```
> CocoaPods 1.1+ is required to build SwiftDate.
To integrate SwiftDate into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'SwiftDate', '~> 5.0'
end
```
Then, run the following command:
```bash
$ pod install
```
### Carthage
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
```bash
$ brew update
$ brew install carthage
```
To integrate SwiftDate into your Xcode project using Carthage, specify it in your `Cartfile`:
```ogdl
github "malcommac/SwiftDate" ~> 6.0
```
Run `carthage update` to build the framework and drag the built `SwiftDate.framework` into your Xcode project.
### Swift Package Manager
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. It is in early development, but SwiftDate does support its use on supported platforms.
Once you have your Swift package set up, adding SwiftDate as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
```swift
dependencies: [
.package(url: "https://github.com/malcommac/SwiftDate.git", from: "5.0.0")
]
```
<a name="license"/>
## License
SwiftDate is released under the MIT license.
The MIT License (MIT)
Copyright (c) 2018 Daniele Margutti
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Documentation/1.Introduction.md
================================================

<a name="introduction"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Informations](#0.Informations.md)
- [**Next Chapter**: Parsing Dates](#2.Parsing_Dates.md)
## Introduction to SwiftDate
- [1.0 - Dates & Cocoa](1.Introduction.md#datesandcocoa)
- [1.1 - Region & DateInRegion](1.Introduction.md#region_dateinregion)
- [1.2 - The Default Region](1.Introduction.md#default_region)
- [1.3 - Create Region](1.Introduction.md#creating_region)
- [1.4 - Create DateInRegion](1.Introduction.md#creating_dateinregion)
- [1.4.1 - From String](1.Introduction.md#initfromstring)
- [1.4.2 - From Date Components](1.Introduction.md#initfromcomponents)
- [1.4.3 - From TimeInterval](1.Introduction.md#initfromtimeinterval)
- [1.4.4 - From Date](1.Introduction.md#initfromplaindate)
<a name="datesandcocoa"/>
## 1.0 - Dates & Cocoa
Generally when you talk about dates you are brought to think about a particular instance of time in a particular location of the world. However, in order to get a fully generic implementation Apple made `Date` fully independent from any particular geographic location, calendar or locale.
A plain `Date` object just represent an absolute value: in fact it count the number of seconds elapsed since January 1, 2001.
This is what we call **Universal Time because it represent the same moment everywhere around the world**.
You can see absolute time as the moment that someone in the USA has a telephone conversation with someone in Dubai; both have that conversation at the same moment (the absolute time) but the local time will be different due to time zones, different calendars, alphabets or notation methods.
```swift
let now = Date()
print("\(now.timeIntervalSinceReferenceDate) seconds elapsed since Jan 1, 2001 @ 00:00 UTC")
```
However, we often need to represent a date in a more specific context: **a particular place in the world, printing them using the rules of a specified locale and more**.
In order to accomplish it we need to introduce several other objects: a `Calendar`, a `TimeZone` and a `Locale`; combining these attributes we're finally ready to provide a **representation** of the date into the real world.
SwiftDate allows you to parse, create, manipulate and inspect dates in an easy and more natural way than Cocoa itself.
[^ Top](#introduction)
<a name="region_dateinregion"/>
## 1.1 - Region & DateInRegion
In order to simplify the date management in a specific context SwiftDate introduces two simple structs:
- `Region` is a struct which define a region in the world (`TimeZone`) a language (`Locale`) and reference calendar (`Calendar`).
- `DateInRegion` represent an absolute date in a specific region. When you work with this object all components are evaluated in the context of the region in which the object was created. Inside a DateInRegion you will have an `absoluteDate` and `region` properties.
<a name="default_region"/>
[^ Top](#introduction)
## 1.2 - The Default Region
In SwiftDate you can work both with `DateInRegion` and `Date` instances.
Even plain Date objects uses `Region` when you need to extract time units, compare dates or evaluate specific operations.
However this is a special region called **Default Region** and - by default - it has the following attributes:
- **Time Zone** = GMT - this allows to keep a coerent behaviour with the default Date managment unless you change it.
- **Calendar** = current's device calendar (auto updating)
- **Locale** = current's device locale (auto updating)
While it's a good choice to always uses `DateInRegion` you can also work with `Date` by changing the default region as follow:
```swift
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
SwiftDate.defaultRegion = rome
```
Since now all `Date` instances uses `rome` as default region both for parsing and evaluating date components:
```swift
let dateInRome = "2018-01-01 00:00:00".toDate()!
print("Current year is \(dateInRome.year) and hour is \(dateInRome.hour)") // "Current year is 2018 and hour is 0\n"
```
We can still convert this date to the default absolute representation in UTC using the `convertTo(region:)` function:
```swift
let dateInUTC = dateInRome.convertTo(region: Region.UTC)
print("Current year is \(dateInUTC.year) and hour is \(dateInUTC.hour)") // "Current year is 2017 and hour is 23\n"
```
Be careful while setting the default region.
We still reccomends to use the `DateInRegion` instances instead, this allows you to read the region explicitly.
<a name="creating_region"/>
[^ Top](#introduction)
## 1.3 - Create Region
As you seen from previous example creating a new `Region` is pretty straightforward; you need to specify the locale (used to print localized values like month or weekday name of the date), a timezone and a calendar (usually gregorian).
Region instances accept the following parameters in form of protocols:
- Time zone as `ZoneConvertible` conform object. You can pass both a `TimeZone` instance or any of the predefined timezones region defined inside the `Zones` enumeration.
- Calendar as `CalendarConvertible` conform object. You can pass both a `Calendar` instance or any of the predefined calendars available inside the `Calendars` enumeration.
- Locale as `LocaleConvertible` conform object. You can pass a both a `Locale` instance or any of the predefined locales available inside the `Locales` enumeration.
Using `Zones`, `Calendars` and `Locales` enumeration values is the easiest way to create a region and compiler can also suggest you the best match for your search.
The following example create two regions with different attributes:
```swift
let regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.englishUnitedStates)
let regionTokyo = Region(calendar: Calendars.gregorian, zone: Zones.asiaTokyo, locale: Locales.japanese)
```
<a name="creating_dateinregion"/>
[^ Top](#introduction)
## 1.4 - Create DateInRegion
Now you are ready to create a new `DateInRegion`. There are many different ways to create a new date: parsing a string, setting time components, derivating it from another date or from a given time intervals.
Each initialization method require a region parameter which defines the region in which the date is expressed (default values may vary based upon the init and are listed below).
<a name="initfromstring"/>
### 1.4.1 - From String
The most common case is to parse a string and transform it to a date. As you know `DateFormatter` is an expensive object to create and if you need to parse multiple strings you should avoid creating a new instance in your loop.
Don't worry: using SwiftDate the library helps you by reusing its own parser, shared along the caller thread.
`DateInRegion`'s `init(_:format:region)` can be used to initialize a new date from a string (various shortcut are available under the `toXXX` prefix of `String` extensions.
This object takes three parameters:
- the `string` to parse (`String`)
- the format of the string (`String`): this represent the format in which the string is expressed. It's a unicode format ([See the table of fields](7.Format_UnicodeTable.md)). If you skip this parameter SwiftDate attempts to parse the date using one of the built-in formats defined in `SwiftDate.autoFormats` array. If you know the format of the date you should explicitly set it in order to get better performances.
- the `region` in which the date is expressed (`Region`). By default is set to `SwiftDate.defaultRegion`.
```swift
let date1 = DateInRegion("2016-01-05", format: "yyyy-MM-dd", region: regionNY)
let date2 = DateInRegion("2015-09-24T13:20:55", region: regionNY)
```
<a name="initfromcomponents"/>
### 1.4.2 - From Components
You can create a `DateInRegion` also by setting the date components.
The following method create a date from `DateComponents` instance passed via builder pattern:
```swift
let date3 = DateInRegion(components: {
$0.year = 2018
$0.month = 2
$0.day = 1
$0.hour = 23
}, region: regionNY)
```
You can also instance it by passing single (optional) components:
```swift
let date4 = DateInRegion(year: 2015, month: 2, day: 4, hour: 20, minute: 00, second: 00, region: regionNY)
```
<a name="initfromtimeinterval"/>
### 1.4.3 - From TimeInterval
As plain `Date` you can create a new `DateInRegion` just passing an absolute time interval which represent the seconds/milliseconds from Unix epoch.
The following method create a date 1 year after the Unix Epoch (1971-01-01T00:00:00Z):
```swift
let date5 = DateInRegion(seconds: 1.years.timeInterval, region: regionNY)
let date6 = DateInRegion(milliseconds: 5000, region: regionNY)
```
<a name="initfromplaindate"/>
### 1.4.4 - From Date
Finally you can init a new `DateInRegion` directly specifyng an absolute `Date` and a destination region:
```swift
let absoluteDate: Date = (Date() - 2.months).dateAt(.startOfDay)
let date7 = DateInRegion(absoluteDate, region: regionNY)
```
[^ Top](#introduction)
--
- [**Index**: Table Of Contents](#Index.md)
- [**Next Chapter**: Parsing Dates](#2.Parsing_Dates.md)
================================================
FILE: Documentation/10.Upgrading_SwiftDate4.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
### → [Searching for old SwiftDate 4 builds?](https://github.com/malcommac/SwiftDate/milestones)
## Old Versions
- for **Swift 3.x**: Latest compatible version is 4.3.0 [Download here](https://github.com/malcommac/SwiftDate/releases/tag/4.3.0). If you are using CocoaPods be sure to fix the release (`pod 'SwiftDate', '~> 4.3.0'`)
## Upgrading from Swift 4.x
SwiftDate 5.x is a complete rewrite of the library. While it introduces several new features a great part of the work is about a consistent naming of the functions: some was renamed while deprecated ones was removed.
**If you miss a features or you are interested in a new one create a new Issue.**
### Important Note about Default Region
In SwiftDate 4.x the default region is automatically set to local region, where all attributes are set automatically to the current device's locale, timezone and calendar.
**Since SwiftDate 5, in order to be more comply with `Date`'s default behaviour, the default region's timezone is set to GMT+0 (UTC)**.
If you want to restore the old behaviour just set it to `Region.local` just after the launch of the app.
```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
SwiftDate.defaultRegion = Region.local // set region to local device attributes
// ... do something else
return true
}
```
### Added/Removed/Replaced Methods
- `Date.defaultRegion` was renamed to `SwiftDate.defaultRegion` and you can both read and set it.
- `Date.setDefaultRegion(:)` was removed; use `SwiftDate.defaultRegion`'s setter.
- `TimeZoneName` is now `Zones` and its conform to the new `ZonesConvertible` protocol. All parameters (like `Region` inits function) takes now a generic `ZonesConvertible` instance: this mean you can pass one of the enum values of the zone or just an instance of `TimeZone` object (or you can conform your own class to it).
- `CalendarName` is now `Calendars` and its conform to the new `CalendarConvertible` protocol. All parameters (like `Region` inits function) takes now a generic `CalendarConvertible ` instance: this mean you can pass one of the enum values of the zone or just an instance of `Calendar` object (or you can conform your own class to it).
- `LocaleName` is now `Locales` and its conform to the new `LocaleConvertible` protocol. All parameters (like `Region` inits function) takes now a generic `LocaleConvertible ` instance: this mean you can pass one of the enum values of the zone or just an instance of `Calendar` object (or you can conform your own class to it).
- `Date/DateInRegion`: `isBetween()` function was renamed `isInRange()`
- `Date/DateInRegion`: `isAfter()` was renamed to `isAfterDate()`
- `Date/DateInRegion`: `isBefore()` was renamed to `isBeforeDate()`
- `String` extension methods `date(format:fromRegion)` and `date(formats:fromRegion)` are now replaced by `toDate(_:region)` (while similar shortcuts methods are added to parse common formats: `toISODate()`, `toDotNETDate()`, `toRSSDate()` and `toSQLDate()`.
- Removed `Int` extension `second,minute,hour,day,week,month,year` due to inconsistencies (use plural versions `seconds,minutes` etc.
- Related dates generation is now grouped under the `dateAt()` function as enum list. Therefore `nextWeekend`, `previo
- `DateInRegion`: `roundedAt()` is now replaced by `dateRoundedAt()` which now compacts paramters in a single enum.
- `DateInRegion`: `startOf()` and `endOf()` are now replaced by `dateAtStartOf()` and `dateAtEndOf()`and include support for both single and multiple units.
- `DateInRegion`: `atTime()` is now replaced by `dateBySet(hour:min:secs:options)`
- `DateInRegion`: `DateInRegion`'s static `distantFuture()` and `distantPast` are now `future()` and `past()`
- `DateInRegion`:`next(day:)` is now part of the `dateAt()` function with enum `.weekday(_)`.
- `DateInRegion`: `add(components:)` is now replaced by `dateByAdding(_:_:)`
- `DateInRegion`: `at(unit:value:)` and `at(values:keep:)` are now replaced by `dateBySet()`.
- `nextDSTTransitionDate` property is now part of the enum of `dateAt()`
- `recentDate()` and `oldestDate()` are now called `newestIn()` and `oldestIn()`
- `TimeInterval` Extension: `in(_:toDate:of:)` is now replaced by `toUnits(_:from:)` and `toUnit(_:from:)`
- `TimeInterval` Extension: `string()` is now replaced by `toString()`
- `DateTimeInterval` class is now replaced by `TimePeriod` class and its descendants
- `DateInRegion`: `dates(between:and:increment:)` to enumerate dates is now replaced by `enumerateDates(from:to:increment:)`
- `DateInRegion`: colloquial formatter `colloquial(toDate:options:)` is now replaced by `toRelative(since:style:locale:)`
- Formatting method `iso8601()` is now `toISO8601()` and other methods `to..()` was added to simplify formatting task.
- `Date/DateInRegion`: Introduced `compareCloseTo()` to compare two dates against a precision value.
- Added several shortcut methods as extensions of `String` to parse dates: `toISODate()`, `toDotNETDate()`, `toRSSDate()` and `toSQLDate()`.
- Comparison methods are now grouped by `compare()` function and a list of all available enums.
--
[**Index**: Table Of Contents](#Index.md)
================================================
FILE: Documentation/11.Related_Projects.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
## Related Projects
I'm also working on several other projects you may like.
Take a look below:
<p align="center" >
| Library | Description |
|-----------------|--------------------------------------------------|
| [**SwiftDate**](https://github.com/malcommac/SwiftDate) | The best way to manage date/timezones in Swift |
| [**Hydra**](https://github.com/malcommac/Hydra) | Write better async code: async/await & promises |
| [**FlowKit**](https://github.com/malcommac/FlowKit) | A new declarative approach to table managment. Forget datasource & delegates. |
| [**SwiftRichString**](https://github.com/malcommac/SwiftRichString) | Elegant & Painless NSAttributedString in Swift |
| [**SwiftLocation**](https://github.com/malcommac/SwiftLocation) | Efficient location manager |
| [**SwiftMsgPack**](https://github.com/malcommac/SwiftMsgPack) | Fast/efficient msgPack encoder/decoder |
</p>
================================================
FILE: Documentation/12.Timer_Periods.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
## Time Periods
*NOTE: The following documentation and part of the implementation is heavely inspired by the original work of Matthew York with its [DateTools](https://github.com/MatthewYork/DateTools/) project.*
Dates are important, but the real world is a little less discrete than that. Life is made up of spans of time, like an afternoon appointment or a weeklong vacation. In DateTools, time periods are represented by the TimePeriod class and come with a suite of initializaiton, manipulation, and comparison methods to make working with them a breeze.
Time periods consist of a Date start date and end date.
```swift
let toDate = DateInRegion().dateAtStartOf(.day) // now at 00:00
let fromDate = toDate - 3.days // 3 days ago at 00:00
let period = TimePeriod(start: fromDate , end: toDate)
```
or, if you would like to create a time period of a known length that starts or ends at a certain time, try out a few other init methods.
The method below, for example, creates a time period starting at the current time that is exactly 3 days long.
```swift
let _ = TimePeriod(start: DateInRegion(), duration: 3.days)
```
## Time Period Info
A host of methods have been extended to give information about an instance of TimePeriod:
- `hasFiniteRange` - true if period has both start and end date
- `hasStart`/`hasEnd` - true if period has start/end date
- `isMoment()` - Returns true if the period has the same start and end date or the difference is between a defined range.
- `durationIn()` - Returns the length of the time period in the requested unit/s
## Manipulation
Time periods may also be manipulated. They may be shifted earlier or later as well as expanded and contracted.
### Shifting
When a time period is shifted, the start dates and end dates are both moved earlier or later by the amounts requested. To shift a time period earlier, call `. p.shifted(by:)` function with positive or negative components to shift earlier or later in time.
```swift
// Create a time period at the start of the current week and end at the end of the week
let fromDate = DateInRegion().dateAtStartOf([.weekOfYear,.day])
let toDate = fromDate.dateAtEndOf([.weekOfMonth,.day])
let thisWeek = TimePeriod(start: fromDate, end: toDate)
// Shift the period by 7 days in the future
let shiftedByOneWeek = thisWeek.shifted(by: 7.days)
```
### Lengthening/Shortening
When a time periods is lengthened or shortened, it does so anchoring one date of the time period and then changing the other one. There is also an option to anchor the centerpoint of the time period, changing both the start and end dates.
An example of lengthening a time period is shown below:
```swift
let oneMinutePeriod = TimePeriod(end: DateInRegion(), duration: 1.minutes)
let lengthed = oneMinutePeriod.lengthened(by: 1.minutes.timeInterval, at: .end)
```
This doubles a time period of duration 1 minute to duration 2 minutes. The end date of "now" is retained and only the start date is shifted 1 minute earlier.
### Relationships
There may come a need, say when you are making a scheduling app, when it might be good to know how two time periods relate to one another. Are they the same? Is one inside of another? All these questions may be asked using the relationship methods of TimePeriod.
Below is a chart of all the possible relationships between two time periods:

A suite of methods have been extended to check for the basic relationships.
They are listed below:
- `isEqualToPeriod`
- `isInside`
- `contains`
- `overlapsWith`
- `intersects`
You can also check for the official relationship (like those shown in the chart) with the following method:
```swift
let relationship = periodA.relation(to: periodB)
```
This function returns the right value from `TimePeriodRelation` enum.
## Time Period Groups
Time period groups are the final abstraction of date and time.
Here, time periods are gathered and organized into something useful.
There are two main types of time period groups, `TimePeriodCollection` and `TimePeriodChain`. At a high level, think about a collection as a loose group where overlaps may occur and a chain a more linear, tight group where overlaps are not allowed.
Both collections and chains operate like an array. You may add, insert and remove `TimePeriod` objects from them just as you would objects in an array.
The difference is how these periods are handled under the hood.
### Time Period Collections
Time period collections serve as loose sets of time periods. They are unorganized unless you decide to sort them, and have their own characteristics like a StartDate and EndDate that are extrapolated from the time periods within. Time period collections allow overlaps within their set of time periods.

To make a new collection, call the class method like so:
```swift
// Create collection
let collection = TimePeriodCollection()
// Create a few time periods
let firstPeriod = TimePeriod(start: "2014-11-05 18:15:12".toDate()!, end: "2015-11-05 18:20:12".toDate()!)
let secondPeriod = TimePeriod(start: "2014-11-05 18:30:12".toDate()!, end: "2015-11-05 18:35:12".toDate()!)
collection.append([firstPeriod,secondPeriod])
```
Sorting Sorting time periods in a collection is easy, just call one of the sort methods.
Sorting can take in place (by modifying the original collection) or can return a derivated collection.
You can therefore use:
- `sorted(by type: SortType)`: to return a collection with sorted items
- `sort(by type: SortType)`: to modify the collection in place
both of these methods takes a `SortType` enum which defines the type of sorting you can apply:
- `start(_: SortMode)`: sort by start date (`SortMode` can be `.ascending` or `.descending`)
- `end(_: SortMode)`: sort by end date (`SortMode` can be `.ascending` or `.descending`)
- `duration(_: SortMode)`: sort by duration (`SortMode` can be `.ascending` or `.descending`)
- `custom(_: ((TimePeriodProtocol, TimePeriodProtocol) -> Bool))`: sort using custom function
Operations It is also possible to check an Date's or TimePeriod's relationship to the collection.
For instance, if you would like to see all the time periods that intersect with a certain date, you can call the periodsIntersectedByDate: method. The result is a new TimePeriodCollection with all those periods that intersect the provided date. There are a host of other methods to try out as well, including a full equality check between two collections.

### Time Period Chains
Time period chains serve as a tightly coupled set of time periods.
They are always organized by start and end date, and have their own characteristics like a `start` and `end` that are extrapolated from the time periods within.
Time period chains do not allow overlaps within their set of time periods.
This type of group is ideal for modelling schedules like sequential meetings or appointments.

To make a new chain, call the class method like so:
```swift
```
Any time a date is added to the time chain, it retains its duration, but is modified to have its StartDate be the same as the latest period in the chain's EndDate. This helps keep the tightly coupled structure of the chain's time periods. Inserts (besides those at index 0) shift dates after insertion index by the duration of the new time period while leaving those at indexes before untouched. Insertions at index 0 shift the start date of the collection by the duration of the new time period. A full list of operations can be seen below.
**Operations**
Like collections, chains have an equality check and the ability to be shifted earlier and later. Here is a short list of other operations.

================================================
FILE: Documentation/2.Date_Parsing.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Introduction to SwiftDate](#1.Introduction.md)
- [**Next Chapter**: Manipulate & Derivate Dates](#3.Manipulate_Date.md)
## Date Parsing
- [2.0 - Parse Custom Format](2.Date_Parsing.md#autoparsing)
- [2.1 - Parse ISO8601](2.Date_Parsing.md#iso8601)
- [2.2 - Parse .NET](2.Date_Parsing.md#dotnet)
- [2.3 - Parse RSS & AltRSS](2.Date_Parsing.md#rssaltrss)
- [2.4 - Parse SQL](2.Date_Parsing.md#sql)
Related documents:
- [Unicode Format for Strings](7.Format_UnicodeTable.md)
Parsing dates is pretty straighforward in SwiftDate; library can parse strings with dates automatically by recognizing one of the most common patterns. Moreover you can provide your own formats or use one of the built-in parsers.
In the following chapter you will learn how to transform a string to a date.
<a name="autoparsing"/>
## 2.0 - Parse Custom Format
The easiest way to transform an input string to a valid date is to use one of the `.toDate()` functions available as `String`'s instance extensions. The purpose of these method is to get the best format can represent the input string and use it to generate a valid `DateInRegion`.
As like other libs like moment.js, SwiftDate has a list of built-in formats it can use in order to obtain valid results.
You can get the list of these formats by calling `SwiftDate.autoFormats`.
The order of this array is important because SwiftDate iterates over this list until a valid date is returned (the order itself allows the lib to reduce the list of false positives).
You can alter this list by adding/removing or replacing the contents of this array.
- `.toDate(_ format: String?, region: Region?)`
- `.toDate(_ formats: [String]?, region: Region?)`
functions takes as input two arguments:
- `format (String|Array)`: it's optional and allows you to set explictly the format (or ordered list of formats) SwiftDate must use to parse the date. Allowed values are listed in Unicode DateTime Table [you can found here](7.Format_UnicodeTable.md). If omitted SwiftDates attempts to parse the string iterating over the list of auto patterns listed in `SwiftDate.autoFormats`.
- `region (Region)`: describe the region (locale/calendar/timezone) in which the date is expressed. If omitted the default region (`SwiftDate.defaultRegion`) is used istead.
The result of these functions is an optional `DateInRegion` instance (`nil` is returned if parsing fails).
Some examples:
```swift
let _ = "2018-01-01 15:00".toDate()
let _ = "15:40:50".toDate("HH:mm:ss")
let _ = "2015-01-01 at 14".toDate("yyyy-MM-dd 'at' HH", region: rome)
// Support for locale
let itRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let enRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
let srcString = "July 15 - 15:30"
// it returns nil because itRegion has Locales.italian
let _ = srcString.toDate(["yyyy-MM-dd","MMM dd '-' HH:mm"], region: itRegion)
// it's okay because enRegion has locale set to english
let _ = srcString.toDate(["yyyy-MM-dd","MMM dd '-' HH:mm"], region: enRegion)
```
> **PERFORMANCES** In order to preserve performances you should pass the `format` parameter if you know the input format.
> **LOCALE PARAMETER** If you use readable unit names (like `MMM` for months) be sure to select the right locale inside the `region` parameter in order to get valid results.
[^ Top](#index)
<a name="iso8601"/>
## 2.1 Parse ISO8601
A special note must be made for ISO8601. This format (the extended version and all its variants) may include the timezone information.
If you need to parse an ISO8601 datetime you should therefore use the `.toISODate()` function of `String` in order to get a complete result.
> **NOTE** ISO8601 parser (via `.toISODate()` func) is capable of recognizing all the variants of the 8601 formats; if your date is in this formt use this function instead of passing custom time format. It will lead in better results.
The following function:
`func toISODate(_ options: ISOParser.Options?, region: Region) -> DateInRegion?`
takes 2 arguments:
- `options | ISOParserOptions` allows you to customize some parser attributes: `timeSeparator` (by default is `:`), `calendar` (by default is the gregorian calendar) and the strict parsing behaviour (by default is `false`). Usually you can omit this parameter.
- `region | Region` used to define the region of the date. By default is set to `Region.ISO` (gregorian/gmt/english posix). If parsed string also contains `timezone` it will override the parameter passed by the region. You can use `convertTo(region:)` to perform any additional conversion.
Examples:
```swift
let date = "2017-08-05T16:04:03+02:00".toISODate(region: Region.ISO)!
// returned date's region.zone is GMT+2 not the default's Region.ISO's GMT0.
// This because value is read from the string itself.
```
[^ Top](#index)
<a name="dotnet"/>
## 2.2 - Parse .NET
CSOM DateTime (aka .NET DateTime) is a format defined by Microsoft as the number of 100-nanosecond intervals that have elapsed since 12:00 A.M., January 1, 0001 ([learn more on MSDN documentation page](https://msdn.microsoft.com/en-us/library/dd948679)).
You can parse a CSOM datetime string using the `toDotNETDate()` function.
> **NOTE:** As for ISO8601 even .NET datetime may contain information about timezone. When you set the region as input parameter of the conversion function remember: it will be overriden by default parsed timezone (GMT if not specified). Region is used for `locale` only.
`func toDotNETDate(region: Region = SwiftDate.defaultRegion) -> DateInRegion?`
takes a single parameter:
- `region | Region`: the region in which the date is represented (only `locale` parameter is used). If you omit this parameter the `SwiftDate.ISO` is used instead. You can use `convertTo(region:)` to perform any additional conversion.
Example:
```swift
// This is the 2017-07-22T18:27:02+02:00 date.
let _ = "/Date(1500740822000+0200)/".toDotNETDate()
```
[^ Top](#index)
<a name="rssaltrss"/>
## 2.3 - Parse RSS & AltRSS
RSS & AltRSS datetime format are used in RSS feed files. Parsing in SwiftDate is pretty easy; just call the `.toRSSDate()` function.
> **NOTE:** As for ISO8601 even RSS/AltRSS datetime contain information about timezone. When you set the region as input parameter of the conversion function remember: it will be overriden by default parsed timezone (GMT if not specified). Region is used for `locale` only.
`func toRSSDate(alt: Bool, region: Region = Region.ISO) -> DateInRegion?`
takes two arguments:
- `alt: Bool`: set to `false` if represented string is RSS standard format, `true` to use the Alt-RSS parser.
- `region: Region`: region in which the date is expressed. Only the `locale` parameter is used, `timezone` is read from string and calendar is gregorian. If you omit this parameter the `SwiftDate.ISO` is used instead. You can use `convertTo(region:)` to perform any additional conversion.
```swift
// This is the ISO8601: 2017-07-22T18:27:02+02:00
let _ = "Sat, 22 Jul 2017 18:27:02 +0200".toRSSDate(alt: false)!
let _ = "22 Jul 2017 18:27:02 +0200".toRSSDate(alt: true)!
// NOTE:
// Even if we set a random region with a custom locale,
// calendar and timezone, final parsed date still correct.
// Only the locale parameter is set.
// Other region's parameter are ignored and read from the string itself.
let regionAny = Region(calendar: Calendars.buddhist, zone: Zones.indianMayotte, locale: Locales.italian)
let date1 = "Tue, 20 Jun 2017 14:49:19 +0200".toRSSDate(alt: false, region: regionAny)
```
[^ Top](#index)
<a name="sql"/>
## 2.4 - Parse SQL
SQL datetime is the format used in all SQL-compatible schemas.
You can parse a string in this format using `toSQLDate()` function.
`func toSQLDate(region: Region = Region.ISO) -> DateInRegion?`
takes one argument:
- `region: Region`: region in which the date is expressed. Only the `locale` parameter is used, `timezone` is read from string and calendar is gregorian. If you omit this parameter the `SwiftDate.ISO` is used instead. You can use `convertTo(region:)` to perform any additional conversion.
Example:
```swift
// Date in ISO is 2016-04-14T11:58:58+02:00
let _ = "2016-04-14T11:58:58.000+02".toSQLDate()
```
[^ Top](#index)
--
[**Next Chapter**: Manipulate & Derivate Dates](#3.Manipulate_Date.md)
================================================
FILE: Documentation/3.Manipulate_Date.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Date Parsing](#3.Date_Parsing.md)
- [**Next Chapter**: Compare Dates](#4.CompareDates.md)
## Manipulate & Derivate Dates
- [3.0 - Add & Subtract Time Units from Date](3.Manipulate_Date.md#mathdate)
- [3.1 - Get DateTime Components](3.Manipulate_Date.md#datecomponents)
- [3.2 - Get Interval Between Dates](3.Manipulate_Date.md#interval)
- [3.3 - Convert Date's Region (Locale/TimeZone/Calendar)](3.Manipulate_Date.md#convert)
- [3.4 - Rounding Date](3.Manipulate_Date.md#roundingdate)
- [3.5 - Truncating Date](3.Manipulate_Date.md#truncatingdate)
- [3.6 - Set Time in Date](3.Manipulate_Date.md#altertimedate)
- [3.7 - Set DateTime Components](3.Manipulate_Date.md#altercomponents)
- [3.8 - Generate Related Dates (`nextYear, nextWeeekday, startOfMonth, startOfWeek, prevMonth`...)](3.Manipulate_Date.md#relateddates)
- [3.9 - Date at start/end of time component](3.Manipulate_Date.md#startendcomponent)
- [3.10 - Enumerate Dates](3.Manipulate_Date.md#enumeratedates)
- [3.11 - Enumerate Dates for Weekday in Range](3.Manipulate_Date.md#enumerateweekdays)
- [3.12 - Random Dates](3.Manipulate_Date.md#randomdates)
- [3.13 - Sort Dates](3.Manipulate_Date.md#sort)
- [3.14 - Get the next weekday](3.Manipulate_Date.md#nextWeekDay)
- [3.15 - Get date at given week number/weekday](3.Manipulate_Date.md#dateAtWeeknumberWeekday)
- [3.16 - Difference between dates with components](3.Manipulate_Date.md#differenceBetweenDates)
- [3.17 - Create date at components preserving small components](3.Manipulate_Date.md#dateAtComponents)
- [3.18 - Date at given weekday after # weeks](3.Manipulate_Date.md#dateAfterWeeks)
- [3.19 - Next Date](3.Manipulate_Date.md#nextDate)
Dates can be manipulated as you need by using classic math operators and readable time units.
<a name="mathdate"/>
## 3.0 - Add & Subtract Time Units from Date
SwiftDate allows you to use numbers to work with time components in dates. By extending the `Int` type it defines a list of time units:
- `nanoseconds`
- `seconds`
- `minutes`
- `days`
- `weeks`
- `months`
- `quarters`
- `years`
You can use a value followed by one of these unit specifications to add or remove the component from a date.
So, for example, you can produce a date which can be a mix of intuitive math operations:
```swift
let oneYearAhead = DateInRegion() + 1.years
let someMinutesAgo = date1 - 2.minutes
let fancyDate = date1 + 3.hours - 5.minutes + 1.weeks
```
> IMPORTANT NOTE: These values are converted automatically to `DateComponents` evaluated in the same context of the target `Date` or `DateInRegion`'s `calendar`.
Another way to add time components to a date is to use the `dateByAdding()` function:
`func dateByAdding(_ count: Int, _ component: Calendar.Component) -> DateInRegion`
takes two arguments:
- `count | Int` value to add (maybe negative)
- `component | Calendar.Component` the time unit components to add.
> IMPORTANT NOTE: New date is evaluated in the same context of the target `Date` or `DateInRegion`'s `calendar`.
```swift
let nextDate = dateA.dateByAdding(5, .years) // 5 years from dateA
```
[^ Top](#index)
<a name="datecomponents"/>
## 3.1 - Get DateTime Components Components
With SwiftDate you have several convenience properties to inspect each datetime unit of a date, both for `Date` and `DateInRegion`.
These properties are strictly correlated to the date's calendar (and some also with locale): if you are manipulating a `DateInRegion` remember these properties return values in the context of the associated `region` attributes (Locale, TimeZone and Calendar).
> **IMPORTANT NOTE**: If you are working with plain `Date` properties uses as reference the currently set `SwiftDate.defaultRegion` which, unless you modify it, is set to Gregorian/UTC/Device's Language.
This a complete list of the properties you can inspect for a date object:
| PROPERTY | DESCRIPTION |
|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `year` | current year number |
| `month` | current month number (1 is January) |
| `monthName(_ style: SymbolFormatStyle)` | name of the current month with given style (uses region's locale) |
| `monthDays` | number of the days into the current month |
| `day` | day number in the current month |
| `dayOfYear` | day of the year |
| `ordinalDay` | The number of day in ordinal style format for the receiver in current locale. |
| `hour` | current hour |
| `nearestHour` | nearest rounded hour |
| `minute` | current minute |
| `second` | current second |
| `nanosecond` | current nanosecond |
| `msInDay` | "Milliseconds in day of the receiver. This field behaves exactly like a composite of all time-related fields, not including the zone fields.As such, it also reflects discontinuities of those fields on DST transition days. |
| On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. | |
| This reflects the fact that is must be combined with the offset field to obtain a unique local time value." | |
| `weekday` | Weekday unit of the receiver. The weekday units are the numbers 1-N (where for the Gregorian calendar N=7 and 1 is Sunday). |
| `weekdayName(_ style: SymbolFormatStyle)` | Name of the weekday expressed in given format style. |
| `weekOfYear` | Week of a year of the receiver. |
| `weekOfMonth` | Week of a month of the receiver. |
| `weekdayOrdinal` | Ordinal position within the month unit of the corresponding weekday unit. |
| `firstDayOfWeek` | Return the first day number of the week where the receiver date is located |
| `lastDayOfWeek` | Return the last day number of the week where the receiver date is located. |
| `yearForWeekOfYear` | Relative year for a week within a year calendar unit. |
| `quarter` | Quarter value of the receiver. |
| `quarterName(_ style: SymbolFormatStyle)` | Quarter name expressed in given format style |
| `era` | Era value of the receiver. |
| `eraName(_ style: SymbolFormatStyle)` | Name of the era expressed in given format style |
| `DSTOffset` | The current daylight saving time offset of the represented date |
Several other properties defines additional attributes of the date:
- `date | Date`: return the absolute date instance (for `Date` it just return itself)
- `region | Region`: return the region associated with date (for `Date` it return `SwiftDate.defaultRegion`).
- `calendar | Calendar`: return the associated calendar
- `dateComponents | DateComponents`: return all the date components of the date in the context of its associated region.
[^ Top](#index)
<a name="interval"/>
## 3.2 - Get Interval Between Dates
You can get the interval between two dates and express it in form of time units easily with SwiftDate.
The `.getInterval(toDate:component:)` function allows you to express the difference between to dates in form of a passed time component.
`func getInterval(toDate: DateInRegion?, component: Calendar.Component) -> Int64`
Takes two arguments:
- `toDate | DateInRegion`: reference date to compare against
- `component | Calendar.Component`: the component in which the difference must be returned.
Examples:
```swift
let dateA = DateInRegion("2017-07-22 00:00:00", format: format, region: rome)!
let dateB = DateInRegion("2017-07-23 12:00:00", format: format, region: rome)!
let hours = dateA.getInterval(toDate: dateB, component: .hour) // 36 hours
let days = dateA.getInterval(toDate: dateB, component: .day) // 1 day
```
[^ Top](#index)
<a name="convert"/>
## 3.3 - Convert Date's Region (Locale/TimeZone/Calendar)
`DateInRegion` can be converted easily to another region just using `.convertTo(region:)` or `.convertTo(calendar: timezone:locale:)` functions.
- `convertTo(region:)` convert the receiver date to another region. Region may include a different time zone for example, or a locale.
- `convertTo(calendar:timezone:locale:)` allows to convert the receiver date instance to a specific calendar/timezone/locale. All parameters are optional and only non-nil parameters alter the final region. For a nil param the current receiver's region attribute is kept.
Examples:
```swift
// Create a date in NY: "2001-09-11 12:00:05"
let regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.english)
let dateInNY = DateInRegion(components: {
$0.year = 2001
$0.month = 9
$0.day = 11
$0.hour = 12
$0.minute = 0
$0.second = 5
}, region: regionNY)
// Convert to GMT timezone (and locale)
let inGMT = dateInNY.convertTo(region: Region.UTC) // date is now: "2001-09-11 16:00:05"
```
[^ Top](#index)
<a name="roundingdate"/>
## 3.4 - Rounding Date
`.dateRoundedAt()` function allows to round a given date time to the passed style: off, up or down.
`func dateRoundedAt(at style: RoundDateMode) -> DateInRegion`
takes a single argument:
- `style | RoundDateMode`: define the style of rounding: it can be `off`, `ceil` (up) or `floor` (down). `RoundDateMode` defines a list of convenience rounding to 5,10,30 minutes but you can use `.toMins()`, `.toCeilMins()` or `.toFloorMins()` and pass your own rounding values expressed in minutes.
Example:
```swift
let rome = Region(...)
let format = "yyyy-MM-dd HH:mm:ss"
// Round down 10mins
let date = DateInRegion("2017-07-22 00:03:50", format: format, region: rome)
let r10min = date.dateRoundedAt(.to10Mins) // 2017-07-22T00:00:00+02:00
// Round up 30min
let r30min = "2015-01-24 15:07:20".toDate(format: format, region: rome).dateRoundedAt(.toCeil30Mins) // 2015-01-24T15:30:00+01:00
```
[^ Top](#index)
<a name="truncatingdate"/>
## 3.5 - Truncating Date
Sometimes you may need to truncate a date by zeroing all values below certain time unit. `.dateTruncated(from:) and .dateTruncated(to:)` functions can be used for this scope.
#### Truncating From
It creates a new instance by truncating the components starting from given components down the granurality.
`func dateTruncated(from component: Calendar.Component) -> DateInRegion?`
#### Truncated At
It creates a new instance by truncating all passed components.
`func dateTruncated(at components: [Calendar.Component]) -> DateInRegion?`
Examples:
```swift
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = "2017-07-22 15:03:50".toDate("yyyy-MM-dd HH:mm:ss", region: rome)
let truncatedTime = date.dateTruncated(from: .hour) // 2017-07-22T00:00:00+02:00
let truncatedCmps = date.dateTruncated(at: [.month, .day, .minute]) // 2017-01-01T15:00:50+01:00
```
[^ Top](#index)
<a name="altertimedate"/>
## 3.6 - Set Time in Date
Sometimes you may need to alter time in a specified date. SwiftDate allows you to perform this using `.dateBySet(hour:min:secs:options:)` function.
`func dateBySet(hour: Int?, min: Int?, secs: Int?, ms: Int?, options: TimeAlterOptions = TimeAlterOptions()) -> DateInRegion?`
takes five arguments:
- `hour | Int?`: set a non `nil` value to change the hour value
- `min | Int?`: set a non `nil` value to change the minute value
- `secs | Int?`: set a non `nil` value to change the seconds value
- `ms | Int?`: set a non `nil` value to change the milliseconds value
- `options: TimeCalculationOptions`: allows to specify calculation options attributes (usually you don't need to set it).
Example:
```swift
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = DateInRegion("2010-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: rome)!
let alteredDate = date.dateBySet(hour: 20, min: 13, secs: 15) // 2010-01-01 20:13:15
```
[^ Top](#index)
<a name="altercomponents"/>
## 3.7 - Set DateTime Components
SwiftDate allows you to return a new date representing the date calculated by setting specific components of a given date to given values, while trying to keep lower components the same (altering more components at the same time may result in different-than-expected results, this is because lower components maybe need to be recalculated).
`dateBySet(_ components: [Calendar.Component: Int]) -> DateInRegion?`
Takes one argument:
- `components | [Calendar.Component: Int]` a dictionary of key/values for each component you want to alter.
> **NOTE:** While the algorithm try to keep lower compoments the same, the resulting date may differ from what expected when you alter more than a component a time.
Example:
```swift
let _ = date.dateBySet([.month: 1, .day: 1, .hour: 9, .minute: 26, .second: 0])
```
[^ Top](#index)
<a name="relateddates"/>
## 3.8 - Generate Related Dates (`nextYear, nextWeeekday, startOfMonth, startOfWeek, prevMonth`...)
Sometimes you may need to generate a related date from a specified instance; maybe the next sunday, the first day of the next week or the start datetime of a date.
SwiftDate includes 20+ different "interesting" dates you can obtain by calling `.dateAt()` function from any `Date` or `DateInRegion` instance.
`func dateAt(_ type: DateRelatedType) -> DateInRegion`
takes just an argument which defines the type of date you want to obtain starting from the receiver date.
`DateRelatedType` is an enum which has the following options:
- `startOfDay`
- `endOfDay`
- `startOfWeek`
- `endOfWeek`
- `startOfMonth`
- `endOfMonth`
- `tomorrow`
- `tomorrowAtStart`
- `yesterday`
- `yesterdayAtStart`
- `nearestMinute(minute:Int)`
- `nearestHour(hour:Int)`
- `nextWeekday(_: WeekDay)`
- `nextDSTDate`
- `prevMonth`
- `nextMonth`
- `prevWeek`
- `nextWeek`
- `nextYear`
- `prevYear`
> **CONTRIBUTE!** Have you a new related date you want to be part of this list? Create a [new PR](https://github.com/malcommac/SwiftDate/compare) with the code and unit tests and we'll be happy to add it to the list!
Examples:
```swift
// Return today's datetime at 00:00:00
let _ = DateInRegion().dateAt(.startOfDay)
// Return today's datetime at 23:59:59
let _ = DateInRegion().dateAt(.endOfDay)
// Return the date at the start of this week
let _ = DateInRegion().dateAt(.startOfWeek)
// Return current time tomorrow
let _ = DateInRegion().dateAt(.tomorrow)
// Return the next sunday from specified date
let _ = date.dateAt(.nextWeekday(.sunday))
// and so on...
```
[^ Top](#index)
<a name="startendcomponent"/>
## 3.9 - Date at start/end of time component
Two functions called `.dateAtStartOf()` and `.dateAtEndOf()` allows you to get the related date from a `Date`/`DateInRegion` instance moved at the start or end of the specified component.
You can, for example, get the date at the start of the week, or the year, or a the end of the quarter.
- `func dateAtStartOf(_ unit: Calendar.Component) -> DateInRegion`: return the date at the start of the specified time component.
- `func dateAtEndOf(_ unit: Calendar.Component) -> DateInRegion`: return the date at the end of the specified time component.
Examples:
```swift
// Return today's date at 23:59:59
let _ = DateInRegion().dateAtEndOf(.day)
// Return the first day's date of the month described in date1
let _ = date1.dateAtStartOf(.month)
// Return the first day of this year at 00:00:00
let _ = DateInRegion().dateAtStartOf(.year)
```
<a name="enumeratedates"/>
[^ Top](#index)
## 3.10 - Enumerate Dates
Dates enumeration function allows you to generate a list of dates in a closed date intervals incrementing date components by a fixed or variable interval at each new date.
- **VARIABLE INCREMENT** `static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: ((DateInRegion) -> (DateComponents))) -> [DateInRegion]`
- **FIXED INCREMENT** `static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: DateComponents) -> [DateInRegion]`
Both of these functions are pretty similar; it takes:
- `startDate | DateInRegion`: the initial date of the enumeration (it will be item #0 of the final array)
- `endDate | DateInRegion`: the upper bound limit date. The last item of the array is evaluated automatically and maybe not equal to `endDate`.
- `increment | DateComponents or Function`: for fixed increment it takes a `DateComponents` instance which define the increment of time components at each new date; for variable it provides the latest date generated and require as return object of the closure the increment as `DateComponents`.
Examples:
```swift
let increment = DateComponents.create {
$0.hour = 1
$0.minute = 30
}
// Generate an array of dates where the first item is fromDate
// and each new date is incremented by 1h30m from the previous.
// Latest date is < endDate (but maybe not the same).
let dates = DateInRegion.enumerateDates(from: fromDate, to: toDate, increment: increment)
```
[^ Top](#index)
<a name="enumerateweekdays"/>
## 3.12 - Enumerate Dates for Weekday in Range
The following function allows you to enumerate all dates of a particular weekdays which are in a determinated date range.
Both `Date` and `DateInRegion` implements this static function:
```swift
public static func datesForWeekday(_ weekday: WeekDay, from startDate: Date, to endDate: Date, region: Region = SwiftDate.defaultRegion) -> [Date]
```
Another shortcut method allows you to get weekdays in a particular month of a year:
```swift
public static func datesForWeekday(_ weekday: WeekDay, inMonth month: Int, ofYear year: Int, region: Region = SwiftDate.defaultRegion) -> [Date]
```
Examples:
```swift
// Get all mondays in Jan 2019
let mondaysInJan2019 = Date.datesForWeekday(.monday, inMonth: 1, ofYear: 2019)
// You will get 4 results
// - 2019-01-07T00:00:00Z
// - 2019-01-14T00:00:00Z
// - 2019-01-21T00:00:00Z
// - 2019-01-28T00:00:00Z
// Get all fridays between May 27, 2019 and June 8, 2019
let fromDate = Date(year: 2019, month: 5, day: 27, hour: 0, minute: 0)
let toDate = Date(year: 2019, month: 6, day: 8, hour: 0, minute: 0)
let fridaysInJunePartial = Date.datesForWeekday(.friday, from: fromDate, to: toDate, region: Region.UTC)
// You will get 2 results:
// - 2019-05-31T00:00:00Z
// - 2019-06-07T00:00:00Z
```
<a name="randomdates"/>
## 3.12 - Random Dates
SwiftDate exposes a set of functions to generate a random date or array of random dates in a bounds.
There are several functions to perform this operation:
### Single Random Date
- `randomDate(region:)` generate a random date into the specified region.
- `randomDate(withinDaysBeforeToday:region:)` generate a random date between now and a specified amount days ealier into the specified region.
- `randomDate(between:and:region:)` generate a random date into the specified region between two given date bounds.
### Array of Random Dates
- `randomDates(count:between:and:region:)` return `count` random generated dates into the specified region between two given date bounds.
> **IMPORTANT**: For all of thes function if you don't specify the `region`, `SwiftDate.defaultRegion` is used instead.
Examples:
```swift
// Generate random dates array in limit
let now = DateInRegion()
let someYearsAgo = (upperBound - 3.years)
// generate 40 random dates between 3 years ago today and today
let randomDates = DateInRegion.randomDates(count: 40, between: someYearsAgo, and: now)
// Generate a random date between now and 7 days ago
let rome: Region = ...
let aDate = DateInRegion.randomDate(withinDaysBeforeToday: 7, region: rome)
```
[^ Top](#index)
<a name="sort"/>
## 3.13 - Sort Dates
Two conveniences function allows you to sort an array of dates by newest or oldest. Naming is pretty simple:
- `sortedByOldest()`: sort dates by the oldest
- `sortedByNewest()`: sort dates by the newest
Two other functions allows you to get the oldest/newest date in array:
- `oldestIn()`: return the oldest date in array
- `newestIn()`: return the newest date in array
Examples:
```swift
let arrayOfDates: [DateInRegion] = [...]
// ordered array with the newest on top
let orderedByNewest = DateInRegion.sortedByNewest(list: datesArray)
// get the oldest date of the list
let oldestDate = DateInRegion.oldestIn(list: arrayOfDates)
```
[^ Top](#index)
<a name="nextWeekDay"/>
## 3.14 - Get the next weekday
In order to get the next weekday preserving smaller components (hour, minute, seconds) you can use the `nextWeekday()` function:
```swift
let date1 = DateInRegion("2019-05-11 00:00:00", format: dateFormat, region: regionRome)!
let nextFriday = date1.nextWeekday(.friday) // 2019-05-17T00:00:00+02:00
```
[^ Top](#index)
<a name="dateAtWeeknumberWeekday"/>
## 3.15 - Get date at given week number/weekday
To returns the date at the given week number and week day preserving smaller components (hour, minute, seconds) you can use the `dateAt(weekdayOrdinal:weekday:monthNumber:yearNumber:)` function:
```swift
let date = DateInRegion("2019-05-11 00:00:00", format: dateFormat, region: regionRome)!
let _ = date1.dateAt(weekdayOrdinal: 3, weekday: .friday, monthNumber: date1.month + 1) // 2019-06-21T00:00:00+02:00
```
<a name="differenceBetweenDates"/>
## 3.16 - Difference between dates with components
To get differences between two dates using given time components you can choose between two methods:
- `difference(in:from:)`: Returns the difference in the calendar component given (like day, month or year) with respect to the other date as a positive integer.
- `differences(in:from:)`: Returns the differences in the calendar components given (like day, month and year) with respect to the other date as dictionary with the calendar component as the key and the diffrence as a positive integer as the value.
Example:
```swift
let format = "yyyy-MM-dd HH:mm"
let d1 = "2019-01-01 00:00".toDate(format, region: Region.current)
let d2 = "2019-01-10 14:20".toDate(format, region: Region.current)
let diff = d1!.differences(in: [.day, .hour], from: d2!) // 9 days, 14 hours
```
[^ Top](#index)
<a name="dateAtComponents"/>
## 3.17 - Create date at components preserving small components
You can create a derivated date from a receiver by preserving small components (hour, minute and second) by using the: `dateAt(dayOfMonth:monthNumber:yearNumber:)` method:
```swift
let date = "2019-01-01 00:00".toDate("yyyy-MM-dd HH:mm", region: Region.current)! // Rome zone
let onMarch = date.dateAt(dayOfMonth: 31, monthNumber: 3) // 2019-03-31T00:00:00+01:00
```
[^ Top](#index)
<a name="dateAfterWeeks"/>
## 3.18 - Date at given weekday after # weeks
Returns the date after given number of weeks on the given day of week using `dateAfter(weeks:on:)` function.
```swift
let d = "2019-09-14 00:00".toDate("yyyy-MM-dd HH:mm", region: Region.current)! // Rome Region
let nextTwoMondays = d.dateAfter(weeks: 2, on: .monday) // 2019-09-23T00:00:00+02:00
```
[^ Top](#index)
<a name="nextDate"/>
## 3.19 - Next Date
There are 3 functions to get the next date starting from a receiver date:
- `nextWeekday()`: Returns the next weekday preserving smaller components
- `nextWeekday(:withWeekOfMonth:andMonthNumber)`: Returns next date with the given weekday and the given week number
- `next(dayOfMonth:monthOfYear:)`: Returns the next day of month preserving smaller components (hour, minute, seconds)
[**Next Chapter**: Compare Dates](#4.CompareDates.md)
================================================
FILE: Documentation/4.Compare_Dates.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Manipulate & Derivate Dates](#3,Manipulate_Dates.md)
- [**Next Chapter**: Date Formatting](#5.Date_Formatting.md)
## Compare Dates
Date comparison is available both via simple math operators (`<,>,<=,>=`) or throught several other functions which allows a more fined grained control of the comparison.
- [4.0 - Compare Dates](4.Compare_Dates.md#standard)
- [4.1 - Extended Comparison with Presets (`isToday, isTomorrow, isSameWeek, isNextYear` etc.)](4.Compare_Dates.md#extended)
- [4.2 - Comparison with Granularity](4.Compare_Dates.md#granularity)
- [4.3 - Check if Date is Close to Another](4.Compare_Dates.md#close)
- [4.4 - Check if Date is Inside another](4.Compare_Dates.md#isinside)
- [4.5 - Check if Date is Inside a Range](4.Compare_Dates.md#range)
<a name="standard"/>
## 4.0 - Compare Dates
Standard comparison between dates can be done using the classic `.compare()` functions or mathematical operators.
SwiftDate also introduces two additional convenience methods:
- `isBeforeDate(_:orEqual:granularity:)` Compares whether the receiver is before/before equal `date` based on their components down to a given unit granularity.
- `isAfterDate(_:orEqual:granularity:)` Compares whether the receiver is after `date` based on their components down to a given unit granularity.
<a name="extended"/>
## 4.1 - Extended Comparison with Presets
While standard comparison between two dates can be done by using mathematical operators, extended comparison is made via `.compare()` method which offer more than 25+ different types of relevant comparisons.
`func compare(_ compareType: DateComparisonType) -> Bool`
takes only one argument:
- `compareType | DateComparisonType`: the type of comparison to make
`DateComparisonType` is an enum which defines the type of comparison to make. This is the actual list of compare functions you can use:
**For Days**
- `isToday`
- `isTomorrow`
- `isYesterday`
- `isSameDay(_ : DateRepresentable)`
**For Weeks**
- `isThisWeek`
- `isNextWeek`
- `isLastWeek`
- `isSameWeek(_: DateRepresentable)`
**For Months**
- `isThisMonth`
- `isNextMonth`
- `isLastMonth`
- `isSameMonth(_: DateRepresentable)`
**For Years**
- `isThisYear`
- `isNextYear`
- `isLastYear`
- `isSameYear(_: DateRepresentable)`
**For Relative Time**
- `isInTheFuture`
- `isInThePast`
- `isEarlier(than: DateRepresentable)`
- `isLater(than: DateRepresentable)`
- `isWeekday`
- `isWeekend`
**For Day Time**
- `isMorning`
- `isAfternoon`
- `isEvening`
- `isNight`
**For TZ**
- `isInDST`
> **CONTRIBUTE!** Have you a new related date you want to be part of this list? Create a [new PR](https://github.com/malcommac/SwiftDate/compare) with the code and unit tests and we'll be happy to add it to the list!
Examples:
```swift
// return false
let _ = DateInRegion().dateAt(.endOfDay).compare(.isTomorrow)
// return true
let _ = DateInRegion() + 7.days).compare(.isNextWeek)
// return true
let _ = DateInRegion().dateAt(.startOfWeek) - 1.days).compare(.isLastWeek)
```
<a name="granularity"/>
## 4.2 - Comparison with Granularity
A more fined grained control for dates comparison can be obtained using the `.compare(toDate:granularity:)` function which offers to return a `ComparisonResult` value that indicates the ordering of two given dates based on their components down to a given unit granularity.
`func compare(toDate refDate: DateInRegion, granularity: Calendar.Component) -> ComparisonResult`
takes two arguments:
- `refDate | DateInRegion`: date to compare against to.
- `granularity | Calendar.Component`: The smallest unit that must, along with all larger units, be less for the given dates
Example:
```swift
```
[^ Top](#index)
<a name="close"/>
## 4.3 - Check if Date is Close to Another
Decides whether a Date is "close by" another one passed in parameter, where "Being close" is measured using a precision argument which is initialized with a 300 second interval (5 minute) or a specified interval.
The function is called `.compareCloseTo(_:precision:)` and takes two arguments:
- `refDate | DateInRegion` reference date compare against to
- `precision | TimeInterval` The precision of the comparison.
Examples:
```swift
let date = DateInRegion("2015-01-01 04:00:00", format: dateFormat, region: regionRome)!
let refDate = DateInRegion("2015-01-01 00:00:00", format: dateFormat, region: regionRome)!
// return true because prevision is set to 5 hours and date differs for only 4 hours
let _ = dateC.compareCloseTo(refDate, precision: 5.hours.timeInterval)
```
<a name="isinside"/>
## 4.4 - Check if Date is Inside another
Compares equality of two given dates based on their components down to a given unit granularity.
The function is called `.isInside(date:granularity:)` and takes two arguments:
- `date`: date to compare
- `granularity`: The smallest unit that must, along with all larger units, be equal for the given dates to be considered the same.
<a name="range"/>
## 4.5 - Check if Date is Inside a Range
Using `.isInRange()` function you can check if a given date is inside the range between two dates.
`func isInRange(date startDate: Date, and endDate: Date, orEqual: Bool = false, granularity: Calendar.Component = .nanosecond) -> Bool`
takes 4 arguments:
- `startDate`: the lower bound limit of the range
- `endDate`: the upper bound limit of the range
- `orEqualt`: true to also validate the equality
- `granularity`: smallest unit that must, along with all larger units, be greater for the given dates
Example:
```swift
let lowerBound = DateInRegion("2018-05-31 23:00:00", format: dateFormat, region: regionRome)!
let upperBound = DateInRegion("2018-06-01 01:00:00", format: dateFormat, region: regionRome)!
let testDate = DateInRegion("2018-06-01 00:02:00", format: dateFormat, region: regionRome)!
// return true, date is inside the hour granularity
let _ = testDate.isInRange(date: lowerBound, and: upperBound, orEqual: true, granularity: .hour)
```
[^ Top](#index)
[**Next Chapter**: Date Formatting](#5.Date_Formatting.md)
================================================
FILE: Documentation/5.Date_Formatting.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Parsing Dates](#Parsing_Dates.md)
- [**Next Chapter**: Time Interval Formatting](#6.TimeInterval_Formatting.md)
## Date Formatting
Formatting dates and transforms to string representation is really easy with SwiftDate. All the major formats are supported and are really easy to configure.
- [5.0 - Format Custom Style](5.Date_Formatting.md#customformatted)
- [5.1 - ISO8601 Formatted String](5.Date_Formatting.md#isoformatted)
- [5.2 - .NET Formatted String](5.Date_Formatting.md#dotnet)
- [5.3 - RSS/AltRSS Formatted String](5.Date_Formatting.md#rss)
- [5.4 - SQL Formatted String](5.Date_Formatting.md#sql)
- [5.5 - Relative/Colloquial Formatted String](5.Date_Formatting.md#colloquial)
- [5.6 - Mixed Date/Time Style](5.Date_Formatting.md#mixeddatetime)
<a name="customformatted"/>
## 5.0 - Format Custom Style
If you need to format a `Date` or `DateInRegion` in a String using a custom format you need to use `toFormat(_:locale:)` function.
`func toFormat(_ format: String, locale: LocaleConvertible?) -> String`
it takes two arguments:
- `format | String`: the format of the string. It's defined by the Unicode DateTime format specs you [can found here](7.Format_UnicodeTable.md).
- `locale | LocaleConvertible?`: Set a non nil value to force the formatter to use a different locale than the one assigned to the date itself (from date's `region.locale` property). Leave it `nil` to use the one assigned by the region itself (or, for plain `Date` instances the one set as `SwiftDate.defaultRegion`).
Example:
```swift
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = DateInRegion(year: 2015, month: 1, day: 15, hour: 20, minute: 00, second: 5, nanosecond: 0, region: rome)
// Even if date's locale is set to `italian` we can still
// print in a different language by passing a non nil locale
// to the function.
let formattedString = date.toFormat("MMM dd yyyy", locale: Locales.english) // "Jan 15 2015"
```
[^ Top](#index)
<a name="isoformatted"/>
## 5.1 - ISO8601 Formatted String
SwiftDate allows you to print date instances using a configurable ISO8601 formatter which is also compatible with older versions of iOS where Apple's own class is not available.
To use the ISO formatter call `.toISO()` function
`func toISO(_ options: ISOFormatter.Options?) -> String`
takes one optional argument:
- `options | ISOFormatter.Options`: allows to customize the format of the output string by defining which kind of date must be into the final string (if you omit it `withInternetDateTime` is used).
`ISOFormatter.Options` defines an `OptionSet` with the following values:
- `withYear`: The date representation includes the year. The format for year is inferred based on the other specified options. If `withWeekOfYear` is specified, `YYYY` is used. Otherwise, `yyyy` is used.
- `withMonth`: The date representation includes the month. The format for month is `MM`.
- `withWeekOfYear`: The date representation includes the week of the year. The format for week of year is `ww`, including the `W` prefix.
- `withDay`: The date representation includes the day. The format for day is inferred based on provided options: If `withMonth` is specified, `dd` is used. If `withWeekOfYear` is specified, `ee` is used. Otherwise, `DDD` is used.
- `withTime`: The date representation includes the time. The format for time is `HH:mm:ss`.
- `withTimeZone`: The date representation includes the timezone. The format for timezone is `ZZZZZ`.
- `withSpaceBetweenDateAndTime`: The date representation uses a space (` `) instead of `T` between the date and time.
- `withDashSeparatorInDate`: The date representation uses the dash separator (`-`) in the date.
- `withFullDate`: The date representation uses the colon separator (`:`) in the time.
- `withFullTime`: The date representation includes the hour, minute, and second.
- `withInternetDateTime`: The format used for internet date times, according to the RFC 3339 standard. Equivalent to specifying `withFullDate`, `withFullTime`, `withDashSeparatorInDate`,
`withColonSeparatorInTime`, and `withColonSeparatorInTimeZone`.
- `withInternetDateTimeExtended`: The format used for internet date times; it's similar to `.withInternetDateTime` but include milliseconds (`yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ`).
- `withoutTZSeparators`: Print timezone of the date without time separator (`+0200` instead of `+02:00`). You can combine it with the following other options: `withInternetDateTimeExtended`, `withInternetDateTime` and when `withTimeZone == false && withTimeZone == true`
Examples:
```swift
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = DateInRegion("2017-07-22 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
// ISO Formatting
let _ = date.toISO() // "2017-07-22T00:00:00+02:00"
let _ = date.toISO([.withFullDate]) // "2017-07-22"
let _ = date.toISO([.withFullDate, .withFullTime, .withDashSeparatorInDate, .withSpaceBetweenDateAndTime]) // "2017-07-22 00:00:00+02:00"
```
[^ Top](#index)
<a name="dotnet"/>
## 5.2 - .NET Formatted String
CSOM DateTime (aka .NET DateTime) is a format defined by Microsoft as the number of 100-nanosecond intervals that have elapsed since 12:00 A.M., January 1, 0001 ([learn more on MSDN documentation page](https://msdn.microsoft.com/en-us/library/dd948679)).
Use the `.toDotNET()` function to create a string from a date instance.
`func toDotNET() -> String`
```swift
let date = "2017-06-20T14:49:19+02:00".toISODate()!
let dotNetString = date.toDotNET() // "/Date(1497962959000+0200)/"
```
[^ Top](#index)
<a name="rss"/>
## 5.3 - RSS/AltRSS Formatted String
RSS and AltRSS formatted string can be generated from an instance of `Date` or `DateInRegion` using the `.toRSS()` function.
`func toRSS(alt: Bool) -> String`
takes only one argument:
- `alt | Bool`: true to print the AltRSS variant, false to print the default RSS formatted string.
Examples:
```swift
let date = ... // 2017-06-20T14:49:19+02:00
let rssString = date.toRSS(alt: false) // "Tue, 20 Jun 2017 14:49:19 +0200"
let altRSSString = date.toRSS(alt: true) // "20 Jun 2017 14:49:19 +0200"
```
[^ Top](#index)
<a name="sql"/>
## 5.4 - SQL Formatted String
To print a SQL formatted string from a date instance you need to use the `.toSQL()` function.
`func toSQL() -> String`
Examples:
```swift
let date = ... // 2015-11-19T22:20:40+01:00
let sqlString = date.toSQL() // "2015-11-19T22:20:40.000+01"
```
[^ Top](#index)
<a name="colloquial"/>
## 5.5 - Relative/Colloquial Formatted String
Colloquial format allows you to produce a human friendly string as result of the difference between a date and a a reference date (typically now).
Examples of colloquial formatted strings are `3 mins ago`, `2 days ago` or `just now`.
SwiftDate supports over 140+ languages to produce colloquial formatted strings; the entire engine behind the library is fully customizable so, if you need, you can override default strings and produce your own variants.
To print a colloquial variant of a string just call `.toRelative()` function from a `Date` or `DateInRegion` instance.
`func toRelative(since: DateInRegion?, style: RelativeFormatter.Style?, locale: LocaleConvertible?) -> String`
It takes three arguments:
- `since | DateInRegion?`: reference date, if you omit it current date is set as reference date.
- `style | RelativeFormatter.Style?`: the style used to print the formatted value. There are 3 styles: `RelativeFormatter.defaultStyle()`, `RelativeFormatter.timeStyle()` and `RelativeFormstter.twitterStyle()`.
- `locale | LocaleConvertible?`: pass non nil value to override the receiver region's `locale` and print the representation using passed locale.
Examples:
```swift
let ago5Mins = DateInRegion() - 5.minutes
let _ = ago5Mins.toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.italian) // "5 minuti fa"
let justNow2 = DateInRegion() - 2.hours
let _ = justNow2.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian) // "2h fa"
let justNow = DateInRegion() - 10.seconds
let _ = justNow.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian) // "ora"
```
You can fully customize the language and results.
See the guide ["Customize Colloquial Formatter"](Customize_ColloquialFormatter.md).
[^ Top](#index)
<a name="mixeddatetime"/>
## 5.6 - Mixed Date/Time Style
If you to format date with different date/time styles you can use the `. dateTimeMixed` formatter option where you can choose a format both for date and time (`DateFormatter.Style`).
```swify
// Just print full date with no time
let formatted = date.toString(.dateTimeMixed(dateStyle: .full, timeStyle: .none))
```
[^ Top](#index)
[**Next Chapter**: Time Interval Formatting](#6.TimeInterval_Formatting.md)
================================================
FILE: Documentation/6.TimeInterval_Formatting.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
- [**Prev Chapter**: Date Formatting](#5.Date_Formatting.md)
## Time Interval Formatting
- [6.0 - Format Interval as String](6.TimeInterval_Formatting.md#format)
- [6.1 - Format Interval as Clock](6.TimeInterval_Formatting.md#clock)
- [6.2 - Convert TimeInterval to Time Units](6.TimeInterval_Formatting.md#express)
The following methods are part of the `TimeInterval` extension provided by SwiftDate.
<a name="format"/>
## 6.0 - Format Interval as String
Formatting a time interval as a string is pretty simple, you just need to call the `.toString()` function of the `TimeInterval`.
It allows you to pick the right formatting options to represent the interval as a valid string.
`func toString(options callback: ((inout ComponentsFormatterOptions) -> Void)? = nil) -> String`
Using a callback function you can configure the formatter as you need.
- `options: ComponentsFormatterOptions`: allows to define the formatting options of the string.
`ComponentsFormatterOptions` is a struct with the following properties:
- `allowsFractionalUnits | Bool`: Fractional units may be used when a value cannot be exactly represented using the available units. For example, if minutes are not allowed, the value “1h 30m” could be formatted as “1.5h”. The default value of this property is false.
- `allowedUnits | NSCalendar.Unit`: Specify the units that can be used in the output. By default `[.year, .month, .weekOfMonth, .day, .hour, .minute, .second]` are used.
- `collapsesLargestUnit | Bool`: A Boolean value indicating whether to collapse the largest unit into smaller units when a certain threshold is met. By default is `false`.
- `maximumUnitCount | Int`: The maximum number of time units to include in the output string. The default value of this property is 0, which does not cause the elimination of any units.
- `zeroFormattingBehavior | DateComponentsFormatter.ZeroFormattingBehavior`: The formatting style for units whose value is 0. By default is `.default`
- `unitsStyle | DateComponentsFormatter.UnitsStyle`: The preferred style for units. By default is `.abbreviated`.
Examples:
```swift
// "2 hours, 5 minutes, 32 seconds"
let _ = (2.hours + 5.minutes + 32.seconds).timeInterval.toString {
$0.unitsStyle = .full
$0.collapsesLargestUnit = false
$0.allowsFractionalUnits = true
}
// "2d 5h"
let _ = (5.hours + 2.days).timeInterval.toString {
$0.unitsStyle = .abbreviated
}
```
[^ Top](#index)
<a name="clock"/>
## 6.1 - Format Interval as Clock
SwiftDate allows to format a `TimeInterval` in the form of a clock or a countdown timer. (ie. `57:00:00`).
To format an interval using this style use `.toClock()` function.
`func toClock(zero: DateComponentsFormatter.ZeroFormattingBehavior)`
takes one argument:
- `zero | DateComponentsFormatter.ZeroFormattingBehavior`: define the representation of the zero values into the destination string. By default is set to `.pad`.
Example:
```swift
let _ = (2.hours + 5.minutes).timeInterval.toClock() // "2:05:00"
let _ = (4.minutes + 50.minutes).timeInterval.toClock(zero: .dropAll) // "54:00"
```
[^ Top](#index)
<a name="express"/>
## 6.2 - Convert TimeInterval to Time Units
While SwiftDate allows to carefully evaluate the differences between two dates and express them with the requested time unit (see ["Get Intervals Between Two Dates"](Date_Manipulation.md#interval), sometimes you may need to calculate this value from an absolute - calendar/locale indipendent - value expressed as `TimeInterval`.
The methods used to convert a an absolute amount of seconds is `toUnits()` (for multiple extraction) and `toUnit()` for single component extraction.
`func toUnits(_ units: Set<Calendar.Component>, to refDate: DateInRegion? = nil) -> [Calendar.Component: Int]`
`func toUnit(_ unit: Calendar.Component, to refDate: DateInRegion? = nil) -> Int?`
takes two arguments:
- `units | Set<Calendar.Component>`: units to extract
- `refDate | DateInRegion`: ending reference date, `nil` means `now()` in the context of the default region set. If `refDate` is `nil` evaluation is made from `now()` and `now() - interval` in the context of the `SwiftDate.defaultRegion` set.
- `calendar | CalendarConvertible`: reference calendar; uses
Example:
```swift
// "[.day: 10, .hour: 12]"
let _ = (36.hours + 2.days + 1.weeks).timeInterval.toUnits([.day, .hour])
```
[^ Top](#index)
================================================
FILE: Documentation/7.Format_UnicodeTable.md
================================================

- [**Index**: Table Of Contents](#Index.md)
## Unicode Table for Date Formats
The following table is a summary of the most common configuration for Unicode Date Formatting.
If you want to know more check out [Date Field Symbol Table](https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) on unicode.org.
| Format | Description | Example |
| ------------- | ------------- | ------------- |
| "y" | 1 digit min year | 1, 42, 2017 |
| "yy" | 2 digit year | 01, 42, 17 |
| "yyy" | 3 digit min year | 001, 042, 2017 |
| "yyyy" | 4 digit min year | 0001, 0042, 2017 |
| "M" | 1 digit min month | 7, 12 |
| "MM" | 2 digit month | 07, 12 |
| "MMM" | 3 letter month abbr. | Jul, Dec |
| "MMMM" | Full month | July, December |
| "MMMMM" | 1 letter month abbr. | J, D |
| "d" | 1 digit min day | 4, 25 |
| "dd" | 2 digit day | 04, 25 |
| "E", "EE", "EEE" | 3 letter day name abbr. | Wed, Thu |
| "EEEE" | full day name | Wednesday, Thursday |
| "EEEEE" | 1 letter day name abbr. | W, T |
| "EEEEEE" | 2 letter day name abbr. | We, Th |
| "a" | Period of day | AM, PM |
| "h" | AM/PM 1 digit min hour | 5, 7 |
| "hh" | AM/PM 2 digit hour | 05, 07 |
| "H" | 24 hr 1 digit min hour | 17, 7 |
| "HH" | 24 hr 2 digit hour | 17, 07 |
| "m" | 1 digit min minute | 1, 40 |
| "mm" | 2 digit minute | 01, 40 |
| "s" | 1 digit min second | 1, 40 |
| "ss" | 2 digit second | 01, 40 |
| "S" | 10th's place of fractional second | 123ms -> 1, 7ms -> 0 |
| "SS" | 10th's & 100th's place of fractional second | 123ms -> 12, 7ms -> 00 |
| "SSS" | 10th's & 100th's & 1,000's place of fractional second | 123ms -> 123, 7ms -> 007 |
[**Index**: Table Of Contents](#Index.md)
================================================
FILE: Documentation/8.Customize_ColloquialFormatter.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
## Customize Colloquial Formatter
*To Be Completed*
================================================
FILE: Documentation/9.ColloquialSupportedLanguages.md
================================================

<a name="index"/>
- [**Index**: Table Of Contents](#Index.md)
## Colloquial Formatter Supported Languages
Currently supported languages for relative formatter are (in long form):
- Northern Sami
- Persian
- Turkmen
- Faroese
- Friulian
- Hebrew
- Polish
- Romanian
- Chinese (Simplified, Singapore)
- Bangla
- Indonesian
- Northern Sami (Finland)
- Slovak
- Uyghur
- Urdu (India)
- Danish
- Walser
- Lao
- Norwegian Nynorsk
- Sinhala
- Portuguese
- Mazanderani
- Kalaallisut
- Spanish (Argentina)
- Bulgarian
- Lower Sorbian
- Scottish Gaelic
- Telugu
- Punjabi
- Pashto
- Nepali
- Albanian
- Estonian
- Welsh
- Konkani
- Colognian
- Lakota
- Malay
- Chinese (Simplified, Macau China)
- Bosnian
- Swahili
- Sakha
- Filipino
- Icelandic
- Finnish
- Tigrinya
- Ngomba
- English
- Slovenian
- Ewe
- Spanish
- Serbian (Latin)
- Khmer
- Japanese
- Turkish
- Sindhi
- Kannada
- Kazakh
- Arabic (United Arab Emirates)
- Uzbek
- Western Frisian
- Cantonese (Simplified)
- German
- Korean
- Lithuanian
- Tongan
- Kyrgyz
- French (Canada)
- Odia
- Belarusian
- Mongolian
- Tamil
- Bosnian (Cyrillic)
- Basque
- Chinese (Simplified, Hong Kong China)
- Chinese (Traditional, Hong Kong China)
- Gujarati
- Galician
- Ukrainian
- Greek
- Spanish (United States)
- Spanish (Mexico)
- Malayalam
- Vietnamese
- Maltese
- Chinese (Traditional)
- Italian
- Croatian
- Assamese
- Latvian
- Chinese
- Urdu
- Catalan
- Czech
- Spanish (Paraguay)
- Norwegian Bokmål
- Burmese
- Uzbek (Cyrillic)
- Amharic
- Afrikaans
- Hungarian
- Breton
- Kabuverdianu
- Luxembourgish
- Russian
- Cantonese (Traditional)
- Marathi
- Irish
- Swedish
- Thai
- Armenian
- Zulu
- Georgian
- Macedonian
- Dzongkha
- Dutch
- Yiddish
- Hindi
- Arabic
- French
- Upper Sorbian
================================================
FILE: Documentation/Index.md
================================================

## Documentation
- Reference Version: **6.3.0**
- Last Update: **Nov 2020**
The following documentation explores all the major features of the library. If you are interested in a detailed, method by method documentation you can refeer to the Jazzy documentation generated by CocoaPods (you can also install in Dash).
### Table Of Contents
### [0 - Info & Install](0.Informations.md)
- [Future Plans](0.Informations.md#futureplans)
- [Compatibility & Requirements](0.Informations.md#compatibility)
- [Installation (CocoaPods,Carthage,SwiftPM](0.Informations.md#installation)
### [1 - Introduction to SwiftDate](1.Introduction.md)
- [1.0 - Dates & Cocoa](1.Introduction.md#datesandcocoa)
- [1.1 - Region & DateInRegion](1.Introduction.md#region_dateinregion)
- [1.2 - The Default Region](1.Introduction.md#default_region)
- [1.3 - Create Region](1.Introduction.md#creating_region)
- [1.4 - Create DateInRegion](1.Introduction.md#creating_dateinregion)
- [1.4.1 - From String](1.Introduction.md#initfromstring)
- [1.4.2 - From Date Components](1.Introduction.md#initfromcomponents)
- [1.4.3 - From TimeInterval](1.Introduction.md#initfromtimeinterval)
- [1.4.4 - From Date](1.Introduction.md#initfromplaindate)
### [2 - Date Parsing](2.Date_Parsing.md)
- [2.0 - Parse Custom Format](2.Date_Parsing.md#autoparsing)
- [2.1 - Parse ISO8601](2.Date_Parsing.md#iso8601)
- [2.2 - Parse .NET](2.Date_Parsing.md#dotnet)
- [2.3 - Parse RSS & AltRSS](2.Date_Parsing.md#rssaltrss)
- [2.4 - Parse SQL](2.Date_Parsing.md#sql)
### [3 - Date Manipulation & Creation](3.Manipulate_Date.md)
- [3.0 - Add & Subtract Time Units from Date](3.Manipulate_Date.md#mathdate)
- [3.1 - Get DateTime Components](3.Manipulate_Date.md#datecomponents)
- [3.2 - Get Interval Between Dates](3.Manipulate_Date.md#interval)
- [3.3 - Convert Date's Region (Locale/TimeZone/Calendar)](3.Manipulate_Date.md#convert)
- [3.4 - Rounding Date](3.Manipulate_Date.md#roundingdate)
- [3.5 - Trouncating Date](3.Manipulate_Date.md#trouncatingdate)
- [3.6 - Set Time in Date](3.Manipulate_Date.md#altertimedate)
- [3.7 - Set DateTime Components](3.Manipulate_Date.md#altercomponents)
- [3.8 - Generate Related Dates (`nextYear, nextWeeekday, startOfMonth, startOfWeek, prevMonth`...)](3.Manipulate_Date.md#relateddates)
- [3.9 - Date at start/end of time component](3.Manipulate_Date.md#startendcomponent)
- [3.10 - Enumerate Dates](3.Manipulate_Date.md#enumeratedates)
- [3.11 - Enumerate Dates for Weekday in Range](3.Manipulate_Date.md#enumerateweekdays)
- [3.12 - Random Dates](3.Manipulate_Date.md#randomdates)
- [3.13 - Sort Dates](3.Manipulate_Date.md#sort)
- [3.14 - Get the next weekday](3.Manipulate_Date.md#nextWeekDay)
- [3.15 - Get date at given week number/weekday](3.Manipulate_Date.md#dateAtWeeknumberWeekday)
- [3.16 - Difference between dates with components](3.Manipulate_Date.md#differenceBetweenDates)
- [3.17 - Create date at components preserving small components](3.Manipulate_Date.md#dateAtComponents)
- [3.18 - Date at given weekday after # weeks](3.Manipulate_Date.md#dateAfterWeeks)
- [3.19 - Next Date](3.Manipulate_Date.md#nextDate)
### [4 - Compare Dates](4.Compare_Dates.md)
- [4.0 - Compare Dates](4.Compare_Dates.md#standard)
- [4.1 - Extended Comparison with Presets (`isToday, isTomorrow, isSameWeek, isNextYear` etc.)](4.Compare_Dates.md#extended)
- [4.2 - Comparison with Granularity](4.Compare_Dates.md#granularity)
- [4.3 - Check if Date is Close to Another](4.Compare_Dates.md#close)
- [4.4 - Check if Date is Inside a Range](4.Compare_Dates.md#range)
### [5 - Date Formatting](5.Date_Formatting.md)
- [5.0 - Format Custom Style](5.Date_Formatting.md#customformatted)
- [5.1 - ISO8601 Formatted String](5.Date_Formatting.md#isoformatted)
- [5.2 - .NET Formatted String](5.Date_Formatting.md#dotnet)
- [5.3 - RSS/AltRSS Formatted String](5.Date_Formatting.md#rss)
- [5.4 - SQL Formatted String](5.Date_Formatting.md#sql)
- [5.5 - Relative/Colloquial Formatted String](5.Date_Formatting.md#colloquial)
- [5.6 - Mixed Date/Time Style](5.Date_Formatting.md#mixeddatetime)
### [6 - Time Intervals Formatting](6.TimeInterval_Formatting.md)
- [6.0 - Format Interval as String](6.TimeInterval_Formatting.md#format)
- [6.1 - Format Interval as Clock](6.TimeInterval_Formatting.md#clock)
- [6.2 - Convert TimeInterval to Time Units](6.TimeInterval_Formatting.md#express)
#### Other Links
- [7 - Unicode Table for Date Formats](7.Format_UnicodeTable.md)
- [8 - Customize Colloquial Formatter](8.Customize_ColloquialFormatter.md)
- [9 - Colloquial Formatter Supported Languages](9.ColloquialSupportedLanguages.md)
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2018 Daniele Margutti
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Package.swift
================================================
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "SwiftDate",
defaultLocalization: "it",
platforms: [
.macOS(.v10_15), .iOS(.v13), .watchOS(.v6), .tvOS(.v13)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(name: "SwiftDate", targets: ["SwiftDate"])
],
dependencies: [],
targets: [
.target(
name: "SwiftDate",
dependencies: [],
resources: [
.copy("Formatters/RelativeFormatter/langs"),
.process("Resources")
]),
.testTarget(
name: "SwiftDateTests",
dependencies: ["SwiftDate"])
]
)
================================================
FILE: Playgrounds/SwiftDate.playground/Pages/Compare Dates.xcplaygroundpage/Contents.swift
================================================
import Foundation
import SwiftDate
================================================
FILE: Playgrounds/SwiftDate.playground/Pages/Date Formatting.xcplaygroundpage/Contents.swift
================================================
import Foundation
import SwiftDate
/*:
## 5.0 - Format Custom Style
If you need to format a `Date` or `DateInRegion` in a String using a custom format you need to use `toFormat(_:locale:)` function.
*/
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date1 = DateInRegion(year: 2015, month: 1, day: 15, hour: 20, minute: 00, second: 5, nanosecond: 0, region: rome)
// Even if date's locale is set to `italin` we can still
// print in a different language by passing a non nil locale
// to the function.
let formattedString = date1.toFormat("MMM dd yyyy", locale: Locales.english) // "Jan 15 2015"
/*:
## 5.1 - ISO8601 Formatted String
SwiftDate allows to print date instances using a configurable ISO8601 formatter which is also compatible with older version of iOS where the Apple's own class is not available.
To use the ISO formatter call `.toISO()` function
*/
let date2 = DateInRegion("2017-07-22 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: rome)!
// ISO Formatting
let date2_str1 = date2.toISO() // "2017-07-22T00:00:00+02:00"
let date2_str2 = date2.toISO([.withFullDate]) // "2017-07-22"
let date2_str3 = date2.toISO([.withFullDate, .withFullTime, .withDashSeparatorInDate, .withSpaceBetweenDateAndTime]) // "2017-07-22 00:00:00+02:00"
/*:
## 5.2 - .NET Formatted String
CSOM DateTime (aka .NET DateTime) is a format defined by Microsoft as the number of 100-nanosecond intervals that have elapsed since 12:00 A.M., January 1, 0001 ([learn more on MSDN documentation page](https://msdn.microsoft.com/en-us/library/dd948679)).
Use the `.toDotNET()` function to create a string from a date instance.
*/
let date3 = "2017-06-20T14:49:19+02:00".toISODate()!
let date3_dotNetString = date3.toDotNET() // "/Date(1497962959000+0200)/"
/*:
## 5.3 - RSS/AltRSS Formatted String
RSS and AltRSS formatted string can be generated from an instance of `Date` or `DateInRegion` using the `.toRSS()` function.
*/
let rssString = date3.toRSS(alt: false) // "Tue, 20 Jun 2017 14:49:19 +0200"
let altRSSString = date3.toRSS(alt: true) // "20 Jun 2017 14:49:19 +0200"
/*:
## 5.4 - SQL Formatted String
To print SQL formatted string from a date instance you need to use the `.toSQL()` function.
*/
let sqlString = date3.toSQL() // "2015-11-19T22:20:40.000+01"
/*:
## 5.5 - Relative/Colloquial Formatted String
Colloquial format allows you to produce human friendly string as result of the difference between a date and a a reference date (typically now).
Examples of colloquial formatted strings are `3 mins ago`, `2 days ago` or `just now`.
SwiftDate supports over 140+ languages to produce colloquial formatted strings; the entire engine behind the library is fully customizable so, if you need, you can override default strings and produce your own variants.
To print a colloquial variant of a string just call `.toRelative()` function from a `Date` or `DateInRegion` instance.
*/
let ago5Mins = DateInRegion() - 5.minutes
_ = ago5Mins.toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.italian) // "5 minuti fa"
let justNow2 = DateInRegion() - 2.hours
_ = justNow2.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian) // "2h fa"
let justNow = DateInRegion() - 10.seconds
_ = justNow.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian) // "ora"
================================================
FILE: Playgrounds/SwiftDate.playground/Pages/Date Manipulation.xcplaygroundpage/Contents.swift
================================================
import Foundation
import SwiftDate
/*:
## 3.0 - Add & Subtract Time Units from Date
SwiftDate allows to use numbers to work with time components in dates. By extending the `Int` type it defines a list of time units:
- `nanoseconds`
- `seconds`
- `minutes`
- `days`
- `weeks`
- `months`
- `quarters`
- `years`
You can use a value followed by one of these unit specifications to add or remove the component from a date.
So, for example, you can produce a date which can be a mix of intuitive math operations:
*/
let date1 = DateInRegion() + 1.years
let date2 = DateInRegion() - 2.minutes
let date3 = date2 + 3.hours - 5.minutes + 1.weeks
/*:
> IMPORTANT NOTE: These values are converted automatically to `DateComponents` evaluated in the same context of the target `Date` or `DateInRegion`'s `calendar`.
Another way to add time components to a date is to use the `dateByAdding()` function:
*/
let date4 = DateInRegion().dateByAdding(5, .month)
/*:
## 3.1 - Get DateTime Components Components
With SwiftDate you have several convenience properties to inspect each datetime unit of a date, both for `Date` and `DateInRegion`.
These properties are strictly correlated to the date's calendar (and some also with locale): if you are manipulating a `DateInRegion` remember these properties return values in the context of the associated `region` attributes (Locale, TimeZone and Calendar).
> **IMPORTANT NOTE**: If you are working with plain `Date` properties uses as reference the currently set `SwiftDate.defaultRegion` which, unless you modify it, is set to Gregorian/UTC/Device's Language.
*/
let regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.english)
let date5 = DateInRegion(components: {
$0.year = 2015
$0.month = 6
$0.day = 1
$0.hour = 23
}, region: regionNY)!
print("Origin date in NY: \(date5.hour):\(date5.minute) of \(date5.day)/\(date5.month)")
print("Month is \(date5.monthName(.default).uppercased())")
print("Month in italian is \(date5.convertTo(locale: Locales.italian).monthName(.default).uppercased())")
// We can convert it to UTC and get the same properties which are now updated!
let date5InUTC = date5.convertTo(region: Region.UTC)
print("Converted date in UTC: \(date5InUTC.hour):\(date5InUTC.minute) of \(date5InUTC.day)/\(date5InUTC.month)")
/*:
## 3.2 - Get Interval Between Dates
You can get the interval between two dates and express it in form of time units easily with SwiftDate.
*/
let format = "yyyy-MM-dd HH:mm:ss"
let date6 = DateInRegion("2017-07-22 00:00:00", format: format, region: regionNY)!
let date7 = DateInRegion("2017-07-23 12:00:00", format: format, region: regionNY)!
let hours = date6.getInterval(toDate: date7, component: .hour) // 36 hours
let days = date6.getInterval(toDate: date7, component: .day) // 1 day
/*:
## 3.3 - Convert Date's Region (Locale/TimeZone/Calendar)
`DateInRegion` can be converted easily to anothe region just using `.convertTo(region:)` or `.convertTo(calendar: timezone:locale:)` functions.
- `convertTo(region:)` convert the receiver date to another region. Region may include a different time zone for example, or a locale.
- `convertTo(calendar:timezone:locale:)` allows to convert the receiver date instance to a specific calendar/timezone/locale. All parameters are optional and only non-nil parameters alter the final region. For a nil param the current receiver's region attribute is kept.
Examples:
*/
// Create a date in NY: "2001-09-11 12:00:05"
let date8 = DateInRegion(components: {
$0.year = 2001
$0.month = 9
$0.day = 11
$0.hour = 12
$0.minute = 0
$0.second = 5
}, region: regionNY)!
// Convert to GMT timezone (and locale)
let date8inGMT = date8.convertTo(region: Region.UTC) // date is now: "2001-09-11 16:00:05"
print(date8inGMT.toISO())
/*:
## 3.4 - Rounding Date
`.dateRoundedAt()` function allows to round a given date time to the passed style: off, up or down.
*/
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
// Round down 10mins
let date9 = DateInRegion("2017-07-22 00:03:50", format: format, region: regionRome)!
let r10min = date9.dateRoundedAt(.to10Mins)
/*:
## 3.5 - Trouncating Date
Sometimes you may need to truncate a date by zeroing all values below certain time unit. `.dateTruncated(from:) and .dateTruncated(to:)` functions can be used for this scope.
#### Truncating From
It creates a new instance by truncating the components starting from given components down the granurality.
`func dateTruncated(from component: Calendar.Component) -> DateInRegion?`
#### Truncated At
It creates a new instance by truncating all passed components.
`func dateTruncated(at components: [Calendar.Component]) -> DateInRegion?`
*/
let date10 = "2017-07-22 15:03:50".toDate("yyyy-MM-dd HH:mm:ss", region: regionRome)!
let truncatedTime = date10.dateTruncated(from: .hour)
let truncatedCmps = date10.dateTruncated(at: [.month, .day, .minute])
/*:
## 3.6 - Set Time in Date
Sometimes you may need to alter time in a specified date. SwiftDate allows you to perform this using `.dateBySet(hour:min:secs:options:)` function.
*/
let date11 = DateInRegion("2010-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
let alteredDate = date11.dateBySet(hour: 20, min: 13, secs: 15)
/*:
## 3.7 - Set DateTime Components
SwiftDate allows you to return new date representing the date calculated by setting a specific components of a given date to given values, while trying to keep lower components the same (altering more components at the same time may result in different-than-expected results, this because lower components maybe need to be recalculated).
*/
let date12 = date11.dateBySet([.month: 1, .day: 1, .hour: 9, .minute: 26, .second: 0])
/*:
## 3.8 - Generate Related Dates (`nextYear, nextWeeekday, startOfMonth, startOfWeek, prevMonth`...)
Sometimes you may need to generate a related date from a specified instance; maybe the next sunday, the first day of the next week or the start datetime of a date.
SwiftDate includes 20+ different "interesting" dates you can obtain by calling `.dateAt()` function from any `Date` or `DateInRegion` instance.
*/
// Return today's datetime at 00:00:00
let date13 = DateInRegion().dateAt(.startOfDay)
// Return today's datetime at 23:59:59
let date14 = DateInRegion().dateAt(.endOfDay)
// Return the date at the start of this week
let date15 = DateInRegion().dateAt(.startOfWeek)
// Return current time tomorrow
let date16 = DateInRegion().dateAt(.tomorrow)
// Return the next sunday from specified date
let date17 = date16.dateAt(.nextWeekday(.sunday))
// and so on...
/*:
## 3.9 - Date at start/end of time component
Two functions called `.dateAtStartOf()` and `.dateAtEndOf()` allows you to get the related date from a `Date`/`DateInRegion` instance moved at the start or end of the specified component.
*/
// Return today's date at 23:59:59
let date18 = DateInRegion().dateAtEndOf(.day)
// Return the first day's date of the month described in date1
let date19 = DateInRegion().dateAtStartOf(.month)
// Return the first day of the current week at 00:00
let date20 = DateInRegion().dateAtStartOf([.week, .day])
/*:
## 3.10 - Enumerate Dates
Dates enumeration function allows you to generate a list of dates in a closed date intervals incrementing date components by a fixed or variable interval at each new date.
- **VARIABLE INCREMENT** `static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: ((DateInRegion) -> (DateComponents))) -> [DateInRegion]`
- **FIXED INCREMENT** `static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: DateComponents) -> [DateInRegion]`
*/
let toDate = DateInRegion()
let fromDate = toDate - 30.days
let dates = DateInRegion.enumerateDates(from: fromDate, to: toDate, increment: DateComponents.create {
$0.hour = 1
$0.minute = 30
})
/*:
## 3.11 - Random Dates
SwiftDate exposes a set of functions to generate a random date or array of random dates in a bounds.
There are several functions to perform this operation:
*/
let randomDates = DateInRegion.randomDates(count: 40,
between: fromDate,
and: toDate)
let randomDate = DateInRegion.randomDate(withinDaysBeforeToday: 7, region: regionRome)
/*:
## 3.12 - Sort Dates
Two conveniences function allows you to sort an array of dates by newest or oldest. Naming is pretty simple:
*/
let orderedByNewest = DateInRegion.sortedByNewest(list: randomDates)
let oldestDate = DateInRegion.oldestIn(list: randomDates)
================================================
FILE: Playgrounds/SwiftDate.playground/Pages/Date Parsing.xcplaygroundpage/Contents.swift
================================================
import Foundation
import SwiftDate
/*:
Parsing dates is pretty straighforward in SwiftDate; library can parse strings with dates automatically by recognizing one of the most common patterns. Moreover you can provide your own formats or use one of the built-in parsers.
In the following chapter you will learn how to transform a string to a date.
## 2.0 - Parse Custom Format
The easiest way to transform an input string to a valid date is to use one of the `.toDate()` functions available as `String`'s instance extensions. The purpose of these method is to get the best format can represent the input string and use it to generate a valid `DateInRegion`.
As like other libs like moment.js, SwiftDate has a list of built-in formats it can use in order to obtain valid results.
You can get the list of these formats by calling `SwiftDate.autoFormats`.
The order of this array is important because SwiftDate iterates over this list until a valid date is returned (the order itself allows the lib to reduce the list of false positives).
*/
let itRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let enRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
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: itRegion)
let srcString = "July 15 - 15:30"
// it returns nil because itRegion has Locales.italian
let date4 = srcString.toDate(["yyyy-MM-dd", "MMM dd '-' HH:mm"], region: itRegion)
// it's okay because enRegion has locale set to english
let date5 = srcString.toDate(["yyyy-MM-dd", "MMM dd '-' HH:mm"], region: enRegion)
/*:
> **PERFORMANCES** In order to preserve performances you should pass the `format` parameter if you know the input format.
> **LOCALE PARAMETER** If you use readable unit names (like `MMM` for months) be sure to select the right locale inside the `region` parameter in order to get valid results.
## 2.1 Parse ISO8601
A special note must be made for ISO8601. This format (the extended version and all its variants) may include the timezone information.
If you need to parse an ISO8601 datetime you should therefore use the `.toISODate()` function of `String` in order to get a complete result.
> **NOTE** ISO8601 parser (via `.toISODate()` func) is capable of recognizing all the variants of the 8601 formats; if your date is in this formt use this function instead of passing custom time format. It will lead in better results.
*/
let date = "2017-08-05T16:04:03+02:00".toISODate(region: Region.ISO)!
// returned date's region.zone is GMT+2 not the default's Region.ISO's GMT0.
// This because value is read from the string itself.
print("Date is located in \(date.region)")
/*:
## 2.2 - Parse .NET
CSOM DateTime (aka .NET DateTime) is a format defined by Microsoft as the number of 100-nanosecond intervals that have elapsed since 12:00 A.M., January 1, 0001 ([learn more on MSDN documentation page](https://msdn.microsoft.com/en-us/library/dd948679)).
You can parse a CSOM datetime string using the `toDotNETDate()` function.
> **NOTE:** As for ISO8601 even .NET datetime may contain information about timezone. When you set the region as input parameter of the conversion function remember: it will be overriden by default parsed timezone (GMT if not specified). Region is used for `locale` only.
*/
// This is the 2017-07-22T18:27:02+02:00 date.
let date6 = "/Date(1500740822000+0200)/".toDotNETDate()!
print("Date is \(date6.toISO())")
/*:
## 2.3 - Parse RSS & AltRSS
RSS & AltRSS datetime format are used in RSS feed files. Parsing in SwiftDate is pretty easy; just call the `.toRSSDate()` function.
> **NOTE:** As for ISO8601 even RSS/AltRSS datetime contain information about timezone. When you set the region as input parameter of the conversion function remember: it will be overriden by default parsed timezone (GMT if not specified). Region is used for `locale` only.
*/
// This is the ISO8601: 2017-07-22T18:27:02+02:00
let date7 = "Sat, 22 Jul 2017 18:27:02 +0200".toRSSDate(alt: false)!
print("RSS Date is \(date7.toISO())")
// NOTE:
// Even if we set a random region with a custom locale,
// calendar and timezone, final parsed date still correct.
// Only the locale parameter is set.
// Other region's parameter are ignored and read from the string itself.
let regionAny = Region(calendar: Calendars.buddhist, zone: Zones.indianMayotte, locale: Locales.italian)
let date8 = "Tue, 20 Jun 2017 14:49:19 +0200".toRSSDate(alt: false, region: regionAny)!
print("RSS Date is \(date8.toISO())")
/*:
## 2.4 - Parse SQL
SQL datetime is the format used in all SQL-compatible schemas.
You can parse a string in this format using `toSQLDate()` function.
*/
// Date in ISO is 2016-04-14T11:58:58+02:00
let date9 = "2016-04-14T11:58:58.000+02".toSQLDate()!
print("SQL Date is \(date9.toISO())")
================================================
FILE: Playgrounds/SwiftDate.playground/Pages/Introduction.xcplaygroundpage/Contents.swift
================================================
import Foundation
import SwiftDate
/*:
## 1.0 - Dates & Cocoa
Generally when you talk about dates you are brought to think about a particular instance of time in a particular location of the world. However, in order to get a fully generic implementationn Apple made `Date` fully indipendent from any particular geographic location, calendar or locale.
A plain `Date` object just represent an absolute value: in fact it count the number of seconds elapsed since January 1, 2001.
This is what we call **Universal Time because it represent the same moment everywhere around the world**.
You can see absolute time as the moment that someone in the USA has a telephone conversation with someone in Dubai; both have that conversation at the same moment (the absolute time) but the local time will be different due to time zones, different calendars, alphabets or notation methods.
*/
let now = Date()
print("\(now.timeIntervalSinceReferenceDate) seconds elapsed since Jan 1, 2001 @ 00:00 UTC")
/*:
However, we often need to represent a date in a more specific context: **a particular place in the world, printing them using the rules of a specified locale and more**.
In order to accomplish it we need to introduce several other objects: a `Calendar`, a `TimeZone` and a `Locale`; combining these attributes we're finally ready to provide a **representation** of the date into the real world.
SwiftDate allows you to parse, create, manipulate and inspect dates in an easy and more natural way than Cocoa itself.
## 1.1 - Region & DateInRegion
In order to simplify the date management in a specific context SwiftDate introduces two simple structs:
- `Region` is a struct which define a region in the world (`TimeZone`) a language (`Locale`) and reference calendar (`Calendar`).
- `DateInRegion` represent an absolute date in a specific region. When you work with this object all components are evaluated in the context of the region in which the object was created. Inside a DateInRegion you will have an `absoluteDate` and `region` properties.
## 1.2 - The Default Region
In SwiftDate you can work both with `DateInRegion` and `Date` instances.
Even plain Date objects uses `Region` when you need to extract time units, compare dates or evaluate specific operations.
However this is a special region called **Default Region** and - by default - it has the following attributes:
- **Time Zone** = GMT - this allows to keep a coerent behaviour with the default Date managment unless you change it.
- **Calendar** = current's device calendar (auto updating)
- **Locale** = current's device lcoale (auto updating)
While it's a good choice to always uses `DateInRegion` you can also work with `Date` by changing the default region as follow:
*/
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
SwiftDate.defaultRegion = rome
/*:
Since now all `Date` instances uses `rome` as default region both for parsing and evaluating date components:
*/
let dateInRome = "2018-01-01 00:00:00".toDate()!
print("Current year is \(dateInRome.year) and hour is \(dateInRome.hour)") // "Current year is 2018 and hour is 0\n"
/*:
We can still convert this date to the default absolute representation in UTC using the `convertTo(region:)` function:
*/
let dateInUTC = dateInRome.convertTo(region: Region.UTC)
print("Current year is \(dateInUTC.year) and hour is \(dateInUTC.hour)") // "Current year is 2017 and hour is 23\n"
/*:
Be careful while setting the default region.
We still reccomends to use the `DateInRegion` instances instead, this allows you to read the region explicitly.
## 1.3 - Create Region
As you seen from previous example creating a new `Region` is pretty straightforward; you need to specify the locale (used to print localized values like month or weekday name of the date), a timezone and a calendar (usually gregorian).
Region instances accept the following parameters in form of protocols:
- Time zone as `ZoneConvertible` conform object. You can pass both a `TimeZone` instance or any of the predefined timezones region defined inside the `Zones` enumeration.
- Calendar as `CalendarConvertible` conform object. You can pass both a `Calendar` instance or any of the predefined calendars available inside the `Calendars` enumeration.
- Locale as `LocaleConvertible` conform object. You can pass a both a `Locale` instance or any of the predefined locales available inside the `Locales` enumeration.
Using `Zones`, `Calendars` and `Locales` enumeration values is the easiest way to create a region and compiler can also suggest you the best match for your search.
The following example create two regions with different attributes:
*/
let regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.englishUnitedStates)
let regionTokyo = Region(calendar: Calendars.gregorian, zone: Zones.asiaTokyo, locale: Locales.japanese)
/*:
## 1.4 - Create DateInRegion
Now you are ready to create a new `DateInRegion`. There are many different ways to create a new date: parsing a string, setting time components, derivating it from another date or from a given time intervals.
Each initialization method require a region parameter which defines the region in which the date is expressed (default values may vary based upon the init and are listed below).
### 1.4.1 - From String
The most common case is to parse a string and transform it to a date. As you know `DateFormatter` is an expensive object to create and if you need to parse multiple strings you should avoid creating a new instance in your loop.
Don't worry: using SwiftDate the library helps you by reusing its own parser, shared along the caller thread.
`DateInRegion`'s `init(_:format:region)` can be used to initialize a new date from a string (various shortcut are available under the `toXXX` prefix of `String` extensions.
This object takes three parameters:
- the `string` to parse (`String`)
- the format of the string (`String`): this represent the format in which the string is expressed. It's a unicode format ([See the table of fields](7.Format_UnicodeTable.md)). If you skip this parameter SwiftDate attempts to parse the date using one of the built-in formats defined in `SwiftDate.autoFormats` array. If you know the format of the date you should explicitly set it in order to get better performances.
- the `region` in which the date is expressed (`Region`). By default is set to `SwiftDate.defaultRegion`.
*/
let date1 = DateInRegion("2016-01-05", format: "yyyy-MM-dd", region: regionNY)
let date2 = DateInRegion("2015-09-24T13:20:55", region: regionNY)
/*:
### 1.4.2 - From Components
You can create a `DateInRegion` also by setting the date components.
The following method create a date from `DateComponents` instance passed via builder pattern:
*/
let date3 = DateInRegion(components: {
$0.year = 2018
$0.month = 2
$0.day = 1
$0.hour = 23
}, region: regionNY)
/*:
You can also instance it by passing single (optional) components:
*/
let date4 = DateInRegion(year: 2015, month: 2, day: 4, hour: 20, minute: 00, second: 00, region: regionNY)
/*:
### 1.4.3 - From TimeInterval
As plain `Date` you can create a new `DateInRegion` just passing an absolute time interval which represent the seconds/milliseconds from Unix epoch.
The following method create a date 1 year after the Unix Epoch (1971-01-01T00:00:00Z):
*/
let date5 = DateInRegion(seconds: 1.years.timeInterval, region: regionNY)
let date6 = DateInRegion(milliseconds: 5000, region: regionNY)
/*:
### 1.4.4 - From Date
Finally you can init a new `DateInRegion` directly specifyng an absolute `Date` and a destination region:
*/
let absoluteDate: Date = (Date() - 2.months).dateAt(.startOfDay)
let date7 = DateInRegion(absoluteDate, region: regionNY)
================================================
FILE: Playgrounds/SwiftDate.playground/contents.xcplayground
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='ios' display-mode='raw' executeOnSourceChanges='false'>
<pages>
<page name='Introduction'/>
<page name='Date Parsing'/>
<page name='Date Manipulation'/>
<page name='Compare Dates'/>
<page name='Date Formatting'/>
</pages>
</playground>
================================================
FILE: README.md
================================================
<p align="center" >
<img src="banner.png" width=300px alt="SwiftDate" title="SwiftDate">
</p>
<p align="center"><strong>Toolkit to parse, validate, manipulate, compare and display dates, time & timezones in Swift.</strong></p>
## What's This?
SwiftDate is the **definitive toolchain to manipulate and display dates and time zones** on all Apple platform and even on Linux and Swift Server Side frameworks like Vapor or Kitura.
**Over 3 million of downloads on [CocoaPods](https://cocoapods.org/pods/SwiftDate).**
From simple date manipulation to complex business logic SwiftDate maybe the right choice for your next project.
- [x] **Easy Date Parsing** (custom formats, iso8601, rss & many more)
- [x] **Easy Date Formatting** even with colloquial formatter and 140+ supported languages
- [x] **Easy math operations with time units** (`2.hours + 5.minutes`...)
- [x] **Intuitive components extraction** (`day, hour, nearestHour, weekdayNameShort` etc.)
- [x] **Derivated dates generation** (`nextWeek, nextMonth, nextWeekday, tomorrow`...)
- [x] Over **20+ fine grained date comparison** functions (`isToday, isTomorrow, isSameWeek, isNextYear`...)
- [x] Swift 4's **Codable Support**
- [x] **Random dates** generation
- [x] **Fine grained date enumeration** functions
- [x] **Time period** support
- [x] **Convert TimeIntervals** to other units (`2.hours.toUnits(.minutes)`)
and of course...
- **IT'S TESTED!**. As 5.x the project has 90% of code coverage (want help us? write some unit tests and make a PR)
- **IT'S FULLY DOCUMENTED!**, [both with a complete guide](/Documentation/Index.md) and with Jazzy!
- **WE LOVE PLAYGROUND!** [Check out](/Playgrounds/SwiftDate.playground) our interactive playground!
## Start with SwiftDate
The entire library is fully documented both via XCode method inspector and a complete markdown documentation you can found below.
- → **[Full Documentation](/Documentation/Index.md)**
- → **[Requirements, Install, License & More](/Documentation/0.Informations.md)**
- → **[Upgrading from SwiftDate 4](/Documentation/10.Upgrading_SwiftDate4.md)**
### Explore SwiftDate
From simple date manipulation to complex business logic SwiftDate maybe the right choice for your next project.
Let me show to you the main features of the library:
- [Date Parsing](#1)
- [Date Manipulation](#2)
- [Date Comparsion](#3)
- [Date Creation with Region (Timezone, Calendar & Locale)](#4)
- [Derivated Dates](#5)
- [Components Extraction](#6)
- [Switch between timezones/locale and calendars](#7)
- [Date Formatting](#8)
- [Relative Date Formatting (fully customizable!)](#9)
- [Codable Support](#10)
- [Time Periods](#11)
<a name="1"/>
### 1. Date Parsing
SwiftDate can recognize all the major datetime formats automatically (ISO8601, RSS, Alt RSS, .NET, SQL, HTTP...) and you can also provide your own formats.
Creating a new date has never been so easy!
```swift
// All default datetime formats (15+) are recognized automatically
let _ = "2010-05-20 15:30:00".toDate()
// You can also provide your own format!
let _ = "2010-05-20 15:30".toDate("yyyy-MM-dd HH:mm")
// All ISO8601 variants are supported too with timezone parsing!
let _ = "2017-09-17T11:59:29+02:00".toISODate()
// RSS, Extended, HTTP, SQL, .NET and all the major variants are supported!
let _ = "19 Nov 2015 22:20:40 +0100".toRSS(alt: true)
```
<a name="2"/>
### 2. Date Manipulation
Date can be manipulated by adding or removing time components using a natural language; time unit extraction is also easy and includes the support for timezone, calendar and locales!
Manipulation can be done with standard math operators and between dates, time intervals, date components and relevant time units!
```swift
// Math operations support time units
let _ = ("2010-05-20 15:30:00".toDate() + 3.months - 2.days)
let _ = Date() + 3.hours
let _ = date1 + [.year:1, .month:2, .hour:5]
let _ = date1 + date2
// extract single time unit components from date manipulation
let over1Year = (date3 - date2).year > 1
```
<a name="3"/>
### 3. Date Comparison
SwiftDate include an extensive set of comparison functions; you can compare two dates by granularity, check if a date is an particular day, range and practically any other comparison you ever need.
Comparison is also available via standard math operators like (`>, >=, <, <=`).
```swift
// Standard math comparison is allowed
let _ = dateA >= dateB || dateC < dateB
// Complex comparisons includes granularity support
let _ = dateA.compare(toDate: dateB, granularity: .hour) == .orderedSame
let _ = dateA.isAfterDate(dateB, orEqual: true, granularity: .month) // > until month granularity
let _ = dateC.isInRange(date: dateA, and: dateB, orEqual: true, granularity: .day) // > until day granularity
let _ = dateA.earlierDate(dateB) // earlier date
let _ = dateA.laterDate(dateB) // later date
// Check if date is close to another with a given precision
let _ = dateA.compareCloseTo(dateB, precision: 1.hours.timeInterval
// Compare for relevant events:
// .isToday, .isYesterday, .isTomorrow, .isWeekend, isNextWeek
// .isSameDay, .isMorning, .isWeekday ...
let _ = date.compare(.isToday)
let _ = date.compare(.isNight)
let _ = date.compare(.isNextWeek)
let _ = date.compare(.isThisMonth)
let _ = date.compare(.startOfWeek)
let _ = date.compare(.isNextYear)
// ...and MORE THAN 30 OTHER COMPARISONS BUILT IN
// Operation in arrays (oldestIn, newestIn, sortedByNewest, sortedByOldest...)
let _ = DateInRegion.oldestIn(list: datesArray)
let _ = DateInRegion.sortedByNewest(list: datesArray)
```
<a name="4"/>
### 4. Date Creation with Region (Timezone, Calendar & Locale)
You can create new dates from a string, time intervals or using date components. SwiftDate offers a wide set of functions to create and derivate your dates even with random generation!
```swift
// All dates includes timezone, calendar and locales!
// Create from string
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date1 = DateInRegion("2010-01-01 00:00:00", region: rome)!
// Create date from intervals
let _ = DateInRegion(seconds: 39940, region: rome)
let _ = DateInRegion(milliseconds: 5000, region: rome)
// Date from components
let _ = DateInRegion(components: {
$0.year = 2001
$0.month = 9
$0.day = 11
$0.hour = 12
$0.minute = 0
}, region: rome)
let _ = DateInRegion(year: 2001, month: 1, day: 5, hour: 23, minute: 30, second: 0, region: rome)
// Random date generation with/without bounds
let _ = DateInRegion.randomDate(region: rome)
let _ = DateInRegion.randomDate(withinDaysBeforeToday: 5)
let _ = DateInRegion.randomDates(count: 50, between: lowerLimitDate, and: upperLimitDate, region: rome)
```
<a name="5"/>
### 5. Derivated Dates
Date can be also generated starting from other dates; SwiftDate includes an extensive set of functions to generate.
Over 20 different derivated dates can be created easily using `dateAt()` function.
```swift
let _ = DateInRegion().dateAt(.endOfDay) // today at the end of the day
// Over 20 different relevant dates including .startOfDay,
// .endOfDay, .startOfWeek, .tomorrow, .nextWeekday, .nextMonth, .prevYear, .nearestMinute and many others!
let _ = dateA.nextWeekday(.friday) // the next friday after dateA
let _ = (date.dateAt(.startOfMonth) - 3.days)
let _ = dateA.compare(.endOfWeek)
// Enumerate dates in range by providing your own custom
// increment expressed in date components
let from = DateInRegion("2015-01-01 10:00:00", region: rome)!
let to = DateInRegion("2015-01-02 03:00:00", region: rome)!
let increment2 = DateComponents.create {
$0.hour = 1
$0.minute = 30
$0.second = 10
}
// generate dates in range by incrementing +1h,30m,10s each new date
let dates = DateInRegion.enumerateDates(from: fromDate2, to: toDate2, increment: increment2)
// Get all mondays in Jan 2019
let mondaysInJan2019 = Date.datesForWeekday(.monday, inMonth: 1, ofYear: 2019)
// Altering time components
let _ = dateA.dateBySet(hour: 10, min: 0, secs: 0)
// Truncating a date
let _ = dateA.dateTruncated(at: [.year,.month,.day]) // reset all time components keeping only date
// Rounding a date
let _ = dateA.dateRoundedAt(.toMins(10))
let _ = dateA.dateRoundedAt(.toFloor30Mins)
// Adding components
let _ = dateA.dateByAdding(5,.year)
// Date at the start/end of any time component
let _ = dateA.dateAtEndOf(.year) // 31 of Dec at 23:59:59
let _ = dateA.dateAtStartOf(.day) // at 00:00:00 of the same day
let _ = dateA.dateAtStartOf(.month) // at 00:00:00 of the first day of the month
```
<a name="6"/>
### 6. Components Extraction
You can extract components directly from dates and it includes the right value expressed in date's region (the right timezone and set locale!).
```swift
// Create a date in a region, London but with the lcoale set to IT
let london = Region(calendar: .gregorian, zone: .europeLondon, locale: .italian)
let date = DateInRegion("2018-02-05 23:14:45", format: dateFormat, region: london)!
// You can extract any of the all available time units.
// VALUES ARE EXPRESSED IN THE REGION OF THE DATE (THE RIGHT TIMEZONE).
// (you can still get the UTC/absolute value by getting the inner's absoluteDate).
let _ = date.year // 2018
let _ = date.month // 2
let _ = date.monthNameDefault // 'Febbraio' as the locale is the to IT!
let _ = date.firstDayOfWeek // 5
let _ = date.weekdayNameShort // 'Lun' as locale is the to IT
// ... all components are supported: .year, .month, .day, .hour, .minute, .second,
// .monthName, .weekday, .nearestHour, .firstDayOfWeek. .quarter and so on...
```
<a name="7"/>
### 7. Switch between timezones/locale and calendars
You can easily convert any date to another region (aka another calendar, locale or timezone) easily!
New date contains all values expressed into the destination reason
```swift
// Conversion between timezones is easy using convertTo(region:) function
let rNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.english)
let rRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let dateInNY = "2017-01-01 00:00:00".toDate(region: rNY)
let dateInRome = dateInNY?.convertTo(region: rRome)!
print(dateInRome.toString()) // "dom gen 01 06:00:00 +0100 2017\n"
// You can also convert single region's attributes
let dateInIndia = dateInNY?.convertTo(timezone: Zones.indianChristmas, locale: Locales.nepaliIndia)
print("\(dateInIndia!.toString())") // "आइत जनवरी ०१ १२:००:०० +0700 २०१७\n"
```
<a name="8"/>
### 8. Date Formatting
Date formatting is easy, you can specify your own format, locale or use any of the provided ones.
```swift
// Date Formatting
let london = Region(calendar: .gregorian, zone: .europeLondon, locale: .english)
let date = ... // 2017-07-22T18:27:02+02:00 in london region
let _ = date.toDotNET() // /Date(1500740822000+0200)/
let _ = date.toISODate() // 2017-07-22T18:27:02+02:00
let _ = date.toFormat("dd MMM yyyy 'at' HH:mm") // "22 July 2017 at 18:27"
// You can also easily change locale when formatting a region
let _ = date.toFormat("dd MMM", locale: .italian) // "22 Luglio"
// Time Interval Formatting as Countdown
let interval: TimeInterval = (2.hours.timeInterval) + (34.minutes.timeInterval) + (5.seconds.timeInterval)
let _ = interval.toClock() // "2:34:05"
// Time Interval Formatting by Components
let _ = interval.toString {
$0.maximumUnitCount = 4
$0.allowedUnits = [.day, .hour, .minute]
$0.collapsesLargestUnit = true
$0.unitsStyle = .abbreviated
} // "2h 34m"
```
<a name="9"/>
### 9. Relative Date Formatting (fully customizable!)
Relative formatting is all new in SwiftDate; it supports 120+ languages with two different styles (`.default, .twitter`), 9 flavours (`.long, .longTime, .longConvenient, .short, .shortTime, .shortConvenient, .narrow, .tiny, .quantify`) and all of them are customizable as you need.
The extensible format allows you to provide your own translations and rules to override the default behaviour.
```swift
// Twitter Style
let _ = (Date() - 3.minutes).toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.english) // "3m"
let _ = (Date() - 6.minutes).toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian) // "6 min fa"
// Default Style
let _ = (now2 - 5.hours).toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.english) // "5 hours ago"
let y = (now2 - 40.minutes).toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.italian) // "45 minuti fa"
```
<a name="10"/>
### 10. Codable Support
Both `DateInRegion` and `Region` fully support the new Swift's `Codable` protocol. This mean you can safely encode/decode them:
```swift
// Encoding/Decoding a Region
let region = Region(calendar: Calendars.gregorian, zone: Zones.europeOslo, locale: Locales.english)
let encodedJSON = try JSONEncoder().encode(region)
let decodedRegion = try JSONDecoder().decode(Region.self, from: encodedJSON)
// Encoding/Decoding a DateInRegion
let date = DateInRegion("2015-09-24T13:20:55", region: region)
let encodedDate = try JSONEncoder().encode(date)
let decodedDate = try JSONDecoder().decode(DateInRegion.self, from: encodedDate)
```
<a name="11"/>
### 11. Time Periods
SwiftDate integrates the great Matthew York's [DateTools](https://github.com/MatthewYork/DateTools) module in order to support Time Periods.
See [Time Periods](/Documentation/12.Timer_Periods.md) section of the documentation.
================================================
FILE: Sources/SwiftDate/Date/Date+Compare.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
public extension Date {
// MARK: - Comparing Close
/// Decides whether a Date is "close by" another one passed in parameter,
/// where "Being close" is measured using a precision argument
/// which is initialized a 300 seconds, or 5 minutes.
///
/// - Parameters:
/// - refDate: reference date compare against to.
/// - precision: The precision of the comparison (default is 5 minutes, or 300 seconds).
/// - Returns: A boolean; true if close by, false otherwise.
func compareCloseTo(_ refDate: Date, precision: TimeInterval = 300) -> Bool {
(abs(timeIntervalSince(refDate)) < precision)
}
// MARK: - Extendend Compare
/// Compare the date with the rule specified in the `compareType` parameter.
///
/// - Parameter compareType: comparison type.
/// - Returns: `true` if comparison succeded, `false` otherwise
func compare(_ compareType: DateComparisonType) -> Bool {
inDefaultRegion().compare(compareType)
}
/// Returns a ComparisonResult value that indicates the ordering of two given dates based on
/// their components down to a given unit granularity.
///
/// - parameter date: date to compare.
/// - parameter granularity: The smallest unit that must, along with all larger units be less for the given dates
/// - returns: `ComparisonResult`
func compare(toDate refDate: Date, granularity: Calendar.Component) -> ComparisonResult {
inDefaultRegion().compare(toDate: refDate.inDefaultRegion(), granularity: granularity)
}
/// Compares whether the receiver is before/before equal `date` based on their components down to a given unit granularity.
///
/// - Parameters:
/// - refDate: reference date
/// - orEqual: `true` to also check for equality
/// - granularity: smallest unit that must, along with all larger units, be less for the given dates
/// - Returns: Boolean
func isBeforeDate(_ refDate: Date, orEqual: Bool = false, granularity: Calendar.Component) -> Bool {
inDefaultRegion().isBeforeDate(refDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity)
}
/// Compares whether the receiver is after `date` based on their components down to a given unit granularity.
///
/// - Parameters:
/// - refDate: reference date
/// - orEqual: `true` to also check for equality
/// - granularity: Smallest unit that must, along with all larger units, be greater for the given dates.
/// - Returns: Boolean
func isAfterDate(_ refDate: Date, orEqual: Bool = false, granularity: Calendar.Component) -> Bool {
inDefaultRegion().isAfterDate(refDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity)
}
/// Returns a value between 0.0 and 1.0 or nil, that is the position of current date between 2 other dates.
///
/// - Parameters:
/// - startDate: range upper bound date
/// - endDate: range lower bound date
/// - Returns: `nil` if current date is not between `startDate` and `endDate`. Otherwise returns position between `startDate` and `endDate`.
func positionInRange(date startDate: Date, and endDate: Date) -> Double? {
inDefaultRegion().positionInRange(date: startDate.inDefaultRegion(), and: endDate.inDefaultRegion())
}
/// Return true if receiver date is contained in the range specified by two dates.
///
/// - Parameters:
/// - startDate: range upper bound date
/// - endDate: range lower bound date
/// - orEqual: `true` to also check for equality on date and date2
/// - granularity: smallest unit that must, along with all larger units, be greater for the given dates.
/// - Returns: Boolean
func isInRange(date startDate: Date, and endDate: Date, orEqual: Bool = false, granularity: Calendar.Component = .nanosecond) -> Bool {
inDefaultRegion().isInRange(date: startDate.inDefaultRegion(), and: endDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity)
}
/// Compares equality of two given dates based on their components down to a given unit
/// granularity.
///
/// - parameter date: date to compare
/// - parameter granularity: The smallest unit that must, along with all larger units, be equal for the given
/// dates to be considered the same.
///
/// - returns: `true` if the dates are the same down to the given granularity, otherwise `false`
func isInside(date: Date, granularity: Calendar.Component) -> Bool {
(compare(toDate: date, granularity: granularity) == .orderedSame)
}
// MARK: - Date Earlier/Later
/// Return the earlier of two dates, between self and a given date.
///
/// - Parameter date: The date to compare to self
/// - Returns: The date that is earlier
func earlierDate(_ date: Date) -> Date {
timeIntervalSince(date) <= 0 ? self : date
}
/// Return the later of two dates, between self and a given date.
///
/// - Parameter date: The date to compare to self
/// - Returns: The date that is later
func laterDate(_ date: Date) -> Date {
timeIntervalSince(date) >= 0 ? self : date
}
}
extension Date {
/// Returns the difference in the calendar component given (like day, month or year)
/// with respect to the other date as a positive integer
public func difference(in component: Calendar.Component, from other: Date) -> Int? {
let (max, min) = orderDate(with: other)
let result = calendar.dateComponents([component], from: min, to: max)
return getValue(of: component, from: result)
}
/// Returns the differences in the calendar components given (like day, month and year)
/// with respect to the other date as dictionary with the calendar component as the key
/// and the diffrence as a positive integer as the value
public func differences(in components: Set<Calendar.Component>, from other: Date) -> [Calendar.Component: Int] {
let (max, min) = orderDate(with: other)
let differenceInDates = calendar.dateComponents(components, from: min, to: max)
var result = [Calendar.Component: Int]()
for component in components {
if let value = getValue(of: component, from: differenceInDates) {
result[component] = value
}
}
return result
}
private func getValue(of component: Calendar.Component, from dateComponents: DateComponents) -> Int? {
switch component {
case .era:
return dateComponents.era
case .year:
return dateComponents.year
case .month:
return dateComponents.month
case .day:
return dateComponents.day
case .hour:
return dateComponents.hour
case .minute:
return dateComponents.minute
case .second:
return dateComponents.second
case .weekday:
return dateComponents.weekday
case .weekdayOrdinal:
return dateComponents.weekdayOrdinal
case .quarter:
return dateComponents.quarter
case .weekOfMonth:
return dateComponents.weekOfMonth
case .weekOfYear:
return dateComponents.weekOfYear
case .yearForWeekOfYear:
return dateComponents.yearForWeekOfYear
case .nanosecond:
return dateComponents.nanosecond
case .calendar, .timeZone:
return nil
@unknown default:
assert(false, "unknown date component")
}
return nil
}
private func orderDate(with other: Date) -> (Date, Date) {
let first = self.timeIntervalSince1970
let second = other.timeIntervalSince1970
if first >= second {
return (self, other)
}
return (other, self)
}
}
================================================
FILE: Sources/SwiftDate/Date/Date+Components.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
public extension Date {
/// Indicates whether the month is a leap month.
var isLeapMonth: Bool {
inDefaultRegion().isLeapMonth
}
/// Indicates whether the year is a leap year.
var isLeapYear: Bool {
inDefaultRegion().isLeapYear
}
/// Julian day is the continuous count of days since the beginning of
/// the Julian Period used primarily by astronomers.
var julianDay: Double {
inDefaultRegion().julianDay
}
/// The Modified Julian Date (MJD) was introduced by the Smithsonian Astrophysical Observatory
/// in 1957 to record the orbit of Sputnik via an IBM 704 (36-bit machine)
/// and using only 18 bits until August 7, 2576.
var modifiedJulianDay: Double {
inDefaultRegion().modifiedJulianDay
}
/// Return elapsed time expressed in given components since the current receiver and a reference date.
///
/// - Parameters:
/// - refDate: reference date (`nil` to use current date in the same region of the receiver)
/// - component: time unit to extract.
/// - Returns: value
func getInterval(toDate: Date?, component: Calendar.Component) -> Int64 {
inDefaultRegion().getInterval(toDate: toDate?.inDefaultRegion(), component: component)
}
}
================================================
FILE: Sources/SwiftDate/Date/Date+Create.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
public extension Date {
/// Return the oldest date in given list.
///
/// - Parameter list: list of dates
/// - Returns: a tuple with the index of the oldest date and its instance.
static func oldestIn(list: [Date]) -> Date? {
guard list.count > 0 else { return nil }
guard list.count > 1 else { return list.first! }
return list.min(by: {
return $0 < $1
})
}
/// Return the newest date in given list.
///
/// - Parameter list: list of dates
/// - Returns: a tuple with the index of the oldest date and its instance.
static func newestIn(list: [Date]) -> Date? {
guard list.count > 0 else { return nil }
guard list.count > 1 else { return list.first! }
return list.max(by: {
return $0 < $1
})
}
/// Enumerate dates between two intervals by adding specified time components defined by a function and return an array of dates.
/// `startDate` interval will be the first item of the resulting array.
/// The last item of the array is evaluated automatically and maybe not equal to `endDate`.
///
/// - Parameters:
/// - start: starting date
/// - endDate: ending date
/// - increment: increment function. It get the last generated date and require a valida `DateComponents` instance which define the increment
/// - Returns: array of dates
static func enumerateDates(from startDate: Date, to endDate: Date, increment: ((Date) -> (DateComponents))) -> [Date] {
var dates: [Date] = []
var currentDate = startDate
while currentDate <= endDate {
dates.append(currentDate)
currentDate = (currentDate + increment(currentDate))
}
return dates
}
/// Enumerate dates between two intervals by adding specified time components and return an array of dates.
/// `startDate` interval will be the first item of the resulting array.
/// The last item of the array is evaluated automatically and maybe not equal to `endDate`.
///
/// - Parameters:
/// - start: starting date
/// - endDate: ending date
/// - increment: components to add
/// - Returns: array of dates
static func enumerateDates(from startDate: Date, to endDate: Date, increment: DateComponents) -> [Date] {
Date.enumerateDates(from: startDate, to: endDate, increment: { _ in
return increment
})
}
/// Round a given date time to the passed style (off|up|down).
///
/// - Parameter style: rounding mode.
/// - Returns: rounded date
func dateRoundedAt(at style: RoundDateMode) -> Date {
inDefaultRegion().dateRoundedAt(style).date
}
/// Returns a new DateInRegion that is initialized at the start of a specified unit of time.
///
/// - Parameter unit: time unit value.
/// - Returns: instance at the beginning of the time unit; `self` if fails.
func dateAtStartOf(_ unit: Calendar.Component) -> Date {
inDefaultRegion().dateAtStartOf(unit).date
}
/// Return a new DateInRegion that is initialized at the start of the specified components
/// executed in order.
///
/// - Parameter units: sequence of transformations as time unit components
/// - Returns: new date at the beginning of the passed components, intermediate results if fails.
func dateAtStartOf(_ units: [Calendar.Component]) -> Date {
units.reduce(self) { (currentDate, currentUnit) -> Date in
return currentDate.dateAtStartOf(currentUnit)
}
}
/// Returns a new Moment that is initialized at the end of a specified unit of time.
///
/// - parameter unit: A TimeUnit value.
///
/// - returns: A new Moment instance.
func dateAtEndOf(_ unit: Calendar.Component) -> Date {
inDefaultRegion().dateAtEndOf(unit).date
}
/// Return a new DateInRegion that is initialized at the end of the specified components
/// executed in order.
///
/// - Parameter units: sequence of transformations as time unit components
/// - Returns: new date at the end of the passed components, intermediate results if fails.
func dateAtEndOf(_ units: [Calendar.Component]) -> Date {
units.reduce(self) { (currentDate, currentUnit) -> Date in
return currentDate.dateAtEndOf(currentUnit)
}
}
/// Create a new date by altering specified components of the receiver.
///
/// - Parameter components: components to alter with their new values.
/// - Returns: new altered `DateInRegion` instance
func dateBySet(_ components: [Calendar.Component: Int]) -> Date? {
DateInRegion(self, region: SwiftDate.defaultRegion).dateBySet(components)?.date
}
/// Create a new date by altering specified time components.
///
/// - Parameters:
/// - hour: hour to set (`nil` to leave it unaltered)
/// - min: min to set (`nil` to leave it unaltered)
/// - secs: sec to set (`nil` to leave it unaltered)
/// - ms: milliseconds to set (`nil` to leave it unaltered)
/// - options: options for calculation
/// - Returns: new altered `DateInRegion` instance
func dateBySet(hour: Int?, min: Int?, secs: Int?, ms: Int? = nil, options: TimeCalculationOptions = TimeCalculationOptions()) -> Date? {
let srcDate = DateInRegion(self, region: SwiftDate.defaultRegion)
return srcDate.dateBySet(hour: hour, min: min, secs: secs, ms: ms, options: options)?.date
}
/// Creates a new instance by truncating the components
///
/// - Parameter components: components to truncate.
/// - Returns: new date with truncated components.
func dateTruncated(_ components: [Calendar.Component]) -> Date? {
DateInRegion(self, region: SwiftDate.defaultRegion).dateTruncated(at: components)?.date
}
/// Creates a new instance by truncating the components starting from given components down the granurality.
///
/// - Parameter component: The component to be truncated from.
/// - Returns: new date with truncated components.
func dateTruncated(from component: Calendar.Component) -> Date? {
DateInRegion(self, region: SwiftDate.defaultRegion).dateTruncated(from: component)?.date
}
/// Offset a date by n calendar components.
/// Note: This operation can be functionally chained.
///
/// - Parameters:
/// - count: value of the offset.
/// - component: component to offset.
/// - Returns: new altered date.
func dateByAdding(_ count: Int, _ component: Calendar.Component) -> DateInRegion {
DateInRegion(self, region: SwiftDate.defaultRegion).dateByAdding(count, component)
}
/// Return related date starting from the receiver attributes.
///
/// - Parameter type: related date to obtain.
/// - Returns: instance of the related date.
func dateAt(_ type: DateRelatedType) -> Date {
inDefaultRegion().dateAt(type).date
}
/// Create a new date at now and extract the related date using passed rule type.
///
/// - Parameter type: related date to obtain.
/// - Returns: instance of the related date.
static func nowAt(_ type: DateRelatedType) -> Date {
Date().dateAt(type)
}
/// Return the dates for a specific weekday inside given month of specified year.
/// Ie. get me all the saturdays of Feb 2018.
/// NOTE: Values are returned in order.
///
/// - Parameters:
/// - weekday: weekday target.
/// - month: month target.
/// - year: year target.
/// - region: region target, omit to use `SwiftDate.defaultRegion`
/// - Returns: Ordered list of the dates for given weekday into given month.
static func datesForWeekday(_ weekday: WeekDay, inMonth month: Int, ofYear year: Int,
region: Region = SwiftDate.defaultRegion) -> [Date] {
let fromDate = DateInRegion(Date(year: year, month: month, day: 1, hour: 0, minute: 0), region: region)
let toDate = fromDate.dateAt(.endOfMonth)
return DateInRegion.datesForWeekday(weekday, from: fromDate, to: toDate, region: region).map { $0.date }
}
/// Return the dates for a specific weekday inside a specified date range.
/// NOTE: Values are returned in order.
///
/// - Parameters:
/// - weekday: weekday target.
/// - startDate: from date of the range.
/// - endDate: to date of the range.
/// - region: region target, omit to use `SwiftDate.defaultRegion`
/// - Returns: Ordered list of the dates for given weekday in passed range.
static func datesForWeekday(_ weekday: WeekDay, from startDate: Date, to endDate: Date,
region: Region = SwiftDate.defaultRegion) -> [Date] {
let fromDate = DateInRegion(startDate, region: region)
let toDate = DateInRegion(endDate, region: region)
return DateInRegion.datesForWeekday(weekday, from: fromDate, to: toDate, region: region).map { $0.date }
}
/// Returns the date at the given week number and week day preserving smaller components (hour, minute, seconds)
///
/// For example: to get the third friday of next month
/// let today = DateInRegion()
/// let result = today.dateAt(weekdayOrdinal: 3, weekday: .friday, monthNumber: today.month + 1)
///
/// - Parameters:
/// - weekdayOrdinal: the week number (by set position in a recurrence rule)
/// - weekday: WeekDay
/// - monthNumber: a number from 1 to 12 representing the month, optional parameter
/// - yearNumber: a number representing the year, optional parameter
/// - Returns: new date created with the given parameters
func dateAt(weekdayOrdinal: Int, weekday: WeekDay, monthNumber: Int? = nil,
yearNumber: Int? = nil) -> Date {
let date = DateInRegion(self, region: region)
return date.dateAt(weekdayOrdinal: weekdayOrdinal, weekday: weekday, monthNumber: monthNumber, yearNumber: yearNumber).date
}
/// Returns the next weekday preserving smaller components (hour, minute, seconds)
///
/// - Parameters:
/// - weekday: weekday to get.
/// - region: region target, omit to use `SwiftDate.defaultRegion`
/// - Returns: `Date`
func nextWeekday(_ weekday: WeekDay, region: Region = SwiftDate.defaultRegion) -> Date {
let date = DateInRegion(self, region: region)
return date.nextWeekday(weekday).date
}
}
================================================
FILE: Sources/SwiftDate/Date/Date+Math.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
/// Subtracts two dates and returns the relative components from `lhs` to `rhs`.
/// Follows this mathematical pattern:
/// let difference = lhs - rhs
/// rhs + difference = lhs
public func - (lhs: Date, rhs: Date) -> DateComponents {
SwiftDate.defaultRegion.calendar.dateComponents(DateComponents.allComponentsSet, from: rhs, to: lhs)
}
/// Adds date components to a date and returns a new date.
public func + (lhs: Date, rhs: DateComponents) -> Date {
rhs.from(lhs)!
}
/// Adds date components to a date and returns a new date.
public func + (lhs: DateComponents, rhs: Date) -> Date {
(rhs + lhs)
}
/// Subtracts date components from a date and returns a new date.
public func - (lhs: Date, rhs: DateComponents) -> Date {
(lhs + (-rhs))
}
public func + (lhs: Date, rhs: TimeInterval) -> Date {
lhs.addingTimeInterval(rhs)
}
================================================
FILE: Sources/SwiftDate/Date/Date.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
internal enum AssociatedKeys: String {
case customDateFormatter = "SwiftDate.CustomDateFormatter"
}
extension Date: DateRepresentable {
/// Just return itself to be compliant with `DateRepresentable` protocol.
public var date: Date { return self }
/// For absolute Date object the default region is obtained from the global `defaultRegion` variable.
public var region: Region {
SwiftDate.defaultRegion
}
/// Assign a custom formatter if you need a special behaviour during formatting of the object.
/// Usually you will not need to do it, SwiftDate uses the local thread date formatter in order to
/// optimize the formatting process. By default is `nil`.
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)
}
}
/// Extract the date components.
public var dateComponents: DateComponents {
region.calendar.dateComponents(DateComponents.allComponentsSet, from: self)
}
/// Initialize a new date object from string expressed in given region.
///
/// - Parameters:
/// - string: date expressed as string.
/// - format: format of the date (`nil` uses provided list of auto formats patterns.
/// Pass it if you can in order to optimize the parse task).
/// - region: region in which the date is expressed. `nil` uses the `SwiftDate.defaultRegion`.
public init?(_ string: String, format: String? = nil, region: Region = SwiftDate.defaultRegion) {
guard let dateInRegion = DateInRegion(string, format: format, region: region) else { return nil }
self = dateInRegion.date
}
/// Initialize a new date from the number of seconds passed since Unix Epoch.
///
/// - Parameter interval: seconds
/// Initialize a new date from the number of seconds passed since Unix Epoch.
///
/// - Parameters:
/// - interval: seconds from Unix epoch time.
/// - region: region in which the date, `nil` uses the default region at UTC timezone
public init(seconds interval: TimeInterval, region: Region = Region.UTC) {
self = DateInRegion(seconds: interval, region: region).date
}
/// Initialize a new date corresponding to the number of milliseconds since the Unix Epoch.
///
/// - Parameters:
/// - interval: seconds since the Unix Epoch timestamp.
/// - region: region in which the date must be expressed, `nil` uses the default region at UTC timezone
public init(milliseconds interval: Int, region: Region = Region.UTC) {
self = DateInRegion(milliseconds: interval, region: region).date
}
/// Initialize a new date with the opportunity to configure single date components via builder pattern.
/// Date is therfore expressed in passed region (`DateComponents`'s `timezone`,`calendar` and `locale` are ignored
/// and overwritten by the region if not `nil`).
///
/// - Parameters:
/// - configuration: configuration callback
/// - region: region in which the date is expressed. Ignore to use `SwiftDate.defaultRegion`, `nil` to use `DateComponents` data.
public init?(components configuration: ((inout DateComponents) -> Void), region: Region? = SwiftDate.defaultRegion) {
guard let date = DateInRegion(components: configuration, region: region)?.date else { return nil }
self = date
}
/// Initialize a new date with given components.
///
/// - Parameters:
/// - components: components of the date.
/// - region: region in which the date is expressed.
/// Ignore to use `SwiftDate.defaultRegion`, `nil` to use `DateComponents` data.
public init?(components: DateComponents, region: Region?) {
guard let date = DateInRegion(components: components, region: region)?.date else { return nil }
self = date
}
/// Initialize a new date with given components.
public init(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int = 0, nanosecond: Int = 0, region: Region = SwiftDate.defaultRegion) {
var components = DateComponents()
components.year = year
components.month = month
components.day = day
components.hour = hour
components.minute = minute
components.second = second
components.nanosecond = nanosecond
components.timeZone = region.timeZone
components.calendar = region.calendar
self = region.calendar.date(from: components)!
}
/// Express given absolute date in the context of the default region.
///
/// - Returns: `DateInRegion`
public func inDefaultRegion() -> DateInRegion {
DateInRegion(self, region: SwiftDate.defaultRegion)
}
/// Express given absolute date in the context of passed region.
///
/// - Parameter region: destination region.
/// - Returns: `DateInRegion`
public func `in`(region: Region) -> DateInRegion {
DateInRegion(self, region: region)
}
/// Return a date in the distant past.
///
/// - Returns: Date instance.
public static func past() -> Date {
Date.distantPast
}
/// Return a date in the distant future.
///
/// - Returns: Date instance.
public static func future() -> Date {
Date.distantFuture
}
}
================================================
FILE: Sources/SwiftDate/DateInRegion/DateInRegion+Compare.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
// MARK: - Comparing DateInRegion
public func == (lhs: DateInRegion, rhs: DateInRegion) -> Bool {
(lhs.date.timeIntervalSince1970 == rhs.date.timeIntervalSince1970)
}
public func <= (lhs: DateInRegion, rhs: DateInRegion) -> Bool {
let result = lhs.date.compare(rhs.date)
return (result == .orderedAscending || result == .orderedSame)
}
public func >= (lhs: DateInRegion, rhs: DateInRegion) -> Bool {
let result = lhs.date.compare(rhs.date)
return (result == .orderedDescending || result == .orderedSame)
}
public func < (lhs: DateInRegion, rhs: DateInRegion) -> Bool {
lhs.date.compare(rhs.date) == .orderedAscending
}
public func > (lhs: DateInRegion, rhs: DateInRegion) -> Bool {
lhs.date.compare(rhs.date) == .orderedDescending
}
// The type of comparison to do against today's date or with the suplied date.
///
/// - isToday: hecks if date today.
/// - isTomorrow: Checks if date is tomorrow.
/// - isYesterday: Checks if date is yesterday.
/// - isSameDay: Compares date days
/// - isThisWeek: Checks if date is in this week.
/// - isNextWeek: Checks if date is in next week.
/// - isLastWeek: Checks if date is in last week.
/// - isSameWeek: Compares date weeks
/// - isThisMonth: Checks if date is in this month.
/// - isNextMonth: Checks if date is in next month.
/// - isLastMonth: Checks if date is in last month.
/// - isSameMonth: Compares date months
/// - isThisYear: Checks if date is in this year.
/// - isNextYear: Checks if date is in next year.
/// - isLastYear: Checks if date is in last year.
/// - isSameYear: Compare date years
/// - isInTheFuture: Checks if it's a future date
/// - isInThePast: Checks if the date has passed
/// - isEarlier: Checks if earlier than date
/// - isLater: Checks if later than date
/// - isWeekday: Checks if it's a weekday
/// - isWeekend: Checks if it's a weekend
/// - isInDST: Indicates whether the represented date uses daylight saving time.
/// - isMorning: Return true if date is in the morning (>=5 - <12)
/// - isAfternoon: Return true if date is in the afternoon (>=12 - <17)
/// - isEvening: Return true if date is in the morning (>=17 - <21)
/// - isNight: Return true if date is in the morning (>=21 - <5)
public enum DateComparisonType {
// Days
case isToday
case isTomorrow
case isYesterday
case isSameDay(_ : DateRepresentable)
// Weeks
case isThisWeek
case isNextWeek
case isLastWeek
case isSameWeek(_: DateRepresentable)
// Months
case isThisMonth
case isNextMonth
case isLastMonth
case isSameMonth(_: DateRepresentable)
// Years
case isThisYear
case isNextYear
case isLastYear
case isSameYear(_: DateRepresentable)
// Relative Time
case isInTheFuture
case isInThePast
case isEarlier(than: DateRepresentable)
case isLater(than: DateRepresentable)
case isWeekday
case isWeekend
// Day time
case isMorning
case isAfternoon
case isEvening
case isNight
// TZ
case isInDST
}
public extension DateInRegion {
/// Decides whether a DATE is "close by" another one passed in parameter,
/// where "Being close" is measured using a precision argument
/// which is initialized a 300 seconds, or 5 minutes.
///
/// - Parameters:
/// - refDate: reference date compare against to.
/// - precision: The precision of the comparison (default is 5 minutes, or 300 seconds).
/// - Returns: A boolean; true if close by, false otherwise.
func compareCloseTo(_ refDate: DateInRegion, precision: TimeInterval = 300) -> Bool {
(abs(date.timeIntervalSince(refDate.date)) <= precision)
}
/// Compare the date with the rule specified in the `compareType` parameter.
///
/// - Parameter compareType: comparison type.
/// - Returns: `true` if comparison succeded, `false` otherwise
func compare(_ compareType: DateComparisonType) -> Bool {
switch compareType {
case .isToday:
return compare(.isSameDay(region.nowInThisRegion()))
case .isTomorrow:
let tomorrow = DateInRegion(region: region).dateByAdding(1, .day)
return compare(.isSameDay(tomorrow))
case .isYesterday:
let yesterday = DateInRegion(region: region).dateByAdding(-1, .day)
return compare(.isSameDay(yesterday))
case .isSameDay(let refDate):
return calendar.isDate(date, inSameDayAs: refDate.date)
case .isThisWeek:
return compare(.isSameWeek(region.nowInThisRegion()))
case .isNextWeek:
let nextWeek = region.nowInThisRegion().dateByAdding(1, .weekOfYear)
return compare(.isSameWeek(nextWeek))
case .isLastWeek:
let lastWeek = region.nowInThisRegion().dateByAdding(-1, .weekOfYear)
return compare(.isSameWeek(lastWeek))
case .isSameWeek(let refDate):
guard weekOfYear == refDate.weekOfYear else {
return false
}
// Ensure time interval is under 1 week
return (abs(date.timeIntervalSince(refDate.date)) < 1.weeks.timeInterval)
case .isThisMonth:
return compare(.isSameMonth(region.nowInThisRegion()))
case .isNextMonth:
let nextMonth = region.nowInThisRegion().dateByAdding(1, .month)
return compare(.isSameMonth(nextMonth))
case .isLastMonth:
let lastMonth = region.nowInThisRegion().dateByAdding(-1, .month)
return compare(.isSameMonth(lastMonth))
case .isSameMonth(let refDate):
return (year == refDate.year) && (month == refDate.month)
case .isThisYear:
return compare(.isSameYear(region.nowInThisRegion()))
case .isNextYear:
let nextYear = region.nowInThisRegion().dateByAdding(1, .year)
return compare(.isSameYear(nextYear))
case .isLastYear:
let lastYear = region.nowInThisRegion().dateByAdding(-1, .year)
return compare(.isSameYear(lastYear))
case .isSameYear(let refDate):
return (year == refDate.year)
case .isInTheFuture:
return compare(.isLater(than: region.nowInThisRegion()))
case .isInThePast:
return compare(.isEarlier(than: region.nowInThisRegion()))
case .isEarlier(let refDate):
return ((date as NSDate).earlierDate(refDate.date) == date)
case .isLater(let refDate):
return ((date as NSDate).laterDate(refDate.date) == date)
case .isWeekday:
return !compare(.isWeekend)
case .isWeekend:
let range = calendar.maximumRange(of: Calendar.Component.weekday)!
return (weekday == range.lowerBound || weekday == range.upperBound - range.lowerBound)
case .isInDST:
return region.timeZone.isDaylightSavingTime(for: date)
case .isMorning:
return (hour >= 5 && hour < 12)
case .isAfternoon:
return (hour >= 12 && hour < 17)
case .isEvening:
return (hour >= 17 && hour < 21)
case .isNight:
return (hour >= 21 || hour < 5)
}
}
/// Returns a ComparisonResult value that indicates the ordering of two given dates based on
/// their components down to a given unit granularity.
///
/// - parameter date: date to compare.
/// - parameter granularity: The smallest unit that must, along with all larger units
/// - returns: `ComparisonResult`
func compare(toDate refDate: DateInRegion, granularity: Calendar.Component) -> ComparisonResult {
switch granularity {
case .nanosecond:
// There is a possible rounding error using Calendar to compare two dates below the minute granularity
// So we've added this trick and use standard Date compare which return correct results in this case
// https://github.com/malcommac/SwiftDate/issues/346
return date.compare(refDate.date)
default:
return region.calendar.compare(date, to: refDate.date, toGranularity: granularity)
}
}
/// Compares whether the receiver is before/before equal `date` based on their components down to a given unit granularity.
///
/// - Parameters:
/// - refDate: reference date
/// - orEqual: `true` to also check for equality
/// - granularity: smallest unit that must, along with all larger units, be less for the given dates
/// - Returns: Boolean
func isBeforeDate(_ date: DateInRegion, orEqual: Bool = false, granularity: Calendar.Component) -> Bool {
let result = compare(toDate: date, granularity: granularity)
return (orEqual ? (result == .orderedSame || result == .orderedAscending) : result == .orderedAscending)
}
/// Compares whether the receiver is after `date` based on their components down to a given unit granularity.
///
/// - Parameters:
/// - refDate: reference date
/// - orEqual: `true` to also check for equality
/// - granularity: Smallest unit that must, along with all larger units, be greater for the given dates.
/// - Returns: Boolean
func isAfterDate(_ refDate: DateInRegion, orEqual: Bool = false, granularity: Calendar.Component) -> Bool {
let result = compare(toDate: refDate, granularity: granularity)
return (orEqual ? (result == .orderedSame || result == .orderedDescending) : result == .orderedDescending)
}
/// Compares equality of two given dates based on their components down to a given unit
/// granularity.
///
/// - parameter date: date to compare
/// - parameter granularity: The smallest unit that must, along with all larger units, be equal for the given
/// dates to be considered the same.
///
/// - returns: `true` if the dates are the same down to the given granularity, otherwise `false`
func isInside(date: DateInRegion, granularity: Calendar.Component) -> Bool {
(compare(toDate: date, granularity: granularity) == .orderedSame)
}
/// Returns a value between 0.0 and 1.0 or nil, that is the position of current date between 2 other dates.
///
/// - Parameters:
/// - startDate: range upper bound date
/// - endDate: range lower bound date
/// - Returns: `nil` if current date is not between `startDate` and `endDate`. Otherwise returns position between `startDate` and `endDate`.
func positionInRange(date startDate: DateInRegion, and endDate: DateInRegion) -> Double? {
let diffCurrentDateAndStartDate = self - startDate
guard diffCurrentDateAndStartDate >= 0 else {
return nil
}
let diffEndDateAndStartDate = endDate - startDate
guard diffEndDateAndStartDate > 0, diffCurrentDateAndStartDate <= diffEndDateAndStartDate else {
return nil
}
return diffCurrentDateAndStartDate / diffEndDateAndStartDate
}
/// Return `true` if receiver data is contained in the range specified by two dates.
///
/// - Parameters:
/// - startDate: range upper bound date
/// - endDate: range lower bound date
/// - orEqual: `true` to also check for equality on date and date2, default is `true`
/// - granularity: smallest unit that must, along with all larger units, be greater
/// - Returns: Boolean
func isInRange(date startDate: DateInRegion, and endDate: DateInRegion, orEqual: Bool = true, granularity: Calendar.Component = .nanosecond) -> Bool {
isAfterDate(startDate, orEqual: orEqual, granularity: granularity) && isBeforeDate(endDate, orEqual: orEqual, granularity: granularity)
}
// MARK: - Date Earlier/Later
/// Return the earlier of two dates, between self and a given date.
///
/// - Parameter date: The date to compare to self
/// - Returns: The date that is earlier
func earlierDate(_ date: DateInRegion) -> DateInRegion {
self.date.timeIntervalSince(date.date) <= 0 ? self : date
}
/// Return the later of two dates, between self and a given date.
///
/// - Parameter date: The date to compare to self
/// - Returns: The date that is later
func laterDate(_ date: DateInRegion) -> DateInRegion {
self.date.timeIntervalSince(date.date) >= 0 ? self : date
}
/// Returns the difference in the calendar component given (like day, month or year)
/// with respect to the other date as a positive integer
func difference(in component: Calendar.Component, from other: DateInRegion) -> Int? {
self.date.difference(in: component, from: other.date)
}
/// Returns the differences in the calendar components given (like day, month and year)
/// with respect to the other date as dictionary with the calendar component as the key
/// and the diffrence as a positive integer as the value
func differences(in components: Set<Calendar.Component>, from other: DateInRegion) -> [Calendar.Component: Int] {
self.date.differences(in: components, from: other.date)
}
}
================================================
FILE: Sources/SwiftDate/DateInRegion/DateInRegion+Components.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
public extension DateInRegion {
/// Indicates whether the month is a leap month.
var isLeapMonth: Bool {
let calendar = region.calendar
// Library function for leap contains a bug for Gregorian calendars, implemented workaround
if calendar.identifier == Calendar.Identifier.gregorian && year > 1582 {
guard let range: Range<Int> = calendar.range(of: .day, in: .month, for: date) else {
return false
}
return ((range.upperBound - range.lowerBound) == 29)
}
// For other calendars:
return calendar.dateComponents([.day, .month, .year], from: date).isLeapMonth!
}
/// Indicates whether the year is a leap year.
var isLeapYear: Bool {
let calendar = region.calendar
// Library function for leap contains a bug for Gregorian calendars, implemented workaround
if calendar.identifier == Calendar.Identifier.gregorian {
var newComponents = dateComponents
newComponents.month = 2
newComponents.day = 10
let testDate = DateInRegion(components: newComponents, region: region)
return testDate!.isLeapMonth
} else if calendar.identifier == Calendar.Identifier.chinese {
/// There are 12 or 13 months in each year and 29 or 30 days in each month.
/// A 13-month year is a leap year, which meaning more than 376 days is a leap year.
return ( dateAtStartOf(.year).toUnit(.day, to: dateAtEndOf(.year)) > 375 )
}
// For other calendars:
return calendar.dateComponents([.day, .month, .year], from: date).isLeapMonth!
}
/// Julian day is the continuous count of days since the beginning of
/// the Julian Period used primarily by astronomers.
var julianDay: Double {
let destRegion = Region(calendar: Calendars.gregorian, zone: Zones.gmt, locale: Locales.english)
let utc = convertTo(region: destRegion)
let year = Double(utc.year)
let month = Double(utc.month)
let day = Double(utc.day)
let hour = Double(utc.hour) + Double(utc.minute) / 60.0 + (Double(utc.second) + Double(utc.nanosecond) / 1e9) / 3600.0
var jd = 367.0 * year - floor( 7.0 * ( year + floor((month + 9.0) / 12.0)) / 4.0 )
jd -= floor( 3.0 * (floor( (year + (month - 9.0) / 7.0) / 100.0 ) + 1.0) / 4.0 )
jd += floor(275.0 * month / 9.0) + day + 1_721_028.5 + hour / 24.0
return jd
}
/// The Modified Julian Date (MJD) was introduced by the Smithsonian Astrophysical Observatory
/// in 1957 to record the orbit of Sputnik via an IBM 704 (36-bit machine)
/// and using only 18 bits until August 7, 2576.
var modifiedJulianDay: Double {
julianDay - 2_400_000.5
}
/// Return elapsed time expressed in given components since the current receiver and a reference date.
/// Time is evaluated with the fixed measumerent of each unity.
///
/// - Parameters:
/// - refDate: reference date (`nil` to use current date in the same region of the receiver)
/// - component: time unit to extract.
/// - Returns: value
func getInterval(toDate: DateInRegion?, component: Calendar.Component) -> Int64 {
let refDate = (toDate ?? region.nowInThisRegion())
switch component {
case .year:
let end = calendar.ordinality(of: .year, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .year, in: .era, for: date)
return Int64(end! - start!)
case .month:
let end = calendar.ordinality(of: .month, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .month, in: .era, for: date)
return Int64(end! - start!)
case .day:
let end = calendar.ordinality(of: .day, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .day, in: .era, for: date)
return Int64(end! - start!)
case .hour:
let interval = refDate.date.timeIntervalSince(date)
return Int64(interval / 1.hours.timeInterval)
case .minute:
let interval = refDate.date.timeIntervalSince(date)
return Int64(interval / 1.minutes.timeInterval)
case .second:
return Int64(refDate.date.timeIntervalSince(date))
case .weekday:
let end = calendar.ordinality(of: .weekday, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekday, in: .era, for: date)
return Int64(end! - start!)
case .weekdayOrdinal:
let end = calendar.ordinality(of: .weekdayOrdinal, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekdayOrdinal, in: .era, for: date)
return Int64(end! - start!)
case .weekOfYear:
let end = calendar.ordinality(of: .weekOfYear, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekOfYear, in: .era, for: date)
return Int64(end! - start!)
default:
debugPrint("Passed component cannot be used to extract values using interval() function between two dates. Returning 0.")
return 0
}
}
/// The interval between the receiver and the another parameter.
/// If the receiver is earlier than anotherDate, the return value is negative.
/// If anotherDate is nil, the results are undefined.
///
/// - Parameter date: The date with which to compare the receiver.
/// - Returns: time interval between two dates
func timeIntervalSince(_ date: DateInRegion) -> TimeInterval {
self.date.timeIntervalSince(date.date)
}
/// Extract DateComponents from the difference between two dates.
///
/// - Parameter rhs: date to compare
/// - Returns: components
func componentsTo(_ rhs: DateInRegion) -> DateComponents {
calendar.dateComponents(DateComponents.allComponentsSet, from: rhs.date, to: date)
}
/// Returns the difference between two dates (`date - self`) expressed as date components.
///
/// - Parameters:
/// - date: reference date as initial date (left operand)
/// - components: components to extract, `nil` to use default `DateComponents.allComponentsSet`
/// - Returns: extracted date components
func componentsSince(_ date: DateInRegion, components: [Calendar.Component]? = nil) -> DateComponents {
if date.calendar != calendar {
debugPrint("Date has different calendar, results maybe wrong")
}
let cmps = (components != nil ? Calendar.Component.toSet(components!) : DateComponents.allComponentsSet)
return date.calendar.dateComponents(cmps, from: date.date, to: self.date)
}
}
================================================
FILE: Sources/SwiftDate/DateInRegion/DateInRegion+Create.swift
================================================
//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import Foundation
public extension DateInRegion {
// MARK: - Random Date Generator
/// Generate a sequence of dates between a range.
///
/// - Parameters:
/// - count: number of dates to generate.
/// - initial: lower date bound.
/// - final: upper date bound.
/// - region: region of the dates.
/// - Returns: array of dates
static func randomDates(count: Int, between initial: DateInRegion, and final: DateInRegion,
region: Region = SwiftDate.defaultRegion) -> [DateInRegion] {
var list: [DateInRegion] = []
for _ in 0..<count {
list.append(DateInRegion.randomDate(between: initial, and: final, region: region))
}
return list
}
/// Return a date between now and a specified amount days ealier.
///
/// - Parameters:
/// - days: days range
/// - region: destination region, `nil` to use the default region
/// - Returns: random date
static func randomDate(withinDaysBeforeToday days: Int,
region: Region = SwiftDate.defaultRegion) -> DateInRegion {
let today = DateInRegion(region: region)
let earliest = DateInRegion(today.date.addingTimeInterval(TimeInterval(-days * 24 * 60 * 60)), region: region)
return DateInRegion.randomDate(between: earliest, and: today)
}
/// Generate a random date in given region.
///
/// - Parameter region: destination region, `nil` to use the default region
/// - Returns: random date
static func randomDate(region: Region = SwiftDate.defaultRegion) -> DateInRegion {
let randomTime = TimeInterval(UInt32.random(in: UInt32.min..<UInt32.max))
let absoluteDate = Date(timeIntervalSince1970: randomTime)
return DateInRegion(absoluteDate, region: region)
}
/// Generate a random date between two intervals.
///
/// - Parameters:
/// - initial: lower bound date
/// - final: upper bound date
/// - region: destination region, `nil` to use the default region
/// - Returns: random Date
static func randomDate(between initial: DateInRegion, and final: DateInRegion,
region: Region = SwiftDate.defaultRegion) -> DateInRegion {
let interval = final.timeIntervalSince(initial)
let randomInterval = TimeInterval(UInt32.random(in: UInt32.min..<UInt32(interval)))
return initial.addingTimeInterval(randomInterval)
}
/// Return the oldest date in given list (timezone is ignored, comparison uses absolute date).
///
/// - Parameter list: list of dates
/// - Returns: a tuple with the index of the oldest date and its instance.
static func oldestIn(list: [DateInRegion]) -> DateInRegion? {
guard list.count > 0 else { return nil }
guard list.count > 1 else { return list.first! }
return list.min(by: {
return $0 < $1
})
}
/// Sort date by oldest, with the oldest date on top.
///
/// - Parameter list: list to sort
/// - Returns: sorted array
static func sortedByOldest(list: [DateInRegion]) -> [DateInRegion] {
list.sorted(by: { $0.date.compare($1.date) == .orderedAscending })
}
/// Sort date by newest, with the newest date on top.
///
/// - Parameter list: list to sort
/// - Returns: sorted array
static func sortedByNewest(list: [DateInRegion]) -> [DateInRegion] {
list.sorted(by: { $0.date.compare($1.date) == .orderedDescending })
}
/// Return the newest date in given list (timezone is ignored, comparison uses absolute date).
///
/// - Parameter list: list of dates
/// - Returns: a tuple with the index of the newest date and its instance.
static func newestIn(list: [DateInRegion]) -> DateInRegion? {
guard list.count > 0 else { return nil }
guard list.count > 1 else { return list.first! }
return list.max(by: {
return $0 < $1
})
}
/// Enumerate dates between two intervals by adding specified time components and return an array of dates.
/// `startDate` interval will be the first item of the resulting array.
/// The last item of the array is evaluated automatically and maybe not equal to `endDate`.
///
/// - Parameters:
/// - start: starting date
/// - endDate: ending date
/// - increment: components to add
/// - Returns: array of dates
static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: DateComponents) -> [DateInRegion] {
DateInRegion.enumerateDates(from: startDate, to: endDate, increment: { _ in
return increment
})
}
/// Enumerate
gitextract_nwg7vbkr/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .sourcery/ │ └── LinuxMain.stencil ├── .swiftlint.yml ├── .swiftpm/ │ └── xcode/ │ └── package.xcworkspace/ │ └── contents.xcworkspacedata ├── .travis.yml ├── Configs/ │ ├── SwiftDate.plist │ └── SwiftDateTests.plist ├── Documentation/ │ ├── 0.Informations.md │ ├── 1.Introduction.md │ ├── 10.Upgrading_SwiftDate4.md │ ├── 11.Related_Projects.md │ ├── 12.Timer_Periods.md │ ├── 2.Date_Parsing.md │ ├── 3.Manipulate_Date.md │ ├── 4.Compare_Dates.md │ ├── 5.Date_Formatting.md │ ├── 6.TimeInterval_Formatting.md │ ├── 7.Format_UnicodeTable.md │ ├── 8.Customize_ColloquialFormatter.md │ ├── 9.ColloquialSupportedLanguages.md │ └── Index.md ├── LICENSE ├── Package.swift ├── Playgrounds/ │ └── SwiftDate.playground/ │ ├── Contents.o │ ├── Pages/ │ │ ├── Compare Dates.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Date Formatting.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Date Manipulation.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ ├── Date Parsing.xcplaygroundpage/ │ │ │ └── Contents.swift │ │ └── Introduction.xcplaygroundpage/ │ │ └── Contents.swift │ └── contents.xcplayground ├── README.md ├── Sources/ │ └── SwiftDate/ │ ├── Date/ │ │ ├── Date+Compare.swift │ │ ├── Date+Components.swift │ │ ├── Date+Create.swift │ │ ├── Date+Math.swift │ │ └── Date.swift │ ├── DateInRegion/ │ │ ├── DateInRegion+Compare.swift │ │ ├── DateInRegion+Components.swift │ │ ├── DateInRegion+Create.swift │ │ ├── DateInRegion+Math.swift │ │ ├── DateInRegion.swift │ │ └── Region.swift │ ├── DateRepresentable.swift │ ├── Formatters/ │ │ ├── DotNetParserFormatter.swift │ │ ├── Formatter+Protocols.swift │ │ ├── ISOFormatter.swift │ │ └── ISOParser.swift │ ├── Foundation+Extras/ │ │ ├── DateComponents+Extras.swift │ │ ├── Int+DateComponents.swift │ │ ├── String+Parser.swift │ │ └── TimeInterval+Formatter.swift │ ├── Supports/ │ │ ├── AssociatedValues.swift │ │ ├── Calendars.swift │ │ ├── Commons.swift │ │ ├── Locales.swift │ │ ├── TimeStructures.swift │ │ └── Zones.swift │ ├── SwiftDate.swift │ └── TimePeriod/ │ ├── Groups/ │ │ ├── TimePeriodChain.swift │ │ ├── TimePeriodCollection.swift │ │ └── TimePeriodGroup.swift │ ├── TimePeriod+Support.swift │ ├── TimePeriod.swift │ └── TimePeriodProtocol.swift ├── SwiftDate.podspec ├── SwiftDate.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata/ │ └── xcschemes/ │ ├── SwiftDate-iOS Tests.xcscheme │ ├── SwiftDate-iOS.xcscheme │ ├── SwiftDate-macOS.xcscheme │ ├── SwiftDate-tvOS.xcscheme │ └── SwiftDate-watchOS.xcscheme ├── TestApplication/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── Tests/ │ ├── LinuxMain.swift │ └── SwiftDateTests/ │ ├── TestDataStructures.swift │ ├── TestDate.swift │ ├── TestDateInRegion+Compare.swift │ ├── TestDateInRegion+Components.swift │ ├── TestDateInRegion+Create.swift │ ├── TestDateInRegion+Math.swift │ ├── TestDateInRegion.swift │ ├── TestFormatters.swift │ ├── TestRegion.swift │ └── TestSwiftDate.swift └── generateLinuxTests.sh
Condensed preview — 93 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (718K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 104,
"preview": "# These are supported funding model platforms\n\ncustom: https://www.paypal.com/paypalme2/danielemargutti\n"
},
{
"path": ".gitignore",
"chars": 1487,
"preview": "*~\n.DS_Store\n\n# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swi"
},
{
"path": ".sourcery/LinuxMain.stencil",
"chars": 678,
"preview": "@testable import SwiftDateTests\nimport XCTest\n\n// swiftlint:disable line_length file_length\n\n{% for type in types.classe"
},
{
"path": ".swiftlint.yml",
"chars": 2077,
"preview": "disabled_rules: # rule identifiers to exclude from running\n - line_length\n - function_body_length\n - cyclomatic_compl"
},
{
"path": ".swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": ".travis.yml",
"chars": 1072,
"preview": "language: objective-c\nosx_image: xcode10.2\n\nenv:\n global:\n - LC_CTYPE=en_US.UTF-8\n - LANG=en_US.UTF-8\n - PROJECT=Swi"
},
{
"path": "Configs/SwiftDate.plist",
"chars": 934,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Configs/SwiftDateTests.plist",
"chars": 733,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Documentation/0.Informations.md",
"chars": 5452,
"preview": "\n\n<a name=\"introduction\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Next Chapter**: Intr"
},
{
"path": "Documentation/1.Introduction.md",
"chars": 9149,
"preview": "\n\n<a name=\"introduction\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Informa"
},
{
"path": "Documentation/10.Upgrading_SwiftDate4.md",
"chars": 5304,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n\n### → [Searching for old SwiftDate"
},
{
"path": "Documentation/11.Related_Projects.md",
"chars": 1081,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n\n## Related Projects\nI'm also worki"
},
{
"path": "Documentation/12.Timer_Periods.md",
"chars": 7902,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n\n## Time Periods\n\n*NOTE: The follow"
},
{
"path": "Documentation/2.Date_Parsing.md",
"chars": 8463,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Introduction t"
},
{
"path": "Documentation/3.Manipulate_Date.md",
"chars": 31104,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Date Parsing]("
},
{
"path": "Documentation/4.Compare_Dates.md",
"chars": 6121,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Manipulate & D"
},
{
"path": "Documentation/5.Date_Formatting.md",
"chars": 8977,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Parsing Dates]"
},
{
"path": "Documentation/6.TimeInterval_Formatting.md",
"chars": 4440,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n- [**Prev Chapter**: Date Formattin"
},
{
"path": "Documentation/7.Format_UnicodeTable.md",
"chars": 1694,
"preview": "\n\n- [**Index**: Table Of Contents](#Index.md)\n\n## Unicode Table for Date Formats\n \nThe following tab"
},
{
"path": "Documentation/8.Customize_ColloquialFormatter.md",
"chars": 139,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n\n## Customize Colloquial Formatter\n"
},
{
"path": "Documentation/9.ColloquialSupportedLanguages.md",
"chars": 1877,
"preview": "\n\n<a name=\"index\"/>\n\n- [**Index**: Table Of Contents](#Index.md)\n\n## Colloquial Formatter Supported "
},
{
"path": "Documentation/Index.md",
"chars": 4639,
"preview": "\n\n## Documentation\n\n- Reference Version: **6.3.0**\n- Last Update: **Nov 2020**\n\nThe following docume"
},
{
"path": "LICENSE",
"chars": 1084,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Daniele Margutti\n\nPermission is hereby granted, free of charge, to any person "
},
{
"path": "Package.swift",
"chars": 754,
"preview": "// swift-tools-version:5.5\nimport PackageDescription\n\nlet package = Package(\n name: \"SwiftDate\",\n defaultLocalizat"
},
{
"path": "Playgrounds/SwiftDate.playground/Pages/Compare Dates.xcplaygroundpage/Contents.swift",
"chars": 35,
"preview": "import Foundation\nimport SwiftDate\n"
},
{
"path": "Playgrounds/SwiftDate.playground/Pages/Date Formatting.xcplaygroundpage/Contents.swift",
"chars": 3373,
"preview": "import Foundation\nimport SwiftDate\n/*:\n## 5.0 - Format Custom Style\nIf you need to format a `Date` or `DateInRegion` in "
},
{
"path": "Playgrounds/SwiftDate.playground/Pages/Date Manipulation.xcplaygroundpage/Contents.swift",
"chars": 8577,
"preview": "import Foundation\nimport SwiftDate\n\n/*:\n## 3.0 - Add & Subtract Time Units from Date\n\nSwiftDate allows to use numbers to"
},
{
"path": "Playgrounds/SwiftDate.playground/Pages/Date Parsing.xcplaygroundpage/Contents.swift",
"chars": 4929,
"preview": "import Foundation\nimport SwiftDate\n\n/*:\nParsing dates is pretty straighforward in SwiftDate; library can parse strings w"
},
{
"path": "Playgrounds/SwiftDate.playground/Pages/Introduction.xcplaygroundpage/Contents.swift",
"chars": 7785,
"preview": "import Foundation\nimport SwiftDate\n/*:\n## 1.0 - Dates & Cocoa\n\nGenerally when you talk about dates you are brought to th"
},
{
"path": "Playgrounds/SwiftDate.playground/contents.xcplayground",
"chars": 382,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<playground version='6.0' target-platform='ios' display-mode='ra"
},
{
"path": "README.md",
"chars": 13446,
"preview": "<p align=\"center\" >\n <img src=\"banner.png\" width=300px alt=\"SwiftDate\" title=\"SwiftDate\">\n</p>\n\n<p align=\"center\"><stro"
},
{
"path": "Sources/SwiftDate/Date/Date+Compare.swift",
"chars": 8054,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Date/Date+Components.swift",
"chars": 1571,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Date/Date+Create.swift",
"chars": 10289,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Date/Date+Math.swift",
"chars": 1218,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Date/Date.swift",
"chars": 5564,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/DateInRegion+Compare.swift",
"chars": 12489,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/DateInRegion+Components.swift",
"chars": 6477,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/DateInRegion+Create.swift",
"chars": 26272,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/DateInRegion+Math.swift",
"chars": 2827,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/DateInRegion.swift",
"chars": 7355,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateInRegion/Region.swift",
"chars": 5736,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/DateRepresentable.swift",
"chars": 18865,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Formatters/DotNetParserFormatter.swift",
"chars": 2432,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Formatters/Formatter+Protocols.swift",
"chars": 6542,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Formatters/ISOFormatter.swift",
"chars": 5976,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Formatters/ISOParser.swift",
"chars": 25920,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Foundation+Extras/DateComponents+Extras.swift",
"chars": 11178,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Foundation+Extras/Int+DateComponents.swift",
"chars": 2363,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Foundation+Extras/String+Parser.swift",
"chars": 5002,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Foundation+Extras/TimeInterval+Formatter.swift",
"chars": 6748,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/AssociatedValues.swift",
"chars": 2074,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/Calendars.swift",
"chars": 3366,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/Commons.swift",
"chars": 11137,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/Locales.swift",
"chars": 22612,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/TimeStructures.swift",
"chars": 5348,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/Supports/Zones.swift",
"chars": 18778,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/SwiftDate.swift",
"chars": 1251,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/Groups/TimePeriodChain.swift",
"chars": 4793,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/Groups/TimePeriodCollection.swift",
"chars": 8270,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/Groups/TimePeriodGroup.swift",
"chars": 4140,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/TimePeriod+Support.swift",
"chars": 1544,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/TimePeriod.swift",
"chars": 6777,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Sources/SwiftDate/TimePeriod/TimePeriodProtocol.swift",
"chars": 12027,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "SwiftDate.podspec",
"chars": 767,
"preview": "Pod::Spec.new do |s|\n s.name = \"SwiftDate\"\n s.version = \"7.0.0\"\n s.summary = \"The best way to deal "
},
{
"path": "SwiftDate.xcodeproj/project.pbxproj",
"chars": 104954,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 47;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "SwiftDate.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "SwiftDate.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "SwiftDate.xcodeproj/xcshareddata/xcschemes/SwiftDate-iOS Tests.xcscheme",
"chars": 1829,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1220\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "SwiftDate.xcodeproj/xcshareddata/xcschemes/SwiftDate-iOS.xcscheme",
"chars": 3627,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1220\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "SwiftDate.xcodeproj/xcshareddata/xcschemes/SwiftDate-macOS.xcscheme",
"chars": 3639,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1220\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "SwiftDate.xcodeproj/xcshareddata/xcschemes/SwiftDate-tvOS.xcscheme",
"chars": 3633,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1220\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "SwiftDate.xcodeproj/xcshareddata/xcschemes/SwiftDate-watchOS.xcscheme",
"chars": 2820,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1220\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "TestApplication/AppDelegate.swift",
"chars": 2089,
"preview": "//\n// AppDelegate.swift\n// TestApplication\n//\n// Created by Daniele Margutti on 06/06/2018.\n// Copyright © 2018 Swif"
},
{
"path": "TestApplication/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1590,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"size\" : \"20x20\",\n \"scale\" : \"2x\"\n },\n {\n \"idiom\""
},
{
"path": "TestApplication/Assets.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "TestApplication/Base.lproj/LaunchScreen.storyboard",
"chars": 1844,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "TestApplication/Base.lproj/Main.storyboard",
"chars": 1830,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "TestApplication/Info.plist",
"chars": 1463,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "TestApplication/ViewController.swift",
"chars": 351,
"preview": "//\n// ViewController.swift\n// TestApplication\n//\n// Created by Daniele Margutti on 06/06/2018.\n// Copyright © 2018 S"
},
{
"path": "Tests/LinuxMain.swift",
"chars": 5717,
"preview": "// Generated using Sourcery 0.15.0 — https://github.com/krzysztofzablocki/Sourcery\n// DO NOT EDIT\n\n@testable import Swif"
},
{
"path": "Tests/SwiftDateTests/TestDataStructures.swift",
"chars": 3715,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestDate.swift",
"chars": 1999,
"preview": "//\n// TestDate.swift\n// SwiftDate-macOS Tests\n//\n// Created by Imthath M on 30/05/19.\n// Copyright © 2019 SwiftDate."
},
{
"path": "Tests/SwiftDateTests/TestDateInRegion+Compare.swift",
"chars": 19222,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestDateInRegion+Components.swift",
"chars": 14872,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestDateInRegion+Create.swift",
"chars": 11904,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestDateInRegion+Math.swift",
"chars": 5859,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestDateInRegion.swift",
"chars": 23198,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestFormatters.swift",
"chars": 20844,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestRegion.swift",
"chars": 7685,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "Tests/SwiftDateTests/TestSwiftDate.swift",
"chars": 1599,
"preview": "//\n// SwiftDate\n// Parse, validate, manipulate, and display dates, time and timezones in Swift\n//\n// Created by Danie"
},
{
"path": "generateLinuxTests.sh",
"chars": 169,
"preview": "sourcery --sources Tests --templates .sourcery/LinuxMain.stencil --output .sourcery --force-parse generated\nmv .sourcery"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the malcommac/SwiftDate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 93 files (651.0 KB), approximately 192.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.