Giter Club home page Giter Club logo

shdateformatter's Introduction

Twitter Follow Github Current Release Swift Package Manager Compatible Swift Versions Platforms Xcode 13+ Codebeat License Donate

SHDateFormatter

This framework supports encoding as well as decoding many different date and time format. At the moment the following are supported (see SHDateFormat):

  • shortWeekday => Sun, Mon, Tue, ...
  • longWeekday => Sunday, Monday, Tuesday, ...
  • shortMonth => Jan, Feb, ...
  • longMonth => January, February, ...
  • shortTimeNoDate => 12:00 PM, 1:00 AM
  • shortTimeMediumDate => Jan 1, 2000 at 12:00 AM
  • noTimeShortDateNoYear => 12/31
  • noTimeShortDate => 12/02/2018
  • noTimeLongDate => December 2, 2018
  • noTimeRelativeDate => Today, Tomorrow, ...
  • ISO8601 => "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'" / "yyyy'-'MM'-'dd'T'HH':'mm':'ss'+01:00'" / etc.

The ISO8601 format implements the standard for working with dates and times between client and server applications. A good explanation if this standard and how to work with it can be found in the Apple Documenation or on Ole Begemanns Blog.

Additionally the following convenience functions are available on the SHDateFormatter sharedInstance which will return default values if the input date is nil. They allow to easily deal with optionals and can provide additional functionality like giving you a relative date instead of e.g. "yyyy/MM/dd":

  • localizedTimeStringFromDate(_:) - will return --:-- per default
  • ... to be continued ...

Installation

In Xcode open your target list and select your project. Click the tab Swift Packages and there the small + icon. Enter the URL of this repository, select the version you want to install - usually the preset is okay - and confirm.

I dropped CocoaPods support in favor of SPM from version 1.7.0. Please integrate the package via SPM (see above).

Carthage

I dropped Carthage support in favor of SPM from version 1.7.0. Please integrate the package via SPM (see above).

Code Documentation

The code documentation is generated and hosted by Swift Package Index (powered by DocC)

Release

To release this Swift package the following steps have to be taken:

  • Create a new branch release-x.y.z
  • Run bash <(curl -H -s https://raw.githubusercontent.com/Blackjacx/Scripts/master/frameworks/bootstrap.sh) to update to the latest shared development files
  • Run bundle update to update all Ruby gems
  • Commit all changes, make a PR and merge it to develop
  • Run bundle exec fastlane release framework:"SHDateFormatter" version:"x.y.z" to release the new version
  • Post the following on Twitter
SHDateFormatter release x.y.z 🎉

▸ 🚀  SHDateFormatter (x.y.z) successfully published
▸ 📅  September 2nd
▸ 🌎  https://swiftpackageindex.com/Blackjacx/SHDateFormatter
▸ 🌎  https://github.com/Blackjacx/SHDateFormatter/releases/latest
▸ 👍  Tell your friends!

#SPM #Date #Formatter #tested

Contribution

  • If you found a bug, please open an issue.
  • If you have a feature request, please open an issue.
  • If you want to contribute, please submit a pull request.

Author

Stefan Herold • 🐦 @Blackjacxxx

Contributors

Thanks to all of you who are part of this:

License

SHDateFormatter is available under the MIT license. See the LICENSE file for more info.

shdateformatter's People

Contributors

blackjacx avatar turyunus avatar

Stargazers

Ahmed Shendy avatar Alex Sherbakov avatar Rakhim Abdullayev avatar Lukas Würzburger avatar

Watchers

 avatar James Cloos avatar Ahmed Shendy avatar  avatar

shdateformatter's Issues

Enable custom patterns

Actual behaviour

Right now one project that uses SHDateFormatter has a format that defines a special order of date components in SHDateformatter+Extensions.swift, like:

let weekdayString = string(from: date, format: .shortWeekday, timeZone: timeZone)
let dateString = string(from: date, format: .noTimeShortDateNoYear, timeZone: timeZone)
let timeString = string(from: date, format: .shortTimeNoDate, timeZone: timeZone)
return weekdayString + ", " + dateString + " - " + timeString

This doesn't work in other languages like Arabic.

Expected behaviour

For these cases Apple has defined a method that uses template date format strings and changes (reorders) them for the specified language. To make that work we have to either add a custom date format to SHDateFormatter which takes a string OR put that special date format in the enum and handle the custom format in SHDateFormatter, like:

case .shortWeekday, .longWeekday, .shortMonth, .longMonth, .longYear, .shortYear, .noTimeShortDateNoYear,
.shortYearMonth, .longYearMonth:
SHDateFormatter.formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: format.rawValue,
options: 0,
locale: locale)

I guess the latter is the better option!

Don't forget to update the tests…

Use Parametrised Format Enum For Styles Using Apple's Format Style

When using Apple's style like in

case .noTimeLongDate:
            SHDateFormatter.formatter.timeStyle = .none
            SHDateFormatter.formatter.dateStyle = .long

provide the style via enum parameters. This ensures that the number of assignments will get lower which will decrease the code complexity. This also reduces the number of cases since we only need one case for all of the current format cases in SHDateFormatter.

Refactor tests

Expected behaviour

Tests should be designed in a way that makes it easy to add a new language/date. It can then also serve as a nice and compact documentation:

typealias ExpectationsData = (date: Date, expectationsForLocale: [(locale: Locale, expectations: [(format: SHDateFormat, expectedResult: String)])])
let ignorePlaceholder = "IGNORE"

let expectations: [ExpectationsData] = [
    (Date(), [
        (deDELocale, [
            (SHDateFormat.shortWeekday, "Sa"),
            (SHDateFormat.longWeekday, "Samstag"),
            (SHDateFormat.shortMonth, "Jan"),
            (SHDateFormat.longMonth, "Januar"),
            (SHDateFormat.shortYear, "00"),
            (SHDateFormat.longYear, "2000"),
            (SHDateFormat.shortTimeNoDate, "00:00"),
            (SHDateFormat.shortTimeMediumDate, "01.01.2000, 00:00"),
            (SHDateFormat.noTimeShortDateNoYear, "1.1."),
            (SHDateFormat.noTimeShortDate, "01.01.00"),
            (SHDateFormat.noTimeLongDate, "1. Januar 2000"),
            (SHDateFormat.ISO8601, "2000-01-01T00:00:00Z")
        ])
    ])
]

func testExpectedStringFormatting() {

    for item in expectations {
        for expectationForLocale in item.expectationsForLocale {
            for expectation in expectationForLocale.expectations {
                let result = SHDateFormatter.shared.string(from: item.date,
                                                           format: expectation.format,
                                                           locale: expectationForLocale.locale,
                                                           timeZone: gmtZone)
                XCTAssertEqual(result, expectation.expectedResult,
                               message: "\(date) - \(expectationForLocale.locale.identifier) - \(expectation.format) expected \"\(expectation.expectedResult)\" but got \"\(result)\"")
            }
        }
    }
}

In the end we'll only have one test function that should describe the problem as exact as possible so we can identify the problematic item from the test data array.

Implement String Interpolation Extensions to prevent users from using the long formatter syntax

Expected behaviour

The is a super user friendly way to format dates into strings. We need one extension for each Date format (or one with 2 parameters, date and format):

extension String.StringInterpolation {
    mutating func appendInterpolation(date: Date, format: SHDateFormat) {
       let string = SHDateFormatter.shared.string(from: date, format: format)
       appendLiteral(string)
    }
}
// "Your time is \(date: Date(), format: .shortTimeNoDate)" <-- automatically compatible to new formats

OR

extension String.StringInterpolation {
    mutating func appendInterpolation(shortTimeNoDate date: Date) {
       let string = SHDateFormatter.shared.string(from: date, format: .shortTimeNoDate)
       appendLiteral(string)
    }
}
// "Your time is \(shortTimeNoDate: Date())" <-- a bit shorter but needs a new extension for each new format

[...]

Actual behaviour

Right now, to formt a string, a user has to use:

let time = SHDateFormatter.shared.string(from: Date(), format: .shortTimeNoDate)
let string = "Now is \(time)"

Date formatter is using the language to determine 24h/12h format

Current Behaviour

If the phone is using 12h time format with English UK, the date will be formatted with 24h format.
If the phone is using 24h time format with English US, the date will be formatted with 12h format.

Steps to reproduce

  1. Put your phone in English US with 24h format
  2. Try to book a ride

Expected behaviour

The date formatting should not be based on the lang but on the time format settings.

The following code behaves correctly:

import UIKit
import SHDateFormatter

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let sIsoDate = "2019-07-31T12:09:23Z"

        let calendar = Calendar(identifier: .gregorian)
        let formatter = DateFormatter()
        formatter.calendar = calendar
        formatter.formatterBehavior = .default
        formatter.doesRelativeDateFormatting = false
        formatter.timeStyle = .none
        formatter.dateStyle = .none
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)

        formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"

        let dateNoMs = formatter.date(from: sIsoDate)

        formatter.timeStyle = .short
        formatter.locale = Locale.current
        print(formatter.string(from: dateNoMs!))

        print("Is 24h: \(DateFormatter.is24h)")

        let isoDate = SHDateFormatter.shared.date(from: sIsoDate, format: .ISO8601)!
        let converted = SHDateFormatter.shared.string(from: isoDate, format: .shortTimeNoDate, locale: Locale.current)
        print(converted)

    }
}

extension DateFormatter {
    static var is24h: Bool {
        return DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: .current)?.contains("a") == false
    }
}

It seems that the locale isn't reset properly to Locale.current. Right now it is reset to Locale(identifier: Locale.preferredLanguages[0]) which might be wrong.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.